diff options
author | Joel Sherrill <joel.sherrill@OARcorp.com> | 2008-08-15 20:18:41 +0000 |
---|---|---|
committer | Joel Sherrill <joel.sherrill@OARcorp.com> | 2008-08-15 20:18:41 +0000 |
commit | 30abd24b7e7bc1f66b22527792931cf4468b06b8 (patch) | |
tree | fba359b7e4b04fdd1f008f54fcca7b69d810bab6 /c/src/lib/libcpu/bfin/network/ethernet.c | |
parent | 2008-08-15 Joel Sherrill <joel.sherrill@OARcorp.com> (diff) | |
download | rtems-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.c | 884 |
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 = ðernetSoftc[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 = ðernetSoftc[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 = ðernetSoftc[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 = ðernetSoftc[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; +} + |