diff options
Diffstat (limited to 'bsps/arm/gumstix')
-rw-r--r-- | bsps/arm/gumstix/net/rtl8019.c | 1196 | ||||
-rw-r--r-- | bsps/arm/gumstix/net/wd80x3.h | 303 |
2 files changed, 1499 insertions, 0 deletions
diff --git a/bsps/arm/gumstix/net/rtl8019.c b/bsps/arm/gumstix/net/rtl8019.c new file mode 100644 index 0000000000..995835c689 --- /dev/null +++ b/bsps/arm/gumstix/net/rtl8019.c @@ -0,0 +1,1196 @@ +/* + *By Yang Xi <hiyangxi@gmail.com>. + *RTL8019 ethernet driver for Gumstix on SKYEYE simulator + *Based on NE2000 driver for pc386 bsp + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ + +#include <bsp.h> +#include <bsp/irq.h> +#include "wd80x3.h" + +#include <stdio.h> +#include <assert.h> +#include <errno.h> + +#include <rtems/error.h> +#include <rtems/rtems_bsdnet.h> + +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <net/if.h> +#include <net/if_arp.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + + +/* Define this to force byte-wide data transfers with the NIC. This + is needed for boards like the TS-1325 386EX PC, which support only + an 8-bit PC/104 bus. Undefine this on a normal PC.*/ + +#define NE2000_BYTE_TRANSFERS + +/* Define this to print debugging messages with printk. */ + +/*#define DEBUG_NE2000 + #define DEBUG_NE*/ + +/* We expect to be able to read a complete packet into an mbuf. */ + +#if (MCLBYTES < 1520) +# error "Driver must have MCLBYTES >= 1520" +#endif + +/* The 8390 macro definitions in wd80x3.h expect RO to be defined. */ +#define RO 0 + +/* Minimum size of Ethernet packet. */ +#define ET_MINLEN 60 + +/* The number of NE2000 devices supported by this driver. */ + +#define NNEDRIVER 1 + +/* RTEMS event number used by the interrupt handler to signal the + driver task. This must not be any of the events used by the + network task synchronization. */ +#define INTERRUPT_EVENT RTEMS_EVENT_1 + +/* RTEMS event number used to start the transmit daemon. This must + not be the same as INTERRUPT_EVENT. */ +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + +/* Interrupts we want to handle from the device. */ + +#define NE_INTERRUPTS \ + (MSK_PRX | MSK_PTX | MSK_RXE | MSK_TXE | MSK_OVW | MSK_CNT) + +/* The size of a page in device memory. */ + +#define NE_PAGE_SIZE (256) + +/* The first page address in device memory. */ + +#define NE_START_PAGE (0x40) + +/* The last page address, plus 1. */ + +#define NE_STOP_PAGE (0x80) + +/* The number of pages used for a single transmit buffer. This is + 1536 bytes, enough for a full size packet. */ + +#define NE_TX_PAGES (6) + +/* The number of transmit buffers. We use two, so we can load one + packet while the other is being sent. */ + +#define NE_TX_BUFS (2) + +/* We use the first pages in memory as transmit buffers, and the + remaining ones as receive buffers. */ + +#define NE_FIRST_TX_PAGE (NE_START_PAGE) + +#define NE_FIRST_RX_PAGE (NE_FIRST_TX_PAGE + NE_TX_PAGES * NE_TX_BUFS) + +/* Data we store for each NE2000 device. */ + +struct ne_softc { + /* The bsdnet information structure. */ + struct arpcom arpcom; + + /* The interrupt request number. */ + unsigned int irno; + /* The base IO port number. */ + unsigned int port; + + /* Whether we accept broadcasts. */ + int accept_broadcasts; + + /* The thread ID of the transmit task. */ + rtems_id tx_daemon_tid; + /* The thread ID of the receive task. */ + rtems_id rx_daemon_tid; + + /* Whether we use byte-transfers with the device. */ + bool byte_transfers; + + /* The number of memory buffers which the transmit daemon has loaded + with data to be sent, but which have not yet been completely + sent. */ + int inuse; + /* The index of the next available transmit memory buffer. */ + int nextavail; + /* The index of the next transmit buffer to send. */ + int nextsend; + /* Nonzero if the device is currently transmitting a packet. */ + int transmitting; + /* The length of the data stored in each transmit buffer. */ + int sendlen[NE_TX_BUFS]; + + /* Set if we have a packet overrun while receiving. */ + int overrun; + /* Set if we should resend after an overrun. */ + int resend; + + /* Statistics. */ + struct { + /* Number of packets received. */ + unsigned long rx_packets; + /* Number of packets sent. */ + unsigned long tx_packets; + /* Number of interrupts. */ + unsigned long interrupts; + /* Number of receive acknowledgements. */ + unsigned long rx_acks; + /* Number of transmit acknowledgements. */ + unsigned long tx_acks; + /* Number of packet overruns. */ + unsigned long overruns; + /* Number of frame errors. */ + unsigned long rx_frame_errors; + /* Number of CRC errors. */ + unsigned long rx_crc_errors; + /* Number of missed packets. */ + unsigned long rx_missed_errors; + } stats; +}; + +/* The list of NE2000 devices on this system. */ + +static struct ne_softc ne_softc[NNEDRIVER]; + +/* + * receive ring descriptor + * + * The National Semiconductor DS8390 Network interface controller uses + * the following receive ring headers. The way this works is that the + * memory on the interface card is chopped up into 256 bytes blocks. + * A contiguous portion of those blocks are marked for receive packets + * by setting start and end block #'s in the NIC. For each packet that + * is put into the receive ring, one of these headers (4 bytes each) is + * tacked onto the front. The first byte is a copy of the receiver status + * register at the time the packet was received. + */ +struct ne_ring +{ + unsigned char rsr; /* receiver status */ + unsigned char next; /* pointer to next packet */ + unsigned short count; /* bytes in packet (length + 4) */ +}; + +/* Forward declarations to avoid warnings */ + +static void ne_init_irq_handler (struct ne_softc *sc); +static void ne_stop (struct ne_softc *sc); +static void ne_stop_hardware (struct ne_softc *sc); +static void ne_init (void *arg); +static void ne_init_hardware (struct ne_softc *sc); + +static void ne_reset(struct ne_softc *sc); +#ifdef DEBUG_NE +static void ne_dump(struct ne_softc *sc); +#endif + +/* Read data from an NE2000 device. Read LEN bytes at ADDR, storing + them into P. */ + +static void +ne_read_data (struct ne_softc *sc, int addr, int len, unsigned char *p) +{ + unsigned int port = sc->port; + unsigned int dport = port + DATAPORT; + outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STA); + outport_byte (port + RBCR0, len); + outport_byte (port + RBCR1, len >> 8); + outport_byte (port + RSAR0, addr); + outport_byte (port + RSAR1, addr >> 8); + outport_byte (port + CMDR, MSK_PG0 | MSK_RRE | MSK_STA); + + if (sc->byte_transfers) + { + unsigned char d; + while (len > 0) + { + inport_byte(dport, d); + *p++ = d; + len--; + } + } + else /* word transfers */ + { + unsigned short d; + while (len > 1) + { + inport_word(dport, d); + *p++ = d; + *p++ = d >> 8; + len -= 2; + } + if (len) + { + inport_word(dport, d); + *p++ = d; + } + } + + outport_byte (port + ISR, MSK_RDC); +} + +/* Handle the current NE2000 status. This is called when the device + signals an interrupt. It is also called at other times while + NE2000 interrupts have been disabled. */ + +static void +ne_check_status (struct ne_softc *sc, int from_irq_handler) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + unsigned int port = sc->port; + unsigned char status; + + /* It seems that we need to use a loop here, because if the NE2000 + signals an interrupt because packet transmission is complete, and + then receives a packet while interrupts are disabled, it seems to + sometimes fail to signal the interrupt for the received packet + when interrupts are reenabled. (Based on the behaviour of the + Realtek 8019AS chip). */ + + /* int count = 0; */ + while (1) + { + inport_byte (port + ISR, status); +#ifdef DEBUG_NE2000 + printk ("NE2000 status 0x%x\n", status); +#endif + if (status == 0) + break; + + /* ack */ + outport_byte (port + ISR, status); + + /* Check for incoming packet overwrite. */ + if (status & MSK_OVW) + { + ifp->if_timer = 0; +#ifdef DEBUG_NE + printk("^\n"); +#endif + ++sc->stats.overruns; + ne_reset(sc); + /* Reenable device interrupts. */ + if (from_irq_handler) + outport_byte(port + IMR, NE_INTERRUPTS); + return; + } + + /* Check for transmitted packet. The transmit daemon may now be + able to send another packet to the device. */ + if ((status & (MSK_PTX | MSK_TXE)) != 0) + { + ifp->if_timer = 0; + ++sc->stats.tx_acks; + --sc->inuse; + sc->transmitting = 0; + if (sc->inuse > 0 || (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) != 0) + rtems_bsdnet_event_send (sc->tx_daemon_tid, START_TRANSMIT_EVENT); + } + + /* Check for received packet. */ + if ((status & (MSK_PRX | MSK_RXE)) != 0) + { + ++sc->stats.rx_acks; + rtems_bsdnet_event_send (sc->rx_daemon_tid, INTERRUPT_EVENT); + } + + /* Check for counter change. */ + if ((status & MSK_CNT) != 0) + { + unsigned char add; + inport_byte (port + CNTR0, add); + sc->stats.rx_frame_errors += add; + inport_byte (port + CNTR1, add); + sc->stats.rx_crc_errors += add; + inport_byte (port + CNTR2, add); + sc->stats.rx_missed_errors += add; + } + + break; + } + + outport_byte (port + CMDR, MSK_PG0 | MSK_STA | MSK_RD2); + +} + +/* Handle an NE2000 interrupt. */ + +static void +ne_interrupt_handler (void *arg) +{ + struct ne_softc *sc = arg; + + if (sc == NULL) + return; + + ++sc->stats.interrupts; + +#ifdef DEBUG_NE + printk("!"); +#endif + ne_check_status(sc, 1); +} + +/* Turn NE2000 interrupts on. */ + +static void +ne_interrupt_on (struct ne_softc *sc) +{ +#ifdef DEBUG_NE + printk ("ne_interrupt_on()\n"); +#endif + if (sc != NULL) + outport_byte (sc->port + IMR, NE_INTERRUPTS); +} + +/* Initialize the NE2000 hardware. */ + +static void +ne_init_hardware (struct ne_softc *sc) +{ + unsigned int port = sc->port; + int i; + +#ifdef DEBUG_NE2000 + printk ("ne_init_hardware()\n"); +#endif + + /* Initialize registers. */ + + /* Set interface for page 0, Remote DMA complete, Stopped */ + outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP); + + /* Set FIFO threshold to 8, No auto-init Remote DMA, byte order=80x86 */ + /* byte-wide DMA xfers */ + if (sc->byte_transfers) + outport_byte (port + DCR, MSK_FT10 | MSK_BMS); + /* word-wide DMA xfers */ + else + outport_byte (port + DCR, MSK_FT10 | MSK_BMS | MSK_WTS); + + /* Clear Remote Byte Count Registers */ + outport_byte (port + RBCR0, 0); + outport_byte (port + RBCR1, 0); + + /* For the moment, don't store incoming packets in memory. */ + outport_byte (port + RCR, MSK_MON); + + /* Place NIC in internal loopback mode */ + outport_byte (port + TCR, MSK_LOOP); + + /* Initialize transmit/receive (ring-buffer) Page Start */ + outport_byte (port + TPSR, NE_FIRST_TX_PAGE); + outport_byte (port + PSTART, NE_FIRST_RX_PAGE); + + /* Initialize Receiver (ring-buffer) Page Stop and Boundary */ + outport_byte (port + PSTOP, NE_STOP_PAGE); + outport_byte (port + BNRY, NE_STOP_PAGE - 1); + + /* Clear all interrupts */ + outport_byte (port + ISR, 0xff); + /* Disable all interrupts */ + outport_byte (port + IMR, 0); + + /* Program Command Register for page 1 */ + outport_byte (port + CMDR, MSK_PG1 | MSK_RD2 | MSK_STP); + + /* Set the Ethernet hardware address. */ + for (i = 0; i < ETHER_ADDR_LEN; ++i) + outport_byte (port + PAR + i, sc->arpcom.ac_enaddr[i]); + + /* Set Current Page pointer to next_packet */ + outport_byte (port + CURR, NE_FIRST_RX_PAGE); + + /* Clear the multicast address. */ + for (i = 0; i < MARsize; ++i) + outport_byte (port + MAR + i, 0); + + /* Set page 0 registers */ + outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP); + + /* accept broadcast */ + outport_byte (port + RCR, (sc->accept_broadcasts ? MSK_AB : 0)); + + /* Start interface */ + outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STA); + + /* Take interface out of loopback */ + outport_byte (port + TCR, 0); +} + +/* Set up interrupts. +*/ +static void +ne_init_irq_handler(struct ne_softc *sc) +{ + rtems_status_code status = RTEMS_SUCCESSFUL; + +#ifdef DEBUG_NE + printk("ne_init_irq_handler(%d)\n", sc->irno); +#endif + + status = rtems_interrupt_handler_install( + sc->irno, + "RTL8019", + RTEMS_INTERRUPT_UNIQUE, + ne_interrupt_handler, + sc + ); + assert(status == RTEMS_SUCCESSFUL); + ne_interrupt_on(sc); +} + +/* The NE2000 packet receive daemon. This task is started when the + NE2000 driver is initialized. */ + +#ifdef DEBUG_NE +static int ccc = 0; /* experinent! */ +#endif + +static void +ne_rx_daemon (void *arg) +{ + struct ne_softc *sc = (struct ne_softc *) arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + unsigned int port = sc->port; + + while (1) + { + rtems_event_set events; + + /* Wait for the interrupt handler to tell us that there is a + packet ready to receive. */ + rtems_bsdnet_event_receive (INTERRUPT_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + + /* Don't let the device interrupt us now. */ + outport_byte (port + IMR, 0); + + while (1) + { + unsigned char startpage, currpage; + unsigned short len; + unsigned char next, cnt1, cnt2; + struct mbuf *m = NULL; + unsigned char *p; + int startaddr; + int toend; + struct ether_header *eh; + struct ne_ring hdr; /* ring buffer header */ + int reset; + + inport_byte (port + BNRY, startpage); + + outport_byte (port + CMDR, MSK_PG1 | MSK_RD2); + inport_byte (port + CURR, currpage); + outport_byte (port + CMDR, MSK_PG0 | MSK_RD2); + + ++startpage; + if (startpage >= NE_STOP_PAGE) + startpage = NE_FIRST_RX_PAGE; + + if (startpage == currpage) + break; + +#ifdef DEBUG_NE2000 + printk ("ne_rx_daemon: start page %x; current page %x\n", + startpage, currpage); +#endif + + reset = 0; + + /* Read the buffer header */ + startaddr = startpage * NE_PAGE_SIZE; + ne_read_data(sc, startaddr, sizeof(hdr), (unsigned char *)&hdr); + next = hdr.next; + + if (next >= NE_STOP_PAGE) + next = NE_FIRST_RX_PAGE; + + /* check packet length */ + len = hdr.count; + if (currpage < startpage) + cnt1 = currpage + (NE_STOP_PAGE - NE_FIRST_RX_PAGE) - startpage; + else + cnt1 = currpage - startpage; + cnt2 = len / NE_PAGE_SIZE; + if (len % NE_PAGE_SIZE) + cnt2++; + if (cnt1 < cnt2) + { +#ifdef DEBUG_NE + printk("(%x<%x:%x)\n", cnt1, cnt2, len); +/* + printk("start page 0x%x; current page 0x%x\n", + startpage, currpage); + printk("cnt1 < cnt2 (0x%x, 0x%x); len 0x%x\n", + cnt1, cnt2, len); +*/ +#endif + reset = 1; + } + if (len > (ETHER_MAX_LEN - ETHER_CRC_LEN + sizeof(struct ne_ring)) || + len < (ETHER_MIN_LEN - ETHER_CRC_LEN + sizeof(struct ne_ring)) || + len > MCLBYTES) + { +#ifdef DEBUG_NE + printk("(%x)", len); + + printk("start page 0x%x; current page 0x%x\n", + startpage, currpage); + printk("len out of range: 0x%x\n", len); + printk("stat: 0x%x, next: 0x%x\n", hdr.rsr, hdr.next); +#endif + reset = 1; + } +#ifdef DEBUG_NE + if (++ccc == 100) + { ccc = 0; reset = 1; + printk("T"); + } +#endif + + /* reset interface */ + if (reset) + { + printk("Reset in RX\n"); + ne_reset(sc); + goto Next; + } + + /* The first four bytes of the length are the buffer header + * Just decrease them by 2 since in ARM, we have to make sure + * 4bytes memory access align on 4bytes + */ + len -= 2; + startaddr += 2; + + MGETHDR (m, M_WAIT, MT_DATA); + MCLGET (m, M_WAIT); + + if (m == NULL) + panic ("ne_rx_daemon"); + + m->m_pkthdr.rcvif = ifp; + m->m_nextpkt = 0; + + p = mtod (m, unsigned char *); + m->m_len = m->m_pkthdr.len = len - (sizeof(struct ether_header) + 2); + + toend = NE_STOP_PAGE * NE_PAGE_SIZE - startaddr; + if (toend < len) + { + ne_read_data (sc, startaddr, toend, p); + p += toend; + len -= toend; + startaddr = NE_FIRST_RX_PAGE * NE_PAGE_SIZE; + } + + if (len > 0) + ne_read_data (sc, startaddr, len, p); + + m->m_data +=2; + eh = mtod(m, struct ether_header *); + m->m_data += sizeof (struct ether_header); + +#ifdef DEBUG_NE + /*printk("[r%d]", hdr.count - sizeof(hdr));*/ + printk("<\n"); +#endif + ether_input (ifp, eh, m); + ++sc->stats.rx_packets; + outport_byte (port + BNRY, next - 1); + } + + if (sc->overrun) { + outport_byte (port + ISR, MSK_OVW); + outport_byte (port + TCR, 0); + if (sc->resend) + outport_byte (port + CMDR, MSK_PG0 | MSK_TXP | MSK_RD2 | MSK_STA); + sc->resend = 0; + sc->overrun = 0; + } + + Next: + /* Reenable device interrupts. */ + outport_byte (port + IMR, NE_INTERRUPTS); + } +} + +/* Load an NE2000 packet onto the device. */ + +static void +ne_loadpacket (struct ne_softc *sc, struct mbuf *m) +{ + unsigned int port = sc->port; + unsigned int dport = port + DATAPORT; + struct mbuf *mhold = m; + int leftover; + unsigned char leftover_data; + int timeout; + int send_cnt = 0; + +#ifdef DEBUG_NE2000 + printk ("Uploading NE2000 packet\n"); +#endif + + /* Reset remote DMA complete flag. */ + outport_byte (port + ISR, MSK_RDC); + + /* Write out the count. */ + outport_byte (port + RBCR0, m->m_pkthdr.len); + outport_byte (port + RBCR1, m->m_pkthdr.len >> 8); + + sc->sendlen[sc->nextavail] = m->m_pkthdr.len; + + /* Tell the device which address we want to write to. */ + outport_byte (port + RSAR0, 0); + outport_byte (port + RSAR1, + NE_FIRST_TX_PAGE + (sc->nextavail * NE_TX_PAGES)); + + /* Set up the write. */ + outport_byte (port + CMDR, MSK_PG0 | MSK_RWR | MSK_STA); + + /* Transfer the mbuf chain to device memory. NE2000 devices require + that the data be transferred as words, so we need to handle odd + length mbufs. Never occurs if we force byte transfers. */ + + leftover = 0; + leftover_data = '\0'; + + for (; m != NULL; m = m->m_next) { + int len; + unsigned char *data; + + len = m->m_len; + if (len == 0) + continue; + + data = mtod (m, unsigned char *); + + if (leftover) { + unsigned char next; + + /* Data left over from previous mbuf in chain. */ + next = *data++; + --len; + outport_word (dport, leftover_data | (next << 8)); + send_cnt += 2; + leftover = 0; + } + + /* If using byte transfers, len always ends up as zero so + there are no leftovers. */ + + if (sc->byte_transfers) + while (len > 0) { + outport_byte (dport, *data++); + len--; + } + else + while (len > 1) { + outport_word (dport, data[0] | (data[1] << 8)); + data += 2; + len -= 2; + send_cnt += 2; + } + + if (len > 0) + { + leftover = 1; + leftover_data = *data++; + } + } + + if (leftover) + { + outport_word (dport, leftover_data); + send_cnt += 2; + } + +#ifdef DEBUG_NE + /* printk("{l%d|%d}", send_cnt, sc->nextavail); */ + printk("v"); +#endif + m_freem (mhold); + + /* Wait for the device to complete accepting the data, with a + limiting counter so that we don't wait too long. */ + for (timeout = 0; timeout < 1000; ++timeout) + { + unsigned char status; + + inport_byte (port + ISR, status); + +#ifdef DEBUG_NE2000 + if ((status &~ MSK_RDC) != 0) + printk ("Status 0x%x while waiting for acknowledgement of uploaded packet\n", + status); +#endif + + if ((status & MSK_RDC) != 0) { + outport_byte (port + ISR, MSK_RDC); + break; + } + } + + if (timeout >= 1000) + printk ("Timed out waiting for acknowledgement of uploaded NE2000 packet\n"); + + ++sc->nextavail; + if (sc->nextavail == NE_TX_BUFS) + sc->nextavail = 0; +} + +/* Tell the NE2000 to transmit a buffer whose contents we have already + loaded onto the device. */ + +static void +ne_transmit (struct ne_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + unsigned int port = sc->port; + int len; + +#ifdef DEBUG_NE2000 + printk ("Transmitting NE2000 packet\n"); +#endif + + len = sc->sendlen[sc->nextsend]; + if (len < ET_MINLEN) + len = ET_MINLEN; + outport_byte (port + TBCR0, len); + outport_byte (port + TBCR1, len >> 8); + + outport_byte (port + TPSR, NE_FIRST_TX_PAGE + (sc->nextsend * NE_TX_PAGES)); + + outport_byte (port + CMDR, MSK_PG0 | MSK_TXP | MSK_RD2 | MSK_STA); + +#ifdef DEBUG_NE + /* printk("{s%d|%d}", len, sc->nextsend); */ + printk(">"); +#endif + ++sc->nextsend; + if (sc->nextsend == NE_TX_BUFS) + sc->nextsend = 0; + + ++sc->stats.tx_packets; + + /* set watchdog timer */ + ifp->if_timer = 2; +} + +/* The NE2000 packet transmit daemon. This task is started when the + NE2000 driver is initialized. */ + +static void +ne_tx_daemon (void *arg) +{ + struct ne_softc *sc = (struct ne_softc *) arg; + unsigned int port = sc->port; + struct ifnet *ifp = &sc->arpcom.ac_if; + + while (1) { + rtems_event_set events; + + /* Wait for a packet to be ready for sending, or for there to be + room for another packet in the device memory. */ + rtems_bsdnet_event_receive (START_TRANSMIT_EVENT, + RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, + &events); + +#ifdef DEBUG_NE2000 + printk ("ne_tx_daemon\n"); +#endif + + /* This daemon handles both uploading data onto the device and + telling the device to transmit data which has been uploaded. + These are separate tasks, because while the device is + transmitting one buffer we will upload another. */ + + /* Don't let the device interrupt us now. */ + outport_byte (port + IMR, 0); + + while (1) { + struct mbuf *m; + + /* If the device is not transmitting a packet, and we have + uploaded a packet, tell the device to transmit it. */ + if (! sc->transmitting && sc->inuse > 0) { + sc->transmitting = 1; + ne_transmit (sc); + } + + /* If we don't have any more buffers to send, quit now. */ + if (ifp->if_snd.ifq_head == NULL) { + ifp->if_flags &= ~IFF_OACTIVE; + break; + } + + /* Allocate a buffer to load data into. If there are none + available, quit until a buffer has been transmitted. */ + if (sc->inuse >= NE_TX_BUFS) + break; + + ++sc->inuse; + + IF_DEQUEUE (&ifp->if_snd, m); + if (m == NULL) + panic ("ne_tx_daemon"); + + ne_loadpacket (sc, m); + + /* Check the device status. It may have finished transmitting + the last packet. */ + ne_check_status(sc, 0); + } + + /* Reenable device interrupts. */ + outport_byte (port + IMR, NE_INTERRUPTS); + } +} + +/* Start sending an NE2000 packet. */ + +static void +ne_start (struct ifnet *ifp) +{ + struct ne_softc *sc = ifp->if_softc; + +#ifdef DEBUG_NE + printk("S"); +#endif + /* Tell the transmit daemon to wake up and send a packet. */ + rtems_bsdnet_event_send (sc->tx_daemon_tid, START_TRANSMIT_EVENT); + ifp->if_flags |= IFF_OACTIVE; +} + +/* Initialize and start and NE2000. */ + +static void +ne_init (void *arg) +{ + struct ne_softc *sc = (struct ne_softc *) arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + +#ifdef DEBUG_NE + printk("ne_init()\n"); + ne_dump(sc); +#endif + + /* only once... */ + if (sc->tx_daemon_tid == 0) + { + sc->inuse = 0; + sc->nextavail = 0; + sc->nextsend = 0; + sc->transmitting = 0; + + ne_init_hardware (sc); + + sc->tx_daemon_tid = rtems_bsdnet_newproc ("SCtx", 4096, ne_tx_daemon, sc); + sc->rx_daemon_tid = rtems_bsdnet_newproc ("SCrx", 4096, ne_rx_daemon, sc); + + /* install rtems irq handler */ + ne_init_irq_handler(sc); + } + + ifp->if_flags |= IFF_RUNNING; +} + +/* Stop an NE2000. */ + +static void +ne_stop (struct ne_softc *sc) +{ + sc->arpcom.ac_if.if_flags &= ~IFF_RUNNING; + + ne_stop_hardware(sc); + + sc->inuse = 0; + sc->nextavail = 0; + sc->nextsend = 0; + sc->transmitting = 0; + sc->overrun = 0; + sc->resend = 0; +} + +static void +ne_stop_hardware (struct ne_softc *sc) +{ + unsigned int port = sc->port; + int i; + + /* Stop everything. */ + outport_byte (port + CMDR, MSK_STP | MSK_RD2); + + /* Wait for the interface to stop, using I as a time limit. */ + for (i = 0; i < 5000; ++i) + { + unsigned char status; + + inport_byte (port + ISR, status); + if ((status & MSK_RST) != 0) + break; + } +} + +/* reinitializing interface +*/ +static void +ne_reset(struct ne_softc *sc) +{ + ne_stop(sc); + ne_init_hardware(sc); + sc->arpcom.ac_if.if_flags |= IFF_RUNNING; + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; +#ifdef DEBUG_NE + printk("*"); +#endif +} + +#ifdef DEBUG_NE +/* show anything about ne +*/ +static void +ne_dump(struct ne_softc *sc) +{ + int i; + printk("\nne configuration:\n"); + printk("ethernet addr:"); + for (i=0; i<ETHER_ADDR_LEN; i++) + printk(" %x", sc->arpcom.ac_enaddr[i]); + printk("\n"); + printk("irq = %d\n", sc->irno); + printk("port = 0x%x\n", sc->port); + printk("accept_broadcasts = %d\n", sc->accept_broadcasts); + printk("byte_transfers = %d\n", sc->byte_transfers); +} +#endif + +/* Show NE2000 interface statistics. */ + +static void +ne_stats (struct ne_softc *sc) +{ + printf (" Received packets: %-8lu", sc->stats.rx_packets); + printf (" Transmitted packets: %-8lu\n", sc->stats.tx_packets); + printf (" Receive acks: %-8lu", sc->stats.rx_acks); + printf (" Transmit acks: %-8lu\n", sc->stats.tx_acks); + printf (" Packet overruns: %-8lu", sc->stats.overruns); + printf (" Frame errors: %-8lu\n", sc->stats.rx_frame_errors); + printf (" CRC errors: %-8lu", sc->stats.rx_crc_errors); + printf (" Missed packets: %-8lu\n", sc->stats.rx_missed_errors); + printf (" Interrupts: %-8lu\n", sc->stats.interrupts); +} + +/* NE2000 driver ioctl handler. */ + +static int +ne_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data) +{ + struct ne_softc *sc = ifp->if_softc; + int error = 0; + + switch (command) { + case SIOCGIFADDR: + case SIOCSIFADDR: + error = ether_ioctl (ifp, command, data); + break; + + case SIOCSIFFLAGS: + switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { + case IFF_RUNNING: + ne_stop (sc); + break; + + case IFF_UP: + printk("IFF_UP\n"); + ne_init (sc); + break; + + case IFF_UP | IFF_RUNNING: + ne_stop (sc); + ne_init (sc); + break; + + default: + break; + } + break; + + case SIO_RTEMS_SHOW_STATS: + ne_stats (sc); + break; + + /* FIXME: Multicast commands must be added here. */ + + default: + error = EINVAL; + break; + } + + return error; +} + +/* + * Device timeout/watchdog routine. Entered if the device neglects to + * generate an interrupt after a transmit has been started on it. + */ +static void +ne_watchdog(struct ifnet *ifp) +{ + struct ne_softc *sc = ifp->if_softc; + + printk("ne2000: device timeout\n"); + ifp->if_oerrors++; + + ne_reset(sc); +} + +static void +print_byte(unsigned char b) +{ + printk("%x%x", b >> 4, b & 0x0f); +} + +/* Attach an NE2000 driver to the system. */ + +int +rtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach) +{ + int i; + struct ne_softc *sc; + struct ifnet *ifp; + int mtu; + + /* dettach ... */ + if (!attach) + return 0; + + /* Find a free driver. */ + sc = NULL; + for (i = 0; i < NNEDRIVER; ++i) { + sc = &ne_softc[i]; + ifp = &sc->arpcom.ac_if; + if (ifp->if_softc == NULL) + break; + } + + if (sc == NULL) { + printf ("Too many NE2000 drivers.\n"); + return 0; + } + + memset (sc, 0, sizeof *sc); + + /* Check whether we do byte-wide or word-wide transfers. */ + +#ifdef NE2000_BYTE_TRANSFERS + sc->byte_transfers = true; +#else + sc->byte_transfers = false; +#endif + + /* Handle the options passed in by the caller. */ + + if (config->mtu != 0) + mtu = config->mtu; + else + mtu = ETHERMTU; + + /* We use 16 as the default IRQ. */ + sc->irno = XSCALE_IRQ_NETWORK; + + + + /*IO prts are mapped to 0X40000600 */ + sc->port = 0x40000600; + + sc->accept_broadcasts = ! config->ignore_broadcast; + + if (config->hardware_address != NULL) + memcpy (sc->arpcom.ac_enaddr, config->hardware_address, + ETHER_ADDR_LEN); + else + { + unsigned char prom[16]; + int ia; + + /* Read the PROM to get the Ethernet hardware address. */ + + outport_byte (sc->port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP); + + if (sc->byte_transfers) { + outport_byte (sc->port + DCR, MSK_FT10 | MSK_BMS); + } + else { + outport_byte (sc->port + DCR, MSK_FT10 | MSK_BMS | MSK_WTS); + } + + outport_byte (sc->port + RBCR0, 0); + outport_byte (sc->port + RBCR1, 0); + outport_byte (sc->port + RCR, MSK_MON); + outport_byte (sc->port + TCR, MSK_LOOP); + outport_byte (sc->port + IMR, 0); + outport_byte (sc->port + ISR, 0xff); + + ne_read_data (sc, 0, sizeof prom, prom); + + outport_byte (sc->port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP); + + for (ia = 0; ia < ETHER_ADDR_LEN; ++ia) + sc->arpcom.ac_enaddr[ia] = prom[ia * 2]; + } + + /* Set up the network interface. */ + + ifp->if_softc = sc; + ifp->if_unit = i + 1; + ifp->if_name = "ne"; + ifp->if_mtu = mtu; + ifp->if_init = ne_init; + ifp->if_ioctl = ne_ioctl; + ifp->if_watchdog = ne_watchdog; + ifp->if_start = ne_start; + ifp->if_output = ether_output; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; + if (ifp->if_snd.ifq_maxlen == 0) + ifp->if_snd.ifq_maxlen = ifqmaxlen; + + /* Attach the interface. */ + + if_attach (ifp); + ether_ifattach (ifp); + + printk("network device '%s' <", config->name); + print_byte(sc->arpcom.ac_enaddr[0]); + for (i=1; i<ETHER_ADDR_LEN; i++) + { printk(":"); + print_byte(sc->arpcom.ac_enaddr[i]); + } + printk("> initialized on port 0x%x, irq %d\n", sc->port, sc->irno); + + return 1; +} diff --git a/bsps/arm/gumstix/net/wd80x3.h b/bsps/arm/gumstix/net/wd80x3.h new file mode 100644 index 0000000000..c4a3aba758 --- /dev/null +++ b/bsps/arm/gumstix/net/wd80x3.h @@ -0,0 +1,303 @@ +/** + * @file + * @ingroup gumstix_dp8390 + * @brief DP8390 Ethernet Controller Support + */ + +/* + * Information about the DP8390 Ethernet controller. + */ + +#ifndef __BSP_WD80x3_h +#define __BSP_WD80x3_h + +/* Register descriptions */ + +/** + * @defgroup gumstix_dp8390 DP8390 Support + * @ingroup arm_gumstix + * @brief DP8390 Ethernet Controller Support + * @{ + */ + +/** + * @name Controller DP8390. + * @{ + */ + +/** @brief Port Window. */ +#define DATAPORT 0x10 +/** @brief Issue a read for reset */ +#define RESET 0x1f +/** @brief I/O port definition */ +#define W83CREG 0x00 +#define ADDROM 0x08 + +/** @} */ + +/** + * @name page 0 read or read/write registers + * @{ + */ + +#define CMDR 0x00+RO +/** @brief current local dma addr 0 for read */ +#define CLDA0 0x01+RO +/** @brief current local dma addr 1 for read */ +#define CLDA1 0x02+RO +/** @brief boundary reg for rd and wr */ +#define BNRY 0x03+RO +/** @brief tx status reg for rd */ +#define TSR 0x04+RO +/** @brief number of collision reg for rd */ +#define NCR 0x05+RO +/** @breif FIFO for rd */ +#define FIFO 0x06+RO +/** @brief interrupt status reg for rd and wr */ +#define ISR 0x07+RO +/** @brief current remote dma address 0 for rd */ +#define CRDA0 0x08+RO +/** @brief current remote dma address 1 for rd */ +#define CRDA1 0x09+RO +/** @brief rx status reg for rd */ +#define RSR 0x0C+RO +/** @brief tally cnt 0 for frm alg err for rd */ +#define CNTR0 0x0D+RO +/** @brief tally cnt 1 for crc err for rd */ +#define CNTR1 RO+0x0E +/** @brief tally cnt 2 for missed pkt for rd */ +#define CNTR2 0x0F+RO + +/** @} */ + +/** + * @name page 0 write registers + * @{ + */ + +/** @brief page start register */ +#define PSTART 0x01+RO +/** @brief page stop register */ +#define PSTOP 0x02+RO +/** @breif tx start page start reg */ +#define TPSR 0x04+RO +/** @brief tx byte count 0 reg */ +#define TBCR0 0x05+RO +/** @brief tx byte count 1 reg */ +#define TBCR1 0x06+RO +/** @brief remote start address reg 0 */ +#define RSAR0 0x08+RO +/** @brief remote start address reg 1 */ +#define RSAR1 0x09+RO +/** @brief remote byte count reg 0 */ +#define RBCR0 0x0A+RO +/** @brief remote byte count reg 1 */ +#define RBCR1 0x0B+RO +/** @brief rx configuration reg */ +#define RCR 0x0C+RO +/** @brief tx configuration reg */ +#define TCR 0x0D+RO +/** @brief data configuration reg */ +#define DCR RO+0x0E +/** @brief interrupt mask reg */ +#define IMR 0x0F+RO + +/** @} */ + +/** + * @name page 1 registers + * @{ + */ + +/** @brief physical addr reg base for rd and wr */ +#define PAR 0x01+RO +/** @brief current page reg for rd and wr */ +#define CURR 0x07+RO +/** @brief multicast addr reg base fro rd and WR */ +#define MAR 0x08+RO +/** @brief size of multicast addr space */ +#define MARsize 8 + +/** @} */ + +/** + * @name W83CREG command bits + * @{ + */ + +/** @brief W83CREG masks */ +#define MSK_RESET 0x80 +#define MSK_ENASH 0x40 +/** @brief memory decode bits, corresponding */ +#define MSK_DECOD 0x3F + +/** @} */ + +/** + * @name CMDR command bits + * @{ + */ + +/** @brief stop the chip */ +#define MSK_STP 0x01 +/** @brief start the chip */ +#define MSK_STA 0x02 +/** @brief initial txing of a frm */ +#define MSK_TXP 0x04 +/** @brief remote read */ +#define MSK_RRE 0x08 +/** @brief remote write */ +#define MSK_RWR 0x10 +/** @brief no DMA used */ +#define MSK_RD2 0x20 +/** @brief select register page 0 */ +#define MSK_PG0 0x00 +/** @brief select register page 1 */ +#define MSK_PG1 0x40 +/** @brief select register page 2 */ +#define MSK_PG2 0x80 + +/** @} */ + +/** + * @name ISR and TSR status bits + * @{ + */ + +/* @brief rx with no error */ +#define MSK_PRX 0x01 +/* @brief tx with no error */ +#define MSK_PTX 0x02 +/* @brief rx with error */ +#define MSK_RXE 0x04 +/* @brief tx with error */ +#define MSK_TXE 0x08 +/* @brief overwrite warning */ +#define MSK_OVW 0x10 +/* @brief MSB of one of the tally counters is set */ +#define MSK_CNT 0x20 +/* @brief remote dma completed */ +#define MSK_RDC 0x40 +/* @brief reset state indicator */ +#define MSK_RST 0x80 + +/** @} */ + +/** + * @name DCR command bits + * @{ + */ + +/** @brief word transfer mode selection */ +#define MSK_WTS 0x01 +/** @brief byte order selection */ +#define MSK_BOS 0x02 +/** @brief long addr selection */ +#define MSK_LAS 0x04 +/** @brief burst mode selection */ +#define MSK_BMS 0x08 +/** @brief autoinitialize remote */ +#define MSK_ARM 0x10 +/** @brief burst lrngth selection */ +#define MSK_FT00 0x00 +/** @brief burst lrngth selection */ +#define MSK_FT01 0x20 +/** @brief burst lrngth selection */ +#define MSK_FT10 0x40 +/** @brief burst lrngth selection */ +#define MSK_FT11 0x60 + +/** @} */ + +/** + * @name RCR command bits + * @{ + */ + +/** @brief save error pkts */ +#define MSK_SEP 0x01 +/** @brief accept runt pkt */ +#define MSK_AR 0x02 +/** @brief 8390 RCR */ +#define MSK_AB 0x04 +/** @brief accept multicast */ +#define MSK_AM 0x08 +/** @brief accept all pkt with physical adr */ +#define MSK_PRO 0x10 +/** @brief monitor mode */ +#define MSK_MON 0x20 + +/** @} */ + +/** + * @name TCR command bits + * @{ + */ + +/** @brief inhibit CRC, do not append crc */ +#define MSK_CRC 0x01 +/** @brief set loopback mode */ +#define MSK_LOOP 0x02 +/** @brief Accept broadcasts */ +#define MSK_BCST 0x04 +/** @brief encoded loopback control */ +#define MSK_LB01 0x06 +/** @brief auto tx disable */ +#define MSK_ATD 0x08 +/** @brief collision offset enable */ +#define MSK_OFST 0x10 + +/** @} */ + +/** + * @name receive status bits + * @{ + */ + +/** @brief rx without error */ +#define SMK_PRX 0x01 +/** @brief CRC error */ +#define SMK_CRC 0x02 +/** @brief frame alignment error */ +#define SMK_FAE 0x04 +/** @brief FIFO overrun */ +#define SMK_FO 0x08 +/** @brief missed pkt */ +#define SMK_MPA 0x10 +/** @brief physical/multicase address */ +#define SMK_PHY 0x20 +/** @brief receiver disable. set in monitor mode */ +#define SMK_DIS 0x40 +/** @brief deferring */ +#define SMK_DEF 0x80 + +/** @} */ + +/** + * @name transmit status bits + * @{ + */ + +/** @brief tx without error */ +#define SMK_PTX 0x01 +/** @brief non deferred tx */ +#define SMK_DFR 0x02 +/** @brief tx collided */ +#define SMK_COL 0x04 +/** @brief tx abort because of excessive collisions */ +#define SMK_ABT 0x08 +/** @brief carrier sense lost */ +#define SMK_CRS 0x10 +/** @brief FIFO underrun */ +#define SMK_FU 0x20 +/** @brief collision detect heartbeat */ +#define SMK_CDH 0x40 +/** @brief out of window collision */ +#define SMK_OWC 0x80 + +/** @} */ + +/** @} */ + +#endif +/* end of include */ |