summaryrefslogtreecommitdiffstats
path: root/bsps/mips/csb350/net/network.c
diff options
context:
space:
mode:
Diffstat (limited to 'bsps/mips/csb350/net/network.c')
-rw-r--r--bsps/mips/csb350/net/network.c909
1 files changed, 909 insertions, 0 deletions
diff --git a/bsps/mips/csb350/net/network.c b/bsps/mips/csb350/net/network.c
new file mode 100644
index 0000000000..15a1c320df
--- /dev/null
+++ b/bsps/mips/csb350/net/network.c
@@ -0,0 +1,909 @@
+/**
+ * @file
+ *
+ * Au1x00 ethernet driver
+ */
+
+/*
+ * Copyright (c) 2005 by Cogent Computer Systems
+ * Written by Jay Monkman <jtm@lopingdog.com>
+ *
+ * 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 <rtems.h>
+#include <rtems/rtems_bsdnet.h>
+#include <bsp.h>
+#include <rtems/bspIo.h>
+#include <libcpu/au1x00.h>
+#include <bsp/irq.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <errno.h>
+#include <rtems/error.h>
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <assert.h>
+
+#define NUM_IFACES 1
+#define NUM_TX_DMA_BUFS 4
+#define NUM_RX_DMA_BUFS 4
+
+/* RTEMS event used to start tx daemon. */
+#define START_TX_EVENT RTEMS_EVENT_1
+/* RTEMS event used to start rx daemon. */
+#define START_RX_EVENT RTEMS_EVENT_2
+
+rtems_isr au1x00_emac_isr(rtems_vector_number vector);
+
+#define TX_BUF_SIZE 2048
+
+char tx_buf_base[(4 * TX_BUF_SIZE) + 32];
+
+volatile int wait_count;
+/*
+ * Hardware-specific storage
+ */
+typedef struct
+{
+ /*
+ * Connection to networking code
+ * This entry *must* be the first in the sonic_softc structure.
+ */
+ struct arpcom arpcom;
+
+ /*
+ * Interrupt vector
+ */
+ rtems_vector_number vector;
+
+ /*
+ * Indicates configuration
+ */
+ int acceptBroadcast;
+
+ /*
+ * Tasks waiting for interrupts
+ */
+ rtems_id rx_daemon_tid;
+ rtems_id tx_daemon_tid;
+
+ /*
+ * Buffers
+ */
+ au1x00_macdma_rx_t *rx_dma;
+ au1x00_macdma_tx_t *tx_dma;
+ int rx_head;
+ int rx_tail;
+ int tx_head;
+ int tx_tail;
+ struct mbuf *rx_mbuf[NUM_RX_DMA_BUFS];
+
+ unsigned char *tx_buf[4];
+
+ /*
+ * register addresses
+ */
+ uint32_t ctrl_regs;
+ uint32_t *en_reg;
+ uint32_t int_mask;
+ uint32_t int_ctrlr;
+
+ /*
+ * device
+ */
+ int unitnumber;
+
+ /*
+ * Statistics
+ */
+ unsigned long interrupts;
+ unsigned long rx_interrupts;
+ unsigned long tx_interrupts;
+ unsigned long rx_missed;
+ unsigned long rx_bcast;
+ unsigned long rx_mcast;
+ unsigned long rx_unsupp;
+ unsigned long rx_ctrl;
+ unsigned long rx_len_err;
+ unsigned long rx_crc_err;
+ unsigned long rx_dribble;
+ unsigned long rx_mii_err;
+ unsigned long rx_collision;
+ unsigned long rx_too_long;
+ unsigned long rx_runt;
+ unsigned long rx_watchdog;
+ unsigned long rx_pkts;
+ unsigned long rx_dropped;
+
+ unsigned long tx_deferred;
+ unsigned long tx_underrun;
+ unsigned long tx_aborted;
+ unsigned long tx_pkts;
+} au1x00_emac_softc_t;
+
+static au1x00_emac_softc_t softc[NUM_IFACES];
+
+
+/* function prototypes */
+int rtems_au1x00_emac_attach (struct rtems_bsdnet_ifconfig *config,
+ int attaching);
+void au1x00_emac_init(void *arg);
+void au1x00_emac_init_hw(au1x00_emac_softc_t *sc);
+void au1x00_emac_start(struct ifnet *ifp);
+void au1x00_emac_stop (au1x00_emac_softc_t *sc);
+void au1x00_emac_tx_daemon (void *arg);
+void au1x00_emac_rx_daemon (void *arg);
+void au1x00_emac_sendpacket (struct ifnet *ifp, struct mbuf *m);
+void au1x00_emac_stats (au1x00_emac_softc_t *sc);
+static int au1x00_emac_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data);
+static void mii_write(au1x00_emac_softc_t *sc, uint8_t reg, uint16_t val);
+static void mii_read(au1x00_emac_softc_t *sc, uint8_t reg, uint16_t *val);
+static void mii_init(au1x00_emac_softc_t *sc);
+
+static void mii_write(au1x00_emac_softc_t *sc, uint8_t reg, uint16_t val)
+{
+ /* wait for the interface to get unbusy */
+ while (AU1X00_MAC_MIICTRL(sc->ctrl_regs) & AU1X00_MAC_MIICTRL_MB) {
+ continue;
+ }
+
+ /* write to address 0 - we only support address 0 */
+ AU1X00_MAC_MIIDATA(sc->ctrl_regs) = val;
+ AU1X00_MAC_MIICTRL(sc->ctrl_regs) = (((reg & 0x1f) << 6) |
+ AU1X00_MAC_MIICTRL_MW);
+ au_sync();
+
+ /* wait for it to complete */
+ while (AU1X00_MAC_MIICTRL(sc->ctrl_regs) & AU1X00_MAC_MIICTRL_MB) {
+ continue;
+ }
+}
+
+static void mii_read(au1x00_emac_softc_t *sc, uint8_t reg, uint16_t *val)
+{
+ /* wait for the interface to get unbusy */
+ while (AU1X00_MAC_MIICTRL(sc->ctrl_regs) & AU1X00_MAC_MIICTRL_MB) {
+ continue;
+ }
+
+ /* write to address 0 - we only support address 0 */
+ AU1X00_MAC_MIICTRL(sc->ctrl_regs) = ((reg & 0x1f) << 6);
+ au_sync();
+
+ /* wait for it to complete */
+ while (AU1X00_MAC_MIICTRL(sc->ctrl_regs) & AU1X00_MAC_MIICTRL_MB) {
+ continue;
+ }
+ *val = AU1X00_MAC_MIIDATA(sc->ctrl_regs);
+}
+
+static void mii_init(au1x00_emac_softc_t *sc)
+{
+ uint16_t data;
+
+ mii_write(sc, 0, 0x8000); /* reset */
+ do {
+ mii_read(sc, 0, &data);
+ } while (data & 0x8000);
+
+ mii_write(sc, 0, 0x3200); /* reset autonegotiation */
+ mii_write(sc, 17, 0xffc0); /* setup LEDs */
+
+}
+
+
+
+int rtems_au1x00_emac_attach (
+ struct rtems_bsdnet_ifconfig *config,
+ int attaching
+ )
+{
+ struct ifnet *ifp;
+ int mtu;
+ int unitnumber;
+ char *unitname;
+ static au1x00_emac_softc_t *sc;
+
+ /*
+ * Parse driver name
+ */
+ if ((unitnumber = rtems_bsdnet_parse_driver_name (config, &unitname)) < 0)
+ return 0;
+
+ /*
+ * Is driver free?
+ */
+ if (unitnumber > NUM_IFACES) {
+ printf ("Bad AU1X00 EMAC unit number.\n");
+ return 0;
+ }
+
+ sc = &softc[unitnumber];
+
+ ifp = &sc->arpcom.ac_if;
+ if (ifp->if_softc != NULL) {
+ printf ("Driver already in use.\n");
+ return 0;
+ }
+
+ /*
+ * zero out the control structure
+ */
+
+ memset((void *)sc, 0, sizeof(*sc));
+
+ sc->unitnumber = unitnumber;
+ sc->int_ctrlr = AU1X00_IC0_ADDR;
+
+ if (unitnumber == 0) {
+ sc->ctrl_regs = AU1100_MAC0_ADDR;
+ sc->en_reg = (void *)(AU1100_MACEN_ADDR + 0);
+
+ sc->tx_dma = (void *)(AU1X00_MACDMA0_ADDR + 0x000);
+ sc->rx_dma = (void *)(AU1X00_MACDMA0_ADDR + 0x100);
+ sc->int_mask = AU1X00_IC_IRQ_MAC0;
+ } else {
+ printk("Unknown network device: %d\n", unitnumber);
+ return 0;
+ }
+
+ /* If the ethernet controller is already set up, read the MAC address */
+ if ((*sc->en_reg & 0x33) == 0x33) {
+ sc->arpcom.ac_enaddr[5] = ((AU1X00_MAC_ADDRHIGH(sc->ctrl_regs) >> 8) &
+ 0xff);
+ sc->arpcom.ac_enaddr[4] = ((AU1X00_MAC_ADDRHIGH(sc->ctrl_regs) >> 0) &
+ 0xff);
+ sc->arpcom.ac_enaddr[3] = ((AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 24) &
+ 0xff);
+ sc->arpcom.ac_enaddr[2] = ((AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 16) &
+ 0xff);
+ sc->arpcom.ac_enaddr[1] = ((AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 8) &
+ 0xff);
+ sc->arpcom.ac_enaddr[0] = ((AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 0) &
+ 0xff);
+ } else {
+ /* It's not set up yet, so we set a MAC address */
+ sc->arpcom.ac_enaddr[5] = 0x05;
+ sc->arpcom.ac_enaddr[4] = 0xc0;
+ sc->arpcom.ac_enaddr[3] = 0x50;
+ sc->arpcom.ac_enaddr[2] = 0x31;
+ sc->arpcom.ac_enaddr[1] = 0x23;
+ sc->arpcom.ac_enaddr[0] = 0x00;
+ }
+
+
+ if (config->mtu) {
+ mtu = config->mtu;
+ } else {
+ mtu = ETHERMTU;
+ }
+
+ sc->acceptBroadcast = !config->ignore_broadcast;
+
+ /*
+ * Set up network interface values
+ */
+ ifp->if_softc = sc;
+ ifp->if_unit = unitnumber;
+ ifp->if_name = unitname;
+ ifp->if_mtu = mtu;
+ ifp->if_init = au1x00_emac_init;
+ ifp->if_ioctl = au1x00_emac_ioctl;
+ ifp->if_start = au1x00_emac_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST;
+ if (ifp->if_snd.ifq_maxlen == 0) {
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+ }
+
+ /*
+ * Attach the interface
+ */
+ if_attach (ifp);
+ ether_ifattach (ifp);
+ return 1;
+}
+
+void au1x00_emac_init(void *arg)
+{
+ au1x00_emac_softc_t *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ /*
+ *This is for stuff that only gets done once (au1x00_emac_init()
+ * gets called multiple times
+ */
+ if (sc->tx_daemon_tid == 0)
+ {
+ /* Set up EMAC hardware */
+ au1x00_emac_init_hw(sc);
+
+
+ /* install the interrupt handler */
+ if (sc->unitnumber == 0) {
+ rtems_interrupt_handler_install(
+ AU1X00_IRQ_MAC0,
+ "NIC0",
+ 0,
+ (rtems_interrupt_handler)au1x00_emac_isr,
+ NULL
+ );
+ } else {
+ rtems_interrupt_handler_install(
+ AU1X00_IRQ_MAC1,
+ "NIC1",
+ 0,
+ (rtems_interrupt_handler)au1x00_emac_isr,
+ NULL
+ );
+ }
+ AU1X00_IC_MASKCLR(sc->int_ctrlr) = sc->int_mask;
+ au_sync();
+
+ /* set src bit */
+ AU1X00_IC_SRCSET(sc->int_ctrlr) = sc->int_mask;
+
+ /* high level */
+ AU1X00_IC_CFG0SET(sc->int_ctrlr) = sc->int_mask;
+ AU1X00_IC_CFG1CLR(sc->int_ctrlr) = sc->int_mask;
+ AU1X00_IC_CFG2SET(sc->int_ctrlr) = sc->int_mask;
+
+ /* assign to request 0 - negative logic */
+ AU1X00_IC_ASSIGNSET(sc->int_ctrlr) = sc->int_mask;
+ au_sync();
+
+ /* Start driver tasks */
+ sc->tx_daemon_tid = rtems_bsdnet_newproc("ENTx",
+ 4096,
+ au1x00_emac_tx_daemon,
+ sc);
+
+ sc->rx_daemon_tid = rtems_bsdnet_newproc("ENRx",
+ 4096,
+ au1x00_emac_rx_daemon,
+ sc);
+
+
+ }
+ /* EMAC doesn't support promiscuous, so ignore requests */
+ if (ifp->if_flags & IFF_PROMISC)
+ printf ("Warning - AU1X00 EMAC doesn't support Promiscuous Mode!\n");
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+
+ /*
+ * start tx, rx
+ */
+ AU1X00_MAC_CONTROL(sc->ctrl_regs) |= (AU1X00_MAC_CTRL_TE |
+ AU1X00_MAC_CTRL_RE);
+ au_sync();
+
+
+} /* au1x00_emac_init() */
+
+void au1x00_emac_init_hw(au1x00_emac_softc_t *sc)
+{
+ int i;
+ struct mbuf *m;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ /* reset the MAC */
+ *sc->en_reg = 0x40;
+ au_sync();
+ for (i = 0; i < 10000; i++) {
+ continue;
+ }
+
+/* *sc->en_reg = AU1X00_MAC_EN_CE; */
+ *sc->en_reg = 41;
+ au_sync();
+ for (i = 0; i < 10000; i++) {
+ continue;
+ }
+
+/*
+ *sc->en_reg = (AU1X00_MAC_EN_CE |
+ AU1X00_MAC_EN_E2 |
+ AU1X00_MAC_EN_E1 |
+ AU1X00_MAC_EN_E0);
+*/
+ *sc->en_reg = 0x33;
+ au_sync();
+ mii_init(sc);
+
+ /* set the mac address */
+ AU1X00_MAC_ADDRHIGH(sc->ctrl_regs) = ((sc->arpcom.ac_enaddr[5] << 8) |
+ (sc->arpcom.ac_enaddr[4] << 0));
+ AU1X00_MAC_ADDRLOW(sc->ctrl_regs) = ((sc->arpcom.ac_enaddr[3] << 24) |
+ (sc->arpcom.ac_enaddr[2] << 16) |
+ (sc->arpcom.ac_enaddr[1] << 8) |
+ (sc->arpcom.ac_enaddr[0] << 0));
+
+
+ /* get the MAC address from the chip */
+ sc->arpcom.ac_enaddr[5] = (AU1X00_MAC_ADDRHIGH(sc->ctrl_regs) >> 8) & 0xff;
+ sc->arpcom.ac_enaddr[4] = (AU1X00_MAC_ADDRHIGH(sc->ctrl_regs) >> 0) & 0xff;
+ sc->arpcom.ac_enaddr[3] = (AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 24) & 0xff;
+ sc->arpcom.ac_enaddr[2] = (AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 16) & 0xff;
+ sc->arpcom.ac_enaddr[1] = (AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 8) & 0xff;
+ sc->arpcom.ac_enaddr[0] = (AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 0) & 0xff;
+
+ printk("Setting mac_control to 0x%x\n",
+ (AU1X00_MAC_CTRL_F |
+ AU1X00_MAC_CTRL_PM |
+ AU1X00_MAC_CTRL_RA |
+ AU1X00_MAC_CTRL_DO |
+ AU1X00_MAC_CTRL_EM));
+
+ AU1X00_MAC_CONTROL(sc->ctrl_regs) = (AU1X00_MAC_CTRL_F | /* full duplex */
+ AU1X00_MAC_CTRL_PM | /* pass mcast */
+ AU1X00_MAC_CTRL_RA | /* recv all */
+ AU1X00_MAC_CTRL_DO | /* disable own */
+ AU1X00_MAC_CTRL_EM); /* Big endian */
+ au_sync();
+ printk("mac_control was set to 0x%x\n", AU1X00_MAC_CONTROL(sc->ctrl_regs));
+ printk("mac_control addr is 0x%x\n", &AU1X00_MAC_CONTROL(sc->ctrl_regs));
+
+ /* initialize our receive buffer descriptors */
+ for (i = 0; i < NUM_RX_DMA_BUFS; i++) {
+ MGETHDR(m, M_WAIT, MT_DATA);
+ MCLGET(m, M_WAIT);
+
+ m->m_pkthdr.rcvif = ifp;
+ m->m_nextpkt = 0;
+
+ /*
+ * The receive buffer must be aligned with a cache line
+ * boundary.
+ */
+ if (mtod(m, uint32_t) & 0x1f) {
+ uint32_t *p = mtod(m, uint32_t *);
+ *p = (mtod(m, uint32_t) + 0x1f) & 0x1f;
+ }
+ sc->rx_dma[i].addr = (mtod(m, uint32_t) & ~0xe0000000);
+ sc->rx_mbuf[i] = m;
+ }
+
+ /* Initialize transmit buffer descriptors */
+ for (i = 0; i < NUM_TX_DMA_BUFS; i++) {
+ sc->tx_dma[i].addr = 0;
+ }
+
+ /* initialize the transmit buffers */
+ sc->tx_buf[0] = (void *)((((int)&tx_buf_base[0]) + 0x1f) & ~0x1f);
+ sc->tx_buf[1] = (void *)(((int)sc->tx_buf[0]) + TX_BUF_SIZE);
+ sc->tx_buf[2] = (void *)(((int)sc->tx_buf[1]) + TX_BUF_SIZE);
+ sc->tx_buf[3] = (void *)(((int)sc->tx_buf[2]) + TX_BUF_SIZE);
+
+ sc->rx_head = (sc->rx_dma[0].addr >> 2) & 0x3;
+ sc->rx_tail = (sc->rx_dma[0].addr >> 2) & 0x3;
+ sc->tx_head = (sc->tx_dma[0].addr >> 2) & 0x3;
+ sc->tx_tail = (sc->tx_dma[0].addr >> 2) & 0x3;
+
+ for (i = 0; i < NUM_RX_DMA_BUFS; i++) {
+ sc->rx_dma[i].addr |= AU1X00_MAC_DMA_RXADDR_EN;
+ }
+
+} /* au1x00_emac_init_hw() */
+
+void au1x00_emac_start(struct ifnet *ifp)
+{
+ au1x00_emac_softc_t *sc = ifp->if_softc;
+
+ rtems_bsdnet_event_send(sc->tx_daemon_tid, START_TX_EVENT);
+ ifp->if_flags |= IFF_OACTIVE;
+}
+
+void au1x00_emac_stop (au1x00_emac_softc_t *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /*
+ * Stop the transmitter and receiver.
+ */
+
+ /* Disable TX/RX */
+ AU1X00_MAC_CONTROL(sc->ctrl_regs) &= ~(AU1X00_MAC_CTRL_TE |
+ AU1X00_MAC_CTRL_RE);
+ au_sync();
+}
+
+/*
+ * Driver tx daemon
+ */
+void au1x00_emac_tx_daemon (void *arg)
+{
+ au1x00_emac_softc_t *sc = (au1x00_emac_softc_t *)arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ rtems_event_set events;
+ uint32_t ic_base; /* interrupt controller */
+
+ ic_base = AU1X00_IC0_ADDR;
+
+ /* turn on interrupt, then wait for one */
+ if (sc->unitnumber == 0) {
+ AU1X00_IC_MASKSET(ic_base) = AU1X00_IC_IRQ_MAC0;
+ } else {
+ AU1X00_IC_MASKSET(ic_base) = AU1X00_IC_IRQ_MAC1;
+ }
+ au_sync();
+
+ for (;;)
+ {
+ rtems_bsdnet_event_receive(
+ START_TX_EVENT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &events);
+
+ /* Send packets till queue is empty */
+ for (;;)
+ {
+ /* Get the next mbuf chain to transmit. */
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (!m)
+ break;
+
+ sc->tx_pkts++;
+ au1x00_emac_sendpacket (ifp, m);
+ }
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+}
+
+/*
+ * Driver rx daemon
+ */
+void au1x00_emac_rx_daemon (void *arg)
+{
+ au1x00_emac_softc_t *sc = (au1x00_emac_softc_t *)arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ struct ether_header *eh;
+ rtems_event_set events;
+ uint32_t status;
+
+ while (1) {
+ rtems_bsdnet_event_receive(
+ START_RX_EVENT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &events);
+
+ /* while there are packets to receive */
+
+ while (!(sc->rx_dma[sc->rx_head].addr & (AU1X00_MAC_DMA_RXADDR_DN |
+ AU1X00_MAC_DMA_RXADDR_EN))) {
+ status = sc->rx_dma[sc->rx_head].stat;
+ if (status & AU1X00_MAC_DMA_RXSTAT_MI) {
+ sc->rx_missed++;
+ }
+ if (status & AU1X00_MAC_DMA_RXSTAT_BF) {
+ sc->rx_bcast++;
+ }
+ if (status & AU1X00_MAC_DMA_RXSTAT_MF) {
+ sc->rx_mcast++;
+ }
+ if (status & AU1X00_MAC_DMA_RXSTAT_UC) {
+ sc->rx_unsupp++;
+ }
+ if (status & AU1X00_MAC_DMA_RXSTAT_CF) {
+ sc->rx_ctrl++;
+ }
+ if (status & AU1X00_MAC_DMA_RXSTAT_LE) {
+ sc->rx_len_err++;
+ }
+ if (status & AU1X00_MAC_DMA_RXSTAT_CR) {
+ sc->rx_crc_err++;
+ }
+ if (status & AU1X00_MAC_DMA_RXSTAT_DB) {
+ sc->rx_dribble++;
+ }
+ if (status & AU1X00_MAC_DMA_RXSTAT_ME) {
+ sc->rx_mii_err++;
+ }
+ if (status & AU1X00_MAC_DMA_RXSTAT_CS) {
+ sc->rx_collision++;
+ }
+ if (status & AU1X00_MAC_DMA_RXSTAT_FL) {
+ sc->rx_too_long++;
+ }
+ if (status & AU1X00_MAC_DMA_RXSTAT_RF) {
+ sc->rx_runt++;
+ }
+ if (status & AU1X00_MAC_DMA_RXSTAT_WT) {
+ sc->rx_watchdog++;
+ }
+
+ /* If no errrors, accept packet */
+ if ((status & (AU1X00_MAC_DMA_RXSTAT_CR |
+ AU1X00_MAC_DMA_RXSTAT_DB |
+ AU1X00_MAC_DMA_RXSTAT_RF)) == 0) {
+
+ sc->rx_pkts++;
+
+ /* find the start of the mbuf */
+ m = sc->rx_mbuf[sc->rx_head];
+
+ /* set the length of the mbuf */
+ m->m_len = AU1X00_MAC_DMA_RXSTAT_LEN(sc->rx_dma[sc->rx_head].stat);
+ m->m_len -= 4; /* remove ethernet CRC */
+
+ m->m_pkthdr.len = m->m_len;
+
+ /* strip off the ethernet header from the mbuf */
+ /* but save the pointer to it */
+ eh = mtod (m, struct ether_header *);
+ m->m_data += sizeof(struct ether_header);
+
+ /* give the received packet to the stack */
+ ether_input(ifp, eh, m);
+ /* get a new buf and make it ready for the MAC */
+ MGETHDR(m, M_WAIT, MT_DATA);
+ MCLGET(m, M_WAIT);
+
+ m->m_pkthdr.rcvif = ifp;
+ m->m_nextpkt = 0;
+
+ /*
+ * The receive buffer must be aligned with a cache line
+ * boundary.
+ */
+ {
+ uint32_t *p = mtod(m, uint32_t *);
+ *p = (mtod(m, uint32_t) + 0x1f) & ~0x1f;
+ }
+
+ } else {
+ sc->rx_dropped++;
+
+ /* find the mbuf so we can reuse it*/
+ m = sc->rx_mbuf[sc->rx_head];
+ }
+
+ /* set up the receive dma to use the mbuf's cluster */
+ sc->rx_dma[sc->rx_head].addr = (mtod(m, uint32_t) & ~0xe0000000);
+ au_sync();
+ sc->rx_mbuf[sc->rx_head] = m;
+
+ sc->rx_dma[sc->rx_head].addr |= AU1X00_MAC_DMA_RXADDR_EN;
+ au_sync();
+
+
+ /* increment the buffer index */
+ sc->rx_head++;
+ if (sc->rx_head >= NUM_RX_DMA_BUFS) {
+ sc->rx_head = 0;
+ }
+ }
+ }
+}
+
+/* Send packet */
+void au1x00_emac_sendpacket (struct ifnet *ifp, struct mbuf *m)
+{
+ struct mbuf *l = NULL;
+ unsigned int pkt_offset = 0;
+ au1x00_emac_softc_t *sc = (au1x00_emac_softc_t *)ifp->if_softc;
+ uint32_t txbuf;
+
+ /* Wait for EMAC Transmit Queue to become available. */
+ while((sc->tx_dma[sc->tx_head].addr & (AU1X00_MAC_DMA_TXADDR_EN ||
+ AU1X00_MAC_DMA_TXADDR_DN)) != 0) {
+ continue;
+ }
+
+ /* copy the mbuf chain into the transmit buffer */
+ l = m;
+
+ txbuf = (uint32_t)sc->tx_buf[sc->tx_head];
+ while (l != NULL)
+ {
+
+ memcpy(((char *)txbuf + pkt_offset), /* offset into pkt for mbuf */
+ (char *)mtod(l, void *), /* cast to void */
+ l->m_len); /* length of this mbuf */
+
+ pkt_offset += l->m_len; /* update offset */
+ l = l->m_next; /* get next mbuf, if any */
+ }
+
+ /* Pad if necessary */
+ if (pkt_offset < 60) {
+ memset((char *)(txbuf + pkt_offset), 0, (60 - pkt_offset));
+ pkt_offset = 60;
+ }
+
+ /* send it off */
+ sc->tx_dma[sc->tx_head].stat = 0;
+ sc->tx_dma[sc->tx_head].len = pkt_offset;
+ sc->tx_dma[sc->tx_head].addr = ((txbuf & ~0xe0000000) |
+ AU1X00_MAC_DMA_TXADDR_EN);
+ au_sync();
+
+
+ /*
+ *Without this delay, some outgoing packets never
+ * make it out the device. Nothing in the documentation
+ * explains this.
+ */
+ for (wait_count = 0; wait_count < 5000; wait_count++){
+ continue;
+ }
+
+ /* free the mbuf chain we just copied */
+ m_freem(m);
+
+ sc->tx_head++;
+ if (sc->tx_head >= NUM_TX_DMA_BUFS) {
+ sc->tx_head = 0;
+ }
+
+} /* au1x00_emac_sendpacket () */
+
+
+
+/* Show interface statistics */
+void au1x00_emac_stats (au1x00_emac_softc_t *sc)
+{
+ printf("Interrupts:%-8lu", sc->interrupts);
+ printf(" RX Interrupts:%-8lu", sc->rx_interrupts);
+ printf(" TX Interrupts:%-8lu\n", sc->tx_interrupts);
+ printf("RX Packets:%-8lu", sc->rx_pkts);
+ printf(" RX Control:%-8lu", sc->rx_ctrl);
+ printf(" RX broadcast:%-8lu\n", sc->rx_bcast);
+ printf("RX Mcast:%-8lu", sc->rx_mcast);
+ printf(" RX missed:%-8lu", sc->rx_missed);
+ printf(" RX Unsupported ctrl:%-8lu\n", sc->rx_unsupp);
+ printf("RX Len err:%-8lu", sc->rx_len_err);
+ printf(" RX CRC err:%-8lu", sc->rx_crc_err);
+ printf(" RX dribble:%-8lu\n", sc->rx_dribble);
+ printf("RX MII err:%-8lu", sc->rx_mii_err);
+ printf(" RX collision:%-8lu", sc->rx_collision);
+ printf(" RX too long:%-8lu\n", sc->rx_too_long);
+ printf("RX runt:%-8lu", sc->rx_runt);
+ printf(" RX watchdog:%-8lu", sc->rx_watchdog);
+ printf(" RX dropped:%-8lu\n", sc->rx_dropped);
+
+ printf("TX Packets:%-8lu", sc->tx_pkts);
+ printf(" TX Deferred:%-8lu", sc->tx_deferred);
+ printf(" TX Underrun:%-8lu\n", sc->tx_underrun);
+ printf("TX Aborted:%-8lu\n", sc->tx_aborted);
+
+}
+
+
+/* Driver ioctl handler */
+static int
+au1x00_emac_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ au1x00_emac_softc_t *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command) {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl (ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING))
+ {
+ case IFF_RUNNING:
+ au1x00_emac_stop (sc);
+ break;
+
+ case IFF_UP:
+ au1x00_emac_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ au1x00_emac_stop (sc);
+ au1x00_emac_init (sc);
+ break;
+
+ default:
+ break;
+ } /* switch (if_flags) */
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ au1x00_emac_stats (sc);
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ } /* switch (command) */
+ return error;
+}
+
+/* interrupt handler */
+rtems_isr au1x00_emac_isr (rtems_vector_number v)
+{
+ volatile au1x00_emac_softc_t *sc;
+ int tx_flag = 0;
+ int rx_flag = 0;
+
+ sc = &softc[0];
+ if (v != AU1X00_IRQ_MAC0) {
+ assert(v == AU1X00_IRQ_MAC0);
+ }
+
+ sc->interrupts++;
+
+ /*
+ * Since there's no easy way to find out the source of the
+ * interrupt, we have to look at the tx and rx dma buffers
+ */
+ /* receive interrupt */
+ while(sc->rx_dma[sc->rx_tail].addr & AU1X00_MAC_DMA_RXADDR_DN) {
+ rx_flag = 1;
+ sc->rx_interrupts++;
+ sc->rx_dma[sc->rx_tail].addr &= ~AU1X00_MAC_DMA_RXADDR_DN;
+ au_sync();
+
+ sc->rx_tail++;
+ if (sc->rx_tail >= NUM_RX_DMA_BUFS) {
+ sc->rx_tail = 0;
+ }
+ }
+ if (rx_flag != 0) {
+ rtems_bsdnet_event_send(sc->rx_daemon_tid, START_RX_EVENT);
+ }
+
+ /* transmit interrupt */
+ while (sc->tx_dma[sc->tx_tail].addr & AU1X00_MAC_DMA_TXADDR_DN) {
+ uint32_t status;
+ tx_flag = 1;
+ sc->tx_interrupts++;
+
+ status = sc->tx_dma[sc->tx_tail].stat;
+ if (status & AU1X00_MAC_DMA_TXSTAT_DF) {
+ sc->tx_deferred++;
+ }
+ if (status & AU1X00_MAC_DMA_TXSTAT_UR) {
+ sc->tx_underrun++;
+ }
+ if (status & AU1X00_MAC_DMA_TXSTAT_FA) {
+ sc->tx_aborted++;
+ }
+
+ sc->tx_dma[sc->tx_tail].addr = 0;
+ au_sync();
+
+ sc->tx_tail++;
+ if (sc->tx_tail >= NUM_TX_DMA_BUFS) {
+ sc->tx_tail = 0;
+ }
+ }
+ if (tx_flag != 0) {
+ rtems_bsdnet_event_send(sc->tx_daemon_tid, START_TX_EVENT);
+ }
+}
+