diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 09:53:31 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 15:18:44 +0200 |
commit | 031df3914990db0336a0d386fb53558b05de467e (patch) | |
tree | 4661e22f0cdb3f9d06879f0194b77c75f62bac79 /bsps/m68k | |
parent | bsps: Move interrupt controller support to bsps (diff) | |
download | rtems-031df3914990db0336a0d386fb53558b05de467e.tar.bz2 |
bsps: Move legacy network drivers to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/m68k')
-rw-r--r-- | bsps/m68k/av5282/net/network.c | 940 | ||||
-rw-r--r-- | bsps/m68k/csb360/net/network.c | 984 | ||||
-rw-r--r-- | bsps/m68k/gen68360/net/network.c | 1062 | ||||
-rw-r--r-- | bsps/m68k/genmcf548x/net/network.c | 1696 | ||||
-rw-r--r-- | bsps/m68k/mcf5235/net/network.c | 879 | ||||
-rw-r--r-- | bsps/m68k/mcf5329/net/network.c | 857 | ||||
-rw-r--r-- | bsps/m68k/mvme167/net/network.c | 3099 | ||||
-rw-r--r-- | bsps/m68k/mvme167/net/uti596.h | 369 | ||||
-rw-r--r-- | bsps/m68k/uC5282/net/network.c | 1013 |
9 files changed, 10899 insertions, 0 deletions
diff --git a/bsps/m68k/av5282/net/network.c b/bsps/m68k/av5282/net/network.c new file mode 100644 index 0000000000..457b43c5d6 --- /dev/null +++ b/bsps/m68k/av5282/net/network.c @@ -0,0 +1,940 @@ +#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ + +#include <bsp.h> +#include <stdio.h> +#include <errno.h> +#include <stdarg.h> +#include <string.h> +#include <rtems.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/ethernet.h> +#include <net/if.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + + +/* + * Number of interfaces supported by this driver + */ +#define NIFACES 1 + +#define FEC_INTC0_TX_VECTOR (64+23) +#define FEC_INTC0_RX_VECTOR (64+27) + +#define FEC_INTC0_TX_VECTOR (64+23) +#define FEC_INTC0_RX_VECTOR (64+27) +#define MII_VECTOR (64+7) /* IRQ7* pin connected to external transceiver */ +#define MII_EPPAR MCF5282_EPORT_EPPAR_EPPA7_LEVEL +#define MII_EPDDR MCF5282_EPORT_EPDDR_EPDD7 +#define MII_EPIER MCF5282_EPORT_EPIER_EPIE7 +#define MII_EPPDR MCF5282_EPORT_EPPDR_EPPD7 +/* + * Default number of buffer descriptors set aside for this driver. + * The number of transmit buffer descriptors has to be quite large + * since a single frame often uses three or more buffer descriptors. + */ +#define RX_BUF_COUNT 32 +#define TX_BUF_COUNT 20 +#define TX_BD_PER_BUF 3 + +#define INET_ADDR_MAX_BUF_SIZE (sizeof "255.255.255.255") + +/* + * RTEMS event used by interrupt handler to signal daemons. + * This must *not* be the same event used by the TCP/IP task synchronization. + */ +#define TX_INTERRUPT_EVENT RTEMS_EVENT_1 +#define RX_INTERRUPT_EVENT RTEMS_EVENT_1 + +/* + * RTEMS event used to start transmit daemon. + * This must not be the same as INTERRUPT_EVENT. + */ +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + +/* + * Receive buffer size -- Allow for a full ethernet packet plus CRC (1518). + * Round off to nearest multiple of RBUF_ALIGN. + */ +#define MAX_MTU_SIZE 1518 +#define RBUF_ALIGN 4 +#define RBUF_SIZE ((MAX_MTU_SIZE + RBUF_ALIGN) & ~RBUF_ALIGN) + +#if (MCLBYTES < RBUF_SIZE) + #error "Driver must have MCLBYTES > RBUF_SIZE" +#endif + +typedef struct mcf5282BufferDescriptor_ { + volatile uint16_t status; + uint16_t length; + volatile void *buffer; +} mcf5282BufferDescriptor_t; + +/* + * Per-device data + */ +struct mcf5282_enet_struct { + struct arpcom arpcom; + struct mbuf **rxMbuf; + struct mbuf **txMbuf; + int acceptBroadcast; + int rxBdCount; + int txBdCount; + int txBdHead; + int txBdTail; + int txBdActiveCount; + mcf5282BufferDescriptor_t *rxBdBase; + mcf5282BufferDescriptor_t *txBdBase; + rtems_id rxDaemonTid; + rtems_id txDaemonTid; + + /* + * Statistics + */ + unsigned long rxInterrupts; + unsigned long txInterrupts; + unsigned long miiInterrupts; + unsigned long txRawWait; + unsigned long txRealign; + unsigned long txRealignDrop; + uint16_t mii_sr2; +}; +static struct mcf5282_enet_struct enet_driver[NIFACES]; + +static int +getMII(int phyNumber, int regNumber); + + +static rtems_isr +mcf5282_fec_rx_interrupt_handler( rtems_vector_number v ) +{ + MCF5282_FEC_EIR = MCF5282_FEC_EIR_RXF; + MCF5282_FEC_EIMR &= ~MCF5282_FEC_EIMR_RXF; + enet_driver[0].rxInterrupts++; + rtems_bsdnet_event_send(enet_driver[0].rxDaemonTid, RX_INTERRUPT_EVENT); +} + +static rtems_isr +mcf5282_fec_tx_interrupt_handler( rtems_vector_number v ) +{ + MCF5282_FEC_EIR = MCF5282_FEC_EIR_TXF; + MCF5282_FEC_EIMR &= ~MCF5282_FEC_EIMR_TXF; + enet_driver[0].txInterrupts++; + rtems_bsdnet_event_send(enet_driver[0].txDaemonTid, TX_INTERRUPT_EVENT); +} + +static rtems_isr +mcf5282_mii_interrupt_handler( rtems_vector_number v ) +{ + uint16_t sr2; + + enet_driver[0].miiInterrupts++; + getMII(1, 19); /* Read and clear interrupt status bits */ + enet_driver[0].mii_sr2 = sr2 = getMII(1, 17); + if (((sr2 & 0x200) != 0) + && ((MCF5282_FEC_TCR & MCF5282_FEC_TCR_FDEN) == 0)) + MCF5282_FEC_TCR |= MCF5282_FEC_TCR_FDEN; + else if (((sr2 & 0x200) == 0) + && ((MCF5282_FEC_TCR & MCF5282_FEC_TCR_FDEN) != 0)) + MCF5282_FEC_TCR &= ~MCF5282_FEC_TCR_FDEN; +} + +/* + * Allocate buffer descriptors from (non-cached) on-chip static RAM + * Ensure 128-bit (16-byte) alignment + */ +extern char __SRAMBASE[]; + +static mcf5282BufferDescriptor_t * +mcf5282_bd_allocate(unsigned int count) +{ + static mcf5282BufferDescriptor_t *bdp = (mcf5282BufferDescriptor_t *)__SRAMBASE; + mcf5282BufferDescriptor_t *p = bdp; + + bdp += count; + if ((int)bdp & 0xF) + bdp = (mcf5282BufferDescriptor_t *)((char *)bdp + (16 - ((int)bdp & 0xF))); + return p; +} + + +/* + * Read MII register + * Busy-waits, but transfer time should be short! + */ +static int +getMII(int phyNumber, int regNumber) +{ + MCF5282_FEC_MMFR = (0x1 << 30) | + (0x2 << 28) | + (phyNumber << 23) | + (regNumber << 18) | + (0x2 << 16); + while ((MCF5282_FEC_EIR & MCF5282_FEC_EIR_MII) == 0); + MCF5282_FEC_EIR = MCF5282_FEC_EIR_MII; + return MCF5282_FEC_MMFR & 0xFFFF; +} + + +/* + * Write MII register + * Busy-waits, but transfer time should be short! + */ +static void +setMII(int phyNumber, int regNumber, int value) +{ + MCF5282_FEC_MMFR = (0x1 << 30) | + (0x1 << 28) | + (phyNumber << 23) | + (regNumber << 18) | + (0x2 << 16) | + (value & 0xFFFF); + while ((MCF5282_FEC_EIR & MCF5282_FEC_EIR_MII) == 0); + MCF5282_FEC_EIR = MCF5282_FEC_EIR_MII; +} + +static void +mcf5282_fec_initialize_hardware(struct mcf5282_enet_struct *sc) +{ + int i; + const unsigned char *hwaddr; + rtems_status_code status; + rtems_isr_entry old_handler; + uint32_t clock_speed = get_CPU_clock_speed(); + + /* + * Issue reset to FEC + */ + MCF5282_FEC_ECR = MCF5282_FEC_ECR_RESET; + rtems_task_wake_after(1); + MCF5282_FEC_ECR = 0; + + /* + * Configuration of I/O ports is done outside of this function + */ +#if 0 + imm->gpio.pbcnt |= MCF5282_GPIO_PBCNT_SET_FEC; /* Set up port b FEC pins */ +#endif + + /* + * Set our physical address + */ + hwaddr = sc->arpcom.ac_enaddr; + MCF5282_FEC_PALR = (hwaddr[0] << 24) | (hwaddr[1] << 16) | + (hwaddr[2] << 8) | (hwaddr[3] << 0); + MCF5282_FEC_PAUR = (hwaddr[4] << 24) | (hwaddr[5] << 16); + + + /* + * Clear the hash table + */ + MCF5282_FEC_GAUR = 0; + MCF5282_FEC_GALR = 0; + + /* + * Set up receive buffer size + */ + MCF5282_FEC_EMRBR = 1520; /* Standard Ethernet */ + + /* + * Allocate mbuf pointers + */ + sc->rxMbuf = malloc(sc->rxBdCount * sizeof *sc->rxMbuf, M_MBUF, M_NOWAIT); + sc->txMbuf = malloc(sc->txBdCount * sizeof *sc->txMbuf, M_MBUF, M_NOWAIT); + if (!sc->rxMbuf || !sc->txMbuf) + rtems_panic("No memory for mbuf pointers"); + + /* + * Set receiver and transmitter buffer descriptor bases + */ + sc->rxBdBase = mcf5282_bd_allocate(sc->rxBdCount); + sc->txBdBase = mcf5282_bd_allocate(sc->txBdCount); + MCF5282_FEC_ERDSR = (int)sc->rxBdBase; + MCF5282_FEC_ETDSR = (int)sc->txBdBase; + + /* + * Set up Receive Control Register: + * Not promiscuous + * MII mode + * Full duplex + * No loopback + */ + MCF5282_FEC_RCR = MCF5282_FEC_RCR_MAX_FL(MAX_MTU_SIZE) | + MCF5282_FEC_RCR_MII_MODE; + + /* + * Set up Transmit Control Register: + * Full duplex + * No heartbeat + */ + MCF5282_FEC_TCR = MCF5282_FEC_TCR_FDEN; + + /* + * Initialize statistic counters + */ + MCF5282_FEC_MIBC = MCF5282_FEC_MIBC_MIB_DISABLE; + { + vuint32 *vuip = &MCF5282_FEC_RMON_T_DROP; + while (vuip <= &MCF5282_FEC_IEEE_R_OCTETS_OK) + *vuip++ = 0; + } + MCF5282_FEC_MIBC = 0; + + /* + * Set MII speed to <= 2.5 MHz + */ + i = (clock_speed + 5000000 - 1) / 5000000; + MCF5282_FEC_MSCR = MCF5282_FEC_MSCR_MII_SPEED(i); + + /* + * Set PHYS to 100 Mb/s, full duplex + */ + setMII(1, 0, 0x2100); + setMII(1, 4, 0x0181); + setMII(1, 0, 0x0000); + rtems_task_wake_after(2); + sc->mii_sr2 = getMII(1, 17); + setMII(1, 18, 0x0072); + setMII(1, 0, 0x1000); + /* + * Set up receive buffer descriptors + */ + for (i = 0 ; i < sc->rxBdCount ; i++) + (sc->rxBdBase + i)->status = 0; + + /* + * Set up transmit buffer descriptors + */ + for (i = 0 ; i < sc->txBdCount ; i++) { + sc->txBdBase[i].status = 0; + sc->txMbuf[i] = NULL; + } + sc->txBdHead = sc->txBdTail = 0; + sc->txBdActiveCount = 0; + + /* + * Set up interrupts + */ + status = rtems_interrupt_catch( mcf5282_fec_tx_interrupt_handler, FEC_INTC0_TX_VECTOR, &old_handler ); + if (status != RTEMS_SUCCESSFUL) + rtems_panic ("Can't attach MCF5282 FEC TX interrupt handler: %s\n", + rtems_status_text(status)); + status = rtems_interrupt_catch(mcf5282_fec_rx_interrupt_handler, FEC_INTC0_RX_VECTOR, &old_handler); + if (status != RTEMS_SUCCESSFUL) + rtems_panic ("Can't attach MCF5282 FEC RX interrupt handler: %s\n", + rtems_status_text(status)); + MCF5282_INTC0_ICR23 = MCF5282_INTC_ICR_IL(FEC_IRQ_LEVEL) | + MCF5282_INTC_ICR_IP(FEC_IRQ_TX_PRIORITY); + MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_INT23 | MCF5282_INTC_IMRL_MASKALL); + MCF5282_INTC0_ICR27 = MCF5282_INTC_ICR_IL(FEC_IRQ_LEVEL) | + MCF5282_INTC_ICR_IP(FEC_IRQ_RX_PRIORITY); + MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_INT27 | MCF5282_INTC_IMRL_MASKALL); + + status = rtems_interrupt_catch(mcf5282_mii_interrupt_handler, MII_VECTOR, &old_handler); + if (status != RTEMS_SUCCESSFUL) + rtems_panic ("Can't attach MCF5282 FEC MII interrupt handler: %s\n", + rtems_status_text(status)); + MCF5282_EPORT_EPPAR &= ~MII_EPPAR; + MCF5282_EPORT_EPDDR &= ~MII_EPDDR; + MCF5282_EPORT_EPIER |= MII_EPIER; + MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_INT7 | MCF5282_INTC_IMRL_MASKALL); +} + +/* + * Soak up buffer descriptors that have been sent. + */ +void +fec_retire_tx_bd(volatile struct mcf5282_enet_struct *sc ) +{ + struct mbuf *m, *n; + uint16_t status; + + while ((sc->txBdActiveCount != 0) + && (((status = sc->txBdBase[sc->txBdTail].status) & MCF5282_FEC_TxBD_R) == 0)) { + if ((status & MCF5282_FEC_TxBD_TO1) == 0) { + m = sc->txMbuf[sc->txBdTail]; + MFREE(m, n); + } + if (++sc->txBdTail == sc->txBdCount) + sc->txBdTail = 0; + sc->txBdActiveCount--; + } +} + +static void +fec_rxDaemon (void *arg) +{ + volatile struct mcf5282_enet_struct *sc = (volatile struct mcf5282_enet_struct *)arg; + struct ifnet *ifp = (struct ifnet* )&sc->arpcom.ac_if; + struct mbuf *m; + volatile uint16_t status; + volatile mcf5282BufferDescriptor_t *rxBd; + int rxBdIndex; + + /* + * Allocate space for incoming packets and start reception + */ + for (rxBdIndex = 0 ; ;) { + rxBd = sc->rxBdBase + rxBdIndex; + MGETHDR(m, M_WAIT, MT_DATA); + MCLGET(m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod(m, void *); + rxBd->status = MCF5282_FEC_RxBD_E; + if (++rxBdIndex == sc->rxBdCount) { + rxBd->status |= MCF5282_FEC_RxBD_W; + break; + } + } + + /* + * Input packet handling loop + */ + /* Indicate we have some ready buffers available */ + MCF5282_FEC_RDAR = 0; + + rxBdIndex = 0; + for (;;) { + rxBd = sc->rxBdBase + rxBdIndex; + + /* + * Wait for packet if there's not one ready + */ + if ((status = rxBd->status) & MCF5282_FEC_RxBD_E) { + /* + * Clear old events. + */ + MCF5282_FEC_EIR = MCF5282_FEC_EIR_RXF; + + /* + * Wait for packet to arrive. + * Check the buffer descriptor before waiting for the event. + * This catches the case when a packet arrives between the + * `if' above, and the clearing of the RXF bit in the EIR. + */ + while ((status = rxBd->status) & MCF5282_FEC_RxBD_E) { + rtems_event_set events; + int level; + + rtems_interrupt_disable(level); + MCF5282_FEC_EIMR |= MCF5282_FEC_EIMR_RXF; + rtems_interrupt_enable(level); + rtems_bsdnet_event_receive (RX_INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + } + } + + /* + * Check that packet is valid + */ + if (status & MCF5282_FEC_RxBD_L) { + /* + * Pass the packet up the chain. + * FIXME: Packet filtering hook could be done here. + */ + struct ether_header *eh; + int len = rxBd->length - sizeof(uint32_t); + + /* + * Invalidate the cache and push the packet up. + * The cache is so small that it's more efficient to just + * invalidate the whole thing unless the packet is very small. + */ + m = sc->rxMbuf[rxBdIndex]; + if (len < 128) + rtems_cache_invalidate_multiple_data_lines(m->m_data, len); + else + rtems_cache_invalidate_entire_data(); + m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header); + eh = mtod(m, struct ether_header *); + m->m_data += sizeof(struct ether_header); + ether_input(ifp, eh, m); + + /* + * Allocate a new mbuf + */ + MGETHDR(m, M_WAIT, MT_DATA); + MCLGET(m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod(m, void *); + } + + /* + * Reenable the buffer descriptor + */ + rxBd->status = (status & MCF5282_FEC_RxBD_W) | MCF5282_FEC_RxBD_E; + MCF5282_FEC_RDAR = 0; + + /* + * Move to next buffer descriptor + */ + if (++rxBdIndex == sc->rxBdCount) + rxBdIndex = 0; + } +} + +static void +fec_sendpacket(struct ifnet *ifp, struct mbuf *m) +{ + struct mcf5282_enet_struct *sc = ifp->if_softc; + volatile mcf5282BufferDescriptor_t *firstTxBd, *txBd; + int nAdded; + uint16_t status; + + /* + * Free up buffer descriptors + */ + fec_retire_tx_bd(sc); + + /* + * Set up the transmit buffer descriptors. + * No need to pad out short packets since the + * hardware takes care of that automatically. + * No need to copy the packet to a contiguous buffer + * since the hardware is capable of scatter/gather DMA. + */ + nAdded = 0; + firstTxBd = sc->txBdBase + sc->txBdHead; + + while(m != NULL) { + /* + * Wait for buffer descriptor to become available + */ + if ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + /* + * Clear old events. + */ + MCF5282_FEC_EIR = MCF5282_FEC_EIR_TXF; + + /* + * Wait for buffer descriptor to become available. + * Check for buffer descriptors before waiting for the event. + * This catches the case when a buffer became available between + * the `if' above, and the clearing of the TXF bit in the EIR. + */ + fec_retire_tx_bd(sc); + while ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + rtems_event_set events; + int level; + + rtems_interrupt_disable(level); + MCF5282_FEC_EIMR |= MCF5282_FEC_EIMR_TXF; + rtems_interrupt_enable(level); + sc->txRawWait++; + rtems_bsdnet_event_receive(TX_INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + fec_retire_tx_bd(sc); + } + } + + /* + * Don't set the READY flag on the first fragment + * until the whole packet has been readied. + */ + status = nAdded ? MCF5282_FEC_TxBD_R : 0; + + /* + * The IP fragmentation routine in ip_output + * can produce fragments with zero length. + */ + if (m->m_len){ + char *p = mtod(m, char *); + int offset = (int) p & 0x3; + txBd = sc->txBdBase + sc->txBdHead; + if (offset == 0) { + txBd->buffer = p; + txBd->length = m->m_len; + sc->txMbuf[sc->txBdHead] = m; + m = m->m_next; + } + else { + /* + * Stupid FEC can't handle misaligned data! + * Move offending bytes to a local buffer. + * Use buffer descriptor TO1 bit to indicate this. + */ + int nmove = 4 - offset; + char *d = (char *)&sc->txMbuf[sc->txBdHead]; + status |= MCF5282_FEC_TxBD_TO1; + sc->txRealign++; + if (nmove > m->m_len) + nmove = m->m_len; + m->m_data += nmove; + m->m_len -= nmove; + txBd->buffer = d; + txBd->length = nmove; + while (nmove--) + *d++ = *p++; + if (m->m_len == 0) { + struct mbuf *n; + sc->txRealignDrop++; + MFREE(m, n); + m = n; + } + } + nAdded++; + if (++sc->txBdHead == sc->txBdCount) { + status |= MCF5282_FEC_TxBD_W; + sc->txBdHead = 0; + } + txBd->status = status; + } + else { + /* + * Just toss empty mbufs + */ + struct mbuf *n; + MFREE(m, n); + m = n; + } + } + if (nAdded) { + txBd->status = status | MCF5282_FEC_TxBD_R + | MCF5282_FEC_TxBD_L + | MCF5282_FEC_TxBD_TC; + if (nAdded > 1) + firstTxBd->status |= MCF5282_FEC_TxBD_R; + MCF5282_FEC_TDAR = 0; + sc->txBdActiveCount += nAdded; + } +} + +void +fec_txDaemon(void *arg) +{ + struct mcf5282_enet_struct *sc = (struct mcf5282_enet_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + rtems_event_set events; + + for (;;) { + /* + * Wait for packet + */ + rtems_bsdnet_event_receive(START_TRANSMIT_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; + fec_sendpacket(ifp, m); + } + ifp->if_flags &= ~IFF_OACTIVE; + } +} + + +/* + * Send packet (caller provides header). + */ +static void +mcf5282_enet_start(struct ifnet *ifp) +{ + struct mcf5282_enet_struct *sc = ifp->if_softc; + + rtems_bsdnet_event_send(sc->txDaemonTid, START_TRANSMIT_EVENT); + ifp->if_flags |= IFF_OACTIVE; +} + +static void +fec_init(void *arg) +{ + struct mcf5282_enet_struct *sc = arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + + if (sc->txDaemonTid == 0) { + /* + * Set up hardware + */ + mcf5282_fec_initialize_hardware(sc); + + /* + * Start driver tasks + */ + sc->txDaemonTid = rtems_bsdnet_newproc("FECtx", 4096, fec_txDaemon, sc); + sc->rxDaemonTid = rtems_bsdnet_newproc("FECrx", 4096, fec_rxDaemon, sc); + } + + /* + * Set flags appropriately + */ + if (ifp->if_flags & IFF_PROMISC) + MCF5282_FEC_RCR |= MCF5282_FEC_RCR_PROM; + else + MCF5282_FEC_RCR &= ~MCF5282_FEC_RCR_PROM; + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; + + /* + * Enable receiver and transmitter + */ + MCF5282_FEC_ECR = MCF5282_FEC_ECR_ETHER_EN; +} + + +static void +fec_stop(struct mcf5282_enet_struct *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + + ifp->if_flags &= ~IFF_RUNNING; + + /* + * Shut down receiver and transmitter + */ + MCF5282_FEC_ECR = 0x0; +} + +/* + * Show interface statistics + */ +static void +enet_stats(struct mcf5282_enet_struct *sc) +{ + printf(" Rx Interrupts:%-10lu", sc->rxInterrupts); + printf("Rx Packet Count:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_PACKETS); + printf(" Rx Broadcast:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_R_BC_PKT); + printf(" Rx Multicast:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_MC_PKT); + printf("CRC/Align error:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_CRC_ALIGN); + printf(" Rx Undersize:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_R_UNDERSIZE); + printf(" Rx Oversize:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_OVERSIZE); + printf(" Rx Fragment:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_FRAG); + printf(" Rx Jabber:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_R_JAB); + printf(" Rx 64:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_P64); + printf(" Rx 65-127:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_P65T0127); + printf(" Rx 128-255:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_R_P128TO255); + printf(" Rx 256-511:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_P256TO511); + printf(" Rx 511-1023:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_P512TO1023); + printf(" Rx 1024-2047:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_R_P1024TO2047); + printf(" Rx >=2048:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_GTE2048); + printf(" Rx Octets:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_OCTETS); + printf(" Rx Dropped:%-10lu\n", (uint32_t) MCF5282_FEC_IEEE_R_DROP); + printf(" Rx frame OK:%-10lu", (uint32_t) MCF5282_FEC_IEEE_R_FRAME_OK); + printf(" Rx CRC error:%-10lu", (uint32_t) MCF5282_FEC_IEEE_R_CRC); + printf(" Rx Align error:%-10lu\n", (uint32_t) MCF5282_FEC_IEEE_R_ALIGN); + printf(" FIFO Overflow:%-10lu", (uint32_t) MCF5282_FEC_IEEE_R_MACERR); + printf("Rx Pause Frames:%-10lu", (uint32_t) MCF5282_FEC_IEEE_R_FDXFC); + printf(" Rx Octets OK:%-10lu\n", (uint32_t) MCF5282_FEC_IEEE_R_OCTETS_OK); + printf(" Tx Interrupts:%-10lu", sc->txInterrupts); + printf("Tx Output Waits:%-10lu", sc->txRawWait); + printf("Tx mbuf realign:%-10lu\n", sc->txRealign); + printf("Tx realign drop:%-10lu", sc->txRealignDrop); + printf(" Tx Unaccounted:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_DROP); + printf("Tx Packet Count:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_T_PACKETS); + printf(" Tx Broadcast:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_BC_PKT); + printf(" Tx Multicast:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_MC_PKT); + printf("CRC/Align error:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_T_CRC_ALIGN); + printf(" Tx Undersize:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_UNDERSIZE); + printf(" Tx Oversize:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_OVERSIZE); + printf(" Tx Fragment:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_T_FRAG); + printf(" Tx Jabber:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_JAB); + printf(" Tx Collisions:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_COL); + printf(" Tx 64:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_T_P64); + printf(" Tx 65-127:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_P65TO127); + printf(" Tx 128-255:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_P128TO255); + printf(" Tx 256-511:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_T_P256TO511); + printf(" Tx 511-1023:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_P512TO1023); + printf(" Tx 1024-2047:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_P1024TO2047); + printf(" Tx >=2048:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_T_P_GTE2048); + printf(" Tx Octets:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_OCTETS); + printf(" Tx Dropped:%-10lu", (uint32_t) MCF5282_FEC_IEEE_T_DROP); + printf(" Tx Frame OK:%-10lu\n", (uint32_t) MCF5282_FEC_IEEE_T_FRAME_OK); + printf(" Tx 1 Collision:%-10lu", (uint32_t) MCF5282_FEC_IEEE_T_1COL); + printf("Tx >1 Collision:%-10lu", (uint32_t) MCF5282_FEC_IEEE_T_MCOL); + printf(" Tx Deferred:%-10lu\n", (uint32_t) MCF5282_FEC_IEEE_T_DEF); + printf(" Late Collision:%-10lu", (uint32_t) MCF5282_FEC_IEEE_T_LCOL); + printf(" Excessive Coll:%-10lu", (uint32_t) MCF5282_FEC_IEEE_T_EXCOL); + printf(" FIFO Underrun:%-10lu\n", (uint32_t) MCF5282_FEC_IEEE_T_MACERR); + printf(" Carrier Error:%-10lu", (uint32_t) MCF5282_FEC_IEEE_T_CSERR); + printf(" Tx SQE Error:%-10lu", (uint32_t) MCF5282_FEC_IEEE_T_SQE); + printf("Tx Pause Frames:%-10lu\n", (uint32_t) MCF5282_FEC_IEEE_T_FDXFC); + printf(" Tx Octets OK:%-10lu", (uint32_t) MCF5282_FEC_IEEE_T_OCTETS_OK); + printf(" MII interrupts:%-10lu\n", sc->miiInterrupts); + + printf(" EIR:%8.8lx ", (uint32_t) MCF5282_FEC_EIR); + printf("EIMR:%8.8lx ", (uint32_t) MCF5282_FEC_EIMR); + printf("RDAR:%8.8lx ", (uint32_t) MCF5282_FEC_RDAR); + printf("TDAR:%8.8lx\n", (uint32_t) MCF5282_FEC_TDAR); + printf(" ECR:%8.8lx ", (uint32_t) MCF5282_FEC_ECR); + printf(" RCR:%8.8lx ", (uint32_t) MCF5282_FEC_RCR); + printf(" TCR:%8.8lx\n", (uint32_t) MCF5282_FEC_TCR); + printf("FRBR:%8.8lx ", (uint32_t) MCF5282_FEC_FRBR); + printf("FRSR:%8.8lx\n", (uint32_t) MCF5282_FEC_FRSR); + if (sc->txBdActiveCount != 0) { + int i, n; + /* + * Yes, there are races here with adding and retiring descriptors, + * but this diagnostic is more for when things have backed up. + */ + printf("Transmit Buffer Descriptors (Tail %d, Head %d, Unretired %d):\n", + sc->txBdTail, + sc->txBdHead, + sc->txBdActiveCount); + i = sc->txBdTail; + for (n = 0 ; n < sc->txBdCount ; n++) { + if ((sc->txBdBase[i].status & MCF5282_FEC_TxBD_R) != 0) + printf(" %3d: status:%4.4x length:%-4d buffer:%p\n", + i, + sc->txBdBase[i].status, + sc->txBdBase[i].length, + sc->txBdBase[i].buffer); + if (++i == sc->txBdCount) + i = 0; + } + } +} + +static int +fec_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data) +{ + struct mcf5282_enet_struct *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: + fec_stop(sc); + break; + + case IFF_UP: + fec_init(sc); + break; + + case IFF_UP | IFF_RUNNING: + fec_stop(sc); + fec_init(sc); + break; + + default: + break; + } + break; + + case SIO_RTEMS_SHOW_STATS: + enet_stats(sc); + break; + + /* + * FIXME: All sorts of multicast commands need to be added here! + */ + default: + error = EINVAL; + break; + } + return error; +} + +int +rtems_fec_driver_attach(struct rtems_bsdnet_ifconfig *config, int attaching ) +{ + struct mcf5282_enet_struct *sc; + struct ifnet *ifp; + int mtu; + int unitNumber; + char *unitName; + unsigned char *hwaddr; + + /* + * Parse driver name + */ + if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0) + return 0; + + /* + * Is driver free? + */ + if ((unitNumber <= 0) || (unitNumber > NIFACES)) { + printf("Bad FEC unit number.\n"); + return 0; + } + sc = &enet_driver[unitNumber - 1]; + ifp = &sc->arpcom.ac_if; + if (ifp->if_softc != NULL) { + printf("Driver already in use.\n"); + return 0; + } + + /* + * Process options + */ + printf("%s%d: Ethernet address: ", unitName, unitNumber ); + if (config->hardware_address) { + hwaddr = config->hardware_address; + printf("%02x:%02x:%02x:%02x:%02x:%02x\n", + hwaddr[0], hwaddr[1], hwaddr[2], + hwaddr[3], hwaddr[4], hwaddr[5]); + memcpy(sc->arpcom.ac_enaddr, hwaddr, ETHER_ADDR_LEN); + } else { + printf("UNKNOWN\n"); + } + + if (config->mtu) + mtu = config->mtu; + else + mtu = ETHERMTU; + if (config->rbuf_count) + sc->rxBdCount = config->rbuf_count; + else + sc->rxBdCount = RX_BUF_COUNT; + if (config->xbuf_count) + sc->txBdCount = config->xbuf_count; + else + sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF; + + 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 = fec_init; + ifp->if_ioctl = fec_ioctl; + ifp->if_start = mcf5282_enet_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); + return 1; +}; + diff --git a/bsps/m68k/csb360/net/network.c b/bsps/m68k/csb360/net/network.c new file mode 100644 index 0000000000..47e0adb44d --- /dev/null +++ b/bsps/m68k/csb360/net/network.c @@ -0,0 +1,984 @@ +/* + * RTEMS/TCPIP driver for MCF5272 Ethernet + * + * Modified for MPC860 by Jay Monkman (jmonkman@lopingdog.com) + * + * This supports Ethernet on either SCC1 or the FEC of the MPC860T. + * Right now, we only do 10 Mbps, even with the FEC. The function + * rtems_enet_driver_attach determines which one to use. Currently, + * only one may be used at a time. + * + * Based on the MC68360 network driver by + * W. Eric Norum + * Saskatchewan Accelerator Laboratory + * University of Saskatchewan + * Saskatoon, Saskatchewan, CANADA + * eric@skatter.usask.ca + * + * This supports ethernet on SCC1. Right now, we only do 10 Mbps. + * + * Modifications by Darlene Stewart <Darlene.Stewart@iit.nrc.ca> + * and Charles-Antoine Gauthier <charles.gauthier@iit.nrc.ca> + * Copyright (c) 1999, National Research Council of Canada + */ + +#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ + +#include <bsp.h> +#include <stdio.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 <netinet/in.h> +#include <netinet/if_ether.h> + +#include <sys/types.h> +#include <sys/socket.h> + +/* + * Number of interfaces supported by this driver + */ +#define NIFACES 1 + +/* + * Default number of buffer descriptors set aside for this driver. + * The number of transmit buffer descriptors has to be quite large + * since a single frame often uses four or more buffer descriptors. + */ +#define RX_BUF_COUNT 32 +#define TX_BUF_COUNT 16 +#define TX_BD_PER_BUF 4 + +#define INET_ADDR_MAX_BUF_SIZE (sizeof "255.255.255.255") + + +/* + * RTEMS event used by interrupt handler to signal daemons. + * This must *not* be the same event used by the TCP/IP task synchronization. + */ +#define INTERRUPT_EVENT RTEMS_EVENT_1 + +/* + * RTEMS event used to start transmit daemon. + * This must not be the same as INTERRUPT_EVENT. + */ +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + +/* + * Receive buffer size -- Allow for a full ethernet packet plus CRC (1518). + * Round off to nearest multiple of RBUF_ALIGN. + */ +#define MAX_MTU_SIZE 1518 +#define RBUF_ALIGN 4 +#define RBUF_SIZE ((MAX_MTU_SIZE + RBUF_ALIGN) & ~RBUF_ALIGN) + +#if (MCLBYTES < RBUF_SIZE) +# error "Driver must have MCLBYTES > RBUF_SIZE" +#endif + +typedef struct { + uint16_t status; + uint16_t length; + void *buffer; +} bd_t; +#define MCF5272_BD_READY (bit(15)) +#define MCF5272_BD_TO1 (bit(14)) +#define MCF5272_BD_WRAP (bit(13)) +#define MCF5272_BD_TO2 (bit(12)) +#define MCF5272_BD_LAST (bit(11)) +#define MCF5272_BD_TX_CRC (bit(10)) +#define MCF5272_BD_DEFER (bit(9)) +#define MCF5272_BD_HEARTBEAT (bit(8)) +#define MCF5272_BD_LATE_COLLISION (bit(7)) +#define MCF5272_BD_RETRY_LIMIT (bit(6)) +#define MCF5272_BD_UNDERRUN (bit(1)) +#define MCF5272_BD_CARRIER_LOST (bit(0)) + +#define MCF5272_BD_EMPTY (bit(15)) +#define MCF5272_BD_RO1 (bit(14)) +#define MCF5272_BD_WRAP (bit(13)) +#define MCF5272_BD_RO2 (bit(12)) +#define MCF5272_BD_M (bit(8)) +#define MCF5272_BD_BC (bit(7)) +#define MCF5272_BD_MC (bit(6)) +#define MCF5272_BD_LONG (bit(5)) +#define MCF5272_BD_NONALIGNED (bit(4)) +#define MCF5272_BD_SHORT (bit(3)) +#define MCF5272_BD_CRC_ERROR (bit(2)) +#define MCF5272_BD_OVERRUN (bit(1)) +#define MCF5272_BD_TRUNCATED (bit(0)) + + +/* + * Per-device data + */ +struct mcf5272_enet_struct { + struct arpcom arpcom; + struct mbuf **rxMbuf; + struct mbuf **txMbuf; + int acceptBroadcast; + int rxBdCount; + int txBdCount; + int txBdHead; + int txBdTail; + int txBdActiveCount; + bd_t *rxBdBase; + bd_t *txBdBase; + rtems_id rxDaemonTid; + rtems_id txDaemonTid; + + /* + * Statistics + */ + unsigned long rxInterrupts; + unsigned long rxNotFirst; + unsigned long rxNotLast; + unsigned long rxGiant; + unsigned long rxNonOctet; + unsigned long rxRunt; + unsigned long rxBadCRC; + unsigned long rxOverrun; + unsigned long rxTruncated; + + unsigned long txInterrupts; + unsigned long txDeferred; + unsigned long txHeartbeat; + unsigned long txLateCollision; + unsigned long txRetryLimit; + unsigned long txUnderrun; + unsigned long txLostCarrier; + unsigned long txRawWait; +}; +static struct mcf5272_enet_struct enet_driver[NIFACES]; + + +void dump_enet_regs(void) +{ + printf("**************************************************************\n"); + printf("ecr: 0x%08x eir: 0x%08x eimr: 0x%08x ivsr: 0x%08x\n\r", + g_enet_regs->ecr, g_enet_regs->eir, + g_enet_regs->eimr, g_enet_regs->ivsr); + printf("rdar: 0x%08x tdar: 0x%08x mmfr: 0x%08x mscr: 0x%08x\n\r", + g_enet_regs->rdar, g_enet_regs->tdar, + g_enet_regs->mmfr, g_enet_regs->mscr); + printf("frbr: 0x%08x frsr: 0x%08x tfwr: 0x%08x tfsr: 0x%08x\n\r", + g_enet_regs->frbr, g_enet_regs->frsr, + g_enet_regs->tfwr, g_enet_regs->tfsr); + printf("rcr: 0x%08x mflr: 0x%08x tcr: 0x%08x malr: 0x%08x\n\r", + g_enet_regs->rcr, g_enet_regs->mflr, + g_enet_regs->tcr, g_enet_regs->malr); + printf("maur: 0x%08x htur: 0x%08x htlr: 0x%08x erdsr: 0x%08x\n\r", + g_enet_regs->maur, g_enet_regs->htur, + g_enet_regs->htlr, g_enet_regs->erdsr); + printf("etdsr: 0x%08x emrbr: 0x%08x\n\r", + g_enet_regs->etdsr, g_enet_regs->emrbr); +} + + + + +/*#define cp printk("%s:%d\n\r", __FUNCTION__, __LINE__) */ +#define cp +#define mcf5272_bd_allocate(_n_) malloc((_n_) * sizeof(bd_t), 0, M_NOWAIT) + + + +rtems_isr enet_rx_isr(rtems_vector_number vector) +{ + cp; + /* + * Frame received? + */ + if (g_enet_regs->eir & MCF5272_ENET_EIR_RXF) { + cp; + g_enet_regs->eir = MCF5272_ENET_EIR_RXF; + enet_driver[0].rxInterrupts++; + rtems_bsdnet_event_send (enet_driver[0].rxDaemonTid, INTERRUPT_EVENT); + } + cp; +} + +rtems_isr enet_tx_isr(rtems_vector_number vector) +{ + cp; + /* + * Buffer transmitted or transmitter error? + */ + if (g_enet_regs->eir & MCF5272_ENET_EIR_TXF) { + cp; + g_enet_regs->eir = MCF5272_ENET_EIR_TXF; + enet_driver[0].txInterrupts++; + rtems_bsdnet_event_send (enet_driver[0].txDaemonTid, INTERRUPT_EVENT); + } + cp; +} + + +/* + * Initialize the ethernet hardware + */ + + +static void +mcf5272_enet_initialize_hardware (struct mcf5272_enet_struct *sc) +{ + int i; + unsigned char *hwaddr; + uint32_t icr; + /* + * Issue reset to FEC + */ + g_enet_regs->ecr=0x1; + + /* + * Set the TX and RX fifo sizes. For now, we'll split it evenly + */ + /* If you uncomment these, the FEC will not work right. + g_enet_regs->r_fstart = ((g_enet_regs->r_bound & 0x3ff) >> 2) & 0x3ff; + g_enet_regs->x_fstart = 0; + */ + + /* Copy mac address to device */ + + hwaddr = sc->arpcom.ac_enaddr; + + g_enet_regs->malr = (hwaddr[0] << 24 | + hwaddr[1] << 16 | + hwaddr[2] << 8 | + hwaddr[3]); + g_enet_regs->maur = (hwaddr[4] << 24 | + hwaddr[5] << 16); + + /* + * Clear the hash table + */ + g_enet_regs->htlr = 0; + g_enet_regs->htur = 0; + + /* + * Set up receive buffer size + */ + g_enet_regs->emrbr = 0x5f0; /* set to 1520 */ + + /* + * Allocate mbuf pointers + */ + sc->rxMbuf = malloc (sc->rxBdCount * sizeof *sc->rxMbuf, + M_MBUF, M_NOWAIT); + sc->txMbuf = malloc (sc->txBdCount * sizeof *sc->txMbuf, + M_MBUF, M_NOWAIT); + if (!sc->rxMbuf || !sc->txMbuf) { + rtems_panic ("No memory for mbuf pointers"); + } + + /* + * Set receiver and transmitter buffer descriptor bases + */ + sc->rxBdBase = mcf5272_bd_allocate(sc->rxBdCount); + sc->txBdBase = mcf5272_bd_allocate(sc->txBdCount); + g_enet_regs->erdsr = (int)sc->rxBdBase; + g_enet_regs->etdsr = (int)sc->txBdBase; + + /* + * Set up Receive Control Register: + * Not promiscuous mode + * MII mode + * Full duplex + * No loopback + */ + g_enet_regs->rcr = 0x00000004; + + /* + * Set up Transmit Control Register: + * Full duplex + * No heartbeat + */ + g_enet_regs->tcr = 0x00000004; + + /* + * Set MII speed to 2.5 MHz for 25 Mhz system clock + */ + g_enet_regs->mscr = 0x0a; + g_enet_regs->mmfr = 0x58021000; + + /* + * Set up receive buffer descriptors + */ + for (i = 0 ; i < sc->rxBdCount ; i++) { + (sc->rxBdBase + i)->status = 0; + } + + /* + * Set up transmit buffer descriptors + */ + for (i = 0 ; i < sc->txBdCount ; i++) { + (sc->txBdBase + i)->status = 0; + sc->txMbuf[i] = NULL; + } + + sc->txBdHead = sc->txBdTail = 0; + sc->txBdActiveCount = 0; + + /* + * Mask all FEC interrupts and clear events + */ + g_enet_regs->eimr = (MCF5272_ENET_EIR_TXF | + MCF5272_ENET_EIR_RXF); + g_enet_regs->eir = ~0; + + /* + * Set up interrupts + */ + set_vector(enet_rx_isr, BSP_INTVEC_ERX, 1); + set_vector(enet_tx_isr, BSP_INTVEC_ETX, 1); + + /* Configure ethernet interrupts */ + icr = g_intctrl_regs->icr3; + icr = icr & ~((MCF5272_ICR3_ERX_MASK | MCF5272_ICR3_ERX_PI) | + (MCF5272_ICR3_ETX_MASK | MCF5272_ICR3_ETX_PI)); + icr |= ((MCF5272_ICR3_ERX_IPL(BSP_INTLVL_ERX) | MCF5272_ICR3_ERX_PI)| + (MCF5272_ICR3_ETX_IPL(BSP_INTLVL_ETX) | MCF5272_ICR3_ETX_PI)); + g_intctrl_regs->icr3 = icr; + +} + + +/* + * Soak up buffer descriptors that have been sent. + * Note that a buffer descriptor can't be retired as soon as it becomes + * ready. The MPC860 manual (MPC860UM/AD 07/98 Rev.1) and the MPC821 + * manual state that, "If an Ethernet frame is made up of multiple + * buffers, the user should not reuse the first buffer descriptor until + * the last buffer descriptor of the frame has had its ready bit cleared + * by the CPM". + */ +static void +mcf5272_enet_retire_tx_bd (struct mcf5272_enet_struct *sc) +{ + uint16_t status; + int i; + int nRetired; + struct mbuf *m, *n; + + i = sc->txBdTail; + nRetired = 0; + while ((sc->txBdActiveCount != 0) && + (((status = sc->txBdBase[i].status) & MCF5272_BD_READY) == 0)) { + /* + * See if anything went wrong + */ + if (status & (MCF5272_BD_DEFER | + MCF5272_BD_HEARTBEAT | + MCF5272_BD_LATE_COLLISION | + MCF5272_BD_RETRY_LIMIT | + MCF5272_BD_UNDERRUN | + MCF5272_BD_CARRIER_LOST)) { + /* + * Check for errors which stop the transmitter. + */ + if (status & (MCF5272_BD_LATE_COLLISION | + MCF5272_BD_RETRY_LIMIT | + MCF5272_BD_UNDERRUN)) { + if (status & MCF5272_BD_LATE_COLLISION) { + enet_driver[0].txLateCollision++; + } + if (status & MCF5272_BD_RETRY_LIMIT) { + enet_driver[0].txRetryLimit++; + } + if (status & MCF5272_BD_UNDERRUN) { + enet_driver[0].txUnderrun++; + } + } + if (status & MCF5272_BD_DEFER) { + enet_driver[0].txDeferred++; + } + if (status & MCF5272_BD_HEARTBEAT) { + enet_driver[0].txHeartbeat++; + } + if (status & MCF5272_BD_CARRIER_LOST) { + enet_driver[0].txLostCarrier++; + } + } + nRetired++; + if (status & MCF5272_BD_LAST) { + /* + * A full frame has been transmitted. + * Free all the associated buffer descriptors. + */ + sc->txBdActiveCount -= nRetired; + while (nRetired) { + nRetired--; + m = sc->txMbuf[sc->txBdTail]; + MFREE (m, n); + if (++sc->txBdTail == sc->txBdCount) + sc->txBdTail = 0; + } + } + if (++i == sc->txBdCount) { + i = 0; + } + } +} + +static void +mcf5272_enet_rxDaemon (void *arg) +{ + struct mcf5272_enet_struct *sc = (struct mcf5272_enet_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + uint16_t status; + bd_t *rxBd; + int rxBdIndex; + + /* + * Allocate space for incoming packets and start reception + */ + for (rxBdIndex = 0 ; ;) { + rxBd = sc->rxBdBase + rxBdIndex; + MGETHDR (m, M_WAIT, MT_DATA); + MCLGET (m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod (m, void *); + rxBd->status = MCF5272_BD_EMPTY; + g_enet_regs->rdar = 0x1000000; + if (++rxBdIndex == sc->rxBdCount) { + rxBd->status |= MCF5272_BD_WRAP; + break; + } + } + + /* + * Input packet handling loop + */ + rxBdIndex = 0; + for (;;) { + rxBd = sc->rxBdBase + rxBdIndex; + + /* + * Wait for packet if there's not one ready + */ + if ((status = rxBd->status) & MCF5272_BD_EMPTY) { + /* + * Clear old events + */ + g_enet_regs->eir = MCF5272_ENET_EIR_RXF; + + /* + * Wait for packet + * Note that the buffer descriptor is checked + * *before* the event wait -- this catches the + * possibility that a packet arrived between the + * `if' above, and the clearing of the event register. + */ + while ((status = rxBd->status) & MCF5272_BD_EMPTY) { + rtems_event_set events; + + /* + * Unmask RXF (Full frame received) event + */ + g_enet_regs->eir |= MCF5272_ENET_EIR_RXF; + + rtems_bsdnet_event_receive (INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + cp; + } + } + cp; + + /* + * Check that packet is valid + */ + if (status & MCF5272_BD_LAST) { + /* + * Pass the packet up the chain. + * FIXME: Packet filtering hook could be done here. + */ + struct ether_header *eh; + + m = sc->rxMbuf[rxBdIndex]; + m->m_len = m->m_pkthdr.len = (rxBd->length - + sizeof(uint32_t) - + sizeof(struct ether_header)); + eh = mtod (m, struct ether_header *); + m->m_data += sizeof(struct ether_header); + ether_input (ifp, eh, m); + + /* + * Allocate a new mbuf + */ + MGETHDR (m, M_WAIT, MT_DATA); + MCLGET (m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod (m, void *); + } + else { + /* + * Something went wrong with the reception + */ + if (!(status & MCF5272_BD_LAST)) { + sc->rxNotLast++; + } + if (status & MCF5272_BD_LONG) { + sc->rxGiant++; + } + if (status & MCF5272_BD_NONALIGNED) { + sc->rxNonOctet++; + } + if (status & MCF5272_BD_SHORT) { + sc->rxRunt++; + } + if (status & MCF5272_BD_CRC_ERROR) { + sc->rxBadCRC++; + } + if (status & MCF5272_BD_OVERRUN) { + sc->rxOverrun++; + } + if (status & MCF5272_BD_TRUNCATED) { + sc->rxTruncated++; + } + } + /* + * Reenable the buffer descriptor + */ + rxBd->status = (status & MCF5272_BD_WRAP) | MCF5272_BD_EMPTY; + g_enet_regs->rdar = 0x1000000; + /* + * Move to next buffer descriptor + */ + if (++rxBdIndex == sc->rxBdCount) { + rxBdIndex = 0; + } + } +} + +static void +mcf5272_enet_sendpacket (struct ifnet *ifp, struct mbuf *m) +{ + struct mcf5272_enet_struct *sc = ifp->if_softc; + volatile bd_t *firstTxBd, *txBd; + /* struct mbuf *l = NULL; */ + uint16_t status; + int nAdded; + cp; + + /* + * Free up buffer descriptors + */ + mcf5272_enet_retire_tx_bd (sc); + + /* + * Set up the transmit buffer descriptors. + * No need to pad out short packets since the + * hardware takes care of that automatically. + * No need to copy the packet to a contiguous buffer + * since the hardware is capable of scatter/gather DMA. + */ + nAdded = 0; + txBd = firstTxBd = sc->txBdBase + sc->txBdHead; + for (;;) { + cp; + /* + * Wait for buffer descriptor to become available. + */ + if ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + /* + * Clear old events + */ + g_enet_regs->eir = MCF5272_ENET_EIR_TXF; + + /* + * Wait for buffer descriptor to become available. + * Note that the buffer descriptors are checked + * *before* * entering the wait loop -- this catches + * the possibility that a buffer descriptor became + * available between the `if' above, and the clearing + * of the event register. + * This is to catch the case where the transmitter + * stops in the middle of a frame -- and only the + * last buffer descriptor in a frame can generate + * an interrupt. + */ + mcf5272_enet_retire_tx_bd (sc); + while ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + rtems_event_set events; + + cp; + /* + * Unmask TXB (buffer transmitted) and + * TXE (transmitter error) events. + */ + g_enet_regs->eir |= MCF5272_ENET_EIR_TXF; + rtems_bsdnet_event_receive (INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + cp; + mcf5272_enet_retire_tx_bd (sc); + } + } + + /* + * Don't set the READY flag till the + * whole packet has been readied. + */ + status = nAdded ? MCF5272_BD_READY : 0; + cp; + + /* + * FIXME: Why not deal with empty mbufs at at higher level? + * The IP fragmentation routine in ip_output + * can produce packet fragments with zero length. + * I think that ip_output should be changed to get + * rid of these zero-length mbufs, but for now, + * I'll deal with them here. + */ + if (m->m_len) { + cp; + /* + * Fill in the buffer descriptor + */ + txBd->buffer = mtod (m, void *); + txBd->length = m->m_len; + + sc->txMbuf[sc->txBdHead] = m; + nAdded++; + if (++sc->txBdHead == sc->txBdCount) { + status |= MCF5272_BD_WRAP; + sc->txBdHead = 0; + } + /* l = m;*/ + m = m->m_next; + } + else { + /* + * Just toss empty mbufs + */ + struct mbuf *n; + cp; + MFREE (m, n); + m = n; + /* + if (l != NULL) + l->m_next = m; + */ + } + + /* + * Set the transmit buffer status. + * Break out of the loop if this mbuf is the last in the frame. + */ + if (m == NULL) { + cp; + if (nAdded) { + cp; + status |= MCF5272_BD_LAST | MCF5272_BD_TX_CRC; + txBd->status = status; + firstTxBd->status |= MCF5272_BD_READY; + g_enet_regs->tdar = 0x1000000; + sc->txBdActiveCount += nAdded; + } + break; + } + txBd->status = status; + txBd = sc->txBdBase + sc->txBdHead; + } + cp; +/* + dump_enet_regs(); + dump_intctrl; +*/ + +} + + +void +mcf5272_enet_txDaemon (void *arg) +{ + struct mcf5272_enet_struct *sc = (struct mcf5272_enet_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + rtems_event_set events; + + cp; + for (;;) { + /* + * Wait for packet + */ + rtems_bsdnet_event_receive (START_TRANSMIT_EVENT, + RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, + &events); + cp; + /* + * Send packets till queue is empty + */ + cp; + for (;;) { + cp; + /* + * Get the next mbuf chain to transmit. + */ + IF_DEQUEUE(&ifp->if_snd, m); + if (!m) + break; + mcf5272_enet_sendpacket (ifp, m); + } + ifp->if_flags &= ~IFF_OACTIVE; + } +} + + +/* + * Send packet (caller provides header). + */ +static void +mcf5272_enet_start (struct ifnet *ifp) +{ + struct mcf5272_enet_struct *sc = ifp->if_softc; + + cp; + rtems_bsdnet_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT); + cp; + ifp->if_flags |= IFF_OACTIVE; +} + + +static void +mcf5272_enet_init (void *arg) +{ + struct mcf5272_enet_struct *sc = arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + + if (sc->txDaemonTid == 0) { + + /* + * Set up SCC hardware + */ + mcf5272_enet_initialize_hardware (sc); + + /* + * Start driver tasks + */ + sc->txDaemonTid = rtems_bsdnet_newproc("SCtx", + 4096, + mcf5272_enet_txDaemon, + sc); + sc->rxDaemonTid = rtems_bsdnet_newproc("SCrx", + 4096, + mcf5272_enet_rxDaemon, + sc); + + } + + /* + * Set flags appropriately + */ + if (ifp->if_flags & IFF_PROMISC) { + g_enet_regs->rcr |= 0x8; + } else { + g_enet_regs->rcr &= ~0x8; + } + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; + + /* + * Enable receiver and transmitter + */ + g_enet_regs->ecr = 0x2; +} + + +static void +mcf5272_enet_stop (struct mcf5272_enet_struct *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + + ifp->if_flags &= ~IFF_RUNNING; + + /* + * Shut down receiver and transmitter + */ + g_enet_regs->ecr = 0x0; +} + + +/* + * Show interface statistics + */ +static void +enet_stats (struct mcf5272_enet_struct *sc) +{ + printf (" Rx Interrupts:%-8lu", sc->rxInterrupts); + printf (" Not First:%-8lu", sc->rxNotFirst); + printf (" Not Last:%-8lu\n", sc->rxNotLast); + printf (" Giant:%-8lu", sc->rxGiant); + printf (" Runt:%-8lu", sc->rxRunt); + printf (" Non-octet:%-8lu\n", sc->rxNonOctet); + printf (" Bad CRC:%-8lu", sc->rxBadCRC); + printf (" Overrun:%-8lu", sc->rxOverrun); + printf (" Truncated:%-8lu\n", sc->rxTruncated); +/* printf (" Discarded:%-8lu\n", (unsigned long)mcf5272.scc1p.un.ethernet.disfc); */ + + printf (" Tx Interrupts:%-8lu", sc->txInterrupts); + printf (" Deferred:%-8lu", sc->txDeferred); + printf (" Missed Hearbeat:%-8lu\n", sc->txHeartbeat); + printf (" No Carrier:%-8lu", sc->txLostCarrier); + printf ("Retransmit Limit:%-8lu", sc->txRetryLimit); + printf (" Late Collision:%-8lu\n", sc->txLateCollision); + printf (" Underrun:%-8lu", sc->txUnderrun); + printf (" Raw output wait:%-8lu\n", sc->txRawWait); +} + + +/* + * Driver ioctl handler + */ +static int +mcf5272_enet_ioctl (struct ifnet *ifp, int command, caddr_t data) +{ + struct mcf5272_enet_struct *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: + mcf5272_enet_stop (sc); + break; + + case IFF_UP: + mcf5272_enet_init (sc); + break; + + case IFF_UP | IFF_RUNNING: + mcf5272_enet_stop (sc); + mcf5272_enet_init (sc); + break; + + default: + break; + } + break; + + case SIO_RTEMS_SHOW_STATS: + enet_stats (sc); + break; + + /* + * FIXME: All sorts of multicast commands need to be added here! + */ + default: + error = EINVAL; + break; + } + return error; +} + + +int +rtems_enet_driver_attach (struct rtems_bsdnet_ifconfig *config) +{ + struct mcf5272_enet_struct *sc; + struct ifnet *ifp; + int mtu; + int unitNumber; + char *unitName; + + /* + * Parse driver name + */ + unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName); + if (unitNumber < 0){ + return 0; + } + + /* + * Is driver free? + */ + if ((unitNumber < 0) || (unitNumber > NIFACES)) { + printf ("Bad unit number: %d.\n", unitNumber); + return 0; + } + + sc = &enet_driver[unitNumber]; + ifp = &sc->arpcom.ac_if; + if (ifp->if_softc != NULL) { + printf ("Driver already in use.\n"); + return 0; + } + + /* + * Process options + */ + + sc->arpcom.ac_enaddr[0] = (g_enet_regs->malr >> 24) & 0xff; + sc->arpcom.ac_enaddr[1] = (g_enet_regs->malr >> 16) & 0xff; + sc->arpcom.ac_enaddr[2] = (g_enet_regs->malr >> 8) & 0xff; + sc->arpcom.ac_enaddr[3] = (g_enet_regs->malr >> 0) & 0xff; + sc->arpcom.ac_enaddr[4] = (g_enet_regs->maur >> 24) & 0xff; + sc->arpcom.ac_enaddr[5] = (g_enet_regs->maur >> 16) & 0xff; + + if (config->mtu) { + mtu = config->mtu; + } else { + mtu = ETHERMTU; + } + + if (config->rbuf_count) { + sc->rxBdCount = config->rbuf_count; + } else { + sc->rxBdCount = RX_BUF_COUNT; + } + if (config->xbuf_count) { + sc->txBdCount = config->xbuf_count; + } else { + sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF; + } + 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 = mcf5272_enet_init; + ifp->if_ioctl = mcf5272_enet_ioctl; + ifp->if_start = mcf5272_enet_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); + cp; + ether_ifattach (ifp); + cp; + return 1; +}; + + diff --git a/bsps/m68k/gen68360/net/network.c b/bsps/m68k/gen68360/net/network.c new file mode 100644 index 0000000000..0a038d3348 --- /dev/null +++ b/bsps/m68k/gen68360/net/network.c @@ -0,0 +1,1062 @@ +/* + * RTEMS driver for M68360 SCC1 Ethernet + * + * W. Eric Norum + * Saskatchewan Accelerator Laboratory + * University of Saskatchewan + * Saskatoon, Saskatchewan, CANADA + * eric@skatter.usask.ca + */ + +#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ + +#include <bsp.h> +#include <rtems/m68k/m68360.h> +#include <stdio.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 <netinet/in.h> +#include <netinet/if_ether.h> + +/* + * Number of SCCs supported by this driver + */ +#define NSCCDRIVER 1 + +/* + * Default number of buffer descriptors set aside for this driver. + * The number of transmit buffer descriptors has to be quite large + * since a single frame often uses four or more buffer descriptors. + */ +#define RX_BUF_COUNT 15 +#define TX_BUF_COUNT 4 +#define TX_BD_PER_BUF 4 + +/* + * RTEMS event used by interrupt handler to signal driver tasks. + * This must not be any of the events used by the network task synchronization. + */ +#define INTERRUPT_EVENT RTEMS_EVENT_1 + +/* + * RTEMS event used to start transmit daemon. + * This must not be the same as INTERRUPT_EVENT. + */ +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + +/* + * Receive buffer size -- Allow for a full ethernet packet including CRC + */ +#define RBUF_SIZE 1520 + +#if (MCLBYTES < RBUF_SIZE) +# error "Driver must have MCLBYTES > RBUF_SIZE" +#endif + +/* + * Per-device data + */ +struct scc_softc { + struct arpcom arpcom; + struct mbuf **rxMbuf; + struct mbuf **txMbuf; + int acceptBroadcast; + int rxBdCount; + int txBdCount; + int txBdHead; + int txBdTail; + int txBdActiveCount; + m360BufferDescriptor_t *rxBdBase; + m360BufferDescriptor_t *txBdBase; + rtems_id rxDaemonTid; + rtems_id txDaemonTid; + + /* + * Statistics + */ + unsigned long rxInterrupts; + unsigned long rxNotFirst; + unsigned long rxNotLast; + unsigned long rxGiant; + unsigned long rxNonOctet; + unsigned long rxRunt; + unsigned long rxBadCRC; + unsigned long rxOverrun; + unsigned long rxCollision; + + unsigned long txInterrupts; + unsigned long txDeferred; + unsigned long txHeartbeat; + unsigned long txLateCollision; + unsigned long txRetryLimit; + unsigned long txUnderrun; + unsigned long txLostCarrier; + unsigned long txRawWait; + unsigned long txCoalesced; + unsigned long txCoalesceFailed; + unsigned long txRetry; +}; +static struct scc_softc scc_softc[NSCCDRIVER]; + +extern void *_RomBase; /* From linkcmds */ + +/* + * SCC1 interrupt handler + */ +static rtems_isr +m360Enet_interrupt_handler (rtems_vector_number v) +{ + /* + * Frame received? + */ + if ((m360.scc1.sccm & 0x8) && (m360.scc1.scce & 0x8)) { + m360.scc1.scce = 0x8; + m360.scc1.sccm &= ~0x8; + scc_softc[0].rxInterrupts++; + rtems_bsdnet_event_send (scc_softc[0].rxDaemonTid, INTERRUPT_EVENT); + } + + /* + * Buffer transmitted or transmitter error? + */ + if ((m360.scc1.sccm & 0x12) && (m360.scc1.scce & 0x12)) { + m360.scc1.scce = 0x12; + m360.scc1.sccm &= ~0x12; + scc_softc[0].txInterrupts++; + rtems_bsdnet_event_send (scc_softc[0].txDaemonTid, INTERRUPT_EVENT); + } + m360.cisr = 1UL << 30; /* Clear SCC1 interrupt-in-service bit */ +} + +/* + * Initialize the ethernet hardware + */ +static void +m360Enet_initialize_hardware (struct scc_softc *sc) +{ + int i; + unsigned char *hwaddr; + rtems_status_code status; + rtems_isr_entry old_handler; + + /* + * Configure port A CLK1, CLK2, TXD1 and RXD1 pins + */ + m360.papar |= 0x303; + m360.padir &= ~0x303; + m360.paodr &= ~0x303; + + /* + * Configure port C CTS1* and CD1* pins + */ + m360.pcpar &= ~0x30; + m360.pcdir &= ~0x30; + m360.pcso |= 0x30; + + /* + * Connect CLK1 and CLK2 to SCC1 + */ + m360.sicr &= ~0xFF; + m360.sicr |= (5 << 3) | 4; + + /* + * Allocate mbuf pointers + */ + sc->rxMbuf = malloc (sc->rxBdCount * sizeof *sc->rxMbuf, M_MBUF, M_NOWAIT); + sc->txMbuf = malloc (sc->txBdCount * sizeof *sc->txMbuf, M_MBUF, M_NOWAIT); + if (!sc->rxMbuf || !sc->txMbuf) + rtems_panic ("No memory for mbuf pointers"); + + /* + * Set receiver and transmitter buffer descriptor bases + */ + sc->rxBdBase = M360AllocateBufferDescriptors(sc->rxBdCount); + sc->txBdBase = M360AllocateBufferDescriptors(sc->txBdCount); + m360.scc1p.rbase = (char *)sc->rxBdBase - (char *)&m360; + m360.scc1p.tbase = (char *)sc->txBdBase - (char *)&m360; + + /* + * Send "Init parameters" command + */ + M360ExecuteRISC (M360_CR_OP_INIT_RX_TX | M360_CR_CHAN_SCC1); + + /* + * Set receive and transmit function codes + */ + m360.scc1p.rfcr = M360_RFCR_MOT | M360_RFCR_DMA_SPACE; + m360.scc1p.tfcr = M360_TFCR_MOT | M360_TFCR_DMA_SPACE; + + /* + * Set maximum receive buffer length + */ + m360.scc1p.mrblr = RBUF_SIZE; + + /* + * Set CRC parameters + */ + m360.scc1p.un.ethernet.c_pres = 0xFFFFFFFF; + m360.scc1p.un.ethernet.c_mask = 0xDEBB20E3; + + /* + * Clear diagnostic counters + */ + m360.scc1p.un.ethernet.crcec = 0; + m360.scc1p.un.ethernet.alec = 0; + m360.scc1p.un.ethernet.disfc = 0; + + /* + * Set pad value + */ + m360.scc1p.un.ethernet.pads = 0x8888; + + /* + * Set retry limit + */ + m360.scc1p.un.ethernet.ret_lim = 15; + + /* + * Set maximum and minimum frame length + */ + m360.scc1p.un.ethernet.mflr = 1518; + m360.scc1p.un.ethernet.minflr = 64; + m360.scc1p.un.ethernet.maxd1 = RBUF_SIZE; + m360.scc1p.un.ethernet.maxd2 = RBUF_SIZE; + + /* + * Clear group address hash table + */ + m360.scc1p.un.ethernet.gaddr1 = 0; + m360.scc1p.un.ethernet.gaddr2 = 0; + m360.scc1p.un.ethernet.gaddr3 = 0; + m360.scc1p.un.ethernet.gaddr4 = 0; + + /* + * Set our physical address + */ + hwaddr = sc->arpcom.ac_enaddr; + m360.scc1p.un.ethernet.paddr_h = (hwaddr[5] << 8) | hwaddr[4]; + m360.scc1p.un.ethernet.paddr_m = (hwaddr[3] << 8) | hwaddr[2]; + m360.scc1p.un.ethernet.paddr_l = (hwaddr[1] << 8) | hwaddr[0]; + + /* + * Aggressive retry + */ + m360.scc1p.un.ethernet.p_per = 0; + + /* + * Clear individual address hash table + */ + m360.scc1p.un.ethernet.iaddr1 = 0; + m360.scc1p.un.ethernet.iaddr2 = 0; + m360.scc1p.un.ethernet.iaddr3 = 0; + m360.scc1p.un.ethernet.iaddr4 = 0; + + /* + * Set up receive buffer descriptors + */ + for (i = 0 ; i < sc->rxBdCount ; i++) + (sc->rxBdBase + i)->status = 0; + + /* + * Set up transmit buffer descriptors + */ + for (i = 0 ; i < sc->txBdCount ; i++) { + (sc->txBdBase + i)->status = 0; + sc->txMbuf[i] = NULL; + } + sc->txBdHead = sc->txBdTail = 0; + sc->txBdActiveCount = 0; + + /* + * Clear any outstanding events + */ + m360.scc1.scce = 0xFFFF; + + /* + * Set up interrupts + */ + status = rtems_interrupt_catch (m360Enet_interrupt_handler, + (m360.cicr & 0xE0) | 0x1E, + &old_handler); + if (status != RTEMS_SUCCESSFUL) + rtems_panic ("Can't attach M360 SCC1 interrupt handler: %s\n", + rtems_status_text (status)); + m360.scc1.sccm = 0; /* No interrupts unmasked till necessary */ + m360.cimr |= (1UL << 30); /* Enable SCC1 interrupt */ + + /* + * Set up General SCC Mode Register + * Ethernet configuration + */ + m360.scc1.gsmr_h = 0x0; + m360.scc1.gsmr_l = 0x1088000c; + + /* + * Set up data synchronization register + * Ethernet synchronization pattern + */ + m360.scc1.dsr = 0xd555; + + /* + * Set up protocol-specific mode register + * Heartbeat check + * No force collision + * Discard short frames + * Individual address mode + * Ethernet CRC + * Not promisuous + * Ignore/accept broadcast packets as specified + * Normal backoff timer + * No loopback + * No input sample at end of frame + * 64-byte limit for late collision + * Wait 22 bits before looking for start of frame delimiter + * Disable full-duplex operation + */ + m360.scc1.psmr = 0x880A | (sc->acceptBroadcast ? 0 : 0x100); + + /* + * Enable the TENA (RTS1*) pin + */ +#if (defined (M68360_ATLAS_HSB)) + m360.pbpar |= 0x1000; + m360.pbdir |= 0x1000; +#else + m360.pcpar |= 0x1; + m360.pcdir &= ~0x1; +#endif +} + +/* + * Soak up buffer descriptors that have been sent + * Note that a buffer descriptor can't be retired as soon as it becomes + * ready. The MC68360 Errata (May 96) says that, "If an Ethernet frame is + * made up of multiple buffers, the user should not reuse the first buffer + * descriptor until the last buffer descriptor of the frame has had its + * ready bit cleared by the CPM". + */ +static void +m360Enet_retire_tx_bd (struct scc_softc *sc) +{ + uint16_t status; + int i; + int nRetired; + struct mbuf *m, *n; + int retries = 0; + int saveStatus = 0; + + i = sc->txBdTail; + nRetired = 0; + while ((sc->txBdActiveCount != 0) + && (((status = (sc->txBdBase + i)->status) & M360_BD_READY) == 0)) { + /* + * Check for errors which stop the transmitter. + */ + if (status & (M360_BD_LATE_COLLISION | + M360_BD_RETRY_LIMIT | + M360_BD_UNDERRUN)) { + int j; + + if (status & M360_BD_LATE_COLLISION) + sc->txLateCollision++; + if (status & M360_BD_RETRY_LIMIT) + sc->txRetryLimit++; + if (status & M360_BD_UNDERRUN) + sc->txUnderrun++; + + /* + * Reenable buffer descriptors + */ + j = sc->txBdTail; + for (;;) { + status = (sc->txBdBase + j)->status; + if (status & M360_BD_READY) + break; + (sc->txBdBase + j)->status = M360_BD_READY | + (status & (M360_BD_PAD | + M360_BD_WRAP | + M360_BD_INTERRUPT | + M360_BD_LAST | + M360_BD_TX_CRC)); + if (status & M360_BD_LAST) + break; + if (++j == sc->txBdCount) + j = 0; + } + + /* + * Move transmitter back to the first + * buffer descriptor in the frame. + */ + m360.scc1p._tbptr = m360.scc1p.tbase + + sc->txBdTail * sizeof (m360BufferDescriptor_t); + + /* + * Restart the transmitter + */ + M360ExecuteRISC (M360_CR_OP_RESTART_TX | M360_CR_CHAN_SCC1); + continue; + } + saveStatus |= status; + retries += (status >> 2) & 0xF; + nRetired++; + if (status & M360_BD_LAST) { + /* + * A full frame has been transmitted. + * Free all the associated buffer descriptors. + */ + if (saveStatus & M360_BD_DEFER) + sc->txDeferred++; + if (saveStatus & M360_BD_HEARTBEAT) + sc->txHeartbeat++; + if (saveStatus & M360_BD_CARRIER_LOST) + sc->txLostCarrier++; + saveStatus = 0; + sc->txRetry += retries; + retries = 0; + sc->txBdActiveCount -= nRetired; + while (nRetired) { + nRetired--; + m = sc->txMbuf[sc->txBdTail]; + MFREE (m, n); + if (++sc->txBdTail == sc->txBdCount) + sc->txBdTail = 0; + } + } + if (++i == sc->txBdCount) + i = 0; + } +} + +/* + * SCC reader task + */ +static void +scc_rxDaemon (void *arg) +{ + struct scc_softc *sc = (struct scc_softc *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + uint16_t status; + volatile m360BufferDescriptor_t *rxBd; + int rxBdIndex; + + /* + * Allocate space for incoming packets and start reception + */ + for (rxBdIndex = 0 ; ;) { + rxBd = sc->rxBdBase + rxBdIndex; + MGETHDR (m, M_WAIT, MT_DATA); + MCLGET (m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod (m, void *); + if (++rxBdIndex == sc->rxBdCount) { + rxBd->status = M360_BD_EMPTY | M360_BD_INTERRUPT | M360_BD_WRAP; + break; + } + rxBd->status = M360_BD_EMPTY | M360_BD_INTERRUPT; + } + + /* + * Input packet handling loop + */ + rxBdIndex = 0; + for (;;) { + rxBd = sc->rxBdBase + rxBdIndex; + + /* + * Wait for packet if there's not one ready + */ + if ((status = rxBd->status) & M360_BD_EMPTY) { + /* + * Clear old events + */ + m360.scc1.scce = 0x8; + + /* + * Wait for packet + * Note that the buffer descriptor is checked + * *before* the event wait -- this catches the + * possibility that a packet arrived between the + * `if' above, and the clearing of the event register. + */ + while ((status = rxBd->status) & M360_BD_EMPTY) { + rtems_interrupt_level level; + rtems_event_set events; + + /* + * Unmask RXF (Full frame received) event + */ + rtems_interrupt_disable (level); + m360.scc1.sccm |= 0x8; + rtems_interrupt_enable (level); + + rtems_bsdnet_event_receive (INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + } + } + + /* + * Check that packet is valid + */ + if ((status & (M360_BD_LAST | + M360_BD_FIRST_IN_FRAME | + M360_BD_LONG | + M360_BD_NONALIGNED | + M360_BD_SHORT | + M360_BD_CRC_ERROR | + M360_BD_OVERRUN | + M360_BD_COLLISION)) == + (M360_BD_LAST | + M360_BD_FIRST_IN_FRAME)) { + /* + * Pass the packet up the chain. + * FIXME: Packet filtering hook could be done here. + */ + struct ether_header *eh; + + m = sc->rxMbuf[rxBdIndex]; + m->m_len = m->m_pkthdr.len = rxBd->length - + sizeof(uint32_t) - + sizeof(struct ether_header); + eh = mtod (m, struct ether_header *); + m->m_data += sizeof(struct ether_header); + ether_input (ifp, eh, m); + + /* + * Allocate a new mbuf + */ + MGETHDR (m, M_WAIT, MT_DATA); + MCLGET (m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod (m, void *); + } + else { + /* + * Something went wrong with the reception + */ + if (!(status & M360_BD_LAST)) + sc->rxNotLast++; + if (!(status & M360_BD_FIRST_IN_FRAME)) + sc->rxNotFirst++; + if (status & M360_BD_LONG) + sc->rxGiant++; + if (status & M360_BD_NONALIGNED) + sc->rxNonOctet++; + if (status & M360_BD_SHORT) + sc->rxRunt++; + if (status & M360_BD_CRC_ERROR) + sc->rxBadCRC++; + if (status & M360_BD_OVERRUN) + sc->rxOverrun++; + if (status & M360_BD_COLLISION) + sc->rxCollision++; + } + + /* + * Reenable the buffer descriptor + */ + rxBd->status = (status & (M360_BD_WRAP | M360_BD_INTERRUPT)) | M360_BD_EMPTY; + + /* + * Move to next buffer descriptor + */ + if (++rxBdIndex == sc->rxBdCount) + rxBdIndex = 0; + } +} + +static void +sendpacket (struct ifnet *ifp, struct mbuf *m) +{ + struct scc_softc *sc = ifp->if_softc; + volatile m360BufferDescriptor_t *firstTxBd, *txBd; + struct mbuf *l = NULL; + uint16_t status; + int nAdded; + + /* + * Free up buffer descriptors + */ + m360Enet_retire_tx_bd (sc); + + /* + * Set up the transmit buffer descriptors. + * No need to pad out short packets since the + * hardware takes care of that automatically. + * No need to copy the packet to a contiguous buffer + * since the hardware is capable of scatter/gather DMA. + */ + status = 0; + nAdded = 0; + txBd = firstTxBd = sc->txBdBase + sc->txBdHead; + while (m) { + /* + * There are more mbufs in the packet than there + * are transmit buffer descriptors. + * Coalesce into a single buffer. + */ + if (nAdded == sc->txBdCount) { + struct mbuf *nm; + int j; + char *dest; + + /* + * Get the pointer to the first mbuf of the packet + */ + if (sc->txBdTail != sc->txBdHead) + rtems_panic ("sendpacket coalesce"); + m = sc->txMbuf[sc->txBdTail]; + + /* + * Rescind the buffer descriptor READY bits + */ + for (j = 0 ; j < sc->txBdCount ; j++) + (sc->txBdBase + j)->status = 0; + + /* + * Allocate an mbuf cluster + * Toss the packet if allocation fails + */ + MGETHDR (nm, M_DONTWAIT, MT_DATA); + if (nm == NULL) { + sc->txCoalesceFailed++; + m_freem (m); + return; + } + MCLGET (nm, M_DONTWAIT); + if (nm->m_ext.ext_buf == NULL) { + sc->txCoalesceFailed++; + m_freem (m); + m_free (nm); + return; + } + nm->m_pkthdr = m->m_pkthdr; + nm->m_len = nm->m_pkthdr.len; + + /* + * Copy data from packet chain to mbuf cluster + */ + sc->txCoalesced++; + dest = nm->m_ext.ext_buf; + while (m) { + struct mbuf *n; + + if (m->m_len) { + memcpy (dest, mtod(m, caddr_t), m->m_len); + dest += m->m_len; + } + MFREE (m, n); + m = n; + } + + /* + * Redo the send with the new mbuf cluster + */ + m = nm; + nAdded = 0; + status = 0; + continue; + } + + /* + * Wait for buffer descriptor to become available. + */ + if ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + /* + * Clear old events + */ + m360.scc1.scce = 0x12; + + /* + * Wait for buffer descriptor to become available. + * Note that the buffer descriptors are checked + * *before* entering the wait loop -- this catches + * the possibility that a buffer descriptor became + * available between the `if' above, and the clearing + * of the event register. + * This is to catch the case where the transmitter + * stops in the middle of a frame -- and only the + * last buffer descriptor in a frame can generate + * an interrupt. + */ + m360Enet_retire_tx_bd (sc); + while ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + rtems_interrupt_level level; + rtems_event_set events; + + /* + * Unmask TXB (buffer transmitted) and + * TXE (transmitter error) events. + */ + rtems_interrupt_disable (level); + m360.scc1.sccm |= 0x12; + rtems_interrupt_enable (level); + rtems_bsdnet_event_receive (INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + m360Enet_retire_tx_bd (sc); + } + } + + /* + * The IP fragmentation routine in ip_output + * can produce packet fragments with zero length. + */ + if (m->m_len) { + /* + * Fill in the buffer descriptor. + * Don't set the READY flag in the first buffer + * descriptor till the whole packet has been readied. + */ + txBd = sc->txBdBase + sc->txBdHead; + txBd->buffer = mtod (m, void *); + txBd->length = m->m_len; + sc->txMbuf[sc->txBdHead] = m; + status = nAdded ? M360_BD_READY : 0; + if (++sc->txBdHead == sc->txBdCount) { + status |= M360_BD_WRAP; + sc->txBdHead = 0; + } + txBd->status = status; + l = m; + m = m->m_next; + nAdded++; + } + else { + /* + * Just toss empty mbufs + */ + struct mbuf *n; + MFREE (m, n); + m = n; + if (l != NULL) + l->m_next = m; + } + } + if (nAdded) { + /* + * Send the packet + */ + txBd->status = status | M360_BD_PAD | M360_BD_LAST | M360_BD_TX_CRC | M360_BD_INTERRUPT; + firstTxBd->status |= M360_BD_READY; + sc->txBdActiveCount += nAdded; + } +} + +/* + * Driver transmit daemon + */ +void +scc_txDaemon (void *arg) +{ + struct scc_softc *sc = (struct scc_softc *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + rtems_event_set events; + + for (;;) { + /* + * Wait for packet + */ + rtems_bsdnet_event_receive (START_TRANSMIT_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; + sendpacket (ifp, m); + } + ifp->if_flags &= ~IFF_OACTIVE; + } +} + +/* + * Send packet (caller provides header). + */ +static void +scc_start (struct ifnet *ifp) +{ + struct scc_softc *sc = ifp->if_softc; + + rtems_bsdnet_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT); + ifp->if_flags |= IFF_OACTIVE; +} + +/* + * Initialize and start the device + */ +static void +scc_init (void *arg) +{ + struct scc_softc *sc = arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + + if (sc->txDaemonTid == 0) { + + /* + * Set up SCC hardware + */ + m360Enet_initialize_hardware (sc); + + /* + * Start driver tasks + */ + sc->txDaemonTid = rtems_bsdnet_newproc ("SCtx", 4096, scc_txDaemon, sc); + sc->rxDaemonTid = rtems_bsdnet_newproc ("SCrx", 4096, scc_rxDaemon, sc); + + } + + /* + * Set flags appropriately + */ + if (ifp->if_flags & IFF_PROMISC) + m360.scc1.psmr |= 0x200; + else + m360.scc1.psmr &= ~0x200; + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; + + /* + * Enable receiver and transmitter + */ + m360.scc1.gsmr_l |= 0x30; +} + +/* + * Stop the device + */ +static void +scc_stop (struct scc_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + + ifp->if_flags &= ~IFF_RUNNING; + + /* + * Shut down receiver and transmitter + */ + m360.scc1.gsmr_l &= ~0x30; +} + +/* + * Show interface statistics + */ +static void +scc_stats (struct scc_softc *sc) +{ + printf (" Rx Interrupts:%-8lu", sc->rxInterrupts); + printf (" Not First:%-8lu", sc->rxNotFirst); + printf (" Not Last:%-8lu\n", sc->rxNotLast); + printf (" Giant:%-8lu", sc->rxGiant); + printf (" Runt:%-8lu", sc->rxRunt); + printf (" Non-octet:%-8lu\n", sc->rxNonOctet); + printf (" Bad CRC:%-8lu", sc->rxBadCRC); + printf (" Overrun:%-8lu", sc->rxOverrun); + printf (" Collision:%-8lu\n", sc->rxCollision); + printf (" Discarded:%-8lu\n", (unsigned long)m360.scc1p.un.ethernet.disfc); + + printf (" Tx Interrupts:%-8lu", sc->txInterrupts); + printf (" Deferred:%-8lu", sc->txDeferred); + printf (" Missed Hearbeat:%-8lu\n", sc->txHeartbeat); + printf (" No Carrier:%-8lu", sc->txLostCarrier); + printf ("Retransmit Limit:%-8lu", sc->txRetryLimit); + printf (" Late Collision:%-8lu\n", sc->txLateCollision); + printf (" Underrun:%-8lu", sc->txUnderrun); + printf (" Raw output wait:%-8lu", sc->txRawWait); + printf (" Coalesced:%-8lu\n", sc->txCoalesced); + printf (" Coalesce failed:%-8lu", sc->txCoalesceFailed); + printf (" Retries:%-8lu\n", sc->txRetry); +} + +/* + * Driver ioctl handler + */ +static int +scc_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data) +{ + struct scc_softc *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: + scc_stop (sc); + break; + + case IFF_UP: + scc_init (sc); + break; + + case IFF_UP | IFF_RUNNING: + scc_stop (sc); + scc_init (sc); + break; + + default: + break; + } + break; + + case SIO_RTEMS_SHOW_STATS: + scc_stats (sc); + break; + + /* + * FIXME: All sorts of multicast commands need to be added here! + */ + default: + error = EINVAL; + break; + } + return error; +} + +/* + * Attach an SCC driver to the system + */ +int +rtems_scc1_driver_attach (struct rtems_bsdnet_ifconfig *config, int attaching) +{ + struct scc_softc *sc; + struct ifnet *ifp; + int mtu; + int unitNumber; + char *unitName; + + /* + * Make sure we're really being attached + */ + if (!attaching) { + printf ("SCC1 driver can not be detached.\n"); + return 0; + } + + /* + * Parse driver name + */ + if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0) + return 0; + + /* + * Is driver free? + */ + if ((unitNumber <= 0) || (unitNumber > NSCCDRIVER)) { + printf ("Bad SCC unit number.\n"); + return 0; + } + sc = &scc_softc[unitNumber - 1]; + ifp = &sc->arpcom.ac_if; + if (ifp->if_softc != NULL) { + printf ("Driver already in use.\n"); + return 0; + } + + /* + * Process options + */ + if (config->hardware_address) { + memcpy (sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); + } + else { + /* + * The first 4 bytes of the bootstrap prom + * contain the value loaded into the stack + * pointer as part of the CPU32's hardware + * reset exception handler. The following + * 4 bytes contain the value loaded into the + * program counter. The boards' Ethernet + * address is stored in the six bytes + * immediately preceding this initial + * program counter value. + * + * See start360/start360.s. + */ + const unsigned long *ExceptionVectors; + const unsigned char *entryPoint; + + /* + * Sanity check -- assume entry point must be + * within 1 MByte of beginning of boot ROM. + */ + ExceptionVectors = (const unsigned long *)&_RomBase; + entryPoint = (const unsigned char *)ExceptionVectors[1]; + if (((unsigned long)entryPoint - (unsigned long)ExceptionVectors) + >= (1 * 1024 * 1024)) { + printf ("Warning -- Ethernet address can not be found in bootstrap PROM.\n"); + sc->arpcom.ac_enaddr[0] = 0x08; + sc->arpcom.ac_enaddr[1] = 0xF3; + sc->arpcom.ac_enaddr[2] = 0x3E; + sc->arpcom.ac_enaddr[3] = 0xC2; + sc->arpcom.ac_enaddr[4] = 0x7E; + sc->arpcom.ac_enaddr[5] = 0x38; + } + else { + memcpy (sc->arpcom.ac_enaddr, entryPoint - ETHER_ADDR_LEN, ETHER_ADDR_LEN); + } + } + if (config->mtu) + mtu = config->mtu; + else + mtu = ETHERMTU; + if (config->rbuf_count) + sc->rxBdCount = config->rbuf_count; + else + sc->rxBdCount = RX_BUF_COUNT; + if (config->xbuf_count) + sc->txBdCount = config->xbuf_count; + else + sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF; + 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 = scc_init; + ifp->if_ioctl = scc_ioctl; + ifp->if_start = scc_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); + return 1; +} diff --git a/bsps/m68k/genmcf548x/net/network.c b/bsps/m68k/genmcf548x/net/network.c new file mode 100644 index 0000000000..13cb5fbdce --- /dev/null +++ b/bsps/m68k/genmcf548x/net/network.c @@ -0,0 +1,1696 @@ +/*===============================================================*\ +| Project: RTEMS generic MCF548X BSP | ++-----------------------------------------------------------------+ +| Partially based on the code references which are named below. | +| Adaptions, modifications, enhancements and any recent parts of | +| the code are: | +| Copyright (c) 2009 | +| Embedded Brains GmbH | +| Obere Lagerstr. 30 | +| D-82178 Puchheim | +| Germany | +| rtems@embedded-brains.de | ++-----------------------------------------------------------------+ +| 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. | +| | ++-----------------------------------------------------------------+ +| this file contains the networking driver | +\*===============================================================*/ +/* + * RTEMS/TCPIP driver for MCF548X FEC Ethernet + * + * Modified for Motorola MPC5200 by Thomas Doerfler, <Thomas.Doerfler@imd-systems.de> + * COPYRIGHT (c) 2003, IMD + * + * Modified for Motorola IceCube (mgt5100) by Peter Rasmussen <prasmus@ipr-engineering.de> + * COPYRIGHT (c) 2003, IPR Engineering + * + * Parts of code are also under property of Driver Information Systems and based + * on Motorola Proprietary Information. + * COPYRIGHT (c) 2002 MOTOROLA INC. + * + * Modified for Motorola MCF548X by Thomas Doerfler, <Thomas.Doerfler@imd-systems.de> + * COPYRIGHT (c) 2009, IMD + * + */ + +#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ + +#include <rtems.h> +#include <rtems/error.h> +#include <rtems/rtems_bsdnet.h> +#include <stdio.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 <net/if_var.h> + +#include <bsp.h> +#include <bsp/irq-generic.h> +#include <mcf548x/mcf548x.h> +#include <rtems/rtems_mii_ioctl.h> +#include <errno.h> + +/* freescale-api-specifics... */ +#include <mcf548x/MCD_dma.h> +#include <mcf548x/mcdma_glue.h> + +/* + * Number of interfaces supported by this driver + */ +#define NIFACES 2 + +#define FEC_WATCHDOG_TIMEOUT 5 /* check media every 5 seconds */ + +#define DMA_BD_RX_NUM 32 /* Number of receive buffer descriptors */ +#define DMA_BD_TX_NUM 32 /* Number of transmit buffer descriptors */ + +#define FEC_EVENT RTEMS_EVENT_0 + +/* + * internal SRAM + * Layout: + * - RxBD channel 0 + * - TxBD channel 0 + * - RxBD channel 1 + * - TxBD channel 1 + * - DMA task memory + */ +extern char _SysSramBase[]; +#define SRAM_RXBD_BASE(base,chan) (((MCD_bufDescFec*)(base)) \ + +((chan) \ + *(DMA_BD_RX_NUM+DMA_BD_TX_NUM))) + +#define SRAM_TXBD_BASE(base,chan) (((MCD_bufDescFec*)(base)) \ + +((chan) \ + *(DMA_BD_RX_NUM+DMA_BD_TX_NUM) \ + +DMA_BD_RX_NUM)) + +#define SRAM_DMA_BASE(base) ((void *)SRAM_RXBD_BASE(base,NIFACES+1)) + + +#undef ETH_DEBUG + +/* + * Default number of buffer descriptors set aside for this driver. + * The number of transmit buffer descriptors has to be quite large + * since a single frame often uses four or more buffer descriptors. + */ +#define RX_BUF_COUNT DMA_BD_RX_NUM +#define TX_BUF_COUNT DMA_BD_TX_NUM +#define TX_BD_PER_BUF 1 + +#define INET_ADDR_MAX_BUF_SIZE (sizeof "255.255.255.255") + +#define MCF548X_FEC0_IRQ_VECTOR (39+64) +#define MCF548X_FEC1_IRQ_VECTOR (38+64) + +#define MCF548X_FEC_IRQ_VECTOR(chan) (MCF548X_FEC0_IRQ_VECTOR \ + +(chan)*(MCF548X_FEC1_IRQ_VECTOR \ + -MCF548X_FEC0_IRQ_VECTOR)) + +#define MCF548X_FEC_VECTOR2CHAN(vector) (((int)(vector)-MCF548X_FEC0_IRQ_VECTOR) \ + /(MCF548X_FEC1_IRQ_VECTOR \ + -MCF548X_FEC0_IRQ_VECTOR)) + +#define MCDMA_FEC_RX_CHAN(chan) (0 + NIFACES*(chan)) +#define MCDMA_FEC_TX_CHAN(chan) (1 + NIFACES*(chan)) + +#define MCF548X_FEC0_RX_INITIATOR (16) +#define MCF548X_FEC1_RX_INITIATOR (30) +#define MCF548X_FEC_RX_INITIATOR(chan) (MCF548X_FEC0_RX_INITIATOR \ + +(chan)*(MCF548X_FEC1_RX_INITIATOR \ + -MCF548X_FEC0_RX_INITIATOR)) +#define MCF548X_FEC0_TX_INITIATOR (17) +#define MCF548X_FEC1_TX_INITIATOR (31) +#define MCF548X_FEC_TX_INITIATOR(chan) (MCF548X_FEC0_TX_INITIATOR \ + +(chan)*(MCF548X_FEC1_TX_INITIATOR \ + -MCF548X_FEC0_TX_INITIATOR)) + +/* BD and parameters are stored in SRAM(refer to sdma.h) */ +#define MCF548X_FEC_BD_BASE ETH_BD_BASE + +/* RBD bits definitions */ +#define MCF548X_FEC_RBD_EMPTY 0x8000 /* Buffer is empty */ +#define MCF548X_FEC_RBD_WRAP 0x2000 /* Last BD in ring */ +#define MCF548X_FEC_RBD_INT 0x1000 /* Interrupt */ +#define MCF548X_FEC_RBD_LAST 0x0800 /* Buffer is last in frame(useless) */ +#define MCF548X_FEC_RBD_MISS 0x0100 /* Miss bit for prom mode */ +#define MCF548X_FEC_RBD_BC 0x0080 /* The received frame is broadcast frame */ +#define MCF548X_FEC_RBD_MC 0x0040 /* The received frame is multicast frame */ +#define MCF548X_FEC_RBD_LG 0x0020 /* Frame length violation */ +#define MCF548X_FEC_RBD_NO 0x0010 /* Nonoctet align frame */ +#define MCF548X_FEC_RBD_SH 0x0008 /* Short frame, FEC does not support SH and this bit is always cleared */ +#define MCF548X_FEC_RBD_CR 0x0004 /* CRC error */ +#define MCF548X_FEC_RBD_OV 0x0002 /* Receive FIFO overrun */ +#define MCF548X_FEC_RBD_TR 0x0001 /* The receive frame is truncated */ +#define MCF548X_FEC_RBD_ERR (MCF548X_FEC_RBD_LG | \ + MCF548X_FEC_RBD_NO | \ + MCF548X_FEC_RBD_CR | \ + MCF548X_FEC_RBD_OV | \ + MCF548X_FEC_RBD_TR) + +/* TBD bits definitions */ +#define MCF548X_FEC_TBD_READY 0x8000 /* Buffer is ready */ +#define MCF548X_FEC_TBD_WRAP 0x2000 /* Last BD in ring */ +#define MCF548X_FEC_TBD_INT 0x1000 /* Interrupt */ +#define MCF548X_FEC_TBD_LAST 0x0800 /* Buffer is last in frame */ +#define MCF548X_FEC_TBD_TC 0x0400 /* Transmit the CRC */ +#define MCF548X_FEC_TBD_ABC 0x0200 /* Append bad CRC */ + +#define FEC_INTR_MASK_USED \ +(MCF548X_FEC_EIMR_LC | MCF548X_FEC_EIMR_RL | \ + MCF548X_FEC_EIMR_XFUN | MCF548X_FEC_EIMR_XFERR | MCF548X_FEC_EIMR_RFERR) + +typedef enum { + FEC_STATE_RESTART_0, + FEC_STATE_RESTART_1, + FEC_STATE_NORMAL, +} fec_state; + +/* + * Device data + */ +struct mcf548x_enet_struct { + struct arpcom arpcom; + struct mbuf **rxMbuf; + struct mbuf **txMbuf; + int chan; + fec_state state; + int acceptBroadcast; + int rxBdCount; + int txBdCount; + MCD_bufDescFec *rxBd; + MCD_bufDescFec *txBd; + int rxDmaChan; /* dma task */ + int txDmaChan; /* dma task */ + rtems_id rxDaemonTid; + rtems_id txDaemonTid; + + /* + * MDIO/Phy info + */ + struct rtems_mdio_info mdio_info; + int phy_default; + int phy_chan; /* which fec channel services this phy access? */ + int media_state; /* (last detected) state of media */ + + unsigned long rxInterrupts; + unsigned long rxNotLast; + unsigned long rxGiant; + unsigned long rxNonOctet; + unsigned long rxBadCRC; + unsigned long rxFIFOError; + unsigned long rxCollision; + + unsigned long txInterrupts; + unsigned long txDeferred; + unsigned long txLateCollision; + unsigned long txUnderrun; + unsigned long txFIFOError; + unsigned long txMisaligned; + unsigned long rxNotFirst; + unsigned long txRetryLimit; + }; + +static struct mcf548x_enet_struct enet_driver[NIFACES]; + +static void mcf548x_fec_restart(struct mcf548x_enet_struct *sc, rtems_id otherDaemon); + +static void fec_send_event(rtems_id task) +{ + rtems_bsdnet_event_send(task, FEC_EVENT); +} + +static void fec_wait_for_event(void) +{ + rtems_event_set out; + rtems_bsdnet_event_receive( + FEC_EVENT, + RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, + &out + ); +} + +static void mcf548x_fec_request_restart(struct mcf548x_enet_struct *sc) +{ + sc->state = FEC_STATE_RESTART_0; + fec_send_event(sc->txDaemonTid); + fec_send_event(sc->rxDaemonTid); +} + +/* + * Function: MCF548X_eth_addr_filter_set + * + * Description: Set individual address filter for unicast address and + * set physical address registers. + * + * Returns: void + * + * Notes: + * + */ +static void mcf548x_eth_addr_filter_set(struct mcf548x_enet_struct *sc) { + unsigned char *mac; + unsigned char currByte; /* byte for which to compute the CRC */ + int byte; /* loop - counter */ + int bit; /* loop - counter */ + unsigned long crc = 0xffffffff; /* initial value */ + int chan = sc->chan; + + /* + * Get the mac address of ethernet controller + */ + mac = (unsigned char *)(&sc->arpcom.ac_enaddr); + + /* + * The algorithm used is the following: + * we loop on each of the six bytes of the provided address, + * and we compute the CRC by left-shifting the previous + * value by one position, so that each bit in the current + * byte of the address may contribute the calculation. If + * the latter and the MSB in the CRC are different, then + * the CRC value so computed is also ex-ored with the + * "polynomium generator". The current byte of the address + * is also shifted right by one bit at each iteration. + * This is because the CRC generatore in hardware is implemented + * as a shift-register with as many ex-ores as the radixes + * in the polynomium. This suggests that we represent the + * polynomiumm itsc as a 32-bit constant. + */ + for(byte = 0; byte < 6; byte++) + { + + currByte = mac[byte]; + + for(bit = 0; bit < 8; bit++) + { + + if((currByte & 0x01) ^ (crc & 0x01)) + { + + crc >>= 1; + crc = crc ^ 0xedb88320; + + } + else + { + + crc >>= 1; + + } + + currByte >>= 1; + + } + + } + + crc = crc >> 26; + + /* + * Set individual hash table register + */ + if(crc >= 32) + { + + MCF548X_FEC_IAUR(chan) = (1 << (crc - 32)); + MCF548X_FEC_IALR(chan) = 0; + + } + else + { + + MCF548X_FEC_IAUR(chan) = 0; + MCF548X_FEC_IALR(chan) = (1 << crc); + + } + + /* + * Set physical address + */ + MCF548X_FEC_PALR(chan) = ((mac[0] << 24) + + (mac[1] << 16) + + (mac[2] << 8) + + mac[3]); + MCF548X_FEC_PAUR(chan) = ((mac[4] << 24) + + (mac[5] << 16)) + 0x8808; + + } + + +/* + * Function: mcf548x_eth_mii_read + * + * Description: Read a media independent interface (MII) register on an + * 18-wire ethernet tranceiver (PHY). Please see your PHY + * documentation for the register map. + * + * Returns: 0 if ok + * + * Notes: + * + */ +int mcf548x_eth_mii_read( + int phyAddr, /* PHY number to access or -1 */ + void *uarg, /* unit argument */ + unsigned regAddr, /* register address */ + uint32_t *retVal) /* ptr to read buffer */ +{ + struct mcf548x_enet_struct *sc = uarg; + int timeout = 0xffff; + int chan = sc->phy_chan; + + /* + * reading from any PHY's register is done by properly + * programming the FEC's MII data register. + */ + MCF548X_FEC_MMFR(chan) = (MCF548X_FEC_MMFR_ST_01 | + MCF548X_FEC_MMFR_OP_READ | + MCF548X_FEC_MMFR_TA_10 | + MCF548X_FEC_MMFR_PA(phyAddr) | + MCF548X_FEC_MMFR_RA(regAddr)); + + /* + * wait for the related interrupt + */ + while ((timeout--) && (!(MCF548X_FEC_EIR(chan) & MCF548X_FEC_EIR_MII))); + + if(timeout == 0) { + +#ifdef ETH_DEBUG + iprintf ("Read MDIO failed..." "\r\n"); +#endif + + return 1; + + } + + /* + * clear mii interrupt bit + */ + MCF548X_FEC_EIR(chan) = MCF548X_FEC_EIR_MII; + + /* + * it's now safe to read the PHY's register + */ + *retVal = (unsigned short) MCF548X_FEC_MMFR(chan); + + return 0; + +} + +/* + * Function: mcf548x_eth_mii_write + * + * Description: Write a media independent interface (MII) register on an + * 18-wire ethernet tranceiver (PHY). Please see your PHY + * documentation for the register map. + * + * Returns: Success (boolean) + * + * Notes: + * + */ +static int mcf548x_eth_mii_write( + int phyAddr, /* PHY number to access or -1 */ + void *uarg, /* unit argument */ + unsigned regAddr, /* register address */ + uint32_t data) /* write data */ +{ + struct mcf548x_enet_struct *sc = uarg; + int chan = sc->phy_chan; + int timeout = 0xffff; + + MCF548X_FEC_MMFR(chan) = (MCF548X_FEC_MMFR_ST_01 | + MCF548X_FEC_MMFR_OP_WRITE | + MCF548X_FEC_MMFR_TA_10 | + MCF548X_FEC_MMFR_PA(phyAddr) | + MCF548X_FEC_MMFR_RA(regAddr) | + MCF548X_FEC_MMFR_DATA(data)); + + /* + * wait for the MII interrupt + */ + while ((timeout--) && (!(MCF548X_FEC_EIR(chan) & MCF548X_FEC_EIR_MII))); + + if(timeout == 0) + { + +#ifdef ETH_DEBUG + iprintf ("Write MDIO failed..." "\r\n"); +#endif + + return 1; + + } + + /* + * clear MII interrupt bit + */ + MCF548X_FEC_EIR(chan) = MCF548X_FEC_EIR_MII; + + return 0; + + } + + +/* + * Function: mcf548x_fec_reset + * + * Description: Reset a running ethernet driver including the hardware + * FIFOs and the FEC. + * + * Returns: Success (boolean) + * + * Notes: + * + */ +static void mcf548x_fec_reset(struct mcf548x_enet_struct *sc) { + volatile int delay; + int chan = sc->chan; + /* + * Clear FIFO status registers + */ + MCF548X_FEC_FECRFSR(chan) = ~0; + MCF548X_FEC_FECTFSR(chan) = ~0; + + /* + * reset the FIFOs + */ + MCF548X_FEC_FRST(chan) = 0x03000000; + + for (delay = 0;delay < 16*4;delay++) {}; + + MCF548X_FEC_FRST(chan) = 0x01000000; + + /* + * Issue a reset command to the FEC chip + */ + MCF548X_FEC_ECR(chan) |= MCF548X_FEC_ECR_RESET; + + /* + * wait at least 16 clock cycles + */ + for (delay = 0;delay < 16*4;delay++) {}; +} + + +/* + * Function: mcf548x_fec_off + * + * Description: Stop the FEC and disable the ethernet SmartComm tasks. + * This function "turns off" the driver. + * + * Returns: void + * + * Notes: + * + */ +void mcf548x_fec_off(struct mcf548x_enet_struct *sc) + { + int counter = 0xffff; + int chan = sc->chan; + + +#if defined(ETH_DEBUG) + uint32_t phyStatus; + int i; + + for(i = 0; i < 9; i++) + { + + mcf548x_eth_mii_read(sc->phy_default, sc, i, &phyStatus); + iprintf ("Mii reg %d: 0x%04lx" "\r\n", i, phyStatus); + + } + + for(i = 16; i < 21; i++) + { + + mcf548x_eth_mii_read(sc->phy_default, sc, i, &phyStatus); + iprintf ("Mii reg %d: 0x%04lx" "\r\n", i, phyStatus); + + } + for(i = 0; i < 32; i++) + { + + mcf548x_eth_mii_read(i, sc, 0, &phyStatus); + iprintf ("Mii Phy=%d, reg 0: 0x%04lx" "\r\n", i, phyStatus); + + } +#endif /* ETH_DEBUG */ + + /* + * block FEC chip interrupts + */ + MCF548X_FEC_EIMR(chan) = 0; + + /* + * issue graceful stop command to the FEC transmitter if necessary + */ + MCF548X_FEC_TCR(chan) |= MCF548X_FEC_TCR_GTS; + + /* + * wait for graceful stop to register + * FIXME: add rtems_task_wake_after here, if it takes to long + */ + while((counter--) && (!( MCF548X_FEC_EIR(chan) & MCF548X_FEC_EIR_GRA))); + + /* + * Disable the SmartDMA transmit and receive tasks. + */ + MCD_killDma( sc->rxDmaChan ); + MCD_killDma( sc->txDmaChan ); + /* + * Disable transmit / receive interrupts + */ + mcdma_glue_irq_disable(sc->txDmaChan); + mcdma_glue_irq_disable(sc->rxDmaChan); + + /* + * Disable the Ethernet Controller + */ + MCF548X_FEC_ECR(chan) &= ~(MCF548X_FEC_ECR_ETHER_EN); +} + +/* + * MCF548X FEC interrupt handler + */ +void mcf548x_fec_irq_handler(rtems_vector_number vector) +{ + struct mcf548x_enet_struct *sc; + volatile uint32_t ievent; + int chan; + + sc = &(enet_driver[MCF548X_FEC_VECTOR2CHAN(vector)]); + chan = sc->chan; + ievent = MCF548X_FEC_EIR(chan); + + MCF548X_FEC_EIR(chan) = ievent; + /* + * check errors, update statistics + */ + if (ievent & MCF548X_FEC_EIR_LC) { + sc->txLateCollision++; + } + if (ievent & MCF548X_FEC_EIR_RL) { + sc->txRetryLimit++; + } + if (ievent & MCF548X_FEC_EIR_XFUN) { + sc->txUnderrun++; + } + if (ievent & MCF548X_FEC_EIR_XFERR) { + sc->txFIFOError++; + } + if (ievent & MCF548X_FEC_EIR_RFERR) { + sc->rxFIFOError++; + } + /* + * fatal error ocurred? + */ + if (ievent & (MCF548X_FEC_EIR_RFERR | MCF548X_FEC_EIR_XFERR)) { + MCF548X_FEC_EIMR(chan) &=~(MCF548X_FEC_EIMR_RFERR | MCF548X_FEC_EIMR_XFERR); + printk("fifo\n"); + mcf548x_fec_request_restart(sc); + } +} + +/* + * MCF548X DMA ethernet interrupt handler + */ +void mcf548x_mcdma_rx_irq_handler(void * param) +{ + struct mcf548x_enet_struct *sc = (struct mcf548x_enet_struct *)param; + /* Frame received? */ + if(MCDMA_GET_PENDING(sc->rxDmaChan)) { + MCDMA_CLR_PENDING(sc->rxDmaChan); + + mcdma_glue_irq_disable(sc->rxDmaChan);/*Disable receive ints*/ + sc->rxInterrupts++; /* Rx int has occurred */ + fec_send_event(sc->rxDaemonTid); + } +} + +/* + * MCF548X DMA ethernet interrupt handler + */ +void mcf548x_mcdma_tx_irq_handler(void * param) +{ + struct mcf548x_enet_struct *sc = (struct mcf548x_enet_struct *)param; + + /* Buffer transmitted or transmitter error? */ + if(MCDMA_GET_PENDING(sc->txDmaChan)) { + + MCDMA_CLR_PENDING(sc->txDmaChan); + + mcdma_glue_irq_disable(sc->txDmaChan);/*Disable tx ints*/ + + sc->txInterrupts++; /* Tx int has occurred */ + + fec_send_event(sc->txDaemonTid); + } +} + +/* + * Function: mcf548x_fec_initialize_hardware + * + * Description: Configure the MCF548X FEC registers and enable the + * SmartComm tasks. This function "turns on" the driver. + * + * Returns: void + * + * Notes: + * + */ +static void mcf548x_fec_initialize_hardware(struct mcf548x_enet_struct *sc) + { + int chan = sc->chan; + + /* + * Reset mcf548x FEC + */ + mcf548x_fec_reset(sc); + + /* + * Clear FEC-Lite interrupt event register (IEVENT) + */ + MCF548X_FEC_EIR(chan) = MCF548X_FEC_EIR_CLEAR_ALL; + + /* + * Set interrupt mask register + */ + MCF548X_FEC_EIMR(chan) = FEC_INTR_MASK_USED; + /* + * Set FEC-Lite receive control register (R_CNTRL) + * frame length=1518, MII mode for 18-wire-transceiver + */ + MCF548X_FEC_RCR(chan) = (MCF548X_FEC_RCR_MAX_FL(ETHER_MAX_LEN) + | MCF548X_FEC_RCR_FCE + | MCF548X_FEC_RCR_MII_MODE); + + /* + * Set FEC-Lite transmit control register (X_CNTRL) + * full-duplex, heartbeat disabled + */ + MCF548X_FEC_TCR(chan) = MCF548X_FEC_TCR_FDEN; + + + + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock(33Mhz) + * and do not drop the Preamble. + */ + MCF548X_FEC_MSCR(chan) = MCF548X_FEC_MSCR_MII_SPEED(7); /* ipb_clk = 33 MHz */ + + /* + * Set Opcode/Pause Duration Register + */ + MCF548X_FEC_PAUR(chan) = 0x00010020; + + /* + * Set Rx FIFO alarm and granularity value + */ + MCF548X_FEC_FECRFCR(chan) = (MCF548X_FEC_FECRFCR_FRM + | MCF548X_FEC_FECRFCR_GR(0x7)); + MCF548X_FEC_FECRFAR(chan) = MCF548X_FEC_FECRFAR_ALARM(256); + + /* + * Set Tx FIFO granularity value + */ + MCF548X_FEC_FECTFCR(chan) = (MCF548X_FEC_FECTFCR_FRM + | MCF548X_FEC_FECTFCR_GR(7)); + + /* + * Set transmit fifo watermark register (X_WMRK), default = 64 + */ + MCF548X_FEC_FECTFAR(chan) = MCF548X_FEC_FECTFAR_ALARM(512); + MCF548X_FEC_FECTFWR(chan) = MCF548X_FEC_FECTFWR_X_WMRK_256; + + /* + * Set individual address filter for unicast address + * and set physical address registers. + */ + mcf548x_eth_addr_filter_set(sc); + + /* + * Set multicast address filter + */ + MCF548X_FEC_GAUR(chan) = 0x00000000; + MCF548X_FEC_GALR(chan) = 0x00000000; + + /* + * enable CRC in finite state machine register + */ + MCF548X_FEC_CTCWR(chan) = MCF548X_FEC_CTCWR_TFCW | MCF548X_FEC_CTCWR_CRC; + } + + +/* + * Send packet (caller provides header). + */ +static void mcf548x_fec_tx_start(struct ifnet *ifp) + { + + struct mcf548x_enet_struct *sc = ifp->if_softc; + + ifp->if_flags |= IFF_OACTIVE; + + fec_send_event(sc->txDaemonTid); + + } + +static void fec_start_dma_and_controller(struct mcf548x_enet_struct *sc) +{ + int chan = sc->chan; + int mcdma_rc; + /* + * Enable the SmartDMA receive task. + */ + mcdma_rc = MCD_startDma + (sc->rxDmaChan, /* the channel on which to run the DMA */ + (void *)sc->rxBd, /* the address to move data from, or buffer-descriptor addr */ + 0, /* the amount to increment the source address per transfer */ + (void *)&MCF548X_FEC_FECRFDR(chan), /* the address to move data to */ + 0, /* the amount to increment the destination address per transfer */ + ETHER_MAX_LEN, /* the number of bytes to transfer independent of the transfer size */ + 0, /* the number bytes in of each data movement (1, 2, or 4) */ + MCF548X_FEC_RX_INITIATOR(chan), /* what device initiates the DMA */ + 2, /* priority of the DMA */ + 0 /* flags describing the DMA */ + | MCD_FECRX_DMA + | MCD_INTERRUPT + | MCD_TT_FLAGS_CW + | MCD_TT_FLAGS_RL + | MCD_TT_FLAGS_SP + , + 0 /* a description of byte swapping, bit swapping, and CRC actions */ + | MCD_NO_CSUM + | MCD_NO_BYTE_SWAP + ); + if (mcdma_rc != MCD_OK) { + rtems_panic("FEC: cannot start rx DMA"); + } + mcdma_rc = MCD_startDma + (sc->txDmaChan, /* the channel on which to run the DMA */ + (void *)sc->txBd, /* the address to move data from, or buffer-descriptor addr */ + 0, /* the amount to increment the source address per transfer */ + (void *)&MCF548X_FEC_FECTFDR(chan), /* the address to move data to */ + 0, /* the amount to increment the destination address per transfer */ + ETHER_MAX_LEN, /* the number of bytes to transfer independent of the transfer size */ + 0, /* the number bytes in of each data movement (1, 2, or 4) */ + MCF548X_FEC_TX_INITIATOR(chan), /* what device initiates the DMA */ + 1, /* priority of the DMA */ + 0 /* flags describing the DMA */ + | MCD_FECTX_DMA + | MCD_INTERRUPT + | MCD_TT_FLAGS_CW + | MCD_TT_FLAGS_RL + | MCD_TT_FLAGS_SP + , + 0 /* a description of byte swapping, bit swapping, and CRC actions */ + | MCD_NO_CSUM + | MCD_NO_BYTE_SWAP + ); + if (mcdma_rc != MCD_OK) { + rtems_panic("FEC: cannot start tx DMA"); + } + + /* + * Enable FEC-Lite controller + */ + MCF548X_FEC_ECR(chan) |= MCF548X_FEC_ECR_ETHER_EN; +} + +static void mcf548x_fec_restart(struct mcf548x_enet_struct *sc, rtems_id otherDaemon) +{ + if (sc->state == FEC_STATE_RESTART_1) { + mcf548x_fec_initialize_hardware(sc); + fec_start_dma_and_controller(sc); + sc->state = FEC_STATE_NORMAL; + } else { + sc->state = FEC_STATE_RESTART_1; + } + + fec_send_event(otherDaemon); + while (sc->state != FEC_STATE_NORMAL) { + fec_wait_for_event(); + } +} + +static void fec_reset_bd_and_discard_tx_frames( + int bdCount, + MCD_bufDescFec *bdRing, + struct mbuf **mbufs +) +{ + int bdIndex = 0; + + for (bdIndex = 0; bdIndex < bdCount; ++bdIndex) { + bool bdIsLast = bdIndex == bdCount - 1; + struct mbuf *m = mbufs[bdIndex]; + + bdRing[bdIndex].statCtrl = bdIsLast ? MCF548X_FEC_TBD_WRAP : 0; + + if (m != NULL) { + mbufs[bdIndex] = NULL; + m_free(m); + } + } +} + +static void fec_reset_tx_dma( + int dmaChan, + int bdCount, + MCD_bufDescFec *bdRing, + struct mbuf **mbufs, + struct mbuf *m +) +{ + if (m != NULL) { + m_freem(m); + } + + MCD_killDma(dmaChan); + + fec_reset_bd_and_discard_tx_frames(bdCount, bdRing, mbufs); +} + +static struct mbuf *fec_next_fragment( + struct ifnet *ifp, + struct mbuf *m, + bool *isFirst +) +{ + struct mbuf *n = NULL; + + *isFirst = false; + + while (true) { + if (m == NULL) { + IF_DEQUEUE(&ifp->if_snd, m); + + if (m != NULL) { + *isFirst = true; + } else { + ifp->if_flags &= ~IFF_OACTIVE; + + return NULL; + } + } + + if (m->m_len > 0) { + break; + } else { + m = m_free(m); + } + } + + n = m->m_next; + while (n != NULL && n->m_len <= 0) { + n = m_free(n); + } + m->m_next = n; + + return m; +} + +static bool fec_transmit( + struct ifnet *ifp, + int dmaChan, + int bdCount, + MCD_bufDescFec *bdRing, + struct mbuf **mbufs, + int *bdIndexPtr, + struct mbuf **mPtr, + MCD_bufDescFec **firstPtr +) +{ + bool bdShortage = false; + int bdIndex = *bdIndexPtr; + struct mbuf *m = *mPtr; + MCD_bufDescFec *first = *firstPtr; + + while (true) { + MCD_bufDescFec *bd = &bdRing[bdIndex]; + + MCDMA_CLR_PENDING(dmaChan); + if ((bd->statCtrl & MCF548X_FEC_TBD_READY) == 0) { + struct mbuf *done = mbufs[bdIndex]; + bool isFirst = false; + + if (done != NULL) { + m_free(done); + mbufs[bdIndex] = NULL; + } + + m = fec_next_fragment(ifp, m, &isFirst); + if (m != NULL) { + bool bdIsLast = bdIndex == bdCount - 1; + u16 status = bdIsLast ? MCF548X_FEC_TBD_WRAP : 0; + + bd->length = (u16) m->m_len; + bd->dataPointer = mtod(m, u32); + + mbufs[bdIndex] = m; + + rtems_cache_flush_multiple_data_lines(mtod(m, void *), m->m_len); + + if (isFirst) { + first = bd; + } else { + status |= MCF548X_FEC_TBD_READY; + } + + if (m->m_next != NULL) { + bd->statCtrl = status; + } else { + bd->statCtrl = status | MCF548X_FEC_TBD_INT | MCF548X_FEC_TBD_LAST; + first->statCtrl |= MCF548X_FEC_TBD_READY; + MCD_continDma(dmaChan); + } + + m = m->m_next; + } else { + break; + } + } else { + bdShortage = true; + break; + } + + if (bdIndex < bdCount - 1) { + ++bdIndex; + } else { + bdIndex = 0; + } + } + + *bdIndexPtr = bdIndex; + *mPtr = m; + *firstPtr = first; + + return bdShortage; +} + +static MCD_bufDescFec *fec_init_tx_dma( + MCD_bufDescFec *bdRing, + int bdCount +) +{ + int bdIndex; + + for (bdIndex = 0; bdIndex < bdCount; ++bdIndex) { + bool bdIsLast = bdIndex == bdCount - 1; + + bdRing[bdIndex].dataPointer = 0; + bdRing[bdIndex].length = 0; + bdRing[bdIndex].statCtrl = bdIsLast ? MCF548X_FEC_RBD_WRAP : 0; + } + + return bdRing; +} + +static void mcf548x_fec_txDaemon(void *arg) +{ + struct mcf548x_enet_struct *sc = arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + int dmaChan = sc->txDmaChan; + int bdIndex = 0; + int bdCount = sc->txBdCount; + struct mbuf **mbufs = &sc->txMbuf[0]; + struct mbuf *m = NULL; + MCD_bufDescFec *bdRing = fec_init_tx_dma(sc->txBd, bdCount); + MCD_bufDescFec *first = NULL; + bool bdShortage = false; + + memset(mbufs, 0, bdCount * sizeof(*mbufs)); + + while (true) { + if (bdShortage) { + mcdma_glue_irq_enable(dmaChan); + } + fec_wait_for_event(); + + if (sc->state != FEC_STATE_NORMAL) { + fec_reset_tx_dma(dmaChan, bdCount, bdRing, mbufs, m); + mcf548x_fec_restart(sc, sc->rxDaemonTid); + bdIndex = 0; + m = NULL; + first = NULL; + } + + bdShortage = fec_transmit( + ifp, + dmaChan, + bdCount, + bdRing, + mbufs, + &bdIndex, + &m, + &first + ); + } +} + +static struct mbuf *fec_add_mbuf( + int how, + struct ifnet *ifp, + MCD_bufDescFec *bd, + bool bdIsLast +) +{ + struct mbuf *m; + + MGETHDR(m, how, MT_DATA); + if (m != NULL) { + MCLGET(m, how); + if ((m->m_flags & M_EXT) != 0) { + m->m_pkthdr.rcvif = ifp; + + rtems_cache_invalidate_multiple_data_lines(mtod(m, void *), ETHER_MAX_LEN); + + bd->dataPointer = mtod(m, u32); + bd->length = ETHER_MAX_LEN; + bd->statCtrl = MCF548X_FEC_RBD_EMPTY + | MCF548X_FEC_RBD_INT + | (bdIsLast ? MCF548X_FEC_RBD_WRAP : 0); + } else { + m_free(m); + } + } + + return m; +} + +static MCD_bufDescFec *fec_init_rx_dma( + MCD_bufDescFec *bdRing, + struct ifnet *ifp, + int bdCount, + struct mbuf **mbufs +) +{ + int bdIndex; + + for (bdIndex = 0; bdIndex < bdCount; ++bdIndex) { + bool bdIsLast = bdIndex == bdCount - 1; + + mbufs[bdIndex] = fec_add_mbuf(M_WAIT, ifp, &bdRing[bdIndex], bdIsLast); + } + + return bdRing; +} + +static void fec_reset_rx_dma( + int dmaChan, + int bdCount, + MCD_bufDescFec *bdRing +) +{ + int bdIndex; + + MCD_killDma(dmaChan); + + for (bdIndex = 0; bdIndex < bdCount - 1; ++bdIndex) { + bdRing[bdIndex].length = ETHER_MAX_LEN; + bdRing[bdIndex].statCtrl = MCF548X_FEC_RBD_EMPTY | MCF548X_FEC_RBD_INT; + } + + bdRing[bdIndex].length = ETHER_MAX_LEN; + bdRing[bdIndex].statCtrl = MCF548X_FEC_RBD_EMPTY | MCF548X_FEC_RBD_INT | MCF548X_FEC_RBD_WRAP; +} + +static int fec_ether_input( + struct ifnet *ifp, + int dmaChan, + int bdIndex, + int bdCount, + MCD_bufDescFec *bdRing, + struct mbuf **mbufs +) +{ + while (true) { + bool bdIsLast = bdIndex == bdCount - 1; + MCD_bufDescFec *bd = &bdRing[bdIndex]; + struct mbuf *m = mbufs[bdIndex]; + struct mbuf *n; + u16 status; + + MCDMA_CLR_PENDING(dmaChan); + status = bd->statCtrl; + + if ((status & MCF548X_FEC_RBD_EMPTY) != 0) { + break; + } + + n = fec_add_mbuf(0, ifp, bd, bdIsLast); + if (n != NULL) { + int len = bd->length - ETHER_HDR_LEN - ETHER_CRC_LEN; + struct ether_header *eh = mtod(m, struct ether_header *); + + m->m_len = len; + m->m_pkthdr.len = len; + m->m_data = mtod(m, char *) + ETHER_HDR_LEN; + + ether_input(ifp, eh, m); + } else { + n = m; + } + + mbufs[bdIndex] = n; + + if (bdIndex < bdCount - 1) { + ++bdIndex; + } else { + bdIndex = 0; + } + } + + return bdIndex; +} + +static void mcf548x_fec_rxDaemon(void *arg) +{ + struct mcf548x_enet_struct *sc = arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + int dmaChan = sc->rxDmaChan; + int bdIndex = 0; + int bdCount = sc->rxBdCount; + struct mbuf **mbufs = &sc->rxMbuf[0]; + MCD_bufDescFec *bdRing = fec_init_rx_dma(sc->rxBd, ifp, bdCount, mbufs); + + while (true) { + mcdma_glue_irq_enable(dmaChan); + fec_wait_for_event(); + + bdIndex = fec_ether_input(ifp, dmaChan, bdIndex, bdCount, bdRing, mbufs); + + if (sc->state != FEC_STATE_NORMAL) { + fec_reset_rx_dma(dmaChan, bdCount, bdRing); + mcf548x_fec_restart(sc, sc->txDaemonTid); + bdIndex = 0; + } + } +} + +/* + * Initialize and start the device + */ +static void mcf548x_fec_init(void *arg) +{ + struct mcf548x_enet_struct *sc = (struct mcf548x_enet_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + int chan = sc->chan; + rtems_isr_entry old_handler; + char *txTaskName = "FTx0"; + char *rxTaskName = "FRx0"; + if(sc->txDaemonTid == 0) + { + /* + * Allocate a set of BDs + */ + sc->rxBd = SRAM_RXBD_BASE(_SysSramBase,chan); + sc->txBd = SRAM_TXBD_BASE(_SysSramBase,chan); + + if(!sc->rxBd || !sc->txBd) + rtems_panic ("No memory for BDs"); + /* + * clear the BDs + */ + memset((void *)sc->rxBd,0,sc->rxBdCount * sizeof *(sc->rxBd)); + memset((void *)sc->txBd,0,sc->txBdCount * sizeof *(sc->txBd)); + /* + * Allocate a set of mbuf pointers + */ + sc->rxMbuf = + malloc(sc->rxBdCount * sizeof *sc->rxMbuf, M_MBUF, M_NOWAIT); + sc->txMbuf = + malloc(sc->txBdCount * sizeof *sc->txMbuf, M_MBUF, M_NOWAIT); + + if(!sc->rxMbuf || !sc->txMbuf) + rtems_panic ("No memory for mbuf pointers"); + + sc->txDmaChan = MCDMA_FEC_TX_CHAN(chan); + sc->rxDmaChan = MCDMA_FEC_RX_CHAN(chan); + + mcdma_glue_init(SRAM_DMA_BASE(_SysSramBase)); + + /* + * Set up interrupts + */ + mcdma_glue_irq_install(sc->rxDmaChan, + mcf548x_mcdma_rx_irq_handler, + sc); + mcdma_glue_irq_install(sc->txDmaChan, + mcf548x_mcdma_tx_irq_handler, + sc); + if(rtems_interrupt_catch(mcf548x_fec_irq_handler, + MCF548X_FEC_IRQ_VECTOR(chan), + &old_handler)) { + rtems_panic ("Can't attach MFC54xx FEX interrupt handler\n"); + } + + bsp_interrupt_vector_enable(MCF548X_IRQ_FEC(chan)); + + MCF548X_FEC_EIMR(chan) = FEC_INTR_MASK_USED; + + /* + * Start driver tasks + */ + txTaskName[3] = '0'+chan; + rxTaskName[3] = '0'+chan; + sc->txDaemonTid = rtems_bsdnet_newproc(txTaskName, 4096, + mcf548x_fec_txDaemon, sc); + sc->rxDaemonTid = rtems_bsdnet_newproc(rxTaskName, 4096, + mcf548x_fec_rxDaemon, sc); + } + + mcf548x_fec_request_restart(sc); + + /* + * Set flags appropriately + */ + if(ifp->if_flags & IFF_PROMISC) + MCF548X_FEC_RCR(chan) |= MCF548X_FEC_RCR_PROM; + else + MCF548X_FEC_RCR(chan) &= ~MCF548X_FEC_RCR_PROM; + + /* + * init timer so the "watchdog function gets called periodically + */ + ifp->if_timer = 1; + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; +} + + +static void enet_stats (struct mcf548x_enet_struct *sc) +{ + printf (" Rx Interrupts:%-8lu", sc->rxInterrupts); + printf (" Rx Not First:%-8lu", sc->rxNotFirst); + printf (" Rx Not Last:%-8lu\n", sc->rxNotLast); + printf (" Rx Giant:%-8lu", sc->rxGiant); + printf (" Rx Non-octet:%-8lu", sc->rxNonOctet); + printf (" Rx Bad CRC:%-8lu\n", sc->rxBadCRC); + printf (" Rx FIFO Error:%-8lu", sc->rxFIFOError); + printf (" Rx Collision:%-8lu", sc->rxCollision); + + printf (" Tx Interrupts:%-8lu\n", sc->txInterrupts); + printf (" Tx Deferred:%-8lu", sc->txDeferred); + printf (" Tx Late Collision:%-8lu", sc->txLateCollision); + printf (" Tx Retransmit Limit:%-8lu\n", sc->txRetryLimit); + printf (" Tx Underrun:%-8lu", sc->txUnderrun); + printf (" Tx FIFO Error:%-8lu", sc->txFIFOError); + printf (" Tx Misaligned:%-8lu\n", sc->txMisaligned); + +} + +int32_t mcf548x_fec_setMultiFilter(struct ifnet *ifp) +{ + /*struct mcf548x_enet_struct *sc = ifp->if_softc; */ + /* XXX anything to do? */ + return 0; +} + + +/* + * Driver ioctl handler + */ +static int mcf548x_fec_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data) + { + struct mcf548x_enet_struct *sc = ifp->if_softc; + int error = 0; + + switch(command) + { + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + rtems_mii_ioctl (&(sc->mdio_info),sc,command,(void *)data); + break; + + case SIOCGIFADDR: + case SIOCSIFADDR: + + ether_ioctl(ifp, command, data); + + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: { + struct ifreq* ifr = (struct ifreq*) data; + error = (command == SIOCADDMULTI) + ? ether_addmulti(ifr, &sc->arpcom) + : ether_delmulti(ifr, &sc->arpcom); + + if (error == ENETRESET) { + if (ifp->if_flags & IFF_RUNNING) + error = mcf548x_fec_setMultiFilter(ifp); + else + error = 0; + } + break; + } + + case SIOCSIFFLAGS: + + switch(ifp->if_flags & (IFF_UP | IFF_RUNNING)) + { + + case IFF_RUNNING: + + mcf548x_fec_off(sc); + + break; + + case IFF_UP: + + mcf548x_fec_init(sc); + + break; + + case IFF_UP | IFF_RUNNING: + + mcf548x_fec_off(sc); + mcf548x_fec_init(sc); + + break; + + default: + break; + + } + + break; + + case SIO_RTEMS_SHOW_STATS: + + enet_stats(sc); + + break; + + /* + * FIXME: All sorts of multicast commands need to be added here! + */ + default: + + error = EINVAL; + + break; + + } + + return error; + + } + + +/* + * init the PHY and adapt FEC settings + */ +int mcf548x_fec_mode_adapt(struct ifnet *ifp) +{ + int result = 0; + struct mcf548x_enet_struct *sc = ifp->if_softc; + int media = IFM_MAKEWORD( 0, 0, 0, sc->phy_default); + int chan = sc->chan; + + /* + * fetch media status + */ + result = mcf548x_fec_ioctl(ifp,SIOCGIFMEDIA,(caddr_t)&media); + if (result != 0) { + return result; + } + /* + * status is unchanged? then do nothing + */ + if (media == sc->media_state) { + return 0; + } + /* + * otherwise: for the first call, try to negotiate mode + */ + if (sc->media_state == 0) { + /* + * set media status: set auto negotiation -> start auto-negotiation + */ + media = IFM_MAKEWORD(0,IFM_AUTO,0,sc->phy_default); + result = mcf548x_fec_ioctl(ifp,SIOCSIFMEDIA,(caddr_t)&media); + if (result != 0) { + return result; + } + /* + * wait for auto-negotiation to terminate + */ + do { + media = IFM_MAKEWORD(0,0,0,sc->phy_default); + result = mcf548x_fec_ioctl(ifp,SIOCGIFMEDIA,(caddr_t)&media); + if (result != 0) { + return result; + } + } while (IFM_NONE == IFM_SUBTYPE(media)); + } + + /* + * now set HW according to media results: + */ + + /* + * if we are half duplex then switch to half duplex + */ + if (0 == (IFM_FDX & IFM_OPTIONS(media))) { + MCF548X_FEC_TCR(chan) &= ~MCF548X_FEC_TCR_FDEN; + } + else { + MCF548X_FEC_TCR(chan) |= MCF548X_FEC_TCR_FDEN; + } + /* + * store current media state for future compares + */ + sc->media_state = media; + + return 0; +} + +/* + * periodically poll the PHY. if mode has changed, + * then adjust the FEC settings + */ +static void mcf548x_fec_watchdog( struct ifnet *ifp) +{ + mcf548x_fec_mode_adapt(ifp); + ifp->if_timer = FEC_WATCHDOG_TIMEOUT; +} + +/* + * Attach the MCF548X fec driver to the system + */ +int rtems_mcf548x_fec_driver_attach(struct rtems_bsdnet_ifconfig *config) + { + struct mcf548x_enet_struct *sc; + struct ifnet *ifp; + int mtu; + int unitNumber; + char *unitName; + + /* + * Parse driver name + */ + if((unitNumber = rtems_bsdnet_parse_driver_name(config, &unitName)) < 0) + return 0; + + /* + * Is driver free? + */ + if ((unitNumber <= 0) || (unitNumber > NIFACES)) + { + + printf ("Bad FEC unit number.\n"); + return 0; + + } + + sc = &enet_driver[unitNumber - 1]; + sc->chan = unitNumber-1; + ifp = &sc->arpcom.ac_if; + + if(ifp->if_softc != NULL) + { + + printf ("Driver already in use.\n"); + return 0; + + } + + /* + * Process options + */ +#if NVRAM_CONFIGURE == 1 + + /* Configure from NVRAM */ + if(addr = nvram->ipaddr) + { + + /* We have a non-zero entry, copy the value */ + if(pAddr = malloc(INET_ADDR_MAX_BUF_SIZE, 0, M_NOWAIT)) + config->ip_address = (char *)inet_ntop(AF_INET, &addr, pAddr, INET_ADDR_MAX_BUF_SIZE -1); + else + rtems_panic("Can't allocate ip_address buffer!\n"); + + } + + if(addr = nvram->netmask) + { + + /* We have a non-zero entry, copy the value */ + if (pAddr = malloc (INET_ADDR_MAX_BUF_SIZE, 0, M_NOWAIT)) + config->ip_netmask = (char *)inet_ntop(AF_INET, &addr, pAddr, INET_ADDR_MAX_BUF_SIZE -1); + else + rtems_panic("Can't allocate ip_netmask buffer!\n"); + + } + + /* Ethernet address requires special handling -- it must be copied into + * the arpcom struct. The following if construct serves only to give the + * User Area NVRAM parameter the highest priority. + * + * If the ethernet address is specified in NVRAM, go ahead and copy it. + * (ETHER_ADDR_LEN = 6 bytes). + */ + if(nvram->enaddr[0] || nvram->enaddr[1] || nvram->enaddr[2]) + { + + /* Anything in the first three bytes indicates a non-zero entry, copy value */ + memcpy((void *)sc->arpcom.ac_enaddr, &nvram->enaddr, ETHER_ADDR_LEN); + + } + else + if(config->hardware_address) + { + + /* There is no entry in NVRAM, but there is in the ifconfig struct, so use it. */ + memcpy((void *)sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); + } + +#else /* NVRAM_CONFIGURE != 1 */ + + if(config->hardware_address) + { + + memcpy(sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); + + } + +#endif /* NVRAM_CONFIGURE != 1 */ +#ifdef HAS_UBOOT + if ((sc->arpcom.ac_enaddr[0] == 0) && + (sc->arpcom.ac_enaddr[1] == 0) && + (sc->arpcom.ac_enaddr[2] == 0)) { + memcpy( + (void *)sc->arpcom.ac_enaddr, + bsp_uboot_board_info.bi_enetaddr, + ETHER_ADDR_LEN + ); + } +#endif +#ifdef HAS_DBUG + if ((sc->arpcom.ac_enaddr[0] == 0) && + (sc->arpcom.ac_enaddr[1] == 0) && + (sc->arpcom.ac_enaddr[2] == 0)) { + memcpy( + (void *)sc->arpcom.ac_enaddr, + DBUG_SETTINGS.macaddr, + ETHER_ADDR_LEN + ); + } +#endif + if ((sc->arpcom.ac_enaddr[0] == 0) && + (sc->arpcom.ac_enaddr[1] == 0) && + (sc->arpcom.ac_enaddr[2] == 0)) { + /* There is no ethernet address provided, so it could be read + * from the Ethernet protocol block of SCC1 in DPRAM. + */ + rtems_panic("No Ethernet address specified!\n"); + } + if(config->mtu) + mtu = config->mtu; + else + mtu = ETHERMTU; + + if(config->rbuf_count) + sc->rxBdCount = config->rbuf_count; + else + sc->rxBdCount = RX_BUF_COUNT; + + if(config->xbuf_count) + sc->txBdCount = config->xbuf_count; + else + sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF; + + sc->acceptBroadcast = !config->ignore_broadcast; + + /* + * setup info about mdio interface + */ + sc->mdio_info.mdio_r = mcf548x_eth_mii_read; + sc->mdio_info.mdio_w = mcf548x_eth_mii_write; + sc->mdio_info.has_gmii = 0; /* we do not support gigabit IF */ + + /* + * XXX: Although most hardware builders will assign the PHY addresses + * like this, this should be more configurable + */ + sc->phy_default = unitNumber-1; + sc->phy_chan = 0; /* assume all MII accesses are via FEC0 */ + + /* + * Set up network interface values + */ + ifp->if_softc = sc; + ifp->if_unit = unitNumber; + ifp->if_name = unitName; + ifp->if_mtu = mtu; + ifp->if_init = mcf548x_fec_init; + ifp->if_ioctl = mcf548x_fec_ioctl; + ifp->if_start = mcf548x_fec_tx_start; + ifp->if_output = ether_output; + ifp->if_watchdog = mcf548x_fec_watchdog; /* XXX: timer is set in "init" */ + ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST; + /*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); + + return 1; + } + + +int rtems_mcf548x_fec_driver_attach_detach(struct rtems_bsdnet_ifconfig *config, int attaching) +{ + if (attaching) { + return rtems_mcf548x_fec_driver_attach(config); + } + else { + return 0; + } +} + + diff --git a/bsps/m68k/mcf5235/net/network.c b/bsps/m68k/mcf5235/net/network.c new file mode 100644 index 0000000000..6c8fa88be2 --- /dev/null +++ b/bsps/m68k/mcf5235/net/network.c @@ -0,0 +1,879 @@ +/* + * RTEMS/TCPIP driver for MCF5235 Fast Ethernet Controller + * + * TO DO: Check network stack code -- force longword alignment of all tx mbufs? + */ + +#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ + +#include <bsp.h> +#include <stdio.h> +#include <errno.h> +#include <stdarg.h> +#include <string.h> +#include <rtems.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/ethernet.h> +#include <net/if.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + + +/* + * Number of interfaces supported by this driver + */ +#define NIFACES 1 + +#define FEC_INTC0_TX_VECTOR (64+23) +#define FEC_INTC0_RX_VECTOR (64+27) + +/* + * Default number of buffer descriptors set aside for this driver. + * The number of transmit buffer descriptors has to be quite large + * since a single frame often uses three or more buffer descriptors. + */ +#define RX_BUF_COUNT 32 +#define TX_BUF_COUNT 20 +#define TX_BD_PER_BUF 3 + +#define INET_ADDR_MAX_BUF_SIZE (sizeof "255.255.255.255") + +/* + * RTEMS event used by interrupt handler to signal daemons. + * This must *not* be the same event used by the TCP/IP task synchronization. + */ +#define TX_INTERRUPT_EVENT RTEMS_EVENT_1 +#define RX_INTERRUPT_EVENT RTEMS_EVENT_1 + +/* + * RTEMS event used to start transmit daemon. + * This must not be the same as INTERRUPT_EVENT. + */ +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + +/* + * Receive buffer size -- Allow for a full ethernet packet plus CRC (1518). + * Round off to nearest multiple of RBUF_ALIGN. + */ +#define MAX_MTU_SIZE 1518 +#define RBUF_ALIGN 4 +#define RBUF_SIZE ((MAX_MTU_SIZE + RBUF_ALIGN) & ~RBUF_ALIGN) + +#if (MCLBYTES < RBUF_SIZE) + #error "Driver must have MCLBYTES > RBUF_SIZE" +#endif + +typedef struct mcf5235BufferDescriptor_ { + volatile uint16_t status; + uint16_t length; + volatile void *buffer; +} mcf5235BufferDescriptor_t; + +/* + * Per-device data + */ +struct mcf5235_enet_struct { + struct arpcom arpcom; + struct mbuf **rxMbuf; + struct mbuf **txMbuf; + int acceptBroadcast; + int rxBdCount; + int txBdCount; + int txBdHead; + int txBdTail; + int txBdActiveCount; + mcf5235BufferDescriptor_t *rxBdBase; + mcf5235BufferDescriptor_t *txBdBase; + rtems_id rxDaemonTid; + rtems_id txDaemonTid; + + /* + * Statistics + */ + unsigned long rxInterrupts; + unsigned long txInterrupts; + unsigned long txRawWait; + unsigned long txRealign; +}; +static struct mcf5235_enet_struct enet_driver[NIFACES]; + +static rtems_isr +mcf5235_fec_rx_interrupt_handler( rtems_vector_number v ) +{ + MCF5235_FEC_EIR = MCF5235_FEC_EIR_RXF; + MCF5235_FEC_EIMR &= ~MCF5235_FEC_EIMR_RXF; + enet_driver[0].rxInterrupts++; + rtems_bsdnet_event_send(enet_driver[0].rxDaemonTid, RX_INTERRUPT_EVENT); +} + +static rtems_isr +mcf5235_fec_tx_interrupt_handler( rtems_vector_number v ) +{ + MCF5235_FEC_EIR = MCF5235_FEC_EIR_TXF; + MCF5235_FEC_EIMR &= ~MCF5235_FEC_EIMR_TXF; + enet_driver[0].txInterrupts++; + rtems_bsdnet_event_send(enet_driver[0].txDaemonTid, TX_INTERRUPT_EVENT); +} + +/* + * Allocate buffer descriptors from (non-cached) on-chip static RAM + * Ensure 128-bit (16-byte) alignment + */ +extern char __SRAMBASE[]; + +static mcf5235BufferDescriptor_t * +mcf5235_bd_allocate(unsigned int count) +{ + static mcf5235BufferDescriptor_t *bdp = (mcf5235BufferDescriptor_t *)__SRAMBASE; + mcf5235BufferDescriptor_t *p = bdp; + + bdp += count; + if ((int)bdp & 0xF) + bdp = (mcf5235BufferDescriptor_t *)((char *)bdp + (16 - ((int)bdp & 0xF))); + return p; +} + +#if UNUSED +/* + * Read MII register + * Busy-waits, but transfer time should be short! + */ +static int +getMII(int phyNumber, int regNumber) +{ + MCF5235_FEC_MMFR = (0x1 << 30) | + (0x2 << 28) | + (phyNumber << 23) | + (regNumber << 18) | + (0x2 << 16); + while ((MCF5235_FEC_EIR & MCF5235_FEC_EIR_MII) == 0); + MCF5235_FEC_EIR = MCF5235_FEC_EIR_MII; + return MCF5235_FEC_MMFR & 0xFFFF; +} +#endif + +/* + * Write MII register + * Busy-waits, but transfer time should be short! + */ +static void +setMII(int phyNumber, int regNumber, int value) +{ + MCF5235_FEC_MMFR = (0x1 << 30) | + (0x1 << 28) | + (phyNumber << 23) | + (regNumber << 18) | + (0x2 << 16) | + (value & 0xFFFF); + while ((MCF5235_FEC_EIR & MCF5235_FEC_EIR_MII) == 0); + MCF5235_FEC_EIR = MCF5235_FEC_EIR_MII; +} + +static void +mcf5235_fec_initialize_hardware(struct mcf5235_enet_struct *sc) +{ + int i; + const unsigned char *hwaddr = 0; + rtems_status_code status; + rtems_isr_entry old_handler; + uint32_t clock_speed = get_CPU_clock_speed(); + + /* + * Issue reset to FEC + */ + MCF5235_FEC_ECR = MCF5235_FEC_ECR_RESET; + rtems_task_wake_after(1); + MCF5235_FEC_ECR = 0; + + /* + * Configuration of I/O ports is done outside of this function + */ +#if 0 + imm->gpio.pbcnt |= MCF5235_GPIO_PBCNT_SET_FEC; /* Set up port b FEC pins */ +#endif + + /* + * Set our physical address + */ + hwaddr = sc->arpcom.ac_enaddr; + MCF5235_FEC_PALR = (hwaddr[0] << 24) | (hwaddr[1] << 16) | + (hwaddr[2] << 8) | (hwaddr[3] << 0); + MCF5235_FEC_PAUR = (hwaddr[4] << 24) | (hwaddr[5] << 16); + + + /* + * Clear the hash table + */ + MCF5235_FEC_GAUR = 0; + MCF5235_FEC_GALR = 0; + + /* + * Set up receive buffer size + */ + MCF5235_FEC_EMRBR = 1520; /* Standard Ethernet */ + + /* + * Allocate mbuf pointers + */ + sc->rxMbuf = malloc(sc->rxBdCount * sizeof *sc->rxMbuf, M_MBUF, M_NOWAIT); + sc->txMbuf = malloc(sc->txBdCount * sizeof *sc->txMbuf, M_MBUF, M_NOWAIT); + if (!sc->rxMbuf || !sc->txMbuf) + rtems_panic("No memory for mbuf pointers"); + + /* + * Set receiver and transmitter buffer descriptor bases + */ + sc->rxBdBase = mcf5235_bd_allocate(sc->rxBdCount); + sc->txBdBase = mcf5235_bd_allocate(sc->txBdCount); + MCF5235_FEC_ERDSR = (int)sc->rxBdBase; + MCF5235_FEC_ETDSR = (int)sc->txBdBase; + + /* + * Set up Receive Control Register: + * Not promiscuous + * MII mode + * Full duplex + * No loopback + */ + MCF5235_FEC_RCR = MCF5235_FEC_RCR_MAX_FL(MAX_MTU_SIZE) | + MCF5235_FEC_RCR_MII_MODE; + + /* + * Set up Transmit Control Register: + * Full duplex + * No heartbeat + */ + MCF5235_FEC_TCR = MCF5235_FEC_TCR_FDEN; + + /* + * Initialize statistic counters + */ + MCF5235_FEC_MIBC = MCF5235_FEC_MIBC_MIB_DISABLE; + { + vuint32 *vuip = &MCF5235_FEC_RMON_T_DROP; + while (vuip <= &MCF5235_FEC_IEEE_R_OCTETS_OK) + *vuip++ = 0; + } + MCF5235_FEC_MIBC = 0; + + /* + * Set MII speed to <= 2.5 MHz + */ + i = (clock_speed + 5000000 - 1) / 5000000; + MCF5235_FEC_MSCR = MCF5235_FEC_MSCR_MII_SPEED(i); + + /* + * Set PHYS to 100 Mb/s, full duplex + */ + setMII(1, 0, 0x2100); + + /* + * Set up receive buffer descriptors + */ + for (i = 0 ; i < sc->rxBdCount ; i++) + (sc->rxBdBase + i)->status = 0; + + /* + * Set up transmit buffer descriptors + */ + for (i = 0 ; i < sc->txBdCount ; i++) { + sc->txBdBase[i].status = 0; + sc->txMbuf[i] = NULL; + } + sc->txBdHead = sc->txBdTail = 0; + sc->txBdActiveCount = 0; + + /* + * Set up interrupts + */ + status = rtems_interrupt_catch( mcf5235_fec_tx_interrupt_handler, FEC_INTC0_TX_VECTOR, &old_handler ); + if (status != RTEMS_SUCCESSFUL) + rtems_panic ("Can't attach MCF5235 FEC TX interrupt handler: %s\n", + rtems_status_text(status)); + status = rtems_interrupt_catch(mcf5235_fec_rx_interrupt_handler, FEC_INTC0_RX_VECTOR, &old_handler); + if (status != RTEMS_SUCCESSFUL) + rtems_panic ("Can't attach MCF5235 FEC RX interrupt handler: %s\n", + rtems_status_text(status)); + MCF5235_INTC0_ICR23 = MCF5235_INTC_ICR_IL(FEC_IRQ_LEVEL) | + MCF5235_INTC_ICR_IP(FEC_IRQ_TX_PRIORITY); + MCF5235_INTC0_IMRL &= ~(MCF5235_INTC0_IMRL_INT23 | MCF5235_INTC0_IMRL_MASKALL); + MCF5235_INTC0_ICR27 = MCF5235_INTC_ICR_IL(FEC_IRQ_LEVEL) | + MCF5235_INTC_ICR_IP(FEC_IRQ_RX_PRIORITY); + MCF5235_INTC0_IMRL &= ~(MCF5235_INTC0_IMRL_INT27 | MCF5235_INTC0_IMRL_MASKALL); +} + +/* + * Get the MAC address from the hardware. + */ +static void +fec_get_mac_address(volatile struct mcf5235_enet_struct *sc, unsigned char* hwaddr) +{ + unsigned long addr; + + addr = MCF5235_FEC_PALR; + + hwaddr[0] = (addr >> 24) & 0xff; + hwaddr[1] = (addr >> 16) & 0xff; + hwaddr[2] = (addr >> 8) & 0xff; + hwaddr[3] = (addr >> 0) & 0xff; + + addr = MCF5235_FEC_PAUR; + + hwaddr[4] = (addr >> 24) & 0xff; + hwaddr[5] = (addr >> 16) & 0xff; +} + + +/* + * Soak up buffer descriptors that have been sent. + */ +static void +fec_retire_tx_bd(volatile struct mcf5235_enet_struct *sc ) +{ + struct mbuf *m, *n; + + while ((sc->txBdActiveCount != 0) + && ((sc->txBdBase[sc->txBdTail].status & MCF5235_FEC_TxBD_R) == 0)) { + m = sc->txMbuf[sc->txBdTail]; + MFREE(m, n); + if (++sc->txBdTail == sc->txBdCount) + sc->txBdTail = 0; + sc->txBdActiveCount--; + } +} + +static void +fec_rxDaemon (void *arg) +{ + volatile struct mcf5235_enet_struct *sc = (volatile struct mcf5235_enet_struct *)arg; + struct ifnet *ifp = (struct ifnet* )&sc->arpcom.ac_if; + struct mbuf *m; + volatile uint16_t status; + volatile mcf5235BufferDescriptor_t *rxBd; + int rxBdIndex; + + /* + * Allocate space for incoming packets and start reception + */ + for (rxBdIndex = 0 ; ;) { + rxBd = sc->rxBdBase + rxBdIndex; + MGETHDR(m, M_WAIT, MT_DATA); + MCLGET(m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod(m, void *); + rxBd->status = MCF5235_FEC_RxBD_E; + if (++rxBdIndex == sc->rxBdCount) { + rxBd->status |= MCF5235_FEC_RxBD_W; + break; + } + } + + /* + * Input packet handling loop + */ + /* Indicate we have some ready buffers available */ + MCF5235_FEC_RDAR = MCF5235_FEC_RDAR_R_DES_ACTIVE; + + rxBdIndex = 0; + for (;;) { + rxBd = sc->rxBdBase + rxBdIndex; + + /* + * Wait for packet if there's not one ready + */ + if ((status = rxBd->status) & MCF5235_FEC_RxBD_E) { + /* + * Clear old events. + */ + MCF5235_FEC_EIR = MCF5235_FEC_EIR_RXF; + + /* + * Wait for packet to arrive. + * Check the buffer descriptor before waiting for the event. + * This catches the case when a packet arrives between the + * `if' above, and the clearing of the RXF bit in the EIR. + */ + while ((status = rxBd->status) & MCF5235_FEC_RxBD_E) { + rtems_event_set events; + int level; + + rtems_interrupt_disable(level); + MCF5235_FEC_EIMR |= MCF5235_FEC_EIMR_RXF; + rtems_interrupt_enable(level); + rtems_bsdnet_event_receive (RX_INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + } + } + + /* + * Check that packet is valid + */ + if (status & MCF5235_FEC_RxBD_L) { + /* + * Pass the packet up the chain. + * FIXME: Packet filtering hook could be done here. + */ + struct ether_header *eh; + int len = rxBd->length - sizeof(uint32_t); + + /* + * Invalidate the cache and push the packet up. + * The cache is so small that it's more efficient to just + * invalidate the whole thing unless the packet is very small. + */ + m = sc->rxMbuf[rxBdIndex]; + if (len < 128) + rtems_cache_invalidate_multiple_data_lines(m->m_data, len); + else + rtems_cache_invalidate_entire_data(); + m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header); + eh = mtod(m, struct ether_header *); + m->m_data += sizeof(struct ether_header); + ether_input(ifp, eh, m); + + /* + * Allocate a new mbuf + */ + MGETHDR(m, M_WAIT, MT_DATA); + MCLGET(m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod(m, void *); + } + + /* + * Reenable the buffer descriptor + */ + rxBd->status = (status & MCF5235_FEC_RxBD_W) | MCF5235_FEC_RxBD_E; + MCF5235_FEC_RDAR = MCF5235_FEC_RDAR_R_DES_ACTIVE; + + /* + * Move to next buffer descriptor + */ + if (++rxBdIndex == sc->rxBdCount) + rxBdIndex = 0; + } +} + +static void +fec_sendpacket(struct ifnet *ifp, struct mbuf *m) +{ + struct mcf5235_enet_struct *sc = ifp->if_softc; + volatile mcf5235BufferDescriptor_t *firstTxBd, *txBd; + uint16_t status; + int nAdded; + + /* + * Free up buffer descriptors + */ + fec_retire_tx_bd(sc); + + /* + * Set up the transmit buffer descriptors. + * No need to pad out short packets since the + * hardware takes care of that automatically. + * No need to copy the packet to a contiguous buffer + * since the hardware is capable of scatter/gather DMA. + */ + nAdded = 0; + firstTxBd = sc->txBdBase + sc->txBdHead; + + for (;;) { + /* + * Wait for buffer descriptor to become available + */ + if ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + /* + * Clear old events. + */ + MCF5235_FEC_EIR = MCF5235_FEC_EIR_TXF; + + /* + * Wait for buffer descriptor to become available. + * Check for buffer descriptors before waiting for the event. + * This catches the case when a buffer became available between + * the `if' above, and the clearing of the TXF bit in the EIR. + */ + fec_retire_tx_bd(sc); + while ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + rtems_event_set events; + int level; + + rtems_interrupt_disable(level); + MCF5235_FEC_EIMR |= MCF5235_FEC_EIMR_TXF; + rtems_interrupt_enable(level); + sc->txRawWait++; + rtems_bsdnet_event_receive(TX_INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + fec_retire_tx_bd(sc); + } + } + + /* + * Don't set the READY flag on the first fragment + * until the whole packet has been readied. + */ + status = nAdded ? MCF5235_FEC_TxBD_R : 0; + + /* + * The IP fragmentation routine in ip_output + * can produce fragments with zero length. + */ + txBd = sc->txBdBase + sc->txBdHead; + if (m->m_len) { + char *p = mtod(m, char *); + /* + * Stupid FEC can't handle misaligned data! + * Given the way that mbuf's are layed out it should be + * safe to shuffle the data down like this..... + * Perhaps this code could be improved with a "Duff's Device". + */ + if ((int)p & 0x3) { + int l = m->m_len; + char *dest = p - ((int)p & 0x3); + uint16_t *o = (uint16_t *)dest, *i = (uint16_t *)p; + while (l > 0) { + *o++ = *i++; + l -= sizeof(uint16_t); + } + p = dest; + sc->txRealign++; + } + txBd->buffer = p; + txBd->length = m->m_len; + sc->txMbuf[sc->txBdHead] = m; + nAdded++; + if (++sc->txBdHead == sc->txBdCount) { + status |= MCF5235_FEC_TxBD_W; + sc->txBdHead = 0; + } + m = m->m_next; + } + else { + /* + * Just toss empty mbufs + */ + struct mbuf *n; + MFREE(m, n); + m = n; + } + if (m == NULL) { + if (nAdded) { + txBd->status = status | MCF5235_FEC_TxBD_R + | MCF5235_FEC_TxBD_L + | MCF5235_FEC_TxBD_TC; + if (nAdded > 1) + firstTxBd->status |= MCF5235_FEC_TxBD_R; + MCF5235_FEC_TDAR = MCF5235_FEC_TDAR_X_DES_ACTIVE; + sc->txBdActiveCount += nAdded; + } + break; + } + txBd->status = status; + } +} + +void +fec_txDaemon(void *arg) +{ + struct mcf5235_enet_struct *sc = (struct mcf5235_enet_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + rtems_event_set events; + + for (;;) { + /* + * Wait for packet + */ + rtems_bsdnet_event_receive(START_TRANSMIT_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; + fec_sendpacket(ifp, m); + } + ifp->if_flags &= ~IFF_OACTIVE; + } +} + + +/* + * Send packet (caller provides header). + */ +static void +mcf5235_enet_start(struct ifnet *ifp) +{ + struct mcf5235_enet_struct *sc = ifp->if_softc; + + rtems_bsdnet_event_send(sc->txDaemonTid, START_TRANSMIT_EVENT); + ifp->if_flags |= IFF_OACTIVE; +} + +static void +fec_init(void *arg) +{ + struct mcf5235_enet_struct *sc = arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + + if (sc->txDaemonTid == 0) { + /* + * Set up hardware + */ + mcf5235_fec_initialize_hardware(sc); + + /* + * Start driver tasks + */ + sc->txDaemonTid = rtems_bsdnet_newproc("FECtx", 4096, fec_txDaemon, sc); + sc->rxDaemonTid = rtems_bsdnet_newproc("FECrx", 4096, fec_rxDaemon, sc); + } + + /* + * Set flags appropriately + */ + if (ifp->if_flags & IFF_PROMISC) + MCF5235_FEC_RCR |= MCF5235_FEC_RCR_PROM; + else + MCF5235_FEC_RCR &= ~MCF5235_FEC_RCR_PROM; + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; + + /* + * Enable receiver and transmitter + */ + MCF5235_FEC_ECR = MCF5235_FEC_ECR_ETHER_EN; +} + + +static void +fec_stop(struct mcf5235_enet_struct *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + + ifp->if_flags &= ~IFF_RUNNING; + + /* + * Shut down receiver and transmitter + */ + MCF5235_FEC_ECR = 0x0; +} + +/* + * Show interface statistics + */ +static void +enet_stats(struct mcf5235_enet_struct *sc) +{ + printf(" Rx Interrupts:%-10lu", sc->rxInterrupts); + printf("Rx Packet Count:%-10lu", MCF5235_FEC_RMON_R_PACKETS); + printf(" Rx Broadcast:%-10lu\n", MCF5235_FEC_RMON_R_BC_PKT); + printf(" Rx Multicast:%-10lu", MCF5235_FEC_RMON_R_MC_PKT); + printf("CRC/Align error:%-10lu", MCF5235_FEC_RMON_R_CRC_ALIGN); + printf(" Rx Undersize:%-10lu\n", MCF5235_FEC_RMON_R_UNDERSIZE); + printf(" Rx Oversize:%-10lu", MCF5235_FEC_RMON_R_OVERSIZE); + printf(" Rx Fragment:%-10lu", MCF5235_FEC_RMON_R_FRAG); + printf(" Rx Jabber:%-10lu\n", MCF5235_FEC_RMON_R_JAB); + printf(" Rx 64:%-10lu", MCF5235_FEC_RMON_R_P64); + printf(" Rx 65-127:%-10lu", MCF5235_FEC_RMON_R_P65T0127); + printf(" Rx 128-255:%-10lu\n", MCF5235_FEC_RMON_R_P128TO255); + printf(" Rx 256-511:%-10lu", MCF5235_FEC_RMON_R_P256TO511); + printf(" Rx 511-1023:%-10lu", MCF5235_FEC_RMON_R_P512TO1023); + printf(" Rx 1024-2047:%-10lu\n", MCF5235_FEC_RMON_R_P1024TO2047); + printf(" Rx >=2048:%-10lu", MCF5235_FEC_RMON_R_GTE2048); + printf(" Rx Octets:%-10lu", MCF5235_FEC_RMON_R_OCTETS); + printf(" Rx Dropped:%-10lu\n", MCF5235_FEC_IEEE_R_DROP); + printf(" Rx frame OK:%-10lu", MCF5235_FEC_IEEE_R_FRAME_OK); + printf(" Rx CRC error:%-10lu", MCF5235_FEC_IEEE_R_CRC); + printf(" Rx Align error:%-10lu\n", MCF5235_FEC_IEEE_R_ALIGN); + printf(" FIFO Overflow:%-10lu", MCF5235_FEC_IEEE_R_MACERR); + printf("Rx Pause Frames:%-10lu", MCF5235_FEC_IEEE_R_FDXFC); + printf(" Rx Octets OK:%-10lu\n", MCF5235_FEC_IEEE_R_OCTETS_OK); + printf(" Tx Interrupts:%-10lu", sc->txInterrupts); + printf("Tx Output Waits:%-10lu", sc->txRawWait); + printf("Tx Realignments:%-10lu\n", sc->txRealign); + printf(" Tx Unaccounted:%-10lu", MCF5235_FEC_RMON_T_DROP); + printf("Tx Packet Count:%-10lu", MCF5235_FEC_RMON_T_PACKETS); + printf(" Tx Broadcast:%-10lu\n", MCF5235_FEC_RMON_T_BC_PKT); + printf(" Tx Multicast:%-10lu", MCF5235_FEC_RMON_T_MC_PKT); + printf("CRC/Align error:%-10lu", MCF5235_FEC_RMON_T_CRC_ALIGN); + printf(" Tx Undersize:%-10lu\n", MCF5235_FEC_RMON_T_UNDERSIZE); + printf(" Tx Oversize:%-10lu", MCF5235_FEC_RMON_T_OVERSIZE); + printf(" Tx Fragment:%-10lu", MCF5235_FEC_RMON_T_FRAG); + printf(" Tx Jabber:%-10lu\n", MCF5235_FEC_RMON_T_JAB); + printf(" Tx Collisions:%-10lu", MCF5235_FEC_RMON_T_COL); + printf(" Tx 64:%-10lu", MCF5235_FEC_RMON_T_P64); + printf(" Tx 65-127:%-10lu\n", MCF5235_FEC_RMON_T_P65TO127); + printf(" Tx 128-255:%-10lu", MCF5235_FEC_RMON_T_P128TO255); + printf(" Tx 256-511:%-10lu", MCF5235_FEC_RMON_T_P256TO511); + printf(" Tx 511-1023:%-10lu\n", MCF5235_FEC_RMON_T_P512TO1023); + printf(" Tx 1024-2047:%-10lu", MCF5235_FEC_RMON_T_P1024TO2047); + printf(" Tx >=2048:%-10lu", MCF5235_FEC_RMON_T_P_GTE2048); + printf(" Tx Octets:%-10lu\n", MCF5235_FEC_RMON_T_OCTETS); + printf(" Tx Dropped:%-10lu", MCF5235_FEC_IEEE_T_DROP); + printf(" Tx Frame OK:%-10lu", MCF5235_FEC_IEEE_T_FRAME_OK); + printf(" Tx 1 Collision:%-10lu\n", MCF5235_FEC_IEEE_T_1COL); + printf("Tx >1 Collision:%-10lu", MCF5235_FEC_IEEE_T_MCOL); + printf(" Tx Deferred:%-10lu", MCF5235_FEC_IEEE_T_DEF); + printf(" Late Collision:%-10lu\n", MCF5235_FEC_IEEE_T_LCOL); + printf(" Excessive Coll:%-10lu", MCF5235_FEC_IEEE_T_EXCOL); + printf(" FIFO Underrun:%-10lu", MCF5235_FEC_IEEE_T_MACERR); + printf(" Carrier Error:%-10lu\n", MCF5235_FEC_IEEE_T_CSERR); + printf(" Tx SQE Error:%-10lu", MCF5235_FEC_IEEE_T_SQE); + printf("Tx Pause Frames:%-10lu", MCF5235_FEC_IEEE_T_FDXFC); + printf(" Tx Octets OK:%-10lu\n", MCF5235_FEC_IEEE_T_OCTETS_OK); +} + +static int +fec_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data) +{ + struct mcf5235_enet_struct *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: + fec_stop(sc); + break; + + case IFF_UP: + fec_init(sc); + break; + + case IFF_UP | IFF_RUNNING: + fec_stop(sc); + fec_init(sc); + break; + + default: + break; + } + break; + + case SIO_RTEMS_SHOW_STATS: + enet_stats(sc); + break; + + /* + * FIXME: All sorts of multicast commands need to be added here! + */ + default: + error = EINVAL; + break; + } + return error; +} + +int +rtems_fec_driver_attach(struct rtems_bsdnet_ifconfig *config, int attaching ) +{ + struct mcf5235_enet_struct *sc; + struct ifnet *ifp; + int mtu; + int unitNumber; + char *unitName; + unsigned char *hwaddr; + + /* + * Parse driver name + */ + if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0) + return 0; + + /* + * Is driver free? + */ + if ((unitNumber < 0) || (unitNumber >= NIFACES)) { + printf("mcf5235: bad FEC unit number.\n"); + return 0; + } + sc = &enet_driver[unitNumber]; + ifp = &sc->arpcom.ac_if; + if (ifp->if_softc != NULL) { + printf("mcf5235: driver already in use.\n"); + return 0; + } + + /* + * Process options + */ + if (config->hardware_address) + memcpy(sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); + else + fec_get_mac_address(sc, sc->arpcom.ac_enaddr); + + hwaddr = config->hardware_address; + printf("%s%d: mac: %02x:%02x:%02x:%02x:%02x:%02x\n", + unitName, unitNumber, + hwaddr[0], hwaddr[1], hwaddr[2], + hwaddr[3], hwaddr[4], hwaddr[5]); + + if (config->mtu) + mtu = config->mtu; + else + mtu = ETHERMTU; + if (config->rbuf_count) + sc->rxBdCount = config->rbuf_count; + else + sc->rxBdCount = RX_BUF_COUNT; + if (config->xbuf_count) + sc->txBdCount = config->xbuf_count; + else + sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF; + + 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 = fec_init; + ifp->if_ioctl = fec_ioctl; + ifp->if_start = mcf5235_enet_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); + return 1; +}; + diff --git a/bsps/m68k/mcf5329/net/network.c b/bsps/m68k/mcf5329/net/network.c new file mode 100644 index 0000000000..c752f8d669 --- /dev/null +++ b/bsps/m68k/mcf5329/net/network.c @@ -0,0 +1,857 @@ +/* + * RTEMS/TCPIP driver for MCF5329 Fast Ethernet Controller + * + * TO DO: Check network stack code -- force longword alignment of all tx mbufs? + */ + +#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ + +#include <bsp.h> +#include <stdio.h> +#include <errno.h> +#include <stdarg.h> +#include <string.h> +#include <rtems.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/ethernet.h> +#include <net/if.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +/* + * Number of interfaces supported by this driver + */ +#define NIFACES 1 + +#define FEC_INTC0_TX_VECTOR (64+36) +#define FEC_INTC0_RX_VECTOR (64+40) + +/* + * Default number of buffer descriptors set aside for this driver. + * The number of transmit buffer descriptors has to be quite large + * since a single frame often uses three or more buffer descriptors. + */ +#define RX_BUF_COUNT 32 +#define TX_BUF_COUNT 20 +#define TX_BD_PER_BUF 3 + +#define INET_ADDR_MAX_BUF_SIZE (sizeof "255.255.255.255") + +/* + * RTEMS event used by interrupt handler to signal daemons. + * This must *not* be the same event used by the TCP/IP task synchronization. + */ +#define TX_INTERRUPT_EVENT RTEMS_EVENT_1 +#define RX_INTERRUPT_EVENT RTEMS_EVENT_1 + +/* + * RTEMS event used to start transmit daemon. + * This must not be the same as INTERRUPT_EVENT. + */ +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + +/* + * Receive buffer size -- Allow for a full ethernet packet plus CRC (1518). + * Round off to nearest multiple of RBUF_ALIGN. + */ +#define MAX_MTU_SIZE 1518 +#define RBUF_ALIGN 4 +#define RBUF_SIZE ((MAX_MTU_SIZE + RBUF_ALIGN) & ~RBUF_ALIGN) + +#if (MCLBYTES < RBUF_SIZE) +# error "Driver must have MCLBYTES > RBUF_SIZE" +#endif + +typedef struct mcf5329BufferDescriptor_ +{ + volatile uint16_t status; + uint16_t length; + volatile void *buffer; +} mcf5329BufferDescriptor_t; + +/* + * Per-device data + */ +struct mcf5329_enet_struct +{ + struct arpcom arpcom; + struct mbuf **rxMbuf; + struct mbuf **txMbuf; + int acceptBroadcast; + int rxBdCount; + int txBdCount; + int txBdHead; + int txBdTail; + int txBdActiveCount; + mcf5329BufferDescriptor_t *rxBdBase; + mcf5329BufferDescriptor_t *txBdBase; + rtems_id rxDaemonTid; + rtems_id txDaemonTid; + + /* + * Statistics + */ + unsigned long rxInterrupts; + unsigned long txInterrupts; + unsigned long txRawWait; + unsigned long txRealign; +}; +static struct mcf5329_enet_struct enet_driver[NIFACES]; + +static rtems_isr mcf5329_fec_rx_interrupt_handler(rtems_vector_number v) +{ + MCF_FEC_EIR = MCF_FEC_EIR_RXF; + MCF_FEC_EIMR &= ~MCF_FEC_EIMR_RXF; + enet_driver[0].rxInterrupts++; + rtems_bsdnet_event_send(enet_driver[0].rxDaemonTid, RX_INTERRUPT_EVENT); +} + +static rtems_isr mcf5329_fec_tx_interrupt_handler(rtems_vector_number v) +{ + MCF_FEC_EIR = MCF_FEC_EIR_TXF; + MCF_FEC_EIMR &= ~MCF_FEC_EIMR_TXF; + enet_driver[0].txInterrupts++; + rtems_bsdnet_event_send(enet_driver[0].txDaemonTid, TX_INTERRUPT_EVENT); +} + +extern char _CoreSRamBase[]; + +/* + * Allocate buffer descriptors from (non-cached) on-chip static RAM + * Ensure 128-bit (16-byte) alignment + */ +static mcf5329BufferDescriptor_t *mcf5329_bd_allocate(unsigned int count) +{ + static mcf5329BufferDescriptor_t *bdp = + (mcf5329BufferDescriptor_t *) _CoreSRamBase; + mcf5329BufferDescriptor_t *p = bdp; + + bdp += count; + if ((int) bdp & 0xF) + bdp = + (mcf5329BufferDescriptor_t *) ((char *) bdp + (16 - ((int) bdp & 0xF))); + return p; +} + +#if UNUSED + +/* + * Read MII register + * Busy-waits, but transfer time should be short! + */ +static int getMII(int phyNumber, int regNumber) +{ + MCF_FEC_MMFR = (0x1 << 30) | + (0x2 << 28) | (phyNumber << 23) | (regNumber << 18) | (0x2 << 16); + while ((MCF_FEC_EIR & MCF_FEC_EIR_MII) == 0) ; + MCF_FEC_EIR = MCF_FEC_EIR_MII; + return MCF_FEC_MMFR & 0xFFFF; +} +#endif + +/* + * Write MII register + * Busy-waits, but transfer time should be short! + */ +static void setMII(int phyNumber, int regNumber, int value) +{ + MCF_FEC_MMFR = (0x1 << 30) | + (0x1 << 28) | + (phyNumber << 23) | (regNumber << 18) | (0x2 << 16) | (value & 0xFFFF); + while ((MCF_FEC_EIR & MCF_FEC_EIR_MII) == 0) ; + MCF_FEC_EIR = MCF_FEC_EIR_MII; +} + +static void mcf5329_fec_initialize_hardware(struct mcf5329_enet_struct *sc) +{ + int i; + const unsigned char *hwaddr = 0; + rtems_status_code status; + rtems_isr_entry old_handler; + uint32_t clock_speed = bsp_get_BUS_clock_speed(); + + /* + * Issue reset to FEC + */ + MCF_FEC_ECR = MCF_FEC_ECR_RESET; + rtems_task_wake_after(1); + MCF_FEC_ECR = 0; + + /* + * Configuration of I/O ports is done outside of this function + */ +#if 0 + imm->gpio.pbcnt |= MCF_GPIO_PBCNT_SET_FEC; /* Set up port b FEC pins */ +#endif + + /* + * Set our physical address + */ + hwaddr = sc->arpcom.ac_enaddr; + MCF_FEC_PALR = (hwaddr[0] << 24) | (hwaddr[1] << 16) | + (hwaddr[2] << 8) | (hwaddr[3] << 0); + MCF_FEC_PAUR = (hwaddr[4] << 24) | (hwaddr[5] << 16); + + /* + * Clear the hash table + */ + MCF_FEC_GAUR = 0; + MCF_FEC_GALR = 0; + + /* + * Set up receive buffer size + */ + MCF_FEC_EMRBR = 1520; /* Standard Ethernet */ + + /* + * Allocate mbuf pointers + */ + sc->rxMbuf = malloc(sc->rxBdCount * sizeof *sc->rxMbuf, M_MBUF, M_NOWAIT); + sc->txMbuf = malloc(sc->txBdCount * sizeof *sc->txMbuf, M_MBUF, M_NOWAIT); + if (!sc->rxMbuf || !sc->txMbuf) + rtems_panic("No memory for mbuf pointers"); + + /* + * Set receiver and transmitter buffer descriptor bases + */ + sc->rxBdBase = mcf5329_bd_allocate(sc->rxBdCount); + sc->txBdBase = mcf5329_bd_allocate(sc->txBdCount); + MCF_FEC_ERDSR = (int) sc->rxBdBase; + MCF_FEC_ETDSR = (int) sc->txBdBase; + + /* + * Set up Receive Control Register: + * Not promiscuous + * MII mode + * Full duplex + * No loopback + */ + MCF_FEC_RCR = MCF_FEC_RCR_MAX_FL(MAX_MTU_SIZE) | MCF_FEC_RCR_MII_MODE; + + /* + * Set up Transmit Control Register: + * Full duplex + * No heartbeat + */ + MCF_FEC_TCR = MCF_FEC_TCR_FDEN; + + /* + * Initialize statistic counters + */ + MCF_FEC_MIBC = MCF_FEC_MIBC_MIB_DISABLE; + { + vuint32 *vuip = &MCF_FEC_RMON_T_DROP; + + while (vuip <= &MCF_FEC_IEEE_R_OCTETS_OK) + *vuip++ = 0; + } + MCF_FEC_MIBC = 0; + + /* + * Set MII speed to <= 2.5 MHz + */ + i = (clock_speed + 5000000 - 1) / 5000000; + MCF_FEC_MSCR = MCF_FEC_MSCR_MII_SPEED(i); + + /* + * Set PHYS to 100 Mb/s, full duplex + */ + setMII(1, 0, 0x2100); + + /* + * Set up receive buffer descriptors + */ + for (i = 0; i < sc->rxBdCount; i++) + (sc->rxBdBase + i)->status = 0; + + /* + * Set up transmit buffer descriptors + */ + for (i = 0; i < sc->txBdCount; i++) { + sc->txBdBase[i].status = 0; + sc->txMbuf[i] = NULL; + } + sc->txBdHead = sc->txBdTail = 0; + sc->txBdActiveCount = 0; + + /* + * Set up interrupts + */ + status = + rtems_interrupt_catch(mcf5329_fec_tx_interrupt_handler, + FEC_INTC0_TX_VECTOR, &old_handler); + if (status != RTEMS_SUCCESSFUL) + rtems_panic("Can't attach MCF FEC TX interrupt handler: %s\n", + rtems_status_text(status)); + status = + rtems_interrupt_catch(mcf5329_fec_rx_interrupt_handler, + FEC_INTC0_RX_VECTOR, &old_handler); + if (status != RTEMS_SUCCESSFUL) + rtems_panic("Can't attach MCF FEC RX interrupt handler: %s\n", + rtems_status_text(status)); + MCF_INTC0_ICR36 = MCF_INTC_ICR_IL(FEC_IRQ_LEVEL); + MCF_INTC0_IMRH &= ~(MCF_INTC_IMRH_INT_MASK36); + MCF_INTC0_ICR40 = MCF_INTC_ICR_IL(FEC_IRQ_LEVEL); + MCF_INTC0_IMRH &= ~(MCF_INTC_IMRH_INT_MASK40); +} + +/* + * Get the MAC address from the hardware. + */ +static void +fec_get_mac_address(volatile struct mcf5329_enet_struct *sc, + unsigned char *hwaddr) +{ + unsigned long addr; + + addr = MCF_FEC_PALR; + + hwaddr[0] = (addr >> 24) & 0xff; + hwaddr[1] = (addr >> 16) & 0xff; + hwaddr[2] = (addr >> 8) & 0xff; + hwaddr[3] = (addr >> 0) & 0xff; + + addr = MCF_FEC_PAUR; + + hwaddr[4] = (addr >> 24) & 0xff; + hwaddr[5] = (addr >> 16) & 0xff; +} + +/* + * Soak up buffer descriptors that have been sent. + */ +static void fec_retire_tx_bd(volatile struct mcf5329_enet_struct *sc) +{ + struct mbuf *m, *n; + + while ((sc->txBdActiveCount != 0) + && ((sc->txBdBase[sc->txBdTail].status & MCF_FEC_TxBD_R) == 0)) { + m = sc->txMbuf[sc->txBdTail]; + MFREE(m, n); + if (++sc->txBdTail == sc->txBdCount) + sc->txBdTail = 0; + sc->txBdActiveCount--; + } +} + +static void fec_rxDaemon(void *arg) +{ + volatile struct mcf5329_enet_struct *sc = + (volatile struct mcf5329_enet_struct *) arg; + struct ifnet *ifp = (struct ifnet *) &sc->arpcom.ac_if; + struct mbuf *m; + volatile uint16_t status; + volatile mcf5329BufferDescriptor_t *rxBd; + int rxBdIndex; + + /* + * Allocate space for incoming packets and start reception + */ + for (rxBdIndex = 0;;) { + rxBd = sc->rxBdBase + rxBdIndex; + MGETHDR(m, M_WAIT, MT_DATA); + MCLGET(m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod(m, void *); + + rxBd->status = MCF_FEC_RxBD_E; + if (++rxBdIndex == sc->rxBdCount) { + rxBd->status |= MCF_FEC_RxBD_W; + break; + } + } + + /* + * Input packet handling loop + */ + /* Indicate we have some ready buffers available */ + MCF_FEC_RDAR = MCF_FEC_RDAR_R_DES_ACTIVE; + + rxBdIndex = 0; + for (;;) { + rxBd = sc->rxBdBase + rxBdIndex; + + /* + * Wait for packet if there's not one ready + */ + if ((status = rxBd->status) & MCF_FEC_RxBD_E) { + /* + * Clear old events. + */ + MCF_FEC_EIR = MCF_FEC_EIR_RXF; + + /* + * Wait for packet to arrive. + * Check the buffer descriptor before waiting for the event. + * This catches the case when a packet arrives between the + * `if' above, and the clearing of the RXF bit in the EIR. + */ + while ((status = rxBd->status) & MCF_FEC_RxBD_E) { + rtems_event_set events; + int level; + + rtems_interrupt_disable(level); + MCF_FEC_EIMR |= MCF_FEC_EIMR_RXF; + rtems_interrupt_enable(level); + rtems_bsdnet_event_receive(RX_INTERRUPT_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, &events); + } + } + + /* + * Check that packet is valid + */ + if (status & MCF_FEC_RxBD_L) { + /* + * Pass the packet up the chain. + * FIXME: Packet filtering hook could be done here. + */ + struct ether_header *eh; + int len = rxBd->length - sizeof(uint32_t); + + m = sc->rxMbuf[rxBdIndex]; + + rtems_cache_invalidate_multiple_data_lines(m->m_data, len); + + m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header); + eh = mtod(m, struct ether_header *); + m->m_data += sizeof(struct ether_header); + ether_input(ifp, eh, m); + + /* + * Allocate a new mbuf + */ + MGETHDR(m, M_WAIT, MT_DATA); + MCLGET(m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod(m, void *); + } + + /* + * Reenable the buffer descriptor + */ + rxBd->status = (status & MCF_FEC_RxBD_W) | MCF_FEC_RxBD_E; + MCF_FEC_RDAR = MCF_FEC_RDAR_R_DES_ACTIVE; + + /* + * Move to next buffer descriptor + */ + if (++rxBdIndex == sc->rxBdCount) + rxBdIndex = 0; + } +} + +static void fec_sendpacket(struct ifnet *ifp, struct mbuf *m) +{ + struct mcf5329_enet_struct *sc = ifp->if_softc; + volatile mcf5329BufferDescriptor_t *firstTxBd, *txBd; + uint16_t status; + int nAdded; + + /* + * Free up buffer descriptors + */ + fec_retire_tx_bd(sc); + + /* + * Set up the transmit buffer descriptors. + * No need to pad out short packets since the + * hardware takes care of that automatically. + * No need to copy the packet to a contiguous buffer + * since the hardware is capable of scatter/gather DMA. + */ + nAdded = 0; + firstTxBd = sc->txBdBase + sc->txBdHead; + + for (;;) { + /* + * Wait for buffer descriptor to become available + */ + if ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + /* + * Clear old events. + */ + MCF_FEC_EIR = MCF_FEC_EIR_TXF; + + /* + * Wait for buffer descriptor to become available. + * Check for buffer descriptors before waiting for the event. + * This catches the case when a buffer became available between + * the `if' above, and the clearing of the TXF bit in the EIR. + */ + fec_retire_tx_bd(sc); + while ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + rtems_event_set events; + int level; + + rtems_interrupt_disable(level); + MCF_FEC_EIMR |= MCF_FEC_EIMR_TXF; + rtems_interrupt_enable(level); + sc->txRawWait++; + rtems_bsdnet_event_receive(TX_INTERRUPT_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, &events); + fec_retire_tx_bd(sc); + } + } + + /* + * Don't set the READY flag on the first fragment + * until the whole packet has been readied. + */ + status = nAdded ? MCF_FEC_TxBD_R : 0; + + /* + * The IP fragmentation routine in ip_output + * can produce fragments with zero length. + */ + txBd = sc->txBdBase + sc->txBdHead; + if (m->m_len) { + char *p = mtod(m, char *); + + /* + * Stupid FEC can't handle misaligned data! + * Given the way that mbuf's are layed out it should be + * safe to shuffle the data down like this..... + * Perhaps this code could be improved with a "Duff's Device". + */ + if ((int) p & 0x3) { + int l = m->m_len; + char *dest = p - ((int) p & 0x3); + uint16_t *o = (uint16_t *) dest, *i = (uint16_t *) p; + + while (l > 0) { + *o++ = *i++; + l -= sizeof(uint16_t); + } + p = dest; + sc->txRealign++; + } + + txBd->buffer = p; + txBd->length = m->m_len; + + rtems_cache_flush_multiple_data_lines(txBd->buffer, txBd->length); + + sc->txMbuf[sc->txBdHead] = m; + nAdded++; + if (++sc->txBdHead == sc->txBdCount) { + status |= MCF_FEC_TxBD_W; + sc->txBdHead = 0; + } + m = m->m_next; + } else { + /* + * Just toss empty mbufs + */ + struct mbuf *n; + + MFREE(m, n); + m = n; + } + if (m == NULL) { + if (nAdded) { + txBd->status = status | MCF_FEC_TxBD_R + | MCF_FEC_TxBD_L | MCF_FEC_TxBD_TC; + if (nAdded > 1) + firstTxBd->status |= MCF_FEC_TxBD_R; + MCF_FEC_TDAR = MCF_FEC_TDAR_X_DES_ACTIVE; + sc->txBdActiveCount += nAdded; + } + break; + } + txBd->status = status; + } +} + +void fec_txDaemon(void *arg) +{ + struct mcf5329_enet_struct *sc = (struct mcf5329_enet_struct *) arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + rtems_event_set events; + + for (;;) { + /* + * Wait for packet + */ + rtems_bsdnet_event_receive(START_TRANSMIT_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; + fec_sendpacket(ifp, m); + } + ifp->if_flags &= ~IFF_OACTIVE; + } +} + +/* + * Send packet (caller provides header). + */ +static void mcf5329_enet_start(struct ifnet *ifp) +{ + struct mcf5329_enet_struct *sc = ifp->if_softc; + + rtems_bsdnet_event_send(sc->txDaemonTid, START_TRANSMIT_EVENT); + ifp->if_flags |= IFF_OACTIVE; +} + +static void fec_init(void *arg) +{ + struct mcf5329_enet_struct *sc = arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + + if (sc->txDaemonTid == 0) { + /* + * Set up hardware + */ + mcf5329_fec_initialize_hardware(sc); + + /* + * Start driver tasks + */ + sc->txDaemonTid = rtems_bsdnet_newproc("FECtx", 4096, fec_txDaemon, sc); + sc->rxDaemonTid = rtems_bsdnet_newproc("FECrx", 4096, fec_rxDaemon, sc); + } + + /* + * Set flags appropriately + */ + if (ifp->if_flags & IFF_PROMISC) + MCF_FEC_RCR |= MCF_FEC_RCR_PROM; + else + MCF_FEC_RCR &= ~MCF_FEC_RCR_PROM; + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; + + /* + * Enable receiver and transmitter + */ + MCF_FEC_ECR = MCF_FEC_ECR_ETHER_EN; +} + +static void fec_stop(struct mcf5329_enet_struct *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + + ifp->if_flags &= ~IFF_RUNNING; + + /* + * Shut down receiver and transmitter + */ + MCF_FEC_ECR = 0x0; +} + +/* + * Show interface statistics + */ +static void enet_stats(struct mcf5329_enet_struct *sc) +{ + printf(" Rx Interrupts:%-10lu", sc->rxInterrupts); + printf("Rx Packet Count:%-10lu", MCF_FEC_RMON_R_PACKETS); + printf(" Rx Broadcast:%-10lu\n", MCF_FEC_RMON_R_BC_PKT); + printf(" Rx Multicast:%-10lu", MCF_FEC_RMON_R_MC_PKT); + printf("CRC/Align error:%-10lu", MCF_FEC_RMON_R_CRC_ALIGN); + printf(" Rx Undersize:%-10lu\n", MCF_FEC_RMON_R_UNDERSIZE); + printf(" Rx Oversize:%-10lu", MCF_FEC_RMON_R_OVERSIZE); + printf(" Rx Fragment:%-10lu", MCF_FEC_RMON_R_FRAG); + printf(" Rx Jabber:%-10lu\n", MCF_FEC_RMON_R_JAB); + printf(" Rx 64:%-10lu", MCF_FEC_RMON_R_P64); + printf(" Rx 65-127:%-10lu", MCF_FEC_RMON_R_P65TO127); + printf(" Rx 128-255:%-10lu\n", MCF_FEC_RMON_R_P128TO255); + printf(" Rx 256-511:%-10lu", MCF_FEC_RMON_R_P256TO511); + printf(" Rx 511-1023:%-10lu", MCF_FEC_RMON_R_512TO1023); + printf(" Rx 1024-2047:%-10lu\n", MCF_FEC_RMON_R_1024TO2047); + printf(" Rx >=2048:%-10lu", MCF_FEC_RMON_R_P_GTE2048); + printf(" Rx Octets:%-10lu", MCF_FEC_RMON_R_OCTETS); + printf(" Rx Dropped:%-10lu\n", MCF_FEC_IEEE_R_DROP); + printf(" Rx frame OK:%-10lu", MCF_FEC_IEEE_R_FRAME_OK); + printf(" Rx CRC error:%-10lu", MCF_FEC_IEEE_R_CRC); + printf(" Rx Align error:%-10lu\n", MCF_FEC_IEEE_R_ALIGN); + printf(" FIFO Overflow:%-10lu", MCF_FEC_IEEE_R_MACERR); + printf("Rx Pause Frames:%-10lu", MCF_FEC_IEEE_R_FDXFC); + printf(" Rx Octets OK:%-10lu\n", MCF_FEC_IEEE_R_OCTETS_OK); + printf(" Tx Interrupts:%-10lu", sc->txInterrupts); + printf("Tx Output Waits:%-10lu", sc->txRawWait); + printf("Tx Realignments:%-10lu\n", sc->txRealign); + printf(" Tx Unaccounted:%-10lu", MCF_FEC_RMON_T_DROP); + printf("Tx Packet Count:%-10lu", MCF_FEC_RMON_T_PACKETS); + printf(" Tx Broadcast:%-10lu\n", MCF_FEC_RMON_T_BC_PKT); + printf(" Tx Multicast:%-10lu", MCF_FEC_RMON_T_MC_PKT); + printf("CRC/Align error:%-10lu", MCF_FEC_RMON_T_CRC_ALIGN); + printf(" Tx Undersize:%-10lu\n", MCF_FEC_RMON_T_UNDERSIZE); + printf(" Tx Oversize:%-10lu", MCF_FEC_RMON_T_OVERSIZE); + printf(" Tx Fragment:%-10lu", MCF_FEC_RMON_T_FRAG); + printf(" Tx Jabber:%-10lu\n", MCF_FEC_RMON_T_JAB); + printf(" Tx Collisions:%-10lu", MCF_FEC_RMON_T_COL); + printf(" Tx 64:%-10lu", MCF_FEC_RMON_T_P64); + printf(" Tx 65-127:%-10lu\n", MCF_FEC_RMON_T_P65TO127); + printf(" Tx 128-255:%-10lu", MCF_FEC_RMON_T_P128TO255); + printf(" Tx 256-511:%-10lu", MCF_FEC_RMON_T_P256TO511); + printf(" Tx 511-1023:%-10lu\n", MCF_FEC_RMON_T_P512TO1023); + printf(" Tx 1024-2047:%-10lu", MCF_FEC_RMON_T_P1024TO2047); + printf(" Tx >=2048:%-10lu", MCF_FEC_RMON_T_P_GTE2048); + printf(" Tx Octets:%-10lu\n", MCF_FEC_RMON_T_OCTETS); + printf(" Tx Dropped:%-10lu", MCF_FEC_IEEE_T_DROP); + printf(" Tx Frame OK:%-10lu", MCF_FEC_IEEE_T_FRAME_OK); + printf(" Tx 1 Collision:%-10lu\n", MCF_FEC_IEEE_T_1COL); + printf("Tx >1 Collision:%-10lu", MCF_FEC_IEEE_T_MCOL); + printf(" Tx Deferred:%-10lu", MCF_FEC_IEEE_T_DEF); + printf(" Late Collision:%-10lu\n", MCF_FEC_IEEE_T_LCOL); + printf(" Excessive Coll:%-10lu", MCF_FEC_IEEE_T_EXCOL); + printf(" FIFO Underrun:%-10lu", MCF_FEC_IEEE_T_MACERR); + printf(" Carrier Error:%-10lu\n", MCF_FEC_IEEE_T_CSERR); + printf(" Tx SQE Error:%-10lu", MCF_FEC_IEEE_T_SQE); + printf("Tx Pause Frames:%-10lu", MCF_FEC_IEEE_T_FDXFC); + printf(" Tx Octets OK:%-10lu\n", MCF_FEC_IEEE_T_OCTETS_OK); +} + +static int fec_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data) +{ + struct mcf5329_enet_struct *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: + fec_stop(sc); + break; + + case IFF_UP: + fec_init(sc); + break; + + case IFF_UP | IFF_RUNNING: + fec_stop(sc); + fec_init(sc); + break; + + default: + break; + } + break; + + case SIO_RTEMS_SHOW_STATS: + enet_stats(sc); + break; + + /* + * FIXME: All sorts of multicast commands need to be added here! + */ + default: + error = EINVAL; + break; + } + return error; +} + +int +rtems_fec_driver_attach(struct rtems_bsdnet_ifconfig *config, int attaching) +{ + struct mcf5329_enet_struct *sc; + struct ifnet *ifp; + int mtu; + int unitNumber; + char *unitName; + unsigned char *hwaddr; + + /* + * Parse driver name + */ + if ((unitNumber = rtems_bsdnet_parse_driver_name(config, &unitName)) < 0) + return 0; + + /* + * Is driver free? + */ + if ((unitNumber < 0) || (unitNumber >= NIFACES)) { + printf("mcf5329: bad FEC unit number.\n"); + return 0; + } + sc = &enet_driver[unitNumber]; + ifp = &sc->arpcom.ac_if; + if (ifp->if_softc != NULL) { + printf("mcf5329: driver already in use.\n"); + return 0; + } + + /* + * Process options + */ + if (config->hardware_address) + memcpy(sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); + else + fec_get_mac_address(sc, sc->arpcom.ac_enaddr); + + hwaddr = config->hardware_address; + printf("%s%d: mac: %02x:%02x:%02x:%02x:%02x:%02x\n", + unitName, unitNumber, + hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]); + + if (config->mtu) + mtu = config->mtu; + else + mtu = ETHERMTU; + if (config->rbuf_count) + sc->rxBdCount = config->rbuf_count; + else + sc->rxBdCount = RX_BUF_COUNT; + if (config->xbuf_count) + sc->txBdCount = config->xbuf_count; + else + sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF; + + 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 = fec_init; + ifp->if_ioctl = fec_ioctl; + ifp->if_start = mcf5329_enet_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); + return 1; +}; diff --git a/bsps/m68k/mvme167/net/network.c b/bsps/m68k/mvme167/net/network.c new file mode 100644 index 0000000000..06bcbfa84a --- /dev/null +++ b/bsps/m68k/mvme167/net/network.c @@ -0,0 +1,3099 @@ +/* network.c: An 82596 ethernet driver for rtems-bsd. + */ + +#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ +#define KERNEL + +/* + * Selectively define to debug the network driver. If you define any of these + * you must run with polled console I/O. + */ + +/* +#define DBG_ADD_CMD +#define DBG_WAIT +#define DBG_SEND +#define DBG_MEM +#define DBG_SELFTEST_CMD +#define DBG_DUMP_CMD +#define DBG_RESET +#define DBG_ATTACH +#define DBG_START +#define DBG_INIT +#define DBG_STOP +#define DBG_RX +#define DBG_ISR +#define DBG_IOCTL +#define DBG_STAT +#define DBG_PACKETS +*/ + +#define IGNORE_SPURIOUS_IRQ +#define IGNORE_NO_RFA +#define IGNORE_MULTIPLE_RF + +/* + * Default number of buffer descriptors and buffer sizes. + */ +#define RX_BUF_COUNT 15 +#define TX_BUF_COUNT 4 +#define TX_BD_PER_BUF 4 + +#define RBUF_SIZE 1520 + +#define UTI_596_ETH_MIN_SIZE 60 + +#define INET_ADDR_MAX_BUF_SIZE (sizeof "255.255.255.255") + +/* + * RTEMS events + */ +#define INTERRUPT_EVENT RTEMS_EVENT_1 +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 +#define NIC_RESET_EVENT RTEMS_EVENT_3 + +#include <bsp.h> +#include <inttypes.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <rtems/error.h> +#include <rtems/bspIo.h> +#include <rtems/rtems_bsdnet.h> +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/types.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include "uti596.h" + +/* If we are running interrupt driven I/O no debug output is printed */ +#if CD2401_IO_MODE == 0 + #define printk(arglist) do { printk arglist; printk("\r"); } while (0); +#else + #define printk(arglist) +#endif + +#define UTI_596_ASSERT( condition, str ) if (!( condition ) ) { printk((str)) } + +/* Types of PORT commands */ +#define UTI596_RESET_PORT_FUNCTION 0 +#define UTI596_SELFTEST_PORT_FUNCTION 1 +#define UTI596_SCP_PORT_FUNCTION 2 +#define UTI596_DUMP_PORT_FUNCTION 3 + +/* Types of waiting for commands */ +#define UTI596_NO_WAIT 0 +#define UTI596_WAIT_FOR_CU_ACCEPT 1 +#define UTI596_WAIT_FOR_INITIALIZATION 2 +#define UTI596_WAIT_FOR_STAT_C 3 + +/* Device dependent data structure */ +static uti596_softc_ uti596_softc; + +/* Globals */ +int count_rx = 0; +static int scbStatus; +static rtems_status_code sc; +static i596_cmd *pIsrCmd; +static i596_rfd *pIsrRfd; + +/* + * Initial 596 configuration + */ +char uti596initSetup[] = { + 0x0E, /* Byte 0: length, prefetch off ( no RBD's ) */ + 0xC8, /* Byte 1: fifo to 8, monitor off */ + 0x40, /* Byte 2: don't save bad frames ( was save= 80, use intel's 40 )*/ + 0x2E, /* Byte 3: No source address insertion, 8 byte preamble */ + 0x00, /* Byte 4: priority and backoff defaults */ + 0x60, /* Byte 5: interframe spacing */ + 0x00, /* Byte 6: slot time LSB */ + 0xf2, /* Byte 7: slot time and retries */ + 0x0C, /* Byte 8: not promisc, enable bcast, tx no crs, crc inserted 32bit, 802.3 framing */ + 0x08, /* Byte 9: collision detect */ + 0x40, /* Byte 10: minimum frame length */ + 0xfb, /* Byte 11: tried C8 same as byte 1 in bits 6-7, else ignored*/ + 0x00, /* Byte 12: disable full duplex */ + 0x3f /* Byte 13: no multi IA, backoff enabled */ +}; + +/* Local Routines */ + +static unsigned long word_swap ( unsigned long ); +static void * malloc_16byte_aligned ( void **, void ** adjusted_pointer, size_t ); +RTEMS_INLINE_ROUTINE void uti596_writePortFunction ( volatile void *, unsigned long ); +/* currently unused by RTEMS */ +#if 0 +RTEMS_INLINE_ROUTINE void uti596_portReset( void ); +static unsigned long uti596_portSelfTest( i596_selftest * ); +static int uti596_portDump ( i596_dump_result * ); +static void uti596_CU_dump ( i596_dump_result * ); +#endif +static int uti596_wait ( uti596_softc_ *, uint8_t); +static int uti596_issueCA ( uti596_softc_ *, uint8_t); +static void uti596_addCmd ( i596_cmd * ); +static void uti596_addPolledCmd ( i596_cmd * ); +static int uti596_setScpAndScb ( uti596_softc_ * ); +static int uti596_diagnose ( void ); +static int uti596_configure ( uti596_softc_ * ); +static int uti596_IAsetup ( uti596_softc_ * ); +static int uti596_initTBD ( uti596_softc_ * ); +static int uti596_initRFA ( int ); +static void uti596_initMem ( uti596_softc_ * ); +static void uti596_initialize ( uti596_softc_ * ); +static void uti596_initialize_hardware ( uti596_softc_ * ); +static void uti596_reset_hardware ( uti596_softc_ *); +static void uti596_reset ( void ); +static void uti596_clearListStatus ( i596_rfd * ); +static i596_rfd * uti596_dequeue ( i596_rfd ** ); +static void uti596_append ( i596_rfd ** , i596_rfd * ); +static void uti596_supplyFD ( i596_rfd * ); +static void send_packet ( struct ifnet *, struct mbuf * ); + +/* Required RTEMS network driver functions and tasks (plus reset daemon) */ + +static void uti596_start ( struct ifnet * ); +void uti596_init ( void * ); +void uti596_stop ( uti596_softc_ * ); +void uti596_txDaemon ( void * ); +void uti596_rxDaemon ( void * ); +void uti596_resetDaemon( void * ); +rtems_isr uti596_DynamicInterruptHandler ( rtems_vector_number ); +static int uti596_ioctl ( struct ifnet *, u_long, caddr_t ); +void uti596_stats ( uti596_softc_ * ); + +#ifdef DBG_PACKETS +static void dumpQ( void ); +static void show_buffers( void ); +static void show_queues( void ); +static void print_eth ( unsigned char * ); +static void print_hdr ( unsigned char * ); +static void print_pkt ( unsigned char * ); +static void print_echo ( unsigned char * ); +#endif + +/* + * word_swap + * + * Return a 32 bit value, swapping the upper and lower words first. + * + * Input parameters: + * val - 32 bit value to swap + * + * Output parameters: NONE + * + * Return value: + * Input value with upper and lower 16-bit words swapped + */ +static unsigned long word_swap( + unsigned long val +) +{ + return (((val >> 16)&(0x0000ffff)) | ((val << 16)&(0xffff0000))); +} + +/* + * malloc_16byte_aligned + * + * Allocate a block of a least nbytes aligned on a 16-byte boundary. + * Clients are responsible to store both the real address and the adjusted + * address. The real address must be used to free the block. + * + * Input parameters: + * real_pointer - pointer to a void * pointer in which to store the starting + * address of the block. Required for free. + * adjusted_pointer - pointer to a void * pointer in which to store the + * starting address of the block rounded up to the next + * 16 byte boundary. + * nbytes - number of bytes of storage requested + * + * Output parameters: + * real_pointer - starting address of the block. + * adjusted_pointer - starting address of the block rounded up to the next + * 16 byte boundary. + * + * Return value: + * starting address of the block rounded up to the next 16 byte boundary. + * NULL if no storage was allocated. + */ +static void * malloc_16byte_aligned( + void ** real_pointer, + void ** adjusted_pointer, + size_t nbytes +) +{ + *real_pointer = malloc( nbytes + 0xF, 0, M_NOWAIT ); + *adjusted_pointer = (void *)(((unsigned long)*real_pointer + 0xF ) & 0xFFFFFFF0 ); + return *adjusted_pointer; +} + +/* + * uti596_scp_alloc + * + * Allocate a new scp, possibly freeing a previously allocated one. + * + * Input parameters: + * sc - pointer to the global uti596_softc in which to store pointers + * to the newly allocated block. + * + * Output parameters: NONE + * + * Return value: + * Pointer to the newly allocated, 16-byte aligned scp. + */ +static i596_scp * uti596_scp_alloc( + uti596_softc_ * sc +) +{ + if( sc->base_scp != NULL ) { + #ifdef DBG_MEM + printk(("uti596_scp_alloc: Already have an SCP at %p\n", sc->base_scp)) + #endif + return sc->pScp; + } + + /* allocate enough memory for the Scp block to be aligned on 16 byte boundary */ + malloc_16byte_aligned( (void *)&(sc->base_scp), (void *)&(sc->pScp), sizeof( i596_scp ) ); + + #ifdef DBG_MEM + printk(("uti596_scp_alloc: Scp base address is %p\n", sc->base_scp)) + printk(("uti596_scp_alloc: Scp aligned address is : %p\n",sc->pScp)) + #endif + + return sc->pScp; +} + +/* + * uti596_writePortFunction + * + * Write the command into the PORT. + * + * Input parameters: + * addr - 16-byte aligned address to write into the PORT. + * cmd - 4-bit cmd to write into the PORT + * + * Output parameters: NONE + * + * Return value: NONE + * + * The Motorola manual swapped the high and low registers. + */ +RTEMS_INLINE_ROUTINE void uti596_writePortFunction( + volatile void * addr, + unsigned long cmd +) +{ + i82596->port_lower = (unsigned short)(((unsigned long)addr & 0xFFF0) | cmd); + i82596->port_upper = (unsigned short)(((unsigned long)addr >> 16 ) & 0xFFFF); +} + +/* + * uti596_portReset + * + * Issue a port Reset to the uti596 + * + * Input parameters: NONE + * + * Output parameters: NONE + * + * Return value: NONE + */ +RTEMS_INLINE_ROUTINE void uti596_portReset( void ) +{ + uti596_writePortFunction( NULL, UTI596_RESET_PORT_FUNCTION ); +} + +/* currently unused by RTEMS */ +#if 0 +/* + * uti596_portSelfTest + * + * Perform a self-test. Wait for up to 1 second for the test to + * complete. Normally, the test should complete in a very short time, + * so busy waiting is not an issue. + * + * Input parameters: + * stp - pointer to a 16-byte aligned uti596_selftest structure. + * + * Output parameters: NONE + * + * Return value: + * 32-bit result field if successful, -1 otherwise. + */ +static unsigned long uti596_portSelfTest( + i596_selftest * stp +) +{ + rtems_interval ticks_per_second, start_ticks, end_ticks; + + stp->results = 0xFFFFFFFF; + uti596_writePortFunction( stp, UTI596_SELFTEST_PORT_FUNCTION ); + + ticks_per_second = rtems_clock_get_ticks_per_second(); + + start_ticks = rtems_clock_get_ticks_since_boot(); + end_ticks = start_ticks + ticks_per_second; + + do { + if( stp->results != 0xFFFFFFFF ) + break; + else + start_ticks = rtems_clock_get_ticks_since_boot(); + } while (start_ticks <= end_ticks); + + if (start_ticks > end_ticks ) { + #ifdef DBG_SELFTEST_CMD + printk(("uti596_selftest: Timed out\n" )) + #endif + return -1; + } + else { + #ifdef DBG_SELFTEST_CMD + printk(("uti596_selftest: Succeeded with signature = 0x%08x, result = 0x%08x\n", + stp->signature, + stp->results)) + #endif + return stp->results; + } +} +#endif + +/* currently unused by RTEMS */ +#if 0 +/* + * uti596_portDump + * + * Perform a dump Wait for up to 1 second for the test to + * complete. Normally, the test should complete in a very short time, + * so busy waiting is not an issue. + * + * Input parameters: + * dp - pointer to a 16-byte aligned uti596_dump structure. + * + * Output parameters: NONE + * + * Return value: + * 16-bit dump_status field if successful, -1 otherwise. + */ +static int uti596_portDump( + i596_dump_result * dp +) +{ + rtems_interval ticks_per_second, start_ticks, end_ticks; + + dp->dump_status = 0; + uti596_writePortFunction( dp, UTI596_DUMP_PORT_FUNCTION ); + + ticks_per_second = rtems_clock_get_ticks_per_second(); + start_ticks = rtems_clock_get_ticks_since_boot(); + end_ticks = start_ticks + ticks_per_second; + + do { + if( dp->dump_status != 0xA006 ) + break; + else + start_ticks = rtems_clock_get_ticks_since_boot(); + } while (start_ticks <= end_ticks); + + if (start_ticks > end_ticks ) { + #ifdef DBG_DUMP_CMD + printk(("uti596_dump: Timed out with dump at 0x%08x\n", (unsigned long)dp )) + #endif + return -1; + } + else { + #ifdef DBG_DUMP_CMD + printk(("uti596_dump: Succeeded with dump at = 0x%08x\n", (unsigned long)dp )) + #endif + return dp->dump_status; + } +} +#endif + +/* + * uti596_wait + * + * Wait for a certain condition. + * + * Input parameters: + * sc - pointer to the uti596_softc struct + * wait_type - UTI596_NO_WAIT + * UTI596_WAIT + * UTI596_WAIT_FOR_CU_ACCEPT + * UTI596_WAIT_FOR_INITIALIZATION + * UTI596_WAIT_FOR_STAT_C + * + * Output parameters: NONE + * + * Return value: + * 0 if successful, -1 otherwise. + */ +static int uti596_wait( + uti596_softc_ *sc, + uint8_t waitType +) +{ + rtems_interval ticks_per_second, start_ticks, end_ticks; + + ticks_per_second = rtems_clock_get_ticks_per_second(); + start_ticks = rtems_clock_get_ticks_since_boot(); + end_ticks = start_ticks + ticks_per_second; + + switch( waitType ) { + + case UTI596_NO_WAIT: + return 0; + + case UTI596_WAIT_FOR_CU_ACCEPT: + do { + if (sc->scb.command == 0) + break; + else + + start_ticks = rtems_clock_get_ticks_since_boot(); + + } while (start_ticks <= end_ticks); + + if( (sc->scb.command != 0) || (start_ticks > end_ticks) ) { + printf("i82596 timed out with status %x, cmd %x.\n", + sc->scb.status, sc->scb.command); + return -1; + } + else + return 0; + + case UTI596_WAIT_FOR_INITIALIZATION: + do { + if( !sc->iscp.busy ) + break; + else + start_ticks = rtems_clock_get_ticks_since_boot(); + } while (start_ticks <= end_ticks); + + if (start_ticks > end_ticks ) { + #ifdef DBG_WAIT + printk(("uti596_setScpAndScb: Timed out\n" )) + #endif + return -1; + } + else { + #ifdef DBG_WAIT + printk(("uti596_setScpAndScb: Succeeded\n" )) + #endif + return 0; + } + + case UTI596_WAIT_FOR_STAT_C: + do { + if( *sc->pCurrent_command_status & STAT_C ) + break; + else + start_ticks = rtems_clock_get_ticks_since_boot(); + } while (start_ticks <= end_ticks); + + if (start_ticks > end_ticks ) { + #ifdef DBG_WAIT + printk(("uti596_initMem: timed out - STAT_C not obtained\n" )) + #endif + return -1; + } + else { + #ifdef DBG_WAIT + printk(("uti596_initMem: STAT_C obtained OK\n" )) + #endif + return 0; + } + } + return -1; +} + +/* + * uti596_issueCA + * + * Issue a Channel Attention command. Possibly wait for the + * command to start or complete. + * + * Input parameters: + * sc - pointer to the uti596_softc + * wait_type - UTI596_NO_WAIT + * UTI596_WAIT_BEGIN + * UTI596_WAIT_COMPLETION + * + * Output parameters: NONE + * + * Return value: + * 0 if successful, -1 otherwise. + */ +static int uti596_issueCA( + uti596_softc_ *sc, + uint8_t waitType +) +{ + /* Issue Channel Attention */ + i82596->chan_attn = 0x00000000; + + return (uti596_wait ( sc, waitType )); +} + +/* + * uti596_addCmd + * + * Add a uti596_cmd onto the end of the CBL command chain, + * or to the start if the chain is empty. + * + * Input parameters: + * pCmd - a pointer to the command to be added. + * + * Output parameters: NONE + * + * Return value: NONE + */ +static void uti596_addCmd( + i596_cmd *pCmd +) +{ + ISR_Level level; + + #ifdef DBG_ADD_CMD + printk(("uti596_addCmd: Adding command 0x%x\n", pCmd -> command )) + #endif + + /* Mark command as last in list, to return an interrupt */ + pCmd->command |= (CMD_EOL | CMD_INTR ); + pCmd->status = 0; + pCmd->next = I596_NULL; + + _ISR_Local_disable(level); + + if (uti596_softc.pCmdHead == I596_NULL) { + uti596_softc.pCmdHead = uti596_softc.pCmdTail = uti596_softc.scb.pCmd = pCmd; + uti596_softc.scb.cmd_pointer = word_swap ((unsigned long)pCmd); + + uti596_wait ( &uti596_softc, UTI596_WAIT_FOR_CU_ACCEPT ); + uti596_softc.scb.command = CUC_START; + uti596_issueCA ( &uti596_softc, UTI596_NO_WAIT ); + + _ISR_Local_enable(level); + } + else { + uti596_softc.pCmdTail->next = (i596_cmd *) word_swap ((unsigned long)pCmd); + uti596_softc.pCmdTail = pCmd; + _ISR_Local_enable(level); + } + + #ifdef DBG_ADD_CMD + printk(("uti596_addCmd: Scb status & command 0x%x 0x%x\n", + uti596_softc.scb.status, + uti596_softc.scb.command )) + #endif +} + +/* + * uti596_addPolledCmd + * + * Add a single uti596_cmd to the end of the command block list + * for processing, send a CU_START and wait for its acceptance + * + * Input parameters: + * sc - a pointer to the uti596_softc struct + * + * Output parameters: NONE + * + * Return value: NONE + */ +void uti596_addPolledCmd( + i596_cmd *pCmd +) +{ + + #ifdef DBG_ADD_CMD + printk(("uti596_addPolledCmd: Adding command 0x%x\n", pCmd -> command )) + #endif + + pCmd->status = 0; + pCmd->command |= CMD_EOL ; /* only command in list*/ + pCmd->next = I596_NULL; + + uti596_wait ( &uti596_softc, UTI596_WAIT_FOR_CU_ACCEPT ); + + uti596_softc.pCmdHead = uti596_softc.pCmdTail = uti596_softc.scb.pCmd = pCmd; + uti596_softc.scb.cmd_pointer = word_swap((unsigned long)pCmd); + uti596_softc.scb.command = CUC_START; + uti596_issueCA ( &uti596_softc, UTI596_WAIT_FOR_CU_ACCEPT ); + + uti596_softc.pCmdHead = uti596_softc.pCmdTail = uti596_softc.scb.pCmd = I596_NULL; + uti596_softc.scb.cmd_pointer = (unsigned long) I596_NULL; + + #ifdef DBG_ADD_CMD + printk(("uti596_addPolledCmd: Scb status & command 0x%x 0x%x\n", + uti596_softc.scb.status, + uti596_softc.scb.command )) + #endif +} + +/* currently unused by RTEMS */ +#if 0 +/* + * uti596_CU_dump + * + * Dump the LANC 82596 registers + * The outcome is the same as the portDump() but executed + * via the CU instead of via a PORT access. + * + * Input parameters: + * drp - a pointer to a i596_dump_result structure. + * + * Output parameters: NONE + * + * Return value: NONE + */ +static void uti596_CU_dump ( i596_dump_result * drp) +{ + i596_dump dumpCmd; + + dumpCmd.cmd.command = CmdDump; + dumpCmd.cmd.next = I596_NULL; + dumpCmd.pData = (char *) drp; + uti596_softc.cmdOk = 0; + uti596_addCmd ( (i596_cmd *) &dumpCmd ); + +} +#endif + +#if defined(DBG_STAT) || !defined(IGNORE_NO_RFA) +/* + * uti596_dump_scb + * + * Dump the system control block + * This function expands to nothing when using interrupt driven I/O + * + * Input parameters: NONE + * + * Output parameters: NONE + * + * Return value: NONE + */ +static void uti596_dump_scb ( void ) +{ + printk(("status 0x%x\n",uti596_softc.scb.status)) + printk(("command 0x%x\n",uti596_softc.scb.command)) + printk(("cmd 0x%x\n",(int)uti596_softc.scb.pCmd)) + printk(("rfd 0x%x\n",(int)uti596_softc.scb.pRfd)) + printk(("crc_err 0x%" PRIx32 "\n",uti596_softc.scb.crc_err)) + printk(("align_err 0x%" PRIx32 "\n",uti596_softc.scb.align_err)) + printk(("resource_err 0x%" PRIx32 "\n",uti596_softc.scb.resource_err )) + printk(("over_err 0x%" PRIx32 "\n",uti596_softc.scb.over_err)) + printk(("rcvdt_err 0x%" PRIx32 "\n",uti596_softc.scb.rcvdt_err)) + printk(("short_err 0x%" PRIx32 "\n",uti596_softc.scb.short_err)) + printk(("t_on 0x%x\n",uti596_softc.scb.t_on)) + printk(("t_off 0x%x\n",uti596_softc.scb.t_off)) +} +#endif + +/* + * uti596_setScpAndScb + * + * Issue the first channel attention after reset and wait for the busy + * field to clear in the iscp. + * + * Input parameters: + * sc - pointer to the global uti596_softc + * + * Output parameters: NONE + * + * Return value: + * 0 if successful, -1 otherwise. + */ +static int uti596_setScpAndScb( + uti596_softc_ * sc +) +{ + /* set the busy flag in the iscp */ + sc->iscp.busy = 1; + + /* the command block list (CBL) is empty */ + sc->scb.command = 0; + sc->scb.cmd_pointer = (unsigned long) I596_NULL; /* all 1's */ + sc->pCmdHead = sc->scb.pCmd = I596_NULL; /* all 1's */ + + uti596_writePortFunction( sc->pScp, UTI596_SCP_PORT_FUNCTION ); + + /* Issue CA: pass the scb address to the 596 */ + return ( uti596_issueCA ( sc, UTI596_WAIT_FOR_INITIALIZATION ) ); +} + +/* + * uti596_diagnose + * + * Send a diagnose command to the CU + * + * Input parameters: NONE + * + * Output parameters: NONE + * + * Return value: + * 0 if successful, -1 otherwise + */ +static int uti596_diagnose( void ) +{ + i596_cmd diagnose; + + diagnose.command = CmdDiagnose; + diagnose.status = 0; + uti596_softc.pCurrent_command_status = (unsigned short *)&diagnose.status; + uti596_addPolledCmd(&diagnose); + return (uti596_wait ( &uti596_softc, UTI596_WAIT_FOR_STAT_C )); + + #ifdef DBG_INIT + printk(("Status diagnostic: 0xa000 is a success ... 0x%2.2x\n", diagnose.status)) + #endif +} + +/* + * uti596_configure + * + * Send the CU a configure command with the desired + * configuration structure + * + * Input parameters: + * sc - a pointer to the uti596_softc struct + * + * Output parameters: NONE + * + * Return value: + * 0 if successful, -1 otherwise + */ +static int uti596_configure ( + uti596_softc_ * sc +) +{ + sc->set_conf.cmd.command = CmdConfigure; + memcpy ( (void *)sc->set_conf.data, uti596initSetup, 14); + uti596_addPolledCmd( (i596_cmd *) &sc->set_conf); + + /* Poll for successful command completion */ + sc->pCurrent_command_status = (unsigned short *)&(sc->set_conf.cmd.status); + return ( uti596_wait ( sc, UTI596_WAIT_FOR_STAT_C ) ); +} + +/* + * uti596_IAsetup + * + * Send the CU an Individual Address setup command with + * the ethernet hardware address + * + * Input parameters: + * sc - a pointer to the uti596_softc struct + * + * Output parameters: NONE + * + * Return value: + * 0 if successful, -1 otherwise + */ +static int uti596_IAsetup ( + uti596_softc_ * sc +) +{ + int i; + + sc->set_add.cmd.command = CmdSASetup; + for ( i=0; i<6; i++) { + sc->set_add.data[i]=sc->arpcom.ac_enaddr[i]; + } + sc->cmdOk = 0; + uti596_addPolledCmd((i596_cmd *)&sc->set_add); + + /* Poll for successful command completion */ + sc->pCurrent_command_status = (unsigned short *)&(sc->set_add.cmd.status); + return ( uti596_wait ( sc, UTI596_WAIT_FOR_STAT_C ) ); +} + +/* + * uti596_initTBD + * + * Initialize transmit buffer descriptors + * dynamically allocate mem for the number of tbd's required + * + * Input parameters: + * sc - a pointer to the uti596_softc struct + * + * Output parameters: NONE + * + * Return value: + * 0 if successful, -1 otherwise + */ +static int uti596_initTBD ( uti596_softc_ * sc ) +{ + int i; + i596_tbd *pTbd, *pPrev; + + /* Set up a transmit command with a tbd ready */ + sc->pLastUnkRFD = I596_NULL; + sc->pTxCmd = (i596_tx *) calloc (1,sizeof (struct i596_tx) ); + sc->pTbd = (i596_tbd *) calloc (1,sizeof (struct i596_tbd) ); + if ((sc->pTxCmd == NULL) || (sc->pTbd == NULL)) { + return -1; + } + sc->pTxCmd->pTbd = (i596_tbd *) word_swap ((unsigned long) sc->pTbd); + sc->pTxCmd->cmd.command = CMD_FLEX|CmdTx; + sc->pTxCmd->pad = 0; + sc->pTxCmd->count = 0; /* all bytes are in list of TBD's */ + + pPrev = pTbd = sc->pTbd; + + /* Allocate a linked list of tbd's each with it's 'next' field written + * with upper and lower words swapped (for big endian), and mark the end. + */ + for ( i=0; i<sc->txBdCount; i++) { + if ( (pTbd = (i596_tbd *) calloc (1,sizeof (struct i596_tbd) )) == NULL ) { + return -1; + } + pPrev->next = (i596_tbd *) word_swap ((unsigned long) pTbd); + pPrev = pTbd; + } + pTbd->next = I596_NULL; + return 0; +} + +/* + * uti596_initRFA + * + * Initialize the Receive Frame Area + * dynamically allocate mem for the number of rfd's required + * + * Input parameters: + * sc - a pointer to the uti596_softc struct + * + * Output parameters: NONE + * + * Return value: + * # of buffer descriptors successfully allocated + */ +static int uti596_initRFA( int num ) +{ + i596_rfd *pRfd; + int i = 0; + + #ifdef DBG_INIT + printk(("uti596_initRFA: begins\n Requested frame descriptors ... %d.\n", num)) + #endif + + /* + * Create the first RFD in the RFA + */ + pRfd = (i596_rfd *) calloc (1, sizeof (struct i596_rfd)); + if ( !pRfd ) { + printk(("Can't allocate first buffer.\n")) + return 0; + } + else { + uti596_softc.countRFD = 1; + uti596_softc.pBeginRFA = uti596_softc.pEndRFA = pRfd; + } + + /* Create remaining RFDs */ + for (i = 1; i < num; i++) { + pRfd = (i596_rfd *) calloc (1, sizeof (struct i596_rfd) ); + if ( pRfd != NULL ) { + uti596_softc.countRFD++; /* update count */ + uti596_softc.pEndRFA->next = + (i596_rfd *) word_swap ((unsigned long) pRfd); /* write the link */ + uti596_softc.pEndRFA = pRfd; /* move the end */ + } + else { + printk(("Can't allocate all buffers: only %d allocated\n", i)) + break; + } + } /* end for */ + + uti596_softc.pEndRFA->next = I596_NULL; + UTI_596_ASSERT(uti596_softc.countRFD == num,"INIT:WRONG RFD COUNT\n" ) + + #ifdef DBG_INIT + printk (("uti596_initRFA: Head of RFA is buffer %p \n\ + uti596_initRFA: End of RFA is buffer %p \n", + uti596_softc.pBeginRFA, uti596_softc.pEndRFA )) + #endif + + /* Walk and initialize the RFD's */ + for ( pRfd = uti596_softc.pBeginRFA; + pRfd != I596_NULL; + pRfd = (i596_rfd *) word_swap ((unsigned long)pRfd->next) ) + { + pRfd->cmd = 0x0000; + pRfd->stat = 0x0000; + pRfd->pRbd = I596_NULL; + pRfd->count = 0; /* number of bytes in buffer: usually less than size */ + pRfd->size = 1532; /* was 1532; buffer size ( All RBD ) */ + } /* end for */ + + /* mark the last RFD as the last one in the RDL */ + uti596_softc.pEndRFA -> cmd = CMD_EOL; + uti596_softc.pSavedRfdQueue = + uti596_softc.pEndSavedQueue = I596_NULL; /* initially empty */ + + uti596_softc.savedCount = 0; + uti596_softc.nop.cmd.command = CmdNOp; /* initialize the nop command */ + + return (i); /* the number of allocated buffers */ +} + +/* + * uti596_initMem + * + * Initialize the 82596 memory structures for Tx and Rx + * dynamically allocate mem for the number of tbd's required + * + * Input parameters: + * sc - a pointer to the uti596_softc struct + * + * Output parameters: NONE + * + * Return value: NONE + */ +void uti596_initMem( + uti596_softc_ * sc +) +{ + int i; + + #ifdef DBG_INIT + printk(("uti596_initMem: begins\n")) + #endif + + sc->resetDone = 0; + + /* + * Set up receive frame area (RFA) + */ + i = uti596_initRFA( sc->rxBdCount ); + if ( i < sc->rxBdCount ) { + printk(("init_rfd: only able to allocate %d receive frame descriptors\n", i)) + } + + /* + * Write the SCB with a pointer to the receive frame area + * and keep a pointer for our use. + */ + sc->scb.rfd_pointer = word_swap((unsigned long)sc->pBeginRFA); + sc->scb.pRfd = sc->pBeginRFA; + + /* + * Diagnose the health of the board + */ + uti596_diagnose(); + + /* + * Configure the 82596 + */ + uti596_configure( sc ); + + /* + * Set up the Individual (hardware) Address + */ + uti596_IAsetup ( sc ); + + /* + * Initialize the transmit buffer descriptors + */ + uti596_initTBD( sc ); + + /* Padding used to fill short tx frames */ + memset ( RTEMS_DEVOLATILE( char *, sc->zeroes ), 0, 64); + + /* now need ISR */ + sc->resetDone = 1; +} + +/* + * uti596_initialize + * + * Reset the 82596 and initialize it with a new SCP. + * + * Input parameters: + * sc - pointer to the uti596_softc + * + * Output parameters: NONE + * + * Return value: NONE + */ +void uti596_initialize( + uti596_softc_ *sc +) +{ + /* Reset the device. Stops it from doing whatever it might be doing. */ + uti596_portReset(); + + /* Get a new System Configuration Pointer */ + uti596_scp_alloc( sc ); + + /* write the SYSBUS: interrupt pin active high, LOCK disabled, + * internal triggering, linear mode + */ + sc->pScp->sysbus = 0x44; + + /* provide the iscp to the scp, keep a pointer for our use */ + sc->pScp->iscp_pointer = word_swap((unsigned long)&sc->iscp); + sc->pScp->iscp = &sc->iscp; + + /* provide the scb to the iscp, keep a pointer for our use */ + sc->iscp.scb_pointer = word_swap((unsigned long)&sc->scb); + sc->iscp.scb = &sc->scb; + + #ifdef DBG_INIT + printk(("uti596_initialize: Starting i82596.\n")) + #endif + + /* Set up the 82596 */ + uti596_setScpAndScb( sc ); + + /* clear the scb command word */ + sc->scb.command = 0; +} + +/* + * uti596_initialize_hardware + * + * Reset the 82596 and initialize it with a new SCP. Enable bus snooping. + * Install the interrupt handlers. + * + * Input parameters: + * sc - pointer to the uti596_softc + * + * Output parameters: NONE + * + * Return value: NONE + */ +void uti596_initialize_hardware( + uti596_softc_ *sc +) +{ + printk(("uti596_initialize_hardware: begins\n")) + + /* Get the PCCChip2 to assert bus snooping signals on behalf of the i82596 */ + pccchip2->LANC_berr_ctl = 0x40; + + uti596_initialize( sc ); + + /* + * Configure interrupt control in PCCchip2 + */ + pccchip2->LANC_error = 0xff; /* clear status register */ + pccchip2->LANC_int_ctl = 0x5d; /* lvl 5, enabled, edge-sensitive rising */ + pccchip2->LANC_berr_ctl = 0x5d; /* bus error: lvl 5, enabled, snoop control + * will supply dirty data and leave dirty data + * on read access and sink any data on write + */ + /* + * Install the interrupt handler + * calls rtems_interrupt_catch + */ + set_vector( uti596_DynamicInterruptHandler, 0x57, 1 ); + + /* Initialize the 82596 memory */ + uti596_initMem(sc); + + #ifdef DBG_INIT + printk(("uti596_initialize_hardware: After attach, status of board = 0x%x\n", sc->scb.status )) + #endif +} + +/* + * uti596_reset_hardware + * + * Reset the 82596 and initialize it with an SCP. + * + * Input parameters: + * sc - pointer to the uti596_softc + * + * Output parameters: NONE + * + * Return value: NONE + */ +void uti596_reset_hardware( + uti596_softc_ *sc +) +{ + rtems_status_code status_code; + i596_cmd *pCmd; + + pCmd = sc->pCmdHead; /* This is a tx command for sure (99.99999%) */ + + /* the command block list (CBL) is empty */ + sc->scb.cmd_pointer = (unsigned long) I596_NULL; /* all 1's */ + sc->pCmdHead = sc->scb.pCmd = I596_NULL; /* all 1's */ + + #ifdef DBG_RESET + printk(("uti596_reset_hardware\n")) + #endif + uti596_initialize( sc ); + + /* + * Wake the transmitter if needed. + */ + if ( sc->txDaemonTid && pCmd != I596_NULL ) { + printk(("****RESET: wakes transmitter!\n")) + status_code = rtems_bsdnet_event_send (sc->txDaemonTid, + INTERRUPT_EVENT); + + if ( status_code != RTEMS_SUCCESSFUL ) { + printk(("****ERROR:Could NOT send event to tid 0x%" PRIx32 " : %s\n", + sc->txDaemonTid, rtems_status_text (status_code) )) + } + } + + #ifdef DBG_RESET + printk(("uti596_reset_hardware: After reset_hardware, status of board = 0x%x\n", sc->scb.status )) + #endif +} + +/* + * uti596_clearListStatus + * + * Clear the stat fields for all RFDs + * + * Input parameters: + * pRfd - a pointer to the head of the RFA + * + * Output parameters: NONE + * + * Return value: NONE + */ +void uti596_clearListStatus( + i596_rfd *pRfd +) +{ + while ( pRfd != I596_NULL ) { + pRfd -> stat = 0; + pRfd = (i596_rfd *) word_swap((unsigned long)pRfd-> next); + } +} + +/* + * uti596_reset + * + * Reset the 82596 and reconfigure + * + * Input parameters: NONE + * + * Output parameters: NONE + * + * Return value: NONE + */ +void uti596_reset( void ) +{ + uti596_softc_ *sc = &uti596_softc; + + #ifdef DBG_RESET + printk(("uti596_reset: begin\n")) + #endif + + /* Wait for the CU to be available, then + * reset the ethernet hardware. Must re-config. + */ + sc->resetDone = 0; + uti596_wait ( sc, UTI596_WAIT_FOR_CU_ACCEPT ); + uti596_reset_hardware ( &uti596_softc ); + + #ifdef DBG_RESET + uti596_diagnose(); + #endif + + /* + * Configure the 82596 + */ + uti596_configure( sc ); + + /* + * Set up the Individual (hardware) Address + */ + uti596_IAsetup ( sc ); + + sc->pCmdHead = sc->pCmdTail = sc->scb.pCmd = I596_NULL; + + /* restore the RFA */ + + if ( sc->pLastUnkRFD != I596_NULL ) { + sc-> pEndRFA = sc->pLastUnkRFD; /* The end position can be updated */ + sc-> pLastUnkRFD = I596_NULL; + } + + sc->pEndRFA->next = (i596_rfd*)word_swap((uint32_t)sc->pSavedRfdQueue); + if ( sc->pSavedRfdQueue != I596_NULL ) { + sc->pEndRFA = sc->pEndSavedQueue; + sc->pSavedRfdQueue = sc->pEndSavedQueue = I596_NULL; + sc -> countRFD = sc->rxBdCount ; + } + + /* Re-address the head of the RFA in the SCB */ + sc->scb.pRfd = sc->pBeginRFA; + sc->scb.rfd_pointer = word_swap((unsigned long)sc->pBeginRFA); + + /* Clear the status of all RFDs */ + uti596_clearListStatus( sc->pBeginRFA ); + + printk(("uti596_reset: Starting NIC\n")) + + /* Start the receiver */ + sc->scb.command = RX_START; + sc->started = 1; /* assume that the start is accepted */ + sc->resetDone = 1; + uti596_issueCA ( sc, UTI596_WAIT_FOR_CU_ACCEPT ); + + UTI_596_ASSERT(sc->pCmdHead == I596_NULL, "Reset: CMD not cleared\n") + + #ifdef DBG_RESET + printk(("uti596_reset: completed\n")) + #endif +} + +/* + * uti596_dequeue + * + * Remove an RFD from the received fram queue + * + * Input parameters: + * ppQ - a pointer to a i596_rfd pointer + * + * Output parameters: NONE + * + * Return value: + * pRfd - a pointer to the dequeued RFD + */ +i596_rfd * uti596_dequeue( + i596_rfd ** ppQ +) +{ + ISR_Level level; + i596_rfd * pRfd; + + _ISR_Local_disable(level); + + /* invalid address, or empty queue or emptied queue */ + if( ppQ == NULL || *ppQ == NULL || *ppQ == I596_NULL) { + _ISR_Local_enable(level); + return I596_NULL; + } + + /* + * Point to the dequeued buffer, then + * adjust the queue pointer and detach the buffer + */ + pRfd = *ppQ; + *ppQ = (i596_rfd *) word_swap ((unsigned long) pRfd->next); + pRfd->next = I596_NULL; /* unlink the rfd being returned */ + + _ISR_Local_enable(level); + return pRfd; +} + +/* + * uti596_append + * + * Remove an RFD buffer from the RFA and tack it on to + * the received frame queue for processing. + * + * Input parameters: + * ppQ - a pointer to the queue pointer + * pRfd - a pointer to the buffer to be returned + * + * Output parameters: NONE + * + * Return value: NONE + */ + +void uti596_append( + i596_rfd ** ppQ, + i596_rfd * pRfd +) +{ + + i596_rfd *p; + + if ( pRfd != NULL && pRfd != I596_NULL) { + pRfd -> next = I596_NULL; + pRfd -> cmd |= CMD_EOL; /* set EL bit */ + + if ( *ppQ == NULL || *ppQ == I596_NULL ) { + /* empty list */ + *ppQ = pRfd; + } + else { + /* walk to the end of the list */ + for ( p=*ppQ; + p->next != I596_NULL; + p=(i596_rfd *) word_swap ((unsigned long)p->next) ); + + /* append the rfd */ + p->cmd &= ~CMD_EOL; /* Clear EL bit at end */ + p->next = (i596_rfd *) word_swap ((unsigned long)pRfd); + } + } + else { + printk(("Illegal attempt to append: %p\n", pRfd)) + } +} + +/* + * uti596_supplyFD + * + * Return a buffer (RFD) to the receive frame area (RFA). + * Call with interrupts disabled. + * + * Input parameters: + * pRfd - a pointer to the buffer to be returned + * + * Output parameters: NONE + * + * Return value: NONE + */ +void uti596_supplyFD ( + i596_rfd * pRfd +) +{ + i596_rfd *pLastRfd; + + UTI_596_ASSERT(pRfd != I596_NULL, "Supplying NULL RFD!\n") + + pRfd -> cmd = CMD_EOL; + pRfd -> pRbd = I596_NULL; + pRfd -> next = I596_NULL; + pRfd -> stat = 0x0000; /* clear STAT_C and STAT_B bits */ + + /* + * Check if the list is empty: + */ + if ( uti596_softc.pBeginRFA == I596_NULL ) { + + /* Start a list with one entry */ + uti596_softc.pBeginRFA = uti596_softc.pEndRFA = pRfd; + UTI_596_ASSERT(uti596_softc.countRFD == 0, "Null begin, but non-zero count\n") + if ( uti596_softc.pLastUnkRFD != I596_NULL ) { + printk(("LastUnkRFD is NOT NULL!!\n")) + } + uti596_softc.countRFD = 1; + return; + } + + /* + * Check if the last RFD is used/read by the 596. + */ + pLastRfd = uti596_softc.pEndRFA; + + /* C = complete, B = busy (prefetched) */ + if ( pLastRfd != I596_NULL && ! (pLastRfd -> stat & ( STAT_C | STAT_B ) )) { + + /* + * Not yet too late to add it + */ + pLastRfd -> next = (i596_rfd *) word_swap ((unsigned long)pRfd); + pLastRfd -> cmd &= ~CMD_EOL; /* RESET_EL : reset EL bit to 0 */ + uti596_softc.countRFD++; /* Lets assume we add it successfully + If not, the RFD may be used, and may + decrement countRFD < 0 !! */ + /* + * Check if the last RFD was used while appending. + */ + if ( pLastRfd -> stat & ( STAT_C | STAT_B ) ) { /* completed or was prefetched */ + /* + * Either the EL bit of the last rfd has been read by the 82596, + * and it will stop after reception,( true when RESET_EL not reached ) or + * the EL bit was NOT read by the 82596 and it will use the linked + * RFD for the next reception. ( true when RESET_EL was reached ) + * So, it is unknown whether or not the linked rfd will be used. + * Therefore, the end of list CANNOT be updated. + */ + UTI_596_ASSERT ( uti596_softc.pLastUnkRFD == I596_NULL, "Too many Unk RFD's\n" ) + uti596_softc.pLastUnkRFD = pRfd; + return; + } + else { + /* + * The RFD being added was not touched by the 82596 + */ + if (uti596_softc.pLastUnkRFD != I596_NULL ) { + + uti596_append((i596_rfd **)&uti596_softc.pSavedRfdQueue, pRfd); /* Only here! saved Q */ + uti596_softc.pEndSavedQueue = pRfd; + uti596_softc.savedCount++; + uti596_softc.countRFD--; + + } + else { + + uti596_softc.pEndRFA = pRfd; /* the RFA has been extended */ + + if ( ( uti596_softc.scb.status & SCB_STAT_RNR || + uti596_softc.scb.status & RU_NO_RESOURCES ) && + uti596_softc.countRFD > 1 ) { + + /* Ensure that beginRFA is not EOL */ + uti596_softc.pBeginRFA -> cmd &= ~CMD_EOL; + + UTI_596_ASSERT(uti596_softc.pEndRFA -> next == I596_NULL, "supply: List buggered\n") + UTI_596_ASSERT(uti596_softc.pEndRFA -> cmd & CMD_EOL, "supply: No EOL at end.\n") + UTI_596_ASSERT(uti596_softc.scb.command == 0, "Supply: scb command must be zero\n") + + #ifdef DBG_MEM + printk(("uti596_supplyFD: starting receiver")) + #endif + + /* start the receiver */ + UTI_596_ASSERT(uti596_softc.pBeginRFA != I596_NULL, "rx start w/ NULL begin! \n") + uti596_softc.scb.pRfd = uti596_softc.pBeginRFA; + uti596_softc.scb.rfd_pointer = word_swap ((unsigned long) uti596_softc.pBeginRFA); + + /* Don't ack RNR! The receiver should be stopped in this case */ + uti596_softc.scb.command = RX_START | SCB_STAT_RNR; + + UTI_596_ASSERT( !(uti596_softc.scb.status & SCB_STAT_FR),"FRAME RECEIVED INT COMING!\n") + + /* send CA signal */ + uti596_issueCA ( &uti596_softc, UTI596_NO_WAIT ); + } + } + return; + } + } + else { + /* + * too late , pLastRfd in use ( or NULL ), + * in either case, EL bit has been read, and RNR condition will occur + */ + uti596_append( (i596_rfd **)&uti596_softc.pSavedRfdQueue, pRfd); /* save it for RNR */ + + uti596_softc.pEndSavedQueue = pRfd; /* reset end of saved queue */ + uti596_softc.savedCount++; + + return; + } +} + +/* + * send_packet + * + * Send a raw ethernet packet, add a + * transmit command to the CBL + * + * Input parameters: + * ifp - a pointer to the ifnet structure + * m - a pointer to the mbuf being sent + * + * Output parameters: NONE + * + * Return value: NONE + */ +void send_packet( + struct ifnet *ifp, struct mbuf *m +) +{ + i596_tbd *pPrev = I596_NULL; + i596_tbd *pRemainingTbdList; + i596_tbd *pTbd; + struct mbuf *n, *input_m = m; + uti596_softc_ *sc = ifp->if_softc; + struct mbuf *l = NULL; + unsigned int length = 0; + rtems_status_code status; + int bd_count = 0; + rtems_event_set events; + + /* + * For all mbufs in the chain, + * fill a transmit buffer descriptor for each + */ + pTbd = (i596_tbd*) word_swap ((unsigned long)sc->pTxCmd->pTbd); + + do { + if (m->m_len) { + /* + * Fill in the buffer descriptor + */ + length += m->m_len; + pTbd->data = (char *) word_swap ((unsigned long) mtod (m, void *)); + pTbd->size = m->m_len; + pPrev = pTbd; + pTbd = (i596_tbd *) word_swap ((unsigned long) pTbd->next); + l = m; + m = m->m_next; + } + else { + /* + * Just toss empty mbufs + */ + MFREE (m, n); + m = n; + if (l != NULL) + l->m_next = m; + } + } while( m != NULL && ++bd_count < 16 ); + + if ( length < UTI_596_ETH_MIN_SIZE ) { + pTbd->data = (char *) word_swap ((unsigned long) sc->zeroes); /* add padding to pTbd */ + pTbd->size = UTI_596_ETH_MIN_SIZE - length; /* zeroes have no effect on the CRC */ + } + else /* Don't use pTbd in the send routine */ + pTbd = pPrev; + + /* Disconnect the packet from the list of Tbd's */ + pRemainingTbdList = (i596_tbd *) word_swap ((unsigned long)pTbd->next); + pTbd->next = I596_NULL; + pTbd->size |= UTI_596_END_OF_FRAME; + + sc->rawsndcnt++; + + #ifdef DBG_SEND + printk(("send_packet: sending packet\n")) + #endif + + /* Sending Zero length packet: shouldn't happen */ + if (pTbd->size <= 0) return; + + #ifdef DBG_PACKETS + printk (("\nsend_packet: Transmitter adds packet\n")) + print_hdr ( sc->pTxCmd->pTbd->data ); /* print the first part */ + print_pkt ( sc->pTxCmd->pTbd->next->data ); /* print the first part */ + print_echo (sc->pTxCmd->pTbd->data); + #endif + + /* add the command to the output command queue */ + uti596_addCmd ( (i596_cmd *) sc->pTxCmd ); + + /* sleep until the command has been processed or Timeout encountered. */ + status= rtems_bsdnet_event_receive (INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + + if ( status != RTEMS_SUCCESSFUL ) { + printk(("Could not sleep %s\n", rtems_status_text(status))) + } + + #ifdef DBG_SEND + printk(("send_packet: RAW - wake\n")) + #endif + + sc->txInterrupts++; + + if ( sc->pTxCmd -> cmd.status & STAT_OK ) { + sc->stats.tx_packets++; + } + else { + + printk(("*** send_packet: Driver Error 0x%x\n", sc->pTxCmd -> cmd.status )) + + sc->stats.tx_errors++; + if ( sc->pTxCmd->cmd.status & 0x0020 ) + sc->stats.tx_retries_exceeded++; + if (!(sc->pTxCmd->cmd.status & 0x0040)) + sc->stats.tx_heartbeat_errors++; + if ( sc->pTxCmd->cmd.status & 0x0400 ) + sc->stats.tx_carrier_errors++; + if ( sc->pTxCmd->cmd.status & 0x0800 ) + sc->stats.collisions++; + if ( sc->pTxCmd->cmd.status & 0x1000 ) + sc->stats.tx_aborted_errors++; + } /* end if stat_ok */ + + /* + * Restore the transmitted buffer descriptor chain. + */ + pTbd -> next = (i596_tbd *) word_swap ((unsigned long)pRemainingTbdList); + + /* + * Free the mbufs used by the sender. + */ + m = input_m; + while ( m != NULL ) { + MFREE(m,n); + m = n; + } +} + +/*********************************************************************** + * Function: uti596_attach + * + * Description: + * Configure the driver, and connect to the network stack + * + * Algorithm: + * + * Check parameters in the ifconfig structure, and + * set driver parameters accordingly. + * Initialize required rx and tx buffers. + * Link driver data structure onto device list. + * Return 1 on successful completion. + * + ***********************************************************************/ + +int uti596_attach( + struct rtems_bsdnet_ifconfig * pConfig, + int attaching +) +{ + uti596_softc_ *sc = &uti596_softc; /* device dependent data structure */ + struct ifnet * ifp = (struct ifnet *)&sc->arpcom.ac_if; /* ifnet structure */ + int unitNumber; + char *unitName; +#if defined(mvme167) + unsigned char j1; /* State of J1 jumpers */ + char *pAddr; + int addr; +#endif + + #ifdef DBG_ATTACH + printk(("uti596_attach: begins\n")) + #endif + + /* The NIC is not started yet */ + sc->started = 0; + + /* Indicate to ULCS that this is initialized */ + ifp->if_softc = (void *)sc; + sc->pScp = NULL; + + /* Parse driver name */ + if ((unitNumber = rtems_bsdnet_parse_driver_name (pConfig, &unitName)) < 0) + return 0; + + ifp->if_name = unitName; + ifp->if_unit = unitNumber; + + /* Assign mtu */ + if ( pConfig -> mtu ) + ifp->if_mtu = pConfig -> mtu; + else + ifp->if_mtu = ETHERMTU; + + /* + * Check whether parameters should be obtained from NVRAM. If + * yes, and if an IP address, netmask, or ethernet address are + * provided in NVRAM, cheat, and stuff them into the ifconfig + * structure, OVERRIDING and existing or NULL values. + * + * Warning: If values are provided in NVRAM, the ifconfig entries + * must be NULL because buffer memory allocated to hold the + * structure values is unrecoverable and would be lost here. + */ + +#if defined(mvme167) + /* Read the J1 header */ + j1 = (unsigned char)(lcsr->vector_base & 0xFF); + + if ( !(j1 & 0x10) ) { + /* Jumper J1-4 is on, configure from NVRAM */ + + if ( (addr = nvram->ipaddr) ) { + /* We have a non-zero entry, copy the value */ + if ( (pAddr = malloc ( INET_ADDR_MAX_BUF_SIZE, 0, M_NOWAIT )) ) + pConfig->ip_address = (char *)inet_ntop(AF_INET, &addr, pAddr, INET_ADDR_MAX_BUF_SIZE -1 ); + else + rtems_panic("Can't allocate ip_address buffer!\n"); + } + + if ( (addr = nvram->netmask) ) { + /* We have a non-zero entry, copy the value */ + if ( (pAddr = malloc ( INET_ADDR_MAX_BUF_SIZE, 0, M_NOWAIT )) ) + pConfig->ip_netmask = (char *)inet_ntop(AF_INET, &addr, pAddr, INET_ADDR_MAX_BUF_SIZE -1 ); + else + rtems_panic("Can't allocate ip_netmask buffer!\n"); + } + + /* Ethernet address requires special handling -- it must be copied into + * the arpcom struct. The following if construct serves only to give the + * NVRAM parameter the highest priority if J1-4 indicates we are configuring + * from NVRAM. + * + * If the ethernet address is specified in NVRAM, go ahead and copy it. + * (ETHER_ADDR_LEN = 6 bytes). + */ + if ( nvram->enaddr[0] || nvram->enaddr[1] || nvram->enaddr[2] ) { + /* Anything in the first three bytes indicates a non-zero entry, copy value */ + memcpy ((void *)sc->arpcom.ac_enaddr, &nvram->enaddr, ETHER_ADDR_LEN); + } + else if ( pConfig->hardware_address) { + /* There is no entry in NVRAM, but there is in the ifconfig struct, so use it. */ + memcpy ((void *)sc->arpcom.ac_enaddr, pConfig->hardware_address, ETHER_ADDR_LEN); + } + else { + /* There is no ethernet address provided, so it will be read + * from BBRAM at $FFFC1F2C by default. [mvme167 manual p. 1-47] + */ + memcpy ((void *)sc->arpcom.ac_enaddr, (char *)0xFFFC1F2C, ETHER_ADDR_LEN); + } + } + else if ( pConfig->hardware_address) { + /* We are not configuring from NVRAM (J1-4 is off), and the ethernet address + * is given in the ifconfig structure. Copy it. + */ + memcpy ((void *)sc->arpcom.ac_enaddr, pConfig->hardware_address, ETHER_ADDR_LEN); + } + else +#endif + { + /* We are not configuring from NVRAM (J1-4 is off), and there is no ethernet + * address provided in the ifconfig struct, so it will be read from BBRAM at + * $FFFC1F2C by default. [mvme167 manual p. 1-47] + */ + memcpy ((void *)sc->arpcom.ac_enaddr, (char *)0xFFFC1F2C, ETHER_ADDR_LEN); + } + + /* Possibly override default acceptance of broadcast packets */ + if (pConfig->ignore_broadcast) + uti596initSetup[8] |= 0x02; + + /* Assign requested receive buffer descriptor count */ + if (pConfig->rbuf_count) + sc->rxBdCount = pConfig->rbuf_count; + else + sc->rxBdCount = RX_BUF_COUNT; + + /* Assign requested tx buffer descriptor count */ + if (pConfig->xbuf_count) + sc->txBdCount = pConfig->xbuf_count; + else + sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF; + + /* Set up fields in the ifnet structure*/ + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; + ifp->if_snd.ifq_maxlen = ifqmaxlen; + ifp->if_init = uti596_init; + ifp->if_ioctl = uti596_ioctl; + ifp->if_start = uti596_start; + ifp->if_output = ether_output; + + /* uti596_softc housekeeping */ + sc->started = 1; + sc->pInboundFrameQueue = I596_NULL; + sc->scb.command = 0; + + /* + * Attach the interface + */ + if_attach (ifp); + ether_ifattach (ifp); + return 1; +} + +/*********************************************************************** + * Function: uti596_start + * + * Description: + * start the driver + * + * Algorithm: + * send an event to the tx task + * set the if_flags + * + ***********************************************************************/ +static void uti596_start( + struct ifnet *ifp +) +{ + uti596_softc_ *sc = ifp->if_softc; + + #ifdef DBG_START + printk(("uti596_start: begins\n")) + #endif + + rtems_bsdnet_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT); + ifp->if_flags |= IFF_OACTIVE; +} + +/*********************************************************************** + * Function: uti596_init + * + * Description: + * driver initialization + * + * Algorithm: + * initialize the 82596 + * start driver tx and rx tasks, and reset task + * send the RX_START command the the RU + * set if_flags + * + * + ***********************************************************************/ +void uti596_init( + void * arg +) +{ + uti596_softc_ *sc = arg; + struct ifnet *ifp = (struct ifnet *)&sc->arpcom.ac_if; + + if (sc->txDaemonTid == 0) { + + /* + * Initialize the 82596 + */ + #ifdef DBG_INIT + printk(("uti596_init: begins\nuti596_init: initializing the 82596...\n")) + #endif + uti596_initialize_hardware(sc); + + /* + * Start driver tasks + */ + #ifdef DBG_INIT + printk(("uti596_init: starting driver tasks...\n")) + #endif + sc->txDaemonTid = rtems_bsdnet_newproc ("UTtx", 2*4096, uti596_txDaemon, (void *)sc); + sc->rxDaemonTid = rtems_bsdnet_newproc ("UTrx", 2*4096, uti596_rxDaemon, (void *)sc); + sc->resetDaemonTid = rtems_bsdnet_newproc ("UTrt", 2*4096, uti596_resetDaemon, (void *)sc); + + #ifdef DBG_INIT + printk(("uti596_init: After attach, status of board = 0x%x\n", sc->scb.status )) + #endif + } + + /* + * In case the ISR discovers there are no resources it reclaims + * them and restarts + */ + sc->started = 1; + + /* + * Enable receiver + */ + #ifdef DBG_INIT + printk(("uti596_init: enabling the reciever...\n" )) + #endif + sc->scb.command = RX_START; + uti596_issueCA ( sc, UTI596_WAIT_FOR_CU_ACCEPT ); + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; + #ifdef DBG_INIT + printk(("uti596_init: completed.\n")) + #endif +} + +/*********************************************************************** + * Function: uti596stop + * + * Description: + * stop the driver + * + * Algorithm: + * mark driver as not started, + * mark transmitter as busy + * abort any transmissions/receptions + * clean-up all buffers ( RFD's et. al. ) + * + * + ***********************************************************************/ + +/* static */ void uti596_stop( + uti596_softc_ *sc +) +{ + struct ifnet *ifp = (struct ifnet *)&sc->arpcom.ac_if; + + ifp->if_flags &= ~IFF_RUNNING; + sc->started = 0; + + #ifdef DBG_STOP + printk(("uti596stop: %s: Shutting down ethercard, status was %4.4x.\n", + uti596_softc.arpcom.ac_if.if_name, uti596_softc.scb.status)) + #endif + + printk(("Stopping interface\n")) + sc->scb.command = CUC_ABORT | RX_ABORT; + i82596->chan_attn = 0x00000000; +} + +/*********************************************************************** + * Function: void uti596_txDaemon + * + * Description: Transmit task + * + * Algorithm: Get mbufs to be transmitted, stuff into RFDs, send + * + ***********************************************************************/ + +void uti596_txDaemon( + void *arg +) +{ + uti596_softc_ *sc = (uti596_softc_ *)arg; + struct ifnet *ifp = (struct ifnet *)&sc->arpcom.ac_if; + struct mbuf *m; + rtems_event_set events; + + for (;;) { + /* + * Wait for packet from stack + */ + rtems_bsdnet_event_receive (START_TRANSMIT_EVENT, + RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, &events); + + /* + * Send packets till queue is empty. + * Ensure that irq is on before sending. + */ + for (;;) { + /* Get the next mbuf chain to transmit. */ + IF_DEQUEUE(&ifp->if_snd, m); + if (!m) + break; + + send_packet (ifp, m); /* blocks */ + } + ifp->if_flags &= ~IFF_OACTIVE; /* no more to send, mark output inactive */ + } +} + +/*********************************************************************** + * Function: uti596_rxDaemon + * + * Description: Receiver task + * + * Algorithm: Extract the packet from an RFD, and place into an + * mbuf chain. Place the mbuf chain in the network task + * queue. Assumes that the frame check sequence is removed + * by the 82596. + * + ***********************************************************************/ + +/* static */ void uti596_rxDaemon( + void *arg +) +{ + uti596_softc_ *sc = (uti596_softc_ *)arg; + struct ifnet *ifp = (struct ifnet *)&sc->arpcom.ac_if; + struct mbuf *m; + + i596_rfd *pRfd; + ISR_Level level; + rtems_id tid; + rtems_event_set events; + struct ether_header *eh; + + int frames = 0; + + #ifdef DBG_RX + printk(("uti596_rxDaemon: begin\n")) + printk(("&scb = %p, pRfd = %p\n", &sc->scb,sc->scb.pRfd)) + #endif + + rtems_task_ident (0, 0, &tid); + + for(;;) { + /* + * Wait for packet. + */ + #ifdef DBG_RX + printk(("uti596_rxDaemon: Receiver sleeps\n")) + #endif + + rtems_bsdnet_event_receive (INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + + #ifdef DBG_RX + printk(("uti596_rxDaemon: Receiver wakes\n")) + #endif + /* + * While received frames are available. Note that the frame may be + * a fragment, so it is NOT a complete packet. + */ + pRfd = uti596_dequeue( (i596_rfd **)&sc->pInboundFrameQueue); + while ( pRfd && + pRfd != I596_NULL && + pRfd -> stat & STAT_C ) + { + + if ( pRfd->stat & STAT_OK) { /* a good frame */ + int pkt_len = pRfd->count & 0x3fff; /* the actual # of bytes received */ + + #ifdef DBG_RX + printk(("uti596_rxDaemon: Good frame, @%p, data @%p length %d\n", pRfd, pRfd -> data , pkt_len)) + #endif + frames++; + + /* + * Allocate an mbuf to give to the stack + * The format of the data portion of the RFD is: + * <ethernet header, payload>. + * The FRAME CHECK SEQUENCE / CRC is stripped by the uti596. + * This is to be optimized later.... should not have to memcopy! + */ + MGETHDR(m, M_WAIT, MT_DATA); + MCLGET(m, M_WAIT); + + m->m_pkthdr.rcvif = ifp; + /* move everything into an mbuf */ + memcpy(m->m_data, (const char *)pRfd->data, pkt_len); + m->m_len = m->m_pkthdr.len = pkt_len - sizeof(struct ether_header) - 4; + + /* move the header to an mbuf */ + eh = mtod (m, struct ether_header *); + m->m_data += sizeof(struct ether_header); + + #ifdef DBG_PACKETS + { + int i; + printk(("uti596_rxDaemon: mbuf contains:\n")) + print_eth( (char *) (((int)m->m_data)-sizeof(struct ether_header))); + for ( i = 0; i<20; i++) { + printk((".")) + } + } + #endif + + ether_input (ifp, eh, m); + + } /* end if STAT_OK */ + + else { + /* + * A bad frame is present: Note that this could be the last RFD! + */ + #ifdef DBG_RX + printk(("uti596_rxDaemon: Bad frame\n")) + #endif + /* + * FIX ME: use the statistics from the SCB + */ + sc->stats.rx_errors++; + if ((sc->scb.pRfd->stat) & 0x0001) + sc->stats.collisions++; + if ((sc->scb.pRfd->stat) & 0x0080) + sc->stats.rx_length_errors++; + if ((sc->scb.pRfd->stat) & 0x0100) + sc->stats.rx_over_errors++; + if ((sc->scb.pRfd->stat) & 0x0200) + sc->stats.rx_fifo_errors++; + if ((sc->scb.pRfd->stat) & 0x0400) + sc->stats.rx_frame_errors++; + if ((sc->scb.pRfd->stat) & 0x0800) + sc->stats.rx_crc_errors++; + if ((sc->scb.pRfd->stat) & 0x1000) + sc->stats.rx_length_errors++; + } + + UTI_596_ASSERT(pRfd != I596_NULL, "Supplying NULL RFD\n") + + _ISR_Local_disable(level); + uti596_supplyFD ( pRfd ); /* Return RFD to RFA. */ + _ISR_Local_enable(level); + + pRfd = uti596_dequeue( (i596_rfd **)&sc->pInboundFrameQueue); /* grab next frame */ + + } /* end while */ + } /* end for() */ + + #ifdef DBG_RX + printk (("uti596_rxDaemon: frames ... %d\n", frames)) + #endif +} + +/*********************************************************************** + * Function: void uti596_resetDaemon + * + * Description: + ***********************************************************************/ +void uti596_resetDaemon( + void *arg +) +{ + uti596_softc_ *sc = (uti596_softc_ *)arg; + rtems_event_set events; + rtems_time_of_day tm_struct; + + /* struct ifnet *ifp = &sc->arpcom.ac_if; */ + + for (;;) { + /* Wait for reset event from ISR */ + rtems_bsdnet_event_receive (NIC_RESET_EVENT, + RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, &events); + + rtems_clock_get_tod(&tm_struct); + printk(("reset daemon: Resetting NIC @ %" PRIu32 ":%" PRIu32 ":%" PRIu32 " \n", + tm_struct.hour, tm_struct.minute, tm_struct.second)) + + sc->stats.nic_reset_count++; + /* Reinitialize the LANC */ + rtems_bsdnet_semaphore_obtain (); + uti596_reset(); + rtems_bsdnet_semaphore_release (); + } +} + +/*********************************************************************** + * Function: uti596_DynamicInterruptHandler + * + * Description: + * This is the interrupt handler for the uti596 board + * + ***********************************************************************/ + +/* static */ rtems_isr uti596_DynamicInterruptHandler( + rtems_vector_number irq +) +{ +int fullStatus; + + #ifdef DBG_ISR + printk(("uti596_DynamicInterruptHandler: begins")) + #endif + + uti596_wait (&uti596_softc, UTI596_WAIT_FOR_CU_ACCEPT); + + scbStatus = (fullStatus = uti596_softc.scb.status) & 0xf000; + + if ( scbStatus ) { + /* acknowledge interrupts */ + + /* Write to the ICLR bit in the PCCchip2 control registers to clear + * the INT status bit. Clearing INT here *before* sending the CA signal + * to the 82596 should ensure that interrupts won't be lost. + */ + pccchip2->LANC_int_ctl |=0x08; + pccchip2->LANC_berr_ctl |=0x08; + + /* printk(("***INFO: ACK %x\n", scbStatus))*/ + + /* Send the CA signal to acknowledge interrupt */ + uti596_softc.scb.command = scbStatus; + uti596_issueCA ( &uti596_softc, UTI596_NO_WAIT ); + + if( uti596_softc.resetDone ) { + /* stack is attached */ + uti596_wait ( &uti596_softc, UTI596_WAIT_FOR_CU_ACCEPT ); + } + else { + printk(("***INFO: ACK'd w/o processing. status = %x\n", scbStatus)) + return; + } + } + else { +#ifndef IGNORE_SPURIOUS_IRQ + printk(("\n***ERROR: Spurious interrupt (full status 0x%x). Resetting...\n", fullStatus)) + uti596_softc.nic_reset = 1; +#endif + } + + if ( (scbStatus & SCB_STAT_CX) && !(scbStatus & SCB_STAT_CNA) ) { + printk(("\n*****ERROR: Command Complete, and CNA available: 0x%x\nResetting...", scbStatus)) + uti596_softc.nic_reset = 1; + return; + } + + if ( !(scbStatus & SCB_STAT_CX) && (scbStatus & SCB_STAT_CNA) ) { + printk(("\n*****ERROR: CNA, NO CX:0x%x\nResetting...",scbStatus)) + uti596_softc.nic_reset = 1; + return; + } + + if ( scbStatus & SCB_CUS_SUSPENDED ) { + printk(("\n*****ERROR: Command unit suspended!:0x%x\nResetting...",scbStatus)) + uti596_softc.nic_reset = 1; + return; + } + + if ( scbStatus & RU_SUSPENDED ) { + printk(("\n*****ERROR: Receive unit suspended!:0x%x\nResetting...",scbStatus)) + uti596_softc.nic_reset = 1; + return; + } + + if ( scbStatus & SCB_STAT_RNR ) { + printk(("\n*****WARNING: RNR %x\n",scbStatus)) + if (uti596_softc.pBeginRFA != I596_NULL) { + printk(("*****INFO: RFD cmd: %x status:%x\n", uti596_softc.pBeginRFA->cmd, + uti596_softc.pBeginRFA->stat)) + } + else { + printk(("*****WARNING: RNR condition with NULL BeginRFA\n")) + } + } + + /* + * Receive Unit Control + * a frame is received + */ + if ( scbStatus & SCB_STAT_FR ) { + uti596_softc.rxInterrupts++; + + #ifdef DBG_ISR + printk(("uti596_DynamicInterruptHandler: Frame received\n")) + #endif + if ( uti596_softc.pBeginRFA == I596_NULL || + !( uti596_softc.pBeginRFA -> stat & STAT_C)) { +#ifndef IGNORE_NO_RFA + uti596_dump_scb(); + uti596_softc.nic_reset = 1; +#endif + } + else { + while ( uti596_softc.pBeginRFA != I596_NULL && + ( uti596_softc.pBeginRFA -> stat & STAT_C)) { + + #ifdef DBG_ISR + printk(("uti596_DynamicInterruptHandler: pBeginRFA != NULL\n")) + #endif + count_rx ++; +#ifndef IGNORE_MULTIPLE_RF + if ( count_rx > 1) { + printk(("****WARNING: Received %i frames on 1 interrupt \n", count_rx)) + } +#endif + /* Give Received Frame to the ULCS */ + uti596_softc.countRFD--; + + if ( uti596_softc.countRFD < 0 ) { + printk(("ISR: Count < 0 !!! count == %d, beginRFA = %p\n", + uti596_softc.countRFD, uti596_softc.pBeginRFA)) + } + uti596_softc.stats.rx_packets++; + /* the rfd next link is stored with upper and lower words swapped so read it that way */ + pIsrRfd = (i596_rfd *) word_swap ((unsigned long)uti596_softc.pBeginRFA->next); + /* the append destroys the link */ + uti596_append( (i596_rfd **)&uti596_softc.pInboundFrameQueue , uti596_softc.pBeginRFA ); + + /* + * if we have just received the a frame in the last unknown RFD, + * then it is certain that the RFA is empty. + */ + if ( uti596_softc.pLastUnkRFD == uti596_softc.pBeginRFA ) { + UTI_596_ASSERT(uti596_softc.pLastUnkRFD != I596_NULL,"****ERROR:LastUnk is NULL, begin ptr @ end!\n") + uti596_softc.pEndRFA = uti596_softc.pLastUnkRFD = I596_NULL; + } + + #ifdef DBG_ISR + printk(("uti596_DynamicInterruptHandler: Wake %#x\n",uti596_softc.rxDaemonTid)) + #endif + sc = rtems_bsdnet_event_send(uti596_softc.rxDaemonTid, INTERRUPT_EVENT); + if ( sc != RTEMS_SUCCESSFUL ) { + rtems_panic("Can't notify rxDaemon: %s\n", + rtems_status_text (sc)); + } + #ifdef DBG_ISR + else { + printk(("uti596_DynamicInterruptHandler: Rx Wake: %#x\n",uti596_softc.rxDaemonTid)) + } + #endif + + uti596_softc.pBeginRFA = pIsrRfd; + } /* end while */ + } /* end if */ + + if ( uti596_softc.pBeginRFA == I596_NULL ) { + /* adjust the pEndRFA to reflect an empty list */ + if ( uti596_softc.pLastUnkRFD == I596_NULL && uti596_softc.countRFD != 0 ) { + printk(("Last Unk is NULL, BeginRFA is null, and count == %d\n", + uti596_softc.countRFD)) + } + uti596_softc.pEndRFA = I596_NULL; + if ( uti596_softc.countRFD != 0 ) { + printk(("****ERROR:Count is %d, but begin ptr is NULL\n", + uti596_softc.countRFD )) + } + } + } /* end if ( scbStatus & SCB_STAT_FR ) */ + + /* + * Command Unit Control + * a command is completed + */ + if ( scbStatus & SCB_STAT_CX ) { + #ifdef DBG_ISR + printk(("uti596_DynamicInterruptHandler: CU\n")) + #endif + + pIsrCmd = uti596_softc.pCmdHead; + + /* For ALL completed commands */ + if ( pIsrCmd != I596_NULL && pIsrCmd->status & STAT_C ) { + + #ifdef DBG_ISR + printk(("uti596_DynamicInterruptHandler: pIsrCmd != NULL\n")) + #endif + + /* Adjust the command block list */ + uti596_softc.pCmdHead = (i596_cmd *) word_swap ((unsigned long)pIsrCmd->next); + + /* + * If there are MORE commands to process, + * the serialization in the raw routine has failed. + * ( Perhaps AddCmd is bad? ) + */ + UTI_596_ASSERT(uti596_softc.pCmdHead == I596_NULL, "****ERROR: command serialization failed\n") + + /* What if the command did not complete OK? */ + switch ( pIsrCmd->command & 0x7) { + case CmdConfigure: + + uti596_softc.cmdOk = 1; + break; + + case CmdDump: + #ifdef DBG_ISR + printk(("uti596_DynamicInterruptHandler: dump!\n")) + #endif + uti596_softc.cmdOk = 1; + break; + + case CmdDiagnose: + #ifdef DBG_ISR + printk(("uti596_DynamicInterruptHandler: diagnose!\n")) + #endif + uti596_softc.cmdOk = 1; + break; + + case CmdSASetup: + /* printk(("****INFO:Set address interrupt\n")) */ + if ( pIsrCmd -> status & STAT_OK ) { + uti596_softc.cmdOk = 1; + } + else { + printk(("****ERROR:SET ADD FAILED\n")) + } + break; + + case CmdTx: + UTI_596_ASSERT(uti596_softc.txDaemonTid, "****ERROR:Null txDaemonTid\n") + #ifdef DBG_ISR + printk(("uti596_DynamicInterruptHandler: wake TX:0x%x\n",uti596_softc.txDaemonTid)) + #endif + if ( uti596_softc.txDaemonTid ) { + /* Ensure that the transmitter is present */ + sc = rtems_bsdnet_event_send (uti596_softc.txDaemonTid, + INTERRUPT_EVENT); + + if ( sc != RTEMS_SUCCESSFUL ) { + printk(("****ERROR:Could NOT send event to tid 0x%" PRIu32 " : %s\n", + uti596_softc.txDaemonTid, rtems_status_text (sc) )) + } + #ifdef DBG_ISR + else { + printk(("****INFO:Tx wake: %#x\n",uti596_softc.txDaemonTid)) + } + #endif + } + break; + + case CmdMulticastList: + printk(("***ERROR:Multicast?!\n")) + pIsrCmd->next = I596_NULL; + break; + + case CmdTDR: { + unsigned long status = *( (unsigned long *)pIsrCmd)+1; + printk(("****ERROR:TDR?!\n")) + + if (status & STAT_C) { + /* mark the TDR command successful */ + uti596_softc.cmdOk = 1; + } + else { + if (status & 0x4000) { + printk(("****WARNING:Transceiver problem.\n")) + } + if (status & 0x2000) { + printk(("****WARNING:Termination problem.\n")) + } + if (status & 0x1000) { + printk(("****WARNING:Short circuit.\n")) + /* printk(("****INFO:Time %ld.\n", status & 0x07ff)) */ + } + } + } + break; + + default: { + /* + * This should never be reached + */ + printk(("CX but NO known command\n")) + } + } /* end switch */ + + pIsrCmd = uti596_softc.pCmdHead; /* next command */ + if ( pIsrCmd != I596_NULL ) { + printk(("****WARNING: more commands in list, but no start to NIC\n")) + } + } /* end if pIsrCmd != NULL && pIsrCmd->stat & STAT_C */ + + else { + if ( pIsrCmd != I596_NULL ) { + /* The command MAY be NULL from a RESET */ + /* Reset the ethernet card, and wake the transmitter (if necessary) */ + printk(("****INFO: Request board reset ( tx )\n")) + uti596_softc.nic_reset = 1; + if ( uti596_softc.txDaemonTid) { + /* Ensure that a transmitter is present */ + sc = rtems_bsdnet_event_send (uti596_softc.txDaemonTid, + INTERRUPT_EVENT); + if ( sc != RTEMS_SUCCESSFUL ) { + printk(("****ERROR:Could NOT send event to tid 0x%" PRIu32 " : %s\n", + uti596_softc.txDaemonTid, rtems_status_text (sc) )) + } + #ifdef DBG_ISR + else { + printk(("uti596_DynamicInterruptHandler: ****INFO:Tx wake: %#x\n", + uti596_softc.txDaemonTid)) + } + #endif + } + } + } + } /* end if command complete */ + + /* + * If the receiver has stopped, + * check if this is a No Resources scenario, + * Try to add more RFD's ( no RBDs are used ) + */ + if ( uti596_softc.started ) { + if ( scbStatus & SCB_STAT_RNR ) { + #ifdef DBG_ISR + printk(("uti596_DynamicInterruptHandler: INFO:RNR: status %#x \n", + uti596_softc.scb.status )) + #endif + /* + * THE RECEIVER IS OFF! + */ + if ( uti596_softc.pLastUnkRFD != I596_NULL ) { + /* We have an unknown RFD, it is not inbound */ + if ( uti596_softc.pLastUnkRFD -> stat & (STAT_C | STAT_B )) { /* in use */ + uti596_softc.pEndRFA = uti596_softc.pLastUnkRFD; /* update end */ + } + else { + /* + * It is NOT in use, and since RNR, we know EL bit of pEndRFA was read! + * So, unlink it from the RFA and move it to the saved queue. + * But pBegin can equal LastUnk! + */ + + if ( uti596_softc.pEndRFA != I596_NULL ) { + /* check added feb24. */ + #ifdef DBG_ISR + if ((i596_rfd *)word_swap((unsigned long)uti596_softc.pEndRFA->next) != uti596_softc.pLastUnkRFD) { + printk(("***ERROR:UNK: %p not end->next: %p, end: %p\n", + uti596_softc.pLastUnkRFD, + uti596_softc.pEndRFA -> next, + uti596_softc.pEndRFA)) + printk(("***INFO:countRFD now %d\n", + uti596_softc.countRFD)) + printk(("\n\n")) + } + #endif + uti596_softc.pEndRFA -> next = I596_NULL; /* added feb 16 */ + } + uti596_append( (i596_rfd **)&uti596_softc.pSavedRfdQueue, uti596_softc.pLastUnkRFD ); + uti596_softc.savedCount++; + uti596_softc.pEndSavedQueue = uti596_softc.pLastUnkRFD; + uti596_softc.countRFD--; /* It was not in the RFA */ + /* + * The Begin pointer CAN advance this far. We must resynch the CPU side + * with the chip. + */ + if ( uti596_softc.pBeginRFA == uti596_softc.pLastUnkRFD ) { + #ifdef DBG_ISR + if ( uti596_softc.countRFD != 0 ) { + printk(("****INFO:About to set begin to NULL, with count == %d\n\n", + uti596_softc.countRFD )) + } + #endif + uti596_softc.pBeginRFA = I596_NULL; + UTI_596_ASSERT(uti596_softc.countRFD == 0, "****ERROR:Count must be zero here!\n") + } + } + uti596_softc.pLastUnkRFD = I596_NULL; + } /* end if exists UnkRFD */ + + /* + * Append the saved queue to the RFA. + * Any further RFD's being supplied will be added to + * this new list. + */ + if ( uti596_softc.pSavedRfdQueue != I596_NULL ) { + /* entries to add */ + if ( uti596_softc.pBeginRFA == I596_NULL ) { + /* add at beginning to list */ + #ifdef DBG_ISR + if(uti596_softc.countRFD != 0) { + printk(("****ERROR:Begin pointer is NULL, but count == %d\n", + uti596_softc.countRFD)) + } + #endif + uti596_softc.pBeginRFA = uti596_softc.pSavedRfdQueue; + uti596_softc.pEndRFA = uti596_softc.pEndSavedQueue; + uti596_softc.pSavedRfdQueue = uti596_softc.pEndSavedQueue = I596_NULL; /* Reset the End */ + } + else { + #ifdef DBG_ISR + if ( uti596_softc.countRFD <= 0) { + printk(("****ERROR:Begin pointer is not NULL, but count == %d\n", + uti596_softc.countRFD)) + } + #endif + UTI_596_ASSERT( uti596_softc.pEndRFA != I596_NULL, "****WARNING: END RFA IS NULL\n") + UTI_596_ASSERT( uti596_softc.pEndRFA->next == I596_NULL, "****ERROR:END RFA -> next must be NULL\n") + + uti596_softc.pEndRFA->next = (i596_rfd *)word_swap((unsigned long)uti596_softc.pSavedRfdQueue); + uti596_softc.pEndRFA->cmd &= ~CMD_EOL; /* clear the end of list */ + uti596_softc.pEndRFA = uti596_softc.pEndSavedQueue; + uti596_softc.pSavedRfdQueue = uti596_softc.pEndSavedQueue = I596_NULL; /* Reset the End */ + #ifdef DBG_ISR + printk(("uti596_DynamicInterruptHandler: count... %d, saved ... %d \n", + uti596_softc.countRFD, + uti596_softc.savedCount)) + #endif + } + /* printk(("Isr: countRFD = %d\n",uti596_softc.countRFD)) */ + uti596_softc.countRFD += uti596_softc.savedCount; + /* printk(("Isr: after countRFD = %d\n",uti596_softc.countRFD)) */ + uti596_softc.savedCount = 0; + } + + #ifdef DBG_ISR + printk(("uti596_DynamicInterruptHandler: The list starts here %p\n",uti596_softc.pBeginRFA )) + #endif + + if ( uti596_softc.countRFD > 1) { + printk(("****INFO: pBeginRFA -> stat = 0x%x\n",uti596_softc.pBeginRFA -> stat)) + printk(("****INFO: pBeginRFA -> cmd = 0x%x\n",uti596_softc.pBeginRFA -> cmd)) + uti596_softc.pBeginRFA -> stat = 0; + UTI_596_ASSERT(uti596_softc.scb.command == 0, "****ERROR:scb command must be zero\n") + uti596_softc.scb.pRfd = uti596_softc.pBeginRFA; + uti596_softc.scb.rfd_pointer = word_swap((unsigned long)uti596_softc.pBeginRFA); + /* start RX here */ + printk(("****INFO: ISR Starting receiver\n")) + uti596_softc.scb.command = RX_START; /* should this also be CU start? */ + i82596->chan_attn = 0x00000000; + } + } /* end stat_rnr */ + } /* end if receiver started */ + + #ifdef DBG_ISR + printk(("uti596_DynamicInterruptHandler: X\n")) + #endif + count_rx=0; + + /* Do this last, to ensure that the reset is called at the right time. */ + if ( uti596_softc.nic_reset ) { + uti596_softc.nic_reset = 0; + sc = rtems_bsdnet_event_send(uti596_softc.resetDaemonTid, NIC_RESET_EVENT); + if ( sc != RTEMS_SUCCESSFUL ) + rtems_panic ("Can't notify resetDaemon: %s\n", rtems_status_text (sc)); + } + return; +} + +/*********************************************************************** + * Function: uti596_ioctl + * + * Description: + * driver ioctl function + * handles SIOCGIFADDR, SIOCSIFADDR, SIOCSIFFLAGS + * + ***********************************************************************/ + +static int uti596_ioctl( + struct ifnet *ifp, + u_long command, + caddr_t data +) +{ + uti596_softc_ *sc = ifp->if_softc; + int error = 0; + + #ifdef DBG_IOCTL + printk(("uti596_ioctl: begins\n", sc->pScp)) + #endif + + switch (command) { + case SIOCGIFADDR: + case SIOCSIFADDR: + printk(("SIOCSIFADDR\n")) + ether_ioctl (ifp, command, data); + break; + + case SIOCSIFFLAGS: + printk(("SIOCSIFFLAGS\n")) + switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { + case IFF_RUNNING: + printk(("IFF_RUNNING\n")) + uti596_stop (sc); + break; + + case IFF_UP: + printk(("IFF_UP\n")) + uti596_init ( (void *)sc); + break; + + case IFF_UP | IFF_RUNNING: + printk(("IFF_UP and RUNNING\n")) + uti596_stop (sc); + uti596_init ( (void *)sc); + break; + + default: + printk(("default\n")) + break; + } + break; + + case SIO_RTEMS_SHOW_STATS: + printk(("show stats\n")) + uti596_stats (sc); + break; + + /* FIXME: All sorts of multicast commands need to be added here! */ + default: + printk(("default: EINVAL\n")) + error = EINVAL; + break; + } + + return error; +} + +/*********************************************************************** + * Function: uti596_stats + * + * Description: + * print out the collected data + * + * Algorithm: + * use printf + * + ***********************************************************************/ + +void uti596_stats( + uti596_softc_ *sc +) +{ + printf ("CPU Reports:\n"); + printf (" Tx raw send count:%-8lu", sc->rawsndcnt); + printf (" Rx Interrupts:%-8lu", sc->rxInterrupts); + printf (" Tx Interrupts:%-8lu\n", sc->txInterrupts); + printf (" Rx Packets:%-8u", sc->stats.rx_packets); + printf (" Tx Attempts:%-u\n", sc->stats.tx_packets); + + printf (" Rx Dropped:%-8u", sc->stats.rx_dropped); + printf (" Rx IP Packets:%-8u", sc->stats.rx_packets); + printf (" Tx Errors:%-8u\n", sc->stats.tx_errors); + printf (" Tx aborted:%-8u", sc->stats.tx_aborted_errors); + printf (" Tx Dropped:%-8u\n", sc->stats.tx_dropped); + printf (" Tx IP packets:%-8u", sc->stats.tx_packets); + + printf (" Collisions Detected:%-8u\n", sc->stats.collisions); + printf (" Tx Heartbeat Errors:%-8u", sc->stats.tx_heartbeat_errors); + printf (" Tx Carrier Errors:%-8u\n", sc->stats.tx_carrier_errors); + printf (" Tx Aborted Errors:%-8u", sc->stats.tx_aborted_errors); + printf (" Rx Length Errors:%-8u\n", sc->stats.rx_length_errors); + printf (" Rx Overrun Errors:%-8u", sc->stats.rx_over_errors); + printf (" Rx Fifo Errors:%-8u\n", sc->stats.rx_fifo_errors); + printf (" Rx Framing Errors:%-8u", sc->stats.rx_frame_errors); + printf (" Rx crc errors:%-8u\n", sc->stats.rx_crc_errors); + + printf (" TX WAITS: %-8lu\n", sc->txRawWait); + + printf (" NIC resets: %-8u\n", sc->stats.nic_reset_count); + + printf (" NIC reports\n"); + + #ifdef DBG_STAT + uti596_dump_scb(); + #endif +} + +/************************ PACKET DEBUG ROUTINES ************************/ + +#ifdef DBG_PACKETS + +/* + * dumpQ + * + * Dumps frame queues for debugging + */ +static void dumpQ( void ) +{ + i596_rfd *pRfd; + + printk(("savedQ:\n")) + + for( pRfd = uti596_softc.pSavedRfdQueue; + pRfd != I596_NULL; + pRfd = (i596_rfd*)word_swap((uint32_t)pRfd -> next)) { + printk(("pRfd: %p, stat: 0x%x cmd: 0x%x\n",pRfd,pRfd -> stat,pRfd -> cmd)) + } + + printk(("Inbound:\n")) + + for( pRfd = uti596_softc.pInboundFrameQueue; + pRfd != I596_NULL; + pRfd = (i596_rfd*)word_swap((uint32_t)pRfd -> next)) { + printk(("pRfd: %p, stat: 0x%x cmd: 0x%x\n",pRfd,pRfd -> stat,pRfd -> cmd)) + } + + printk(("Last Unk: %p\n", uti596_softc.pLastUnkRFD )) + printk(("RFA:\n")) + + for( pRfd = uti596_softc.pBeginRFA; + pRfd != I596_NULL; + pRfd = (i596_rfd*)word_swap((uint32_t)pRfd -> next)) { + printk(("pRfd: %p, stat: 0x%x cmd: 0x%x\n",pRfd,pRfd -> stat,pRfd -> cmd)) + } +} + +/* + * show_buffers + * + * Print out the RFA and frame queues + */ +static void show_buffers (void) +{ + i596_rfd *pRfd; + + printk(("82596 cmd: 0x%x, status: 0x%x RFA len: %d\n", + uti596_softc.scb.command, + uti596_softc.scb.status, + uti596_softc.countRFD)) + + printk(("\nRFA: \n")) + + for ( pRfd = uti596_softc.pBeginRFA; + pRfd != I596_NULL; + pRfd = (i596_rfd *)word_swap((uint32_t)pRfd->next) ) { + printk(("Frame @ %p, status: %2.2x, cmd: %2.2x\n", + pRfd, pRfd->stat, pRfd->cmd)) + } + printk(("\nInbound: \n")) + + for ( pRfd = uti596_softc.pInboundFrameQueue; + pRfd != I596_NULL; + pRfd = (i596_rfd *)word_swap((uint32_t)pRfd->next) ) { + printk(("Frame @ %p, status: %2.2x, cmd: %2.2x\n", + pRfd, pRfd->stat, pRfd->cmd)) + } + + printk(("\nSaved: \n")) + + for ( pRfd = uti596_softc.pSavedRfdQueue; + pRfd != I596_NULL; + pRfd = (i596_rfd *)word_swap((uint32_t)pRfd->next) ) { + printk(("Frame @ %p, status: %2.2x, cmd: %2.2x\n", + pRfd, pRfd->stat, pRfd->cmd)) + } + + printk(("\nUnknown: %p\n",uti596_softc.pLastUnkRFD)) +} + +/* + * show_queues + * + * Print out the saved frame queue and the RFA + */ +static void show_queues(void) +{ + i596_rfd *pRfd; + + printk(("CMD: 0x%x, Status: 0x%x\n", + uti596_softc.scb.command, + uti596_softc.scb.status)) + printk(("saved Q\n")) + + for ( pRfd = uti596_softc.pSavedRfdQueue; + pRfd != I596_NULL && + pRfd != NULL; + pRfd = (i596_rfd *)word_swap((uint32_t)pRfd->next) ) { + printk(("0x%p\n", pRfd)) + } + + printk(("End saved Q 0x%p\n", uti596_softc.pEndSavedQueue)) + + printk(("\nRFA:\n")) + + for ( pRfd = uti596_softc.pBeginRFA; + pRfd != I596_NULL && + pRfd != NULL; + pRfd = (i596_rfd *)word_swap((uint32_t)pRfd->next) ) { + printk(("0x%p\n", pRfd)) + } + + printk(("uti596_softc.pEndRFA: %p\n",uti596_softc.pEndRFA)) +} + +/* + * print_eth + * + * Print the contents of an ethernet packet + * CANNOT BE CALLED FROM ISR + */ +static void print_eth( + unsigned char *add +) +{ + int i; + short int length; + + printk (("Packet Location %p\n", add)) + printk (("Dest ")) + + for (i = 0; i < 6; i++) { + printk ((" %2.2X", add[i])) + } + printk (("\n")) + printk (("Source")) + + for (i = 6; i < 12; i++) { + printk ((" %2.2X", add[i])) + } + + printk (("\n")) + printk (("frame type %2.2X%2.2X\n", add[12], add[13])) + + if ( add[12] == 0x08 && add[13] == 0x06 ) { + /* an ARP */ + printk (("Hardware type : %2.2X%2.2X\n", add[14],add[15])) + printk (("Protocol type : %2.2X%2.2X\n", add[16],add[17])) + printk (("Hardware size : %2.2X\n", add[18])) + printk (("Protocol size : %2.2X\n", add[19])) + printk (("op : %2.2X%2.2X\n", add[20],add[21])) + printk (("Sender Enet addr: ")) + + for ( i=0; i< 5 ; i++) { + printk (("%x:", add[22 + i])) + } + printk (("%x\n", add[27])) + printk (("Sender IP addr: ")) + + for ( i=0; i< 3 ; i++) { + printk (("%u.", add[28 + i])) + } + printk (("%u\n", add[31])) + printk (("Target Enet addr: ")) + + for ( i=0; i< 5 ; i++) { + printk (( "%x:", add[32 + i])) + } + printk (("%x\n", add[37])) + printk (("Target IP addr: ")) + + for ( i=0; i< 3 ; i++) { + printk (( "%u.", add[38 + i])) + } + printk (("%u\n", add[41])) + } + + if ( add[12] == 0x08 && add[13] == 0x00 ) { + /* an IP packet */ + printk (("*********************IP HEADER******************\n")) + printk (("IP version/IPhdr length: %2.2X TOS: %2.2X\n", add[14] , add[15])) + printk (("IP total length: %2.2X %2.2X, decimal %d\n", add[16], add[17], length = (add[16]<<8 | add[17] ))) + printk (("IP identification: %2.2X %2.2X, 3-bit flags and offset %2.2X %2.2X\n", + add[18],add[19], add[20], add[21])) + printk (("IP TTL: %2.2X, protocol: %2.2X, checksum: %2.2X %2.2X \n", + add[22],add[23],add[24],add[25])) + printk (("IP packet type: %2.2X code %2.2X\n", add[34],add[35])) + printk (("Source IP address: ")) + + for ( i=0; i< 3 ; i++) { + printk (("%u.", add[26 + i])) + } + printk (("%u\n", add[29])) + printk (("Destination IP address: ")) + + for ( i=0; i< 3 ; i++) { + printk (("%u.", add[30 + i])) + } + printk (("%u\n", add[33])) + } +} + +/* + * print_hdr + * + * Print the contents of an ethernet packet header + * CANNOT BE CALLED FROM ISR + */ +static void print_hdr( + unsigned char *add +) +{ + int i; + + printk (("print_hdr: begins\n")) + printk (("Header Location %p\n", add)) + printk (("Dest ")) + + for (i = 0; i < 6; i++) { + printk ((" %2.2X", add[i])) + } + printk (("\nSource")) + + for (i = 6; i < 12; i++) { + printk ((" %2.2X", add[i])) + } + printk (("\nframe type %2.2X%2.2X\n", add[12], add[13])) + printk (("print_hdr: completed")) +} + +/* + * Function: print_pkt + * + * Print the contents of an ethernet packet & data + * CANNOT BE CALLED FROM ISR + */ +static void print_pkt( + unsigned char *add +) +{ + int i; + short int length; + + printk (("print_pkt: begins")) + printk (("Data Location %p\n", add)) + + if ( add[0] == 0x08 && add[1] == 0x06 ) { + /* an ARP */ + printk (("Hardware type : %2.2X%2.2X\n", add[14],add[15])) + printk (("Protocol type : %2.2X%2.2X\n", add[16],add[17])) + printk (("Hardware size : %2.2X\n", add[18])) + printk (("Protocol size : %2.2X\n", add[19])) + printk (("op : %2.2X%2.2X\n", add[20],add[21])) + printk (("Sender Enet addr: ")) + + for ( i=0; i< 5 ; i++) { + printk (( "%x:", add[22 + i])) + } + printk (("%x\n", add[27])) + printk (("Sender IP addr: ")) + + for ( i=0; i< 3 ; i++) { + printk (("%u.", add[28 + i])) + } + printk (("%u\n", add[31])) + printk (("Target Enet addr: ")) + + for ( i=0; i< 5 ; i++) { + printk (( "%x:", add[32 + i])) + } + printk (("%x\n", add[37])) + printk (("Target IP addr: ")) + + for ( i=0; i< 3 ; i++) { + printk (( "%u.", add[38 + i])) + } + printk (("%u\n", add[41])) + } + + if ( add[0] == 0x08 && add[1] == 0x00 ) { + /* an IP packet */ + printk (("*********************IP HEADER******************\n")) + printk (("IP version/IPhdr length: %2.2X TOS: %2.2X\n", add[14] , add[15])) + printk (("IP total length: %2.2X %2.2X, decimal %d\n", add[16], add[17], length = (add[16]<<8 | add[17] ))) + printk (("IP identification: %2.2X %2.2X, 3-bit flags and offset %2.2X %2.2X\n", + add[18],add[19], add[20], add[21])) + printk (("IP TTL: %2.2X, protocol: %2.2X, checksum: %2.2X %2.2X \n", + add[22],add[23],add[24],add[25])) + printk (("IP packet type: %2.2X code %2.2X\n", add[34],add[35])) + printk (("Source IP address: ")) + + for ( i=0; i< 3 ; i++) { + printk(( "%u.", add[26 + i])) + } + printk(("%u\n", add[29])) + printk(("Destination IP address: ")) + + for ( i=0; i< 3 ; i++) { + printk(( "%u.", add[30 + i])) + } + printk(("%u\n", add[33])) + printk(("********************IP Packet Data*******************\n")) + length -=20; + + for ( i=0; i < length ; i++) { + printk(("0x%2.2x ", add[34+i])) + } + printk(("\n")) + printk(("ICMP checksum: %2.2x %2.2x\n", add[36], add[37])) + printk(("ICMP identifier: %2.2x %2.2x\n", add[38], add[39])) + printk(("ICMP sequence nbr: %2.2x %2.2x\n", add[40], add[41])) + printk(("print_pkt: completed")) + } +} + +/* + * print_echo + * + * Print the contents of an echo packet + * CANNOT BE CALLED FROM ISR + */ +static void print_echo( + unsigned char *add +) +{ + int i; + short int length; + + printk (("print_echo: begins")) + + if ( add[12] == 0x08 && add[13] == 0x00 ) { + /* an IP packet */ + printk (("Packet Location %p\n", add)) + printk (("Dest ")) + + for (i = 0; i < 6; i++) { + printk ((" %2.2X", add[i])) + } + printk (("\n")) + printk (("Source")) + + for (i = 6; i < 12; i++) { + printk ((" %2.2X", add[i])) + } + printk (("\n")) + printk (("frame type %2.2X%2.2X\n", add[12], add[13])) + + printk (("*********************IP HEADER******************\n")) + printk (("IP version/IPhdr length: %2.2X TOS: %2.2X\n", add[14] , add[15])) + printk (("IP total length: %2.2X %2.2X, decimal %d\n", add[16], add[17], length = (add[16]<<8 | add[17] ))) + printk (("IP identification: %2.2X %2.2X, 3-bit flags and offset %2.2X %2.2X\n", + add[18],add[19], add[20], add[21])) + printk (("IP TTL: %2.2X, protocol: %2.2X, checksum: %2.2X %2.2X \n", + add[22],add[23],add[24],add[25])) + printk (("IP packet type: %2.2X code %2.2X\n", add[34],add[35])) + printk (("Source IP address: ")) + + for ( i=0; i< 3 ; i++) { + printk (("%u.", add[26 + i])) + } + printk (("%u\n", add[29])) + printk (("Destination IP address: ")) + + for ( i=0; i< 3 ; i++) { + printk (("%u.", add[30 + i])) + } + printk (("%u\n", add[33])) + printk(("********************IP Packet Data*******************\n")) + length -=20; + + for ( i=0; i < length ; i++) { + printk(("0x%2.2x ", add[34+i])) + } + printk(("\n")) + printk(("ICMP checksum: %2.2x %2.2x\n", add[36], add[37])) + printk(("ICMP identifier: %2.2x %2.2x\n", add[38], add[39])) + printk(("ICMP sequence nbr: %2.2x %2.2x\n", add[40], add[41])) + printk(("print_echo: completed")) + } +} + +#endif diff --git a/bsps/m68k/mvme167/net/uti596.h b/bsps/m68k/mvme167/net/uti596.h new file mode 100644 index 0000000000..29e4fed299 --- /dev/null +++ b/bsps/m68k/mvme167/net/uti596.h @@ -0,0 +1,369 @@ +/* uti596.h: Contains the defines and structures used by the uti596 driver */ + +/* + * EII: March 11: Created v. 0.0 + */ + +#ifndef UTI596_H +#define UTI596_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 <netinet/in.h> +#include <netinet/if_ether.h> + +/* Ethernet statistics */ + +struct enet_statistics{ + int rx_packets; /* total packets received */ + int tx_packets; /* total packets transmitted */ + int rx_errors; /* bad packets received */ + int tx_errors; /* packet transmit problems */ + int rx_dropped; /* no space in buffers */ + int tx_dropped; + int tx_retries_exceeded; /* excessive retries */ + int multicast; /* multicast packets received */ + int collisions; + + /* detailed rx_errors: */ + int rx_length_errors; + int rx_over_errors; /* receiver ring buff overflow */ + int rx_crc_errors; /* recved pkt with crc error */ + int rx_frame_errors; /* recv'd frame alignment error */ + int rx_fifo_errors; /* recv'r fifo overrun */ + int rx_missed_errors; /* receiver missed packet */ + + /* detailed tx_errors */ + int tx_aborted_errors; + int tx_carrier_errors; + int tx_fifo_errors; + int tx_heartbeat_errors; + int tx_window_errors; + + /* NIC reset errors */ + int nic_reset_count; /* The number of times uti596reset() has been called. */ +}; + +#define CMD_EOL 0x8000 /* The last command of the list, stop. */ +#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */ +#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */ + +#define CMD_FLEX 0x0008 /* Enable flexible memory model */ + +#define SCB_STAT_CX 0x8000 /* Cmd completes with 'I' bit set */ +#define SCB_STAT_FR 0x4000 /* Frame Received */ +#define SCB_STAT_CNA 0x2000 /* Cmd unit Not Active */ +#define SCB_STAT_RNR 0x1000 /* Receiver Not Ready */ + +#define SCB_CUS_SUSPENDED 0x0100 +#define SCB_CUS_ACTIVE 0x0200 + +#define STAT_C 0x8000 /* Set to 1 after execution */ +#define STAT_B 0x4000 /* 1 : Cmd being executed, 0 : Cmd done. */ +#define STAT_OK 0x2000 /* 1: Command executed ok 0 : Error */ +#define STAT_A 0x1000 /* command has been aborted */ + +#define STAT_S11 0x0800 +#define STAT_S10 0x0400 +#define STAT_S9 0x0200 +#define STAT_S8 0x0100 +#define STAT_S7 0x0080 +#define STAT_S6 0x0040 +#define STAT_S5 0x0020 +#define STAT_MAX_COLLS 0x000F + +#define RBD_STAT_P 0x4000 /* prefetch */ +#define RBD_STAT_F 0x4000 /* used */ + +#define CUC_START 0x0100 +#define CUC_RESUME 0x0200 +#define CUC_SUSPEND 0x0300 +#define CUC_ABORT 0x0400 +#define RX_START 0x0010 +#define RX_RESUME 0x0020 +#define RX_SUSPEND 0x0030 +#define RX_ABORT 0x0040 + +#define RU_SUSPENDED 0x0010 +#define RU_NO_RESOURCES 0x0020 +#define RU_READY 0x0040 + +#define I596_NULL ( ( void * ) 0xffffffff) +#define UTI_596_END_OF_FRAME 0x8000 + +struct i596_tbd; /* necessary forward declaration */ + +enum commands { + CmdNOp = 0, + CmdSASetup = 1, + CmdConfigure = 2, + CmdMulticastList = 3, + CmdTx = 4, + CmdTDR = 5, + CmdDump = 6, + CmdDiagnose = 7 +}; + +/* + * 82596 Dump Command Result + */ +typedef volatile struct i596_dump_result { + unsigned char bf; + unsigned char config_bytes[11]; + unsigned char reserved1[2]; + unsigned char ia_bytes[6]; + unsigned short last_tx_status; + unsigned short tx_crc_byte01; + unsigned short tx_crc_byte23; + unsigned short rx_crc_byte01; + unsigned short rx_crc_byte23; + unsigned short rx_temp_mem01; + unsigned short rx_temp_mem23; + unsigned short rx_temp_mem45; + unsigned short last_rx_status; + unsigned short hash_reg01; + unsigned short hash_reg23; + unsigned short hash_reg45; + unsigned short hash_reg67; + unsigned short slot_time_counter; + unsigned short wait_time_counter; + unsigned short rx_frame_length; + unsigned long reserved2; + unsigned long cb_in3; + unsigned long cb_in2; + unsigned long cb_in1; + unsigned long la_cb_addr; + unsigned long rdb_pointer; + unsigned long int_memory; + unsigned long rfd_size; + unsigned long tbd_pointer; + unsigned long base_addr; + unsigned long ru_temp_reg; + unsigned long tcb_count; + unsigned long next_rb_size; + unsigned long next_rb_addr; + unsigned long curr_rb_size; + unsigned long la_rbd_addr; + unsigned long next_rbd_addr; + unsigned long curr_rbd_addr; + unsigned long curr_rb_count; + unsigned long next_fd_addr; + unsigned long curr_fd_add; + unsigned long temp_cu_reg; + unsigned long next_tb_count; + unsigned long buffer_addr; + unsigned long la_tbd_addr; + unsigned long next_tbd_addr; + unsigned long cb_command; + unsigned long next_cb_addr; + unsigned long curr_cb_addr; + unsigned long scb_cmd_word; + unsigned long scb_pointer; + unsigned long cb_stat_word; + unsigned long mm_lfsr; + unsigned char micro_machine_bit_array[28]; + unsigned char cu_port[16]; + unsigned long mm_alu; + unsigned long reserved3; + unsigned long mm_temp_a_rr; + unsigned long mm_temp_a; + unsigned long tx_dma_b_cnt; + unsigned long mm_input_port_addr_reg; + unsigned long tx_dma_addr; + unsigned long mm_port_reg1; + unsigned long rx_dma_b_cnt; + unsigned long mm_port_reg2; + unsigned long rx_dma_addr; + unsigned long reserved4; + unsigned long bus_t_timers; + unsigned long diu_cntrl_reg; + unsigned long reserved5; + unsigned long sysbus; + unsigned long biu_cntrl_reg; + unsigned long mm_disp_reg; + unsigned long mm_status_reg; + unsigned short dump_status; +} i596_dump_result; + +typedef volatile struct i596_selftest { + unsigned long rom_signature; + unsigned long results; +} i596_selftest; + +/* + * Action commands + * (big endian, linear mode) + */ +typedef volatile struct i596_cmd { + unsigned short status; + unsigned short command; + volatile struct i596_cmd *next; +} i596_cmd; + +typedef volatile struct i596_nop { + i596_cmd cmd; +} i596_nop; + +typedef volatile struct i596_set_add { + i596_cmd cmd; + char data[8]; +} i596_set_add; + +typedef volatile struct i596_configure { + i596_cmd cmd; + char data[16]; +} i596_configure; + +typedef volatile struct i596_tx { + i596_cmd cmd; + volatile struct i596_tbd *pTbd; + unsigned short count; + unsigned short pad; + char data[6]; + unsigned short length; +} i596_tx; + +typedef volatile struct i596_tdr { + i596_cmd cmd; + unsigned long data; +} i596_tdr; + +typedef volatile struct i596_dump { + i596_cmd cmd; + char *pData; +} i596_dump; + +/* + * Transmit buffer descriptor + */ +typedef volatile struct i596_tbd { + unsigned short size; + unsigned short pad; + volatile struct i596_tbd *next; + char *data; +} i596_tbd; + +/* + * Receive buffer descriptor + * (flexible memory structure) + */ +typedef volatile struct i596_rbd { + unsigned short count; + unsigned short offset; + volatile struct i596_rbd *next; + char *data; + unsigned short size; + unsigned short pad; +} i596_rbd; + +/* + * Receive Frame Descriptor + */ +typedef volatile struct i596_rfd { + unsigned short stat; + unsigned short cmd; + volatile struct i596_rfd *next; + i596_rbd *pRbd; + unsigned short count; + unsigned short size; + char data [1532]; +} i596_rfd; + +/* + * System Control Block + */ +typedef volatile struct i596_scb { + unsigned short status; + unsigned short command; + unsigned long cmd_pointer; + unsigned long rfd_pointer; + unsigned long crc_err; + unsigned long align_err; + unsigned long resource_err; + unsigned long over_err; + unsigned long rcvdt_err; + unsigned long short_err; + unsigned short t_off; + unsigned short t_on; + i596_cmd *pCmd; + i596_rfd *pRfd; +} i596_scb; + +/* + * Intermediate System Configuration Pointer + */ +typedef volatile struct i596_iscp { + uint8_t null1; /* Always zero */ + uint8_t busy; /* Busy byte */ + unsigned short scb_offset; /* Not used in linear mode */ + unsigned long scb_pointer; /* Swapped pointer to scb */ + i596_scb *scb; /* Real pointer to scb */ +} i596_iscp; + +/* + * System Configuration Pointer + */ +typedef volatile struct i596_scp { + unsigned long sysbus; /* Only low 8 bits are used */ + unsigned long pad; /* Must be zero */ + unsigned long iscp_pointer; /* Swapped pointer to iscp */ + i596_iscp *iscp; /* Real pointer to iscp */ +} i596_scp; + +/* + * Device Dependent Data Structure + */ +typedef volatile struct uti596_softc { + struct arpcom arpcom; + i596_scp *pScp; /* Block aligned on 16 byte boundary */ + i596_scp *base_scp; /* Unaligned block. Need for free() */ + i596_iscp iscp; + i596_scb scb; + i596_set_add set_add; + i596_configure set_conf; + i596_tdr tdr; + i596_nop nop; + i596_tx *pTxCmd; + i596_tbd *pTbd; + + i596_rfd *pBeginRFA; + i596_rfd *pEndRFA; + i596_rfd *pLastUnkRFD; + i596_rbd *pLastUnkRBD; + i596_rfd *pEndSavedQueue; + i596_cmd *pCmdHead; + i596_cmd *pCmdTail; /* unneeded, as chaining not used, but implemented */ + + rtems_id rxDaemonTid; + rtems_id txDaemonTid; + rtems_id resetDaemonTid; + + struct enet_statistics stats; + int started; + unsigned long rxInterrupts; + unsigned long txInterrupts; + volatile int cmdOk; + unsigned short * pCurrent_command_status; + int resetDone; + unsigned long txRawWait; + i596_rfd *pInboundFrameQueue; + short int rxBdCount; + short int txBdCount; + short int countRFD; + short int savedCount; + i596_rfd *pSavedRfdQueue; + rtems_name semaphore_name; + rtems_id semaphore_id; + char zeroes[64]; + unsigned long rawsndcnt; + int nic_reset; /* flag for requesting that ISR issue a reset quest */ +} uti596_softc_; + +#endif /* UTI596_H */ diff --git a/bsps/m68k/uC5282/net/network.c b/bsps/m68k/uC5282/net/network.c new file mode 100644 index 0000000000..b8afa0b968 --- /dev/null +++ b/bsps/m68k/uC5282/net/network.c @@ -0,0 +1,1013 @@ +/* + * RTEMS driver for MCF5282 Fast Ethernet Controller + * + * Author: W. Eric Norum <norume@aps.anl.gov> + * + * COPYRIGHT (c) 2005. + * On-Line Applications Research Corporation (OAR). + * + * 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 <stdio.h> +#include <errno.h> +#include <stdarg.h> +#include <string.h> +#include <rtems.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/ethernet.h> +#include <net/if.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + + +/* + * Number of interfaces supported by this driver + */ +#define NIFACES 1 + +#define FEC_INTC0_TX_VECTOR (64+23) +#define FEC_INTC0_RX_VECTOR (64+27) +#define MII_VECTOR (64+7) /* IRQ7* pin connected to external transceiver */ +#define MII_EPPAR MCF5282_EPORT_EPPAR_EPPA7_LEVEL +#define MII_EPDDR MCF5282_EPORT_EPDDR_EPDD7 +#define MII_EPIER MCF5282_EPORT_EPIER_EPIE7 +#define MII_EPPDR MCF5282_EPORT_EPPDR_EPPD7 + +/* + * Default number of buffer descriptors set aside for this driver. + * The number of transmit buffer descriptors has to be quite large + * since a single frame often uses three or more buffer descriptors. + */ +#define RX_BUF_COUNT 32 +#define TX_BUF_COUNT 20 +#define TX_BD_PER_BUF 3 + +#define INET_ADDR_MAX_BUF_SIZE (sizeof "255.255.255.255") + +/* + * RTEMS event used by interrupt handler to signal daemons. + * This must *not* be the same event used by the TCP/IP task synchronization. + */ +#define TX_INTERRUPT_EVENT RTEMS_EVENT_1 +#define RX_INTERRUPT_EVENT RTEMS_EVENT_1 + +/* + * RTEMS event used to start transmit daemon. + * This must not be the same as INTERRUPT_EVENT. + */ +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + +/* + * Receive buffer size -- Allow for a full ethernet packet plus CRC (1518). + * Round off to nearest multiple of RBUF_ALIGN. + */ +#define MAX_MTU_SIZE 1518 +#define RBUF_ALIGN 4 +#define RBUF_SIZE ((MAX_MTU_SIZE + RBUF_ALIGN) & ~RBUF_ALIGN) + +#if (MCLBYTES < RBUF_SIZE) + #error "Driver must have MCLBYTES > RBUF_SIZE" +#endif + +/* + * Per-device data + */ +struct mcf5282_enet_struct { + struct arpcom arpcom; + struct mbuf **rxMbuf; + struct mbuf **txMbuf; + int acceptBroadcast; + int rxBdCount; + int txBdCount; + int txBdHead; + int txBdTail; + int txBdActiveCount; + mcf5282BufferDescriptor_t *rxBdBase; + mcf5282BufferDescriptor_t *txBdBase; + rtems_id rxDaemonTid; + rtems_id txDaemonTid; + + /* + * Statistics + */ + unsigned long rxInterrupts; + unsigned long txInterrupts; + unsigned long miiInterrupts; + unsigned long txRawWait; + unsigned long txRealign; + unsigned long txRealignDrop; + + /* + * Link parameters + */ + enum { link_auto, link_100Full, link_10Half } link; + uint16_t mii_cr; + uint16_t mii_sr2; +}; +static struct mcf5282_enet_struct enet_driver[NIFACES]; + +/* + * Read MII register + * Busy-waits, but transfer time should be short! + */ +static int +getMII(int phyNumber, int regNumber) +{ + MCF5282_FEC_MMFR = (0x1 << 30) | + (0x2 << 28) | + (phyNumber << 23) | + (regNumber << 18) | + (0x2 << 16); + while ((MCF5282_FEC_EIR & MCF5282_FEC_EIR_MII) == 0); + MCF5282_FEC_EIR = MCF5282_FEC_EIR_MII; + return MCF5282_FEC_MMFR & 0xFFFF; +} + +/* + * Write MII register + * Busy-waits, but transfer time should be short! + */ +static void +setMII(int phyNumber, int regNumber, int value) +{ + MCF5282_FEC_MMFR = (0x1 << 30) | + (0x1 << 28) | + (phyNumber << 23) | + (regNumber << 18) | + (0x2 << 16) | + (value & 0xFFFF); + while ((MCF5282_FEC_EIR & MCF5282_FEC_EIR_MII) == 0); + MCF5282_FEC_EIR = MCF5282_FEC_EIR_MII; +} + +static rtems_isr +mcf5282_fec_rx_interrupt_handler( rtems_vector_number v ) +{ + MCF5282_FEC_EIR = MCF5282_FEC_EIR_RXF; + MCF5282_FEC_EIMR &= ~MCF5282_FEC_EIMR_RXF; + enet_driver[0].rxInterrupts++; + rtems_bsdnet_event_send(enet_driver[0].rxDaemonTid, RX_INTERRUPT_EVENT); +} + +static rtems_isr +mcf5282_fec_tx_interrupt_handler( rtems_vector_number v ) +{ + MCF5282_FEC_EIR = MCF5282_FEC_EIR_TXF; + MCF5282_FEC_EIMR &= ~MCF5282_FEC_EIMR_TXF; + enet_driver[0].txInterrupts++; + rtems_bsdnet_event_send(enet_driver[0].txDaemonTid, TX_INTERRUPT_EVENT); +} + +static rtems_isr +mcf5282_mii_interrupt_handler( rtems_vector_number v ) +{ + uint16 sr2; + + enet_driver[0].miiInterrupts++; + getMII(1, 19); /* Read and clear interrupt status bits */ + enet_driver[0].mii_sr2 = sr2 = getMII(1, 17); + if (((sr2 & 0x200) != 0) + && ((MCF5282_FEC_TCR & MCF5282_FEC_TCR_FDEN) == 0)) + MCF5282_FEC_TCR |= MCF5282_FEC_TCR_FDEN; + else if (((sr2 & 0x200) == 0) + && ((MCF5282_FEC_TCR & MCF5282_FEC_TCR_FDEN) != 0)) + MCF5282_FEC_TCR &= ~MCF5282_FEC_TCR_FDEN; +} + +/* + * Allocate buffer descriptors from (non-cached) on-chip static RAM + * Ensure 128-bit (16-byte) alignment + * Allow some space at the beginning for other diagnostic counters + */ +static mcf5282BufferDescriptor_t * +mcf5282_bd_allocate(unsigned int count) +{ + static mcf5282BufferDescriptor_t *bdp = __SRAMBASE.fec_descriptors; + mcf5282BufferDescriptor_t *p = bdp; + + bdp += count; + if ((int)bdp & 0xF) + bdp = (mcf5282BufferDescriptor_t *)((char *)bdp + (16 - ((int)bdp & 0xF))); + return p; +} + +static void +mcf5282_fec_initialize_hardware(struct mcf5282_enet_struct *sc) +{ + int i; + const unsigned char *hwaddr; + rtems_status_code status; + rtems_isr_entry old_handler; + uint32_t clock_speed = bsp_get_CPU_clock_speed(); + + /* + * Issue reset to FEC + */ + MCF5282_FEC_ECR = MCF5282_FEC_ECR_RESET; + rtems_task_wake_after(2); + MCF5282_FEC_ECR = 0; + + /* + * Configuration of I/O ports is done outside of this function + */ +#if 0 + imm->gpio.pbcnt |= MCF5282_GPIO_PBCNT_SET_FEC; /* Set up port b FEC pins */ +#endif + + /* + * Set our physical address + */ + hwaddr = sc->arpcom.ac_enaddr; + MCF5282_FEC_PALR = (hwaddr[0] << 24) | (hwaddr[1] << 16) | + (hwaddr[2] << 8) | (hwaddr[3] << 0); + MCF5282_FEC_PAUR = (hwaddr[4] << 24) | (hwaddr[5] << 16); + + + /* + * Clear the hash table + */ + MCF5282_FEC_GAUR = 0; + MCF5282_FEC_GALR = 0; + + /* + * Set up receive buffer size + */ + MCF5282_FEC_EMRBR = 1520; /* Standard Ethernet */ + + /* + * Allocate mbuf pointers + */ + sc->rxMbuf = malloc(sc->rxBdCount * sizeof *sc->rxMbuf, M_MBUF, M_NOWAIT); + sc->txMbuf = malloc(sc->txBdCount * sizeof *sc->txMbuf, M_MBUF, M_NOWAIT); + if (!sc->rxMbuf || !sc->txMbuf) + rtems_panic("No memory for mbuf pointers"); + + /* + * Set receiver and transmitter buffer descriptor bases + */ + sc->rxBdBase = mcf5282_bd_allocate(sc->rxBdCount); + sc->txBdBase = mcf5282_bd_allocate(sc->txBdCount); + MCF5282_FEC_ERDSR = (int)sc->rxBdBase; + MCF5282_FEC_ETDSR = (int)sc->txBdBase; + + /* + * Set up Receive Control Register: + * Not promiscuous + * MII mode + * Full duplex + * No loopback + */ + MCF5282_FEC_RCR = MCF5282_FEC_RCR_MAX_FL(MAX_MTU_SIZE) | + MCF5282_FEC_RCR_MII_MODE; + + /* + * Set up Transmit Control Register: + * Full or half duplex + * No heartbeat + */ + if (sc->link == link_10Half) + MCF5282_FEC_TCR = 0; + else + MCF5282_FEC_TCR = MCF5282_FEC_TCR_FDEN; + + /* + * Initialize statistic counters + */ + MCF5282_FEC_MIBC = MCF5282_FEC_MIBC_MIB_DISABLE; + { + vuint32 *vuip = &MCF5282_FEC_RMON_T_DROP; + while (vuip <= &MCF5282_FEC_IEEE_R_OCTETS_OK) + *vuip++ = 0; + } + MCF5282_FEC_MIBC = 0; + + /* + * Set MII speed to <= 2.5 MHz + */ + i = (clock_speed + 5000000 - 1) / 5000000; + MCF5282_FEC_MSCR = MCF5282_FEC_MSCR_MII_SPEED(i); + + /* + * Set PHYS + * LED1 receive status, LED2 link status, LEDs stretched + * Advertise 100 Mb/s, full-duplex, IEEE-802.3 + * Turn off auto-negotiate + * Clear status + */ + setMII(1, 20, 0x24F2); + setMII(1, 4, 0x0181); + setMII(1, 0, 0x0); + rtems_task_wake_after(2); + sc->mii_sr2 = getMII(1, 17); + switch (sc->link) { + case link_auto: + /* + * Enable speed-change, duplex-change and link-status-change interrupts + * Enable auto-negotiate (start at 100/FULL) + */ + setMII(1, 18, 0x0072); + setMII(1, 0, 0x3100); + break; + + case link_10Half: + /* + * Force 10/HALF + */ + setMII(1, 0, 0x0); + break; + + case link_100Full: + /* + * Force 100/FULL + */ + setMII(1, 0, 0x2100); + break; + } + sc->mii_cr = getMII(1, 0); + + /* + * Set up receive buffer descriptors + */ + for (i = 0 ; i < sc->rxBdCount ; i++) + (sc->rxBdBase + i)->status = 0; + + /* + * Set up transmit buffer descriptors + */ + for (i = 0 ; i < sc->txBdCount ; i++) { + sc->txBdBase[i].status = 0; + sc->txMbuf[i] = NULL; + } + sc->txBdHead = sc->txBdTail = 0; + sc->txBdActiveCount = 0; + + /* + * Set up interrupts + */ + status = rtems_interrupt_catch( mcf5282_fec_tx_interrupt_handler, FEC_INTC0_TX_VECTOR, &old_handler ); + if (status != RTEMS_SUCCESSFUL) + rtems_panic ("Can't attach MCF5282 FEC TX interrupt handler: %s\n", + rtems_status_text(status)); + bsp_allocate_interrupt(FEC_IRQ_LEVEL, FEC_IRQ_TX_PRIORITY); + MCF5282_INTC0_ICR23 = MCF5282_INTC_ICR_IL(FEC_IRQ_LEVEL) | + MCF5282_INTC_ICR_IP(FEC_IRQ_TX_PRIORITY); + MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_INT23 | MCF5282_INTC_IMRL_MASKALL); + + status = rtems_interrupt_catch(mcf5282_fec_rx_interrupt_handler, FEC_INTC0_RX_VECTOR, &old_handler); + if (status != RTEMS_SUCCESSFUL) + rtems_panic ("Can't attach MCF5282 FEC RX interrupt handler: %s\n", + rtems_status_text(status)); + bsp_allocate_interrupt(FEC_IRQ_LEVEL, FEC_IRQ_RX_PRIORITY); + MCF5282_INTC0_ICR27 = MCF5282_INTC_ICR_IL(FEC_IRQ_LEVEL) | + MCF5282_INTC_ICR_IP(FEC_IRQ_RX_PRIORITY); + MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_INT27 | MCF5282_INTC_IMRL_MASKALL); + + status = rtems_interrupt_catch(mcf5282_mii_interrupt_handler, MII_VECTOR, &old_handler); + if (status != RTEMS_SUCCESSFUL) + rtems_panic ("Can't attach MCF5282 FEC MII interrupt handler: %s\n", + rtems_status_text(status)); + MCF5282_EPORT_EPPAR &= ~MII_EPPAR; + MCF5282_EPORT_EPDDR &= ~MII_EPDDR; + MCF5282_EPORT_EPIER |= MII_EPIER; + MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_INT7 | MCF5282_INTC_IMRL_MASKALL); +} + +/* + * Soak up buffer descriptors that have been sent. + */ +static void +fec_retire_tx_bd(volatile struct mcf5282_enet_struct *sc ) +{ + struct mbuf *m, *n; + uint16_t status; + + while ((sc->txBdActiveCount != 0) + && (((status = sc->txBdBase[sc->txBdTail].status) & MCF5282_FEC_TxBD_R) == 0)) { + if ((status & MCF5282_FEC_TxBD_TO1) == 0) { + m = sc->txMbuf[sc->txBdTail]; + MFREE(m, n); + } + if (++sc->txBdTail == sc->txBdCount) + sc->txBdTail = 0; + sc->txBdActiveCount--; + } +} + +static void +fec_rxDaemon (void *arg) +{ + volatile struct mcf5282_enet_struct *sc = (volatile struct mcf5282_enet_struct *)arg; + struct ifnet *ifp = (struct ifnet* )&sc->arpcom.ac_if; + struct mbuf *m; + uint16_t status; + volatile mcf5282BufferDescriptor_t *rxBd; + int rxBdIndex; + + /* + * Allocate space for incoming packets and start reception + */ + for (rxBdIndex = 0 ; ;) { + rxBd = sc->rxBdBase + rxBdIndex; + MGETHDR(m, M_WAIT, MT_DATA); + MCLGET(m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod(m, void *); + rxBd->status = MCF5282_FEC_RxBD_E; + if (++rxBdIndex == sc->rxBdCount) { + rxBd->status |= MCF5282_FEC_RxBD_W; + break; + } + } + + /* + * Input packet handling loop + */ + MCF5282_FEC_RDAR = 0; + + rxBdIndex = 0; + for (;;) { + rxBd = sc->rxBdBase + rxBdIndex; + + /* + * Wait for packet if there's not one ready + */ + if ((status = rxBd->status) & MCF5282_FEC_RxBD_E) { + /* + * Clear old events. + */ + MCF5282_FEC_EIR = MCF5282_FEC_EIR_RXF; + + /* + * Wait for packet to arrive. + * Check the buffer descriptor before waiting for the event. + * This catches the case when a packet arrives between the + * `if' above, and the clearing of the RXF bit in the EIR. + */ + while ((status = rxBd->status) & MCF5282_FEC_RxBD_E) { + rtems_event_set events; + int level; + + rtems_interrupt_disable(level); + MCF5282_FEC_EIMR |= MCF5282_FEC_EIMR_RXF; + rtems_interrupt_enable(level); + rtems_bsdnet_event_receive (RX_INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + } + } + + /* + * Check that packet is valid + */ + if (status & MCF5282_FEC_RxBD_L) { + /* + * Pass the packet up the chain. + * FIXME: Packet filtering hook could be done here. + */ + struct ether_header *eh; + int len = rxBd->length - sizeof(uint32_t); + + m = sc->rxMbuf[rxBdIndex]; +#ifdef RTEMS_MCF5282_BSP_ENABLE_DATA_CACHE + /* + * Invalidate the cache. The cache is so small that it's + * reasonable to simply invalidate the whole thing. + */ + rtems_cache_invalidate_entire_data(); +#endif + m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header); + eh = mtod(m, struct ether_header *); + m->m_data += sizeof(struct ether_header); + ether_input(ifp, eh, m); + + /* + * Allocate a new mbuf + */ + MGETHDR(m, M_WAIT, MT_DATA); + MCLGET(m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod(m, void *); + } + + /* + * Reenable the buffer descriptor + */ + rxBd->status = (status & MCF5282_FEC_RxBD_W) | MCF5282_FEC_RxBD_E; + MCF5282_FEC_RDAR = 0; + + /* + * Move to next buffer descriptor + */ + if (++rxBdIndex == sc->rxBdCount) + rxBdIndex = 0; + } +} + +static void +fec_sendpacket(struct ifnet *ifp, struct mbuf *m) +{ + struct mcf5282_enet_struct *sc = ifp->if_softc; + volatile mcf5282BufferDescriptor_t *firstTxBd, *txBd; + uint16_t status; + int nAdded; + + /* + * Free up buffer descriptors + */ + fec_retire_tx_bd(sc); + + /* + * Set up the transmit buffer descriptors. + * No need to pad out short packets since the + * hardware takes care of that automatically. + * No need to copy the packet to a contiguous buffer + * since the hardware is capable of scatter/gather DMA. + */ + nAdded = 0; + firstTxBd = sc->txBdBase + sc->txBdHead; + + while (m != NULL) { + /* + * Wait for buffer descriptor to become available + */ + if ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + /* + * Clear old events. + */ + MCF5282_FEC_EIR = MCF5282_FEC_EIR_TXF; + + /* + * Wait for buffer descriptor to become available. + * Check for buffer descriptors before waiting for the event. + * This catches the case when a buffer became available between + * the `if' above, and the clearing of the TXF bit in the EIR. + */ + fec_retire_tx_bd(sc); + while ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + rtems_event_set events; + int level; + + rtems_interrupt_disable(level); + MCF5282_FEC_EIMR |= MCF5282_FEC_EIMR_TXF; + rtems_interrupt_enable(level); + sc->txRawWait++; + rtems_bsdnet_event_receive(TX_INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + fec_retire_tx_bd(sc); + } + } + + /* + * Don't set the READY flag on the first fragment + * until the whole packet has been readied. + */ + status = nAdded ? MCF5282_FEC_TxBD_R : 0; + + /* + * The IP fragmentation routine in ip_output + * can produce fragments with zero length. + */ + txBd = sc->txBdBase + sc->txBdHead; + if (m->m_len) { + char *p = mtod(m, char *); + int offset = (int)p & 0x3; + if (offset == 0) { + txBd->buffer = p; + txBd->length = m->m_len; + sc->txMbuf[sc->txBdHead] = m; + m = m->m_next; + } + else { + /* + * Stupid FEC can't handle misaligned data! + * Move offending bytes to a local buffer. + * Use buffer descriptor TO1 bit to indicate this. + */ + int nmove = 4 - offset; + char *d = (char *)&sc->txMbuf[sc->txBdHead]; + status |= MCF5282_FEC_TxBD_TO1; + sc->txRealign++; + if (nmove > m->m_len) + nmove = m->m_len; + m->m_data += nmove; + m->m_len -= nmove; + txBd->buffer = d; + txBd->length = nmove; + while (nmove--) + *d++ = *p++; + if (m->m_len == 0) { + struct mbuf *n; + sc->txRealignDrop++; + MFREE(m, n); + m = n; + } + } + nAdded++; + if (++sc->txBdHead == sc->txBdCount) { + status |= MCF5282_FEC_TxBD_W; + sc->txBdHead = 0; + } + txBd->status = status; + } + else { + /* + * Toss empty mbufs. + */ + struct mbuf *n; + MFREE(m, n); + m = n; + } + } + if (nAdded) { + txBd->status = status | MCF5282_FEC_TxBD_R + | MCF5282_FEC_TxBD_L + | MCF5282_FEC_TxBD_TC; + if (nAdded > 1) + firstTxBd->status |= MCF5282_FEC_TxBD_R; + MCF5282_FEC_TDAR = 0; + sc->txBdActiveCount += nAdded; + } +} + +void +fec_txDaemon(void *arg) +{ + struct mcf5282_enet_struct *sc = (struct mcf5282_enet_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + rtems_event_set events; + + for (;;) { + /* + * Wait for packet + */ + rtems_bsdnet_event_receive(START_TRANSMIT_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; + fec_sendpacket(ifp, m); + } + ifp->if_flags &= ~IFF_OACTIVE; + } +} + + +/* + * Send packet (caller provides header). + */ +static void +mcf5282_enet_start(struct ifnet *ifp) +{ + struct mcf5282_enet_struct *sc = ifp->if_softc; + + rtems_bsdnet_event_send(sc->txDaemonTid, START_TRANSMIT_EVENT); + ifp->if_flags |= IFF_OACTIVE; +} + +static void +fec_init(void *arg) +{ + struct mcf5282_enet_struct *sc = arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + + if (sc->txDaemonTid == 0) { + /* + * Set up hardware + */ + mcf5282_fec_initialize_hardware(sc); + + /* + * Start driver tasks + */ + sc->txDaemonTid = rtems_bsdnet_newproc("FECtx", 4096, fec_txDaemon, sc); + sc->rxDaemonTid = rtems_bsdnet_newproc("FECrx", 4096, fec_rxDaemon, sc); + } + + /* + * Set flags appropriately + */ + if (ifp->if_flags & IFF_PROMISC) + MCF5282_FEC_RCR |= MCF5282_FEC_RCR_PROM; + else + MCF5282_FEC_RCR &= ~MCF5282_FEC_RCR_PROM; + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; + + /* + * Enable receiver and transmitter + */ + MCF5282_FEC_ECR = MCF5282_FEC_ECR_ETHER_EN; +} + + +static void +fec_stop(struct mcf5282_enet_struct *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + + ifp->if_flags &= ~IFF_RUNNING; + + /* + * Shut down receiver and transmitter + */ + MCF5282_FEC_ECR = 0x0; +} + +/* + * Show interface statistics + */ +static void +enet_stats(struct mcf5282_enet_struct *sc) +{ + printf(" Rx Interrupts:%-10lu", sc->rxInterrupts); + printf("Rx Packet Count:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_PACKETS); + printf(" Rx Broadcast:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_R_BC_PKT); + printf(" Rx Multicast:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_MC_PKT); + printf("CRC/Align error:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_CRC_ALIGN); + printf(" Rx Undersize:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_R_UNDERSIZE); + printf(" Rx Oversize:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_OVERSIZE); + printf(" Rx Fragment:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_FRAG); + printf(" Rx Jabber:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_R_JAB); + printf(" Rx 64:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_P64); + printf(" Rx 65-127:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_P65T0127); + printf(" Rx 128-255:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_R_P128TO255); + printf(" Rx 256-511:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_P256TO511); + printf(" Rx 511-1023:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_P512TO1023); + printf(" Rx 1024-2047:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_R_P1024TO2047); + printf(" Rx >=2048:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_GTE2048); + printf(" Rx Octets:%-10lu", (uint32_t) MCF5282_FEC_RMON_R_OCTETS); + printf(" Rx Dropped:%-10lu\n", (uint32_t) MCF5282_FEC_IEEE_R_DROP); + printf(" Rx frame OK:%-10lu", (uint32_t) MCF5282_FEC_IEEE_R_FRAME_OK); + printf(" Rx CRC error:%-10lu", (uint32_t) MCF5282_FEC_IEEE_R_CRC); + printf(" Rx Align error:%-10lu\n", (uint32_t) MCF5282_FEC_IEEE_R_ALIGN); + printf(" FIFO Overflow:%-10lu", (uint32_t) MCF5282_FEC_IEEE_R_MACERR); + printf("Rx Pause Frames:%-10lu", (uint32_t) MCF5282_FEC_IEEE_R_FDXFC); + printf(" Rx Octets OK:%-10lu\n", (uint32_t) MCF5282_FEC_IEEE_R_OCTETS_OK); + printf(" Tx Interrupts:%-10lu", sc->txInterrupts); + printf("Tx Output Waits:%-10lu", sc->txRawWait); + printf("Tx mbuf realign:%-10lu\n", sc->txRealign); + printf("Tx realign drop:%-10lu", sc->txRealignDrop); + printf(" Tx Unaccounted:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_DROP); + printf("Tx Packet Count:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_T_PACKETS); + printf(" Tx Broadcast:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_BC_PKT); + printf(" Tx Multicast:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_MC_PKT); + printf("CRC/Align error:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_T_CRC_ALIGN); + printf(" Tx Undersize:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_UNDERSIZE); + printf(" Tx Oversize:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_OVERSIZE); + printf(" Tx Fragment:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_T_FRAG); + printf(" Tx Jabber:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_JAB); + printf(" Tx Collisions:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_COL); + printf(" Tx 64:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_T_P64); + printf(" Tx 65-127:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_P65TO127); + printf(" Tx 128-255:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_P128TO255); + printf(" Tx 256-511:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_T_P256TO511); + printf(" Tx 511-1023:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_P512TO1023); + printf(" Tx 1024-2047:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_P1024TO2047); + printf(" Tx >=2048:%-10lu\n", (uint32_t) MCF5282_FEC_RMON_T_P_GTE2048); + printf(" Tx Octets:%-10lu", (uint32_t) MCF5282_FEC_RMON_T_OCTETS); + printf(" Tx Dropped:%-10lu", (uint32_t) MCF5282_FEC_IEEE_T_DROP); + printf(" Tx Frame OK:%-10lu\n", (uint32_t) MCF5282_FEC_IEEE_T_FRAME_OK); + printf(" Tx 1 Collision:%-10lu", (uint32_t) MCF5282_FEC_IEEE_T_1COL); + printf("Tx >1 Collision:%-10lu", (uint32_t) MCF5282_FEC_IEEE_T_MCOL); + printf(" Tx Deferred:%-10lu\n", (uint32_t) MCF5282_FEC_IEEE_T_DEF); + printf(" Late Collision:%-10lu", (uint32_t) MCF5282_FEC_IEEE_T_LCOL); + printf(" Excessive Coll:%-10lu", (uint32_t) MCF5282_FEC_IEEE_T_EXCOL); + printf(" FIFO Underrun:%-10lu\n", (uint32_t) MCF5282_FEC_IEEE_T_MACERR); + printf(" Carrier Error:%-10lu", (uint32_t) MCF5282_FEC_IEEE_T_CSERR); + printf(" Tx SQE Error:%-10lu", (uint32_t) MCF5282_FEC_IEEE_T_SQE); + printf("Tx Pause Frames:%-10lu\n", (uint32_t) MCF5282_FEC_IEEE_T_FDXFC); + printf(" Tx Octets OK:%-10lu", (uint32_t) MCF5282_FEC_IEEE_T_OCTETS_OK); + printf(" MII interrupts:%-10lu\n", sc->miiInterrupts); + if ((sc->mii_sr2 & 0x400) == 0) { + printf("LINK DOWN!\n"); + } + else { + int speed; + int full; + int fixed; + if (sc->mii_cr & 0x1000) { + fixed = 0; + speed = sc->mii_sr2 & 0x4000 ? 100 : 10; + full = sc->mii_sr2 & 0x200 ? 1 : 0; + } + else { + fixed = 1; + speed = sc->mii_cr & 0x2000 ? 100 : 10; + full = sc->mii_cr & 0x100 ? 1 : 0; + } + printf("Link %s %d Mb/s, %s-duplex.\n", + fixed ? "fixed" : "auto-negotiate", + speed, + full ? "full" : "half"); + } + printf(" EIR:%8.8lx ", (uint32_t) MCF5282_FEC_EIR); + printf("EIMR:%8.8lx ", (uint32_t) MCF5282_FEC_EIMR); + printf("RDAR:%8.8lx ", (uint32_t) MCF5282_FEC_RDAR); + printf("TDAR:%8.8lx\n", (uint32_t) MCF5282_FEC_TDAR); + printf(" ECR:%8.8lx ", (uint32_t) MCF5282_FEC_ECR); + printf(" RCR:%8.8lx ", (uint32_t) MCF5282_FEC_RCR); + printf(" TCR:%8.8lx\n", (uint32_t) MCF5282_FEC_TCR); + printf("FRBR:%8.8lx ", (uint32_t) MCF5282_FEC_FRBR); + printf("FRSR:%8.8lx\n", (uint32_t) MCF5282_FEC_FRSR); + if (sc->txBdActiveCount != 0) { + int i, n; + /* + * Yes, there are races here with adding and retiring descriptors, + * but this diagnostic is more for when things have backed up. + */ + printf("Transmit Buffer Descriptors (Tail %d, Head %d, Unretired %d):\n", + sc->txBdTail, + sc->txBdHead, + sc->txBdActiveCount); + i = sc->txBdTail; + for (n = 0 ; n < sc->txBdCount ; n++) { + if ((sc->txBdBase[i].status & MCF5282_FEC_TxBD_R) != 0) + printf(" %3d: status:%4.4x length:%-4d buffer:%p\n", + i, + sc->txBdBase[i].status, + sc->txBdBase[i].length, + sc->txBdBase[i].buffer); + if (++i == sc->txBdCount) + i = 0; + } + } +} + +static int +fec_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data) +{ + struct mcf5282_enet_struct *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: + fec_stop(sc); + break; + + case IFF_UP: + fec_init(sc); + break; + + case IFF_UP | IFF_RUNNING: + fec_stop(sc); + fec_init(sc); + break; + + default: + break; + } + break; + + case SIO_RTEMS_SHOW_STATS: + enet_stats(sc); + break; + + /* + * FIXME: All sorts of multicast commands need to be added here! + */ + default: + error = EINVAL; + break; + } + return error; +} + +int +rtems_fec_driver_attach(struct rtems_bsdnet_ifconfig *config, int attaching ) +{ + struct mcf5282_enet_struct *sc; + struct ifnet *ifp; + int mtu; + int unitNumber; + char *unitName; + const unsigned char *hwaddr; + const char *env; + + /* + * Parse driver name + */ + if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0) + return 0; + + /* + * Is driver free? + */ + if ((unitNumber <= 0) || (unitNumber > NIFACES)) { + printf("Bad FEC unit number.\n"); + return 0; + } + sc = &enet_driver[unitNumber - 1]; + ifp = &sc->arpcom.ac_if; + if (ifp->if_softc != NULL) { + printf("Driver already in use.\n"); + return 0; + } + + /* + * Process options + */ + if (config->hardware_address) { + hwaddr = config->hardware_address; + } else if ((hwaddr = bsp_gethwaddr(unitNumber - 1)) == NULL) { + /* Locally-administered address */ + static const unsigned char defaultAddress[ETHER_ADDR_LEN] = { + 0x06, 'R', 'T', 'E', 'M', 'S'}; + printf ("WARNING -- No %s%d Ethernet address specified " + "-- Using default address.\n", unitName, unitNumber); + hwaddr = defaultAddress; + } + printf("%s%d: Ethernet address: %02x:%02x:%02x:%02x:%02x:%02x\n", + unitName, unitNumber, + hwaddr[0], hwaddr[1], hwaddr[2], + hwaddr[3], hwaddr[4], hwaddr[5]); + memcpy(sc->arpcom.ac_enaddr, hwaddr, ETHER_ADDR_LEN); + + if (config->mtu) + mtu = config->mtu; + else + mtu = ETHERMTU; + if (config->rbuf_count) + sc->rxBdCount = config->rbuf_count; + else + sc->rxBdCount = RX_BUF_COUNT; + if (config->xbuf_count) + sc->txBdCount = config->xbuf_count; + else + sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF; + + 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 = fec_init; + ifp->if_ioctl = fec_ioctl; + ifp->if_start = mcf5282_enet_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; + + /* + * Check for environment overrides + */ + if (((env = bsp_getbenv("IPADDR0_100FULL")) != NULL) + && ((*env == 'y') || (*env == 'Y'))) + sc->link = link_100Full; + else if (((env = bsp_getbenv("IPADDR0_10HALF")) != NULL) + && ((*env == 'y') || (*env == 'Y'))) + sc->link = link_10Half; + else + sc->link = link_auto; + + /* + * Attach the interface + */ + if_attach(ifp); + ether_ifattach(ifp); + return 1; +}; |