summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/bfin/network/ethernet.c
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>2008-08-15 20:18:41 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>2008-08-15 20:18:41 +0000
commit30abd24b7e7bc1f66b22527792931cf4468b06b8 (patch)
treefba359b7e4b04fdd1f008f54fcca7b69d810bab6 /c/src/lib/libcpu/bfin/network/ethernet.c
parent2008-08-15 Joel Sherrill <joel.sherrill@OARcorp.com> (diff)
downloadrtems-30abd24b7e7bc1f66b22527792931cf4468b06b8.tar.bz2
2008-08-15 Allan Hessenflow <allanh@kallisti.com>
* ChangeLog, Makefile.am, README, configure.ac, preinstall.am, cache/cache.c, cache/cache_.h, clock/clock.c, clock/rtc.c, clock/tod.h, include/bf533.h, include/bf537.h, include/cecRegs.h, include/coreTimerRegs.h, include/dmaRegs.h, include/ebiuRegs.h, include/ethernetRegs.h, include/gpioRegs.h, include/memoryRegs.h, include/mmuRegs.h, include/ppiRegs.h, include/rtcRegs.h, include/sicRegs.h, include/spiRegs.h, include/sportRegs.h, include/timerRegs.h, include/twiRegs.h, include/uartRegs.h, include/wdogRegs.h, interrupt/interrupt.c, interrupt/interrupt.h, mmu/mmu.c, mmu/mmu.h, network/ethernet.c, network/ethernet.h, serial/spi.c, serial/spi.h, serial/sport.c, serial/sport.h, serial/twi.c, serial/twi.h, serial/uart.c, serial/uart.h, timer/timer.c: New files.
Diffstat (limited to 'c/src/lib/libcpu/bfin/network/ethernet.c')
-rw-r--r--c/src/lib/libcpu/bfin/network/ethernet.c884
1 files changed, 884 insertions, 0 deletions
diff --git a/c/src/lib/libcpu/bfin/network/ethernet.c b/c/src/lib/libcpu/bfin/network/ethernet.c
new file mode 100644
index 0000000000..3b9c8fb32f
--- /dev/null
+++ b/c/src/lib/libcpu/bfin/network/ethernet.c
@@ -0,0 +1,884 @@
+/*
+ * RTEMS network driver for Blackfin ethernet controller
+ *
+ * COPYRIGHT (c) 2008 Kallisti Labs, Los Gatos, CA, USA
+ * written by Allan Hessenflow <allanh@kallisti.com>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ *
+ * $Id$
+ *
+ */
+
+#include <rtems.h>
+#include <rtems/rtems_bsdnet.h>
+#include <rtems/rtems/cache.h>
+
+#include <stdio.h>
+#include <inttypes.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 <sys/sockio.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <libcpu/dmaRegs.h>
+#include <libcpu/ethernetRegs.h>
+#include "ethernet.h"
+
+#if (BFIN_ETHERNET_DEBUG & BFIN_ETHERNET_DEBUG_DUMP_MBUFS)
+#include <rtems/dumpbuf.h>
+#endif
+
+/*
+ * Number of devices supported by this driver
+ */
+#ifndef N_BFIN_ETHERNET
+# define N_BFIN_ETHERNET 1
+#endif
+
+
+/* #define BFIN_IPCHECKSUMS */
+
+
+/*
+ * RTEMS event used by interrupt handler to signal daemons.
+ */
+#define INTERRUPT_EVENT RTEMS_EVENT_1
+
+/*
+ * RTEMS event used to start transmit daemon.
+ */
+#define START_TRANSMIT_EVENT RTEMS_EVENT_2
+
+
+/* largest Ethernet frame MAC will handle */
+#define BFIN_ETHERNET_MAX_FRAME_LENGTH 1556
+
+#if MCLBYTES < (BFIN_ETHERNET_MAX_FRAME_LENGTH + 2)
+#error MCLBYTES too small
+#endif
+
+#define BFIN_REG16(base, offset) \
+ (*((uint16_t volatile *) ((char *)(base) + (offset))))
+#define BFIN_REG32(base, offset) \
+ (*((uint32_t volatile *) ((char *)(base) + (offset))))
+
+
+#define DMA_MODE_RX (DMA_CONFIG_FLOW_DESC_LARGE | \
+ (5 << DMA_CONFIG_NDSIZE_SHIFT) | \
+ DMA_CONFIG_WDSIZE_32 | \
+ DMA_CONFIG_WNR | \
+ DMA_CONFIG_DMAEN)
+
+#define DMA_MODE_TX (DMA_CONFIG_FLOW_DESC_LARGE | \
+ (5 << DMA_CONFIG_NDSIZE_SHIFT) | \
+ DMA_CONFIG_WDSIZE_32 | \
+ DMA_CONFIG_DMAEN)
+
+#define DMA_MODE_STATUS (DMA_CONFIG_FLOW_DESC_LARGE | \
+ (5 << DMA_CONFIG_NDSIZE_SHIFT) | \
+ DMA_CONFIG_DI_EN | \
+ DMA_CONFIG_WDSIZE_32 | \
+ DMA_CONFIG_WNR | \
+ DMA_CONFIG_DMAEN)
+
+#define DMA_MODE_STATUS_NO_INT (DMA_CONFIG_FLOW_DESC_LARGE | \
+ (5 << DMA_CONFIG_NDSIZE_SHIFT) | \
+ DMA_CONFIG_WDSIZE_32 | \
+ DMA_CONFIG_WNR | \
+ DMA_CONFIG_DMAEN)
+
+#define DMA_MODE_STATUS_LAST (DMA_CONFIG_FLOW_STOP | \
+ (0 << DMA_CONFIG_NDSIZE_SHIFT) | \
+ DMA_CONFIG_DI_EN | \
+ DMA_CONFIG_WDSIZE_32 | \
+ DMA_CONFIG_WNR | \
+ DMA_CONFIG_DMAEN)
+
+/* five 16 bit words */
+typedef struct dmaDescS {
+ struct dmaDescS *next;
+ void *addr;
+ uint16_t dmaConfig;
+} dmaDescT;
+
+typedef struct {
+ uint32_t status;
+} txStatusT;
+
+#ifdef BFIN_IPCHECKSUMS
+typedef struct {
+ uint16_t ipHeaderChecksum;
+ uint16_t ipPayloadChecksum;
+ uint32_t status;
+} rxStatusT;
+#else
+typedef struct {
+ uint32_t status;
+} rxStatusT;
+#endif
+
+typedef struct {
+ dmaDescT data;
+ dmaDescT status;
+ struct mbuf *m;
+} rxPacketDescT;
+
+typedef struct {
+ dmaDescT data;
+ dmaDescT status;
+ boolean inUse;
+ union {
+ uint32_t dummy; /* try to force 32 bit alignment */
+ struct {
+ uint16_t length;
+ char data[BFIN_ETHERNET_MAX_FRAME_LENGTH];
+ } packet;
+ } buffer;
+} txPacketDescT;
+
+
+/* hardware-specific storage */
+struct bfin_ethernetSoftc {
+ struct arpcom arpcom; /* this entry must be first */
+
+ uint32_t sclk;
+
+ void *ethBase;
+ void *rxdmaBase;
+ void *txdmaBase;
+
+ int acceptBroadcast;
+
+ rtems_id rxDaemonTid;
+ rtems_id txDaemonTid;
+
+ void *status;
+ int rxDescCount;
+ rxPacketDescT *rx;
+ int txDescCount;
+ txPacketDescT *tx;
+
+ boolean rmii;
+ int phyAddr;
+
+ /* statistics */
+#ifdef BISON
+ unsigned long Interrupts;
+ unsigned long rxInterrupts;
+ unsigned long rxMissed;
+ unsigned long rxGiant;
+ unsigned long rxNonOctet;
+ unsigned long rxBadCRC;
+ unsigned long rxCollision;
+
+ unsigned long txInterrupts;
+ unsigned long txSingleCollision;
+ unsigned long txMultipleCollision;
+ unsigned long txCollision;
+ unsigned long txDeferred;
+ unsigned long txUnderrun;
+ unsigned long txLateCollision;
+ unsigned long txExcessiveCollision;
+ unsigned long txExcessiveDeferral;
+ unsigned long txLostCarrier;
+ unsigned long txRawWait;
+#endif
+};
+
+static struct bfin_ethernetSoftc ethernetSoftc[N_BFIN_ETHERNET];
+
+
+/* Shut down the interface. */
+static void ethernetStop(struct bfin_ethernetSoftc *sc) {
+ struct ifnet *ifp;
+ void *ethBase;
+
+ ifp = &sc->arpcom.ac_if;
+ ethBase = sc->ethBase;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /* stop the transmitter and receiver. */
+ BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) &= ~(EMAC_OPMODE_TE |
+ EMAC_OPMODE_RE);
+}
+
+/* Show interface statistics */
+static void bfin_ethernetStats(struct bfin_ethernetSoftc *sc) {
+#ifdef BISON
+ printf(" Total Interrupts:%-8lu", sc->Interrupts);
+ printf(" Rx Interrupts:%-8lu", sc->rxInterrupts);
+ printf(" Giant:%-8lu", sc->rxGiant);
+ printf(" Non-octet:%-8lu\n", sc->rxNonOctet);
+ printf(" Bad CRC:%-8lu", sc->rxBadCRC);
+ printf(" Collision:%-8lu", sc->rxCollision);
+ printf(" Missed:%-8lu\n", sc->rxMissed);
+
+ printf( " Tx Interrupts:%-8lu", sc->txInterrupts);
+ printf( " Deferred:%-8lu", sc->txDeferred);
+ printf(" Lost Carrier:%-8lu\n", sc->txLostCarrier);
+ printf( "Single Collisions:%-8lu", sc->txSingleCollision);
+ printf( "Multiple Collisions:%-8lu", sc->txMultipleCollision);
+ printf("Excessive Collisions:%-8lu\n", sc->txExcessiveCollision);
+ printf( " Total Collisions:%-8lu", sc->txCollision);
+ printf( " Late Collision:%-8lu", sc->txLateCollision);
+ printf(" Underrun:%-8lu\n", sc->txUnderrun);
+ printf( " Raw output wait:%-8lu\n", sc->txRawWait);
+#endif /*BISON*/
+}
+
+void bfin_ethernet_rxdma_isr(int vector) {
+ struct bfin_ethernetSoftc *sc;
+ void *rxdmaBase;
+ uint16_t status;
+ int i;
+
+ for (i = 0; i < N_BFIN_ETHERNET; i++) {
+ sc = &ethernetSoftc[i];
+ rxdmaBase = sc->rxdmaBase;
+ status = BFIN_REG16(rxdmaBase, DMA_IRQ_STATUS_OFFSET);
+ if (status & DMA_IRQ_STATUS_DMA_DONE)
+ rtems_event_send (sc->rxDaemonTid, INTERRUPT_EVENT);
+ BFIN_REG16(rxdmaBase, DMA_IRQ_STATUS_OFFSET) = status;
+ }
+}
+
+void bfin_ethernet_txdma_isr(int vector) {
+ struct bfin_ethernetSoftc *sc;
+ void *txdmaBase;
+ uint16_t status;
+ int i;
+
+ for (i = 0; i < N_BFIN_ETHERNET; i++) {
+ sc = &ethernetSoftc[i];
+ txdmaBase = sc->txdmaBase;
+ status = BFIN_REG16(txdmaBase, DMA_IRQ_STATUS_OFFSET);
+ if (status & DMA_IRQ_STATUS_DMA_DONE)
+ rtems_event_send (sc->txDaemonTid, INTERRUPT_EVENT);
+ BFIN_REG16(txdmaBase, DMA_IRQ_STATUS_OFFSET) = status;
+ }
+}
+
+void bfin_ethernet_mac_isr(int vector) {
+ struct bfin_ethernetSoftc *sc;
+ void *ethBase;
+ int i;
+
+ for (i = 0; i < N_BFIN_ETHERNET; i++) {
+ sc = &ethernetSoftc[i];
+ ethBase = sc->ethBase;
+ BFIN_REG32(ethBase, EMAC_SYSTAT_OFFSET) = ~(uint32_t) 0;
+ }
+}
+
+static boolean txFree(struct bfin_ethernetSoftc *sc, int index) {
+ boolean freed;
+ txStatusT *status;
+
+ freed = FALSE;
+ if (sc->tx[index].inUse) {
+ status = (txStatusT *) sc->tx[index].status.addr;
+ rtems_cache_invalidate_multiple_data_lines(status, sizeof(*status));
+ if (status->status != 0) {
+ /* update statistics */
+
+ sc->tx[index].inUse = FALSE;
+ freed = TRUE;
+ }
+ }
+
+ return freed;
+}
+
+static void txDaemon(void *arg) {
+ struct bfin_ethernetSoftc *sc;
+ struct ifnet *ifp;
+ struct mbuf *m, *first;
+ rtems_event_set events;
+ void *ethBase;
+ void *txdmaBase;
+ txStatusT *status;
+ int head;
+ int prevHead;
+ int tail;
+ int length;
+ char *ptr;
+
+ sc = (struct bfin_ethernetSoftc *) arg;
+ ifp = &sc->arpcom.ac_if;
+
+ ethBase = sc->ethBase;
+ txdmaBase = sc->txdmaBase;
+ head = 0;
+ prevHead = sc->txDescCount - 1;
+ tail = 0;
+
+ while (1) {
+ /* wait for packet or isr */
+ rtems_bsdnet_event_receive(START_TRANSMIT_EVENT | INTERRUPT_EVENT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT, &events);
+
+ /* if no descriptors are available, try to free one. To reduce
+ transmit latency only do one here. */
+ if (sc->tx[head].inUse && txFree(sc, tail)) {
+ if (++tail == sc->txDescCount)
+ tail = 0;
+ }
+ /* send packets until the queue is empty or we run out of tx
+ descriptors */
+ while (!sc->tx[head].inUse && (ifp->if_flags & IFF_OACTIVE)) {
+ /* get the next mbuf chain to transmit */
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (m != NULL) {
+ /* copy packet into our buffer */
+ ptr = sc->tx[head].buffer.packet.data;
+ length = 0;
+ first = m;
+ while (m && length <= BFIN_ETHERNET_MAX_FRAME_LENGTH) {
+ length += m->m_len;
+ if (length <= BFIN_ETHERNET_MAX_FRAME_LENGTH)
+ memcpy(ptr, m->m_data, m->m_len);
+ ptr += m->m_len;
+ m = m->m_next;
+ }
+ m_freem(first); /* all done with mbuf */
+ if (length <= BFIN_ETHERNET_MAX_FRAME_LENGTH) {
+ sc->tx[head].buffer.packet.length = length;
+
+ /* setup tx dma */
+ status = (txStatusT *) sc->tx[head].status.addr;
+ status->status = 0;
+ sc->tx[head].inUse = TRUE;
+ rtems_cache_flush_multiple_data_lines(status, sizeof(*status));
+
+ /* configure dma to stop after sending this packet */
+ sc->tx[head].status.dmaConfig = DMA_MODE_STATUS_LAST;
+ rtems_cache_flush_multiple_data_lines(
+ &sc->tx[head].status.dmaConfig,
+ sizeof(sc->tx[head].status.dmaConfig));
+ rtems_cache_flush_multiple_data_lines(
+ &sc->tx[head].buffer.packet,
+ length + sizeof(uint16_t));
+
+ /* modify previous descriptor to let it continue
+ automatically */
+ sc->tx[prevHead].status.dmaConfig = DMA_MODE_STATUS;
+ rtems_cache_flush_multiple_data_lines(
+ &sc->tx[prevHead].status.dmaConfig,
+ sizeof(sc->tx[prevHead].status.dmaConfig));
+
+ /* restart dma if it stopped before the packet we just
+ added. this is purely to reduce transmit latency,
+ as it would be restarted anyway after this loop (and
+ needs to be, as there's a very small chance that the
+ dma controller had started the last status transfer
+ before the new dmaConfig word was written above and
+ is still doing that status transfer when we check the
+ status below. this will be caught by the check
+ outside the loop as that is guaranteed to run at least
+ once after the last dma complete interrupt. */
+ if ((BFIN_REG16(txdmaBase, DMA_IRQ_STATUS_OFFSET) &
+ DMA_IRQ_STATUS_DMA_RUN) == 0 &&
+ BFIN_REG32(txdmaBase, DMA_NEXT_DESC_PTR_OFFSET) !=
+ (uint32_t) sc->tx[head].data.next) {
+ BFIN_REG16(txdmaBase, DMA_CONFIG_OFFSET) = DMA_MODE_TX;
+ BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) |= EMAC_OPMODE_TE;
+ }
+
+ if (++head == sc->txDescCount)
+ head = 0;
+ if (++prevHead == sc->txDescCount)
+ prevHead = 0;
+
+ /* if no descriptors are available, try to free one */
+ if (sc->tx[head].inUse && txFree(sc, tail)) {
+ if (++tail == sc->txDescCount)
+ tail = 0;
+ }
+ } else {
+ /* dropping packet: too large */
+
+ }
+ } else {
+ /* no packets queued */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+ }
+
+ /* if dma stopped and there's more to do, restart it */
+ if ((BFIN_REG16(txdmaBase, DMA_IRQ_STATUS_OFFSET) &
+ DMA_IRQ_STATUS_DMA_RUN) == 0 &&
+ BFIN_REG32(txdmaBase, DMA_NEXT_DESC_PTR_OFFSET) !=
+ (uint32_t) &sc->tx[head].data) {
+ BFIN_REG16(txdmaBase, DMA_CONFIG_OFFSET) = DMA_MODE_TX;
+ BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) |= EMAC_OPMODE_TE;
+ }
+
+ /* free up any additional tx descriptors */
+ while (txFree(sc, tail)) {
+ if (++tail == sc->txDescCount)
+ tail = 0;
+ }
+ }
+}
+
+
+static void rxDaemon(void *arg) {
+ struct bfin_ethernetSoftc *sc;
+ struct ifnet *ifp;
+ struct mbuf *m;
+ struct mbuf *rxPacket;
+ void *dataPtr;
+ rtems_event_set events;
+ struct ether_header *eh;
+ rxStatusT *status;
+ uint32_t rxStatus;
+ int head;
+ int prevHead;
+ int length;
+ void *ethBase;
+ void *rxdmaBase;
+
+ sc = (struct bfin_ethernetSoftc *) arg;
+ rxdmaBase = sc->rxdmaBase;
+ ethBase = sc->ethBase;
+ ifp = &sc->arpcom.ac_if;
+ prevHead = sc->rxDescCount - 1;
+ head = 0;
+
+ BFIN_REG16(rxdmaBase, DMA_CONFIG_OFFSET) = DMA_MODE_RX;
+ BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) |= EMAC_OPMODE_RE;
+
+ while (1) {
+ status = sc->rx[head].status.addr;
+ rtems_cache_invalidate_multiple_data_lines(status, sizeof(*status));
+ while (status->status != 0) {
+ if (status->status & EMAC_RX_STAT_RX_OK) {
+ /* get new cluster to replace this one */
+ MGETHDR(m, M_WAIT, MT_DATA);
+ MCLGET(m, M_WAIT);
+ m->m_pkthdr.rcvif = ifp;
+ } else
+ m = NULL;
+
+ rxStatus = status->status;
+ /* update statistics */
+
+
+ if (m) {
+ /* save received packet to send up a little later */
+ rxPacket = sc->rx[head].m;
+ dataPtr = sc->rx[head].data.addr;
+
+ /* setup dma for new cluster */
+ sc->rx[head].m = m;
+ sc->rx[head].data.addr = (void *) (((intptr_t) m->m_data + 3) & ~3);
+ /* invalidate cache for new data buffer, in case any lines
+ are dirty from previous owner */
+ rtems_cache_invalidate_multiple_data_lines(
+ sc->rx[head].data.addr,
+ BFIN_ETHERNET_MAX_FRAME_LENGTH + 2);
+ } else
+ rxPacket = NULL;
+
+ sc->rx[head].status.dmaConfig = DMA_MODE_STATUS_LAST;
+ rtems_cache_flush_multiple_data_lines(&sc->rx[head],
+ sizeof(sc->rx[head]));
+
+ /* mark descriptor as empty */
+ status->status = 0;
+ rtems_cache_flush_multiple_data_lines(&status->status,
+ sizeof(status->status));
+
+ /* allow dma to continue from previous descriptor into this
+ one */
+ sc->rx[prevHead].status.dmaConfig = DMA_MODE_STATUS;
+ rtems_cache_flush_multiple_data_lines(
+ &sc->rx[prevHead].status.dmaConfig,
+ sizeof(sc->rx[prevHead].status.dmaConfig));
+
+ if (rxPacket) {
+ /* send it up */
+ eh = (struct ether_header *) ((intptr_t) dataPtr + 2);
+ rxPacket->m_data = (caddr_t) ((intptr_t) dataPtr + 2 + 14);
+ length = (rxStatus & EMAC_RX_STAT_RX_FRLEN_MASK) >>
+ EMAC_RX_STAT_RX_FRLEN_SHIFT;
+ rxPacket->m_len = length - 14;
+ rxPacket->m_pkthdr.len = rxPacket->m_len;
+ /* invalidate packet buffer cache again (even though it
+ was invalidated prior to giving it to dma engine),
+ because speculative reads might cause cache lines to
+ be filled at any time */
+ rtems_cache_invalidate_multiple_data_lines(eh, length);
+ ether_input(ifp, eh, rxPacket);
+ }
+
+ if (++prevHead == sc->rxDescCount)
+ prevHead = 0;
+ if (++head == sc->rxDescCount)
+ head = 0;
+ status = sc->rx[head].status.addr;
+ rtems_cache_invalidate_multiple_data_lines(status, sizeof(*status));
+ }
+
+ /* if dma stopped before the next descriptor, restart it */
+ if ((BFIN_REG16(rxdmaBase, DMA_IRQ_STATUS_OFFSET) &
+ DMA_IRQ_STATUS_DMA_RUN) == 0 &&
+ BFIN_REG32(rxdmaBase, DMA_NEXT_DESC_PTR_OFFSET) !=
+ (uint32_t) &sc->rx[head].data) {
+ BFIN_REG16(rxdmaBase, DMA_CONFIG_OFFSET) = DMA_MODE_RX;
+ }
+
+ rtems_bsdnet_event_receive(INTERRUPT_EVENT, RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT, &events);
+ }
+
+}
+
+/*
+ ******************************************************************
+ * *
+ * Initialization Routines *
+ * *
+ ******************************************************************
+ */
+
+static void resetHardware(struct bfin_ethernetSoftc *sc) {
+ void *ethBase;
+ void *rxdmaBase;
+ void *txdmaBase;
+
+ ethBase = sc->ethBase;
+ rxdmaBase = sc->rxdmaBase;
+ txdmaBase = sc->txdmaBase;
+ BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) = 0;
+ BFIN_REG16(rxdmaBase, DMA_CONFIG_OFFSET) = 0;
+ BFIN_REG16(txdmaBase, DMA_CONFIG_OFFSET) = 0;
+}
+
+static void initializeHardware(struct bfin_ethernetSoftc *sc) {
+ struct ifnet *ifp;
+ struct mbuf *m;
+ unsigned char *hwaddr;
+ int cacheAlignment;
+ int rxStatusSize;
+ int txStatusSize;
+ char *ptr;
+ int i;
+ void *ethBase;
+ void *rxdmaBase;
+ void *txdmaBase;
+ uint32_t divisor;
+
+ ifp = &sc->arpcom.ac_if;
+ ethBase = sc->ethBase;
+ rxdmaBase = sc->rxdmaBase;
+ txdmaBase = sc->txdmaBase;
+
+ BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) = 0;
+ BFIN_REG32(ethBase, EMAC_FLC_OFFSET) = 0;
+ divisor = (sc->sclk / 25000000) / 2 - 1;
+ BFIN_REG32(ethBase, EMAC_SYSCTL_OFFSET) = (divisor <<
+ EMAC_SYSCTL_MDCDIV_SHIFT) |
+ EMAC_SYSCTL_RXDWA;
+#ifdef BFIN_IPCHECKSUMS
+ BFIN_REG32(ethBase, EMAC_SYSCTL_OFFSET) |= EMAC_SYSCTL_RXCKS;
+#endif
+ BFIN_REG32(ethBase, EMAC_SYSTAT_OFFSET) = ~(uint32_t) 0;
+ BFIN_REG32(ethBase, EMAC_RX_IRQE_OFFSET) = 0;
+ BFIN_REG32(ethBase, EMAC_RX_STKY_OFFSET) = ~(uint32_t) 0;
+ BFIN_REG32(ethBase, EMAC_TX_IRQE_OFFSET) = 0;
+ BFIN_REG32(ethBase, EMAC_TX_STKY_OFFSET) = ~(uint32_t) 0;
+ BFIN_REG32(ethBase, EMAC_MMC_RIRQE_OFFSET) = 0;
+ BFIN_REG32(ethBase, EMAC_MMC_RIRQS_OFFSET) = ~(uint32_t) 0;
+ BFIN_REG32(ethBase, EMAC_MMC_TIRQE_OFFSET) = 0;
+ BFIN_REG32(ethBase, EMAC_MMC_TIRQS_OFFSET) = ~(uint32_t) 0;
+ BFIN_REG32(ethBase, EMAC_MMC_CTL_OFFSET) = EMAC_MMC_CTL_MMCE |
+ EMAC_MMC_CTL_CCOR |
+ EMAC_MMC_CTL_RSTC;
+ BFIN_REG32(ethBase, EMAC_MMC_CTL_OFFSET) = EMAC_MMC_CTL_MMCE |
+ EMAC_MMC_CTL_CCOR;
+
+ BFIN_REG16(rxdmaBase, DMA_CONFIG_OFFSET) = 0;
+ BFIN_REG16(txdmaBase, DMA_CONFIG_OFFSET) = 0;
+ BFIN_REG16(rxdmaBase, DMA_X_COUNT_OFFSET) = 0;
+ BFIN_REG16(txdmaBase, DMA_X_COUNT_OFFSET) = 0;
+ BFIN_REG16(rxdmaBase, DMA_X_MODIFY_OFFSET) = 4;
+ BFIN_REG16(txdmaBase, DMA_X_MODIFY_OFFSET) = 4;
+ BFIN_REG16(rxdmaBase, DMA_Y_COUNT_OFFSET) = 0;
+ BFIN_REG16(txdmaBase, DMA_Y_COUNT_OFFSET) = 0;
+ BFIN_REG16(rxdmaBase, DMA_Y_MODIFY_OFFSET) = 0;
+ BFIN_REG16(txdmaBase, DMA_Y_MODIFY_OFFSET) = 0;
+ BFIN_REG16(rxdmaBase, DMA_IRQ_STATUS_OFFSET) = DMA_IRQ_STATUS_DMA_ERR |
+ DMA_IRQ_STATUS_DMA_DONE;
+
+ /* The status structures cannot share cache lines with anything else,
+ including other status structures, so we can safely manage both the
+ processor and DMA writing to them. So this rounds up the structure
+ sizes to a multiple of the cache line size. */
+ cacheAlignment = rtems_cache_get_data_line_size();
+ if (cacheAlignment == 0)
+ cacheAlignment = 1;
+ rxStatusSize = cacheAlignment * ((sizeof(rxStatusT) + cacheAlignment - 1) /
+ cacheAlignment);
+ txStatusSize = cacheAlignment * ((sizeof(txStatusT) + cacheAlignment - 1) /
+ cacheAlignment);
+ /* Allocate enough extra to allow structures to start at cache aligned
+ boundary. */
+ sc->status = malloc(sc->rxDescCount * rxStatusSize +
+ sc->txDescCount * txStatusSize +
+ cacheAlignment - 1, M_DEVBUF, M_NOWAIT);
+ sc->rx = malloc(sc->rxDescCount * sizeof(*sc->rx), M_DEVBUF, M_NOWAIT);
+ sc->tx = malloc(sc->txDescCount * sizeof(*sc->tx), M_DEVBUF, M_NOWAIT);
+ if (sc->status == NULL || sc->rx == NULL || sc->tx == NULL)
+ rtems_panic("No memory!\n");
+
+ /* Start status structures at cache aligned boundary. */
+ ptr = (char *) (((intptr_t) sc->status + cacheAlignment - 1) &
+ ~(cacheAlignment - 1));
+ memset(ptr, 0, sc->rxDescCount * rxStatusSize +
+ sc->txDescCount * txStatusSize);
+ memset(sc->rx, 0, sc->rxDescCount * sizeof(*sc->rx));
+ memset(sc->tx, 0, sc->txDescCount * sizeof(*sc->tx));
+ rtems_cache_flush_multiple_data_lines(ptr, sc->rxDescCount * rxStatusSize +
+ sc->txDescCount * txStatusSize);
+ for (i = 0; i < sc->rxDescCount; i++) {
+ MGETHDR(m, M_WAIT, MT_DATA);
+ MCLGET(m, M_WAIT);
+ m->m_pkthdr.rcvif = ifp;
+ sc->rx[i].m = m;
+ /* start dma at 32 bit boundary */
+ sc->rx[i].data.addr = (void *) (((intptr_t) m->m_data + 3) & ~3);
+ rtems_cache_invalidate_multiple_data_lines(
+ sc->rx[i].data.addr,
+ BFIN_ETHERNET_MAX_FRAME_LENGTH + 2);
+ sc->rx[i].data.dmaConfig = DMA_MODE_RX;
+ sc->rx[i].data.next = &(sc->rx[i].status);
+ sc->rx[i].status.addr = ptr;
+ if (i < sc->rxDescCount - 1) {
+ sc->rx[i].status.dmaConfig = DMA_MODE_STATUS;
+ sc->rx[i].status.next = &(sc->rx[i + 1].data);
+ } else {
+ sc->rx[i].status.dmaConfig = DMA_MODE_STATUS_LAST;
+ sc->rx[i].status.next = &(sc->rx[0].data);
+ }
+ ptr += rxStatusSize;
+ }
+ rtems_cache_flush_multiple_data_lines(sc->rx, sc->rxDescCount *
+ sizeof(*sc->rx));
+ for (i = 0; i < sc->txDescCount; i++) {
+ sc->tx[i].data.addr = &sc->tx[i].buffer.packet;
+ sc->tx[i].data.dmaConfig = DMA_MODE_TX;
+ sc->tx[i].data.next = &(sc->tx[i].status);
+ sc->tx[i].status.addr = ptr;
+ sc->tx[i].status.dmaConfig = DMA_MODE_STATUS_LAST;
+ if (i < sc->txDescCount - 1)
+ sc->tx[i].status.next = &(sc->tx[i + 1].data);
+ else
+ sc->tx[i].status.next = &(sc->tx[0].data);
+ sc->tx[i].inUse = FALSE;
+ ptr += txStatusSize;
+ }
+ rtems_cache_flush_multiple_data_lines(sc->tx, sc->txDescCount *
+ sizeof(*sc->tx));
+
+ BFIN_REG32(rxdmaBase, DMA_NEXT_DESC_PTR_OFFSET) = (uint32_t) &sc->rx[0].data;
+ BFIN_REG32(txdmaBase, DMA_NEXT_DESC_PTR_OFFSET) = (uint32_t) &sc->tx[0].data;
+
+ hwaddr = sc->arpcom.ac_enaddr;
+ BFIN_REG16(ethBase, EMAC_ADDRHI_OFFSET) = ((uint16_t) hwaddr[5] << 8) |
+ hwaddr[4];
+ BFIN_REG32(ethBase, EMAC_ADDRLO_OFFSET) = ((uint32_t) hwaddr[3] << 24) |
+ ((uint32_t) hwaddr[2] << 16) |
+ ((uint32_t) hwaddr[1] << 8) |
+ hwaddr[0];
+
+ if (sc->acceptBroadcast)
+ BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) &= ~EMAC_OPMODE_DBF;
+ else
+ BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) |= EMAC_OPMODE_DBF;
+
+}
+
+/* send packet (caller provides header) */
+static void ethernetStart(struct ifnet *ifp) {
+ struct bfin_ethernetSoftc *sc;
+
+ sc = ifp->if_softc;
+
+ ifp->if_flags |= IFF_OACTIVE;
+ rtems_event_send(sc->txDaemonTid, START_TRANSMIT_EVENT);
+}
+
+/* initialize and start the device */
+static void ethernetInit(void *arg) {
+ struct bfin_ethernetSoftc *sc;
+ struct ifnet *ifp;
+ void *ethBase;
+ void *rxdmaBase;
+ void *txdmaBase;
+
+ sc = arg;
+ ifp = &sc->arpcom.ac_if;
+ ethBase = sc->ethBase;
+ rxdmaBase = sc->rxdmaBase;
+ txdmaBase = sc->txdmaBase;
+
+ if (sc->txDaemonTid == 0) {
+ initializeHardware(sc);
+
+ /* start driver tasks */
+ sc->rxDaemonTid = rtems_bsdnet_newproc("BFrx", 4096, rxDaemon, sc);
+ sc->txDaemonTid = rtems_bsdnet_newproc("BFtx", 4096, txDaemon, sc);
+
+ }
+
+ if (ifp->if_flags & IFF_PROMISC)
+ BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) |= EMAC_OPMODE_PR;
+ else
+ BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) &= ~EMAC_OPMODE_PR;
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+
+}
+
+/* driver ioctl handler */
+static int ethernetIoctl(struct ifnet *ifp, ioctl_command_t command,
+ caddr_t data) {
+ int result;
+ struct bfin_ethernetSoftc *sc = ifp->if_softc;
+
+ result = 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:
+ ethernetStop(sc);
+ break;
+ case IFF_UP:
+ ethernetInit(sc);
+ break;
+ case IFF_UP | IFF_RUNNING:
+ ethernetStop(sc);
+ ethernetInit(sc);
+ break;
+ default:
+ break;
+ }
+ break;
+ case SIO_RTEMS_SHOW_STATS:
+ bfin_ethernetStats(sc);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ default:
+ result = EINVAL;
+ break;
+ }
+
+ return result;
+}
+
+/* attach a BFIN ETHERNET driver to the system */
+int bfin_ethernet_driver_attach(struct rtems_bsdnet_ifconfig *config,
+ int attaching,
+ bfin_ethernet_configuration_t *chip) {
+ struct bfin_ethernetSoftc *sc;
+ struct ifnet *ifp;
+ int mtu;
+ int unitNumber;
+ char *unitName;
+
+ if ((unitNumber = rtems_bsdnet_parse_driver_name(config, &unitName)) < 0)
+ return 0;
+
+ if ((unitNumber <= 0) || (unitNumber > N_BFIN_ETHERNET)) {
+ printf("Bad bfin ethernet unit number %d.\n", unitNumber);
+ return 0;
+ }
+ sc = &ethernetSoftc[unitNumber - 1];
+ ifp = &sc->arpcom.ac_if;
+ if (ifp->if_softc != NULL) {
+ printf("Driver already in use.\n");
+ return 0;
+ }
+
+ memset(sc, 0, sizeof(*sc));
+
+ /* process options */
+ if (config->hardware_address)
+ memcpy(sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
+ else
+ memset(sc->arpcom.ac_enaddr, 0x08, ETHER_ADDR_LEN);
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+ if (config->rbuf_count)
+ sc->rxDescCount = config->rbuf_count;
+ else
+ sc->rxDescCount = chip->rxDescCount;
+ if (config->xbuf_count)
+ sc->txDescCount = config->xbuf_count;
+ else
+ sc->txDescCount = chip->txDescCount;
+ /* minimum two of each type descriptor */
+ if (sc->rxDescCount <= 1)
+ sc->rxDescCount = 2;
+ if (sc->txDescCount <= 1)
+ sc->txDescCount = 2;
+
+ sc->acceptBroadcast = !config->ignore_broadcast;
+
+ sc->sclk = chip->sclk;
+ sc->ethBase = chip->ethBaseAddress;
+ sc->rxdmaBase = chip->rxdmaBaseAddress;
+ sc->txdmaBase = chip->txdmaBaseAddress;
+
+ /* make sure we should not have any interrupts asserted */
+ resetHardware(sc);
+
+ sc->rmii = (chip->phyType == rmii);
+ sc->phyAddr = chip->phyAddr;
+
+ /* set up network interface values */
+ ifp->if_softc = sc;
+ ifp->if_unit = unitNumber;
+ ifp->if_name = unitName;
+ ifp->if_mtu = mtu;
+ ifp->if_init = ethernetInit;
+ ifp->if_ioctl = ethernetIoctl;
+ ifp->if_start = ethernetStart;
+ 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;
+
+ if_attach(ifp);
+ ether_ifattach(ifp);
+
+ return 1;
+}
+