summaryrefslogtreecommitdiffstats
path: root/bsps/arm
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-04-23 09:53:31 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2018-04-23 15:18:44 +0200
commit031df3914990db0336a0d386fb53558b05de467e (patch)
tree4661e22f0cdb3f9d06879f0194b77c75f62bac79 /bsps/arm
parentbsps: Move interrupt controller support to bsps (diff)
downloadrtems-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/arm')
-rw-r--r--bsps/arm/atsam/net/if_atsam.c1259
-rw-r--r--bsps/arm/csb336/net/lan91c11x.c260
-rw-r--r--bsps/arm/csb336/net/lan91c11x.h229
-rw-r--r--bsps/arm/csb336/net/network.c708
-rw-r--r--bsps/arm/csb337/net/network.c864
-rw-r--r--bsps/arm/edb7312/net/network.c126
-rw-r--r--bsps/arm/gumstix/net/rtl8019.c1196
-rw-r--r--bsps/arm/gumstix/net/wd80x3.h303
-rw-r--r--bsps/arm/rtl22xx/net/network.c126
-rw-r--r--bsps/arm/shared/net/lpc-ethernet.c1839
10 files changed, 6910 insertions, 0 deletions
diff --git a/bsps/arm/atsam/net/if_atsam.c b/bsps/arm/atsam/net/if_atsam.c
new file mode 100644
index 0000000000..7e7e0e6faf
--- /dev/null
+++ b/bsps/arm/atsam/net/if_atsam.c
@@ -0,0 +1,1259 @@
+/*
+ * Copyright (c) 2016 embedded brains GmbH. All rights reserved.
+ *
+ * embedded brains GmbH
+ * Dornierstr. 4
+ * 82178 Puchheim
+ * Germany
+ * <info@embedded-brains.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <libchip/chip.h>
+#include <libchip/include/gmacd.h>
+#include <libchip/include/pio.h>
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ 1
+#define __BSD_VISIBLE 1
+
+#include <bsp.h>
+#include <bsp/irq.h>
+
+#include <stdio.h>
+
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+#include <rtems/rtems_mii_ioctl.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_types.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <dev/mii/mii.h>
+
+/*
+ * Number of interfaces supported by the driver
+ */
+#define NIFACES 1
+
+/** Enable/Disable CopyAllFrame */
+#define GMAC_CAF_DISABLE 0
+#define GMAC_CAF_ENABLE 1
+
+/** Enable/Disable NoBroadCast */
+#define GMAC_NBC_DISABLE 0
+#define GMAC_NBC_ENABLE 1
+
+/** The PIN list of PIO for GMAC */
+#define BOARD_GMAC_PINS \
+ { (PIO_PD0A_GTXCK | PIO_PD1A_GTXEN | PIO_PD2A_GTX0 | PIO_PD3A_GTX1 \
+ | PIO_PD4A_GRXDV | PIO_PD5A_GRX0 | PIO_PD6A_GRX1 \
+ | PIO_PD7A_GRXER \
+ | PIO_PD8A_GMDC | PIO_PD9A_GMDIO), PIOD, ID_PIOD, PIO_PERIPH_A, \
+ PIO_DEFAULT }
+/** The runtime pin configure list for GMAC */
+#define BOARD_GMAC_RUN_PINS BOARD_GMAC_PINS
+
+/** The PIN list of PIO for GMAC */
+#define BOARD_GMAC_RESET_PIN \
+ { PIO_PC10, PIOC, ID_PIOC, \
+ PIO_OUTPUT_1, \
+ PIO_PULLUP }
+
+/** Multicast Enable */
+#define GMAC_MC_ENABLE (1u << 6)
+#define HASH_INDEX_AMOUNT 6
+#define HASH_ELEMENTS_PER_INDEX 8
+#define MAC_ADDR_MASK 0x0000FFFFFFFFFFFF
+#define MAC_IDX_MASK (1u << 0)
+
+/** Promiscuous Mode Enable */
+#define GMAC_PROM_ENABLE (1u << 4)
+
+/** RX Defines */
+#define GMAC_RX_BUFFER_SIZE 1536
+#define GMAC_RX_BUF_DESC_ADDR_MASK 0xFFFFFFFC
+#define GMAC_RX_SET_OFFSET (1u << 15)
+#define GMAC_RX_SET_USED_WRAP ((1u << 1) | (1u << 0))
+#define GMAC_RX_SET_WRAP (1u << 1)
+#define GMAC_RX_SET_USED (1u << 0)
+/** TX Defines */
+#define GMAC_TX_SET_EOF (1u << 15)
+#define GMAC_TX_SET_WRAP (1u << 30)
+#define GMAC_TX_SET_USED (1u << 31)
+
+#define GMAC_DESCRIPTOR_ALIGNMENT 8
+
+/** Events */
+#define ATSAMV7_ETH_RX_EVENT_INTERRUPT RTEMS_EVENT_1
+#define ATSAMV7_ETH_TX_EVENT_INTERRUPT RTEMS_EVENT_2
+#define ATSAMV7_ETH_START_TRANSMIT_EVENT RTEMS_EVENT_3
+
+#define ATSAMV7_ETH_RX_DATA_OFFSET 2
+
+#define WATCHDOG_TIMEOUT 5
+
+/** The PINs for GMAC */
+static const Pin gmacPins[] = { BOARD_GMAC_RUN_PINS };
+
+static const Pin gmacResetPin = BOARD_GMAC_RESET_PIN;
+
+typedef struct if_atsam_gmac {
+ /** The GMAC driver instance */
+ sGmacd gGmacd;
+ uint32_t retries;
+ uint8_t phy_address;
+} if_atsam_gmac;
+
+typedef struct ring_buffer {
+ unsigned tx_bd_used;
+ unsigned tx_bd_free;
+ size_t length;
+} ring_buffer;
+
+/*
+ * Per-device data
+ */
+typedef struct if_atsam_softc {
+ /*
+ * Data
+ */
+ struct arpcom arpcom;
+ if_atsam_gmac Gmac_inst;
+ struct rtems_mdio_info mdio;
+ uint8_t GMacAddress[6];
+ rtems_id rx_daemon_tid;
+ rtems_id tx_daemon_tid;
+ rtems_vector_number interrupt_number;
+ struct mbuf **rx_mbuf;
+ struct mbuf **tx_mbuf;
+ volatile sGmacTxDescriptor *tx_bd_base;
+ uint32_t anlpar;
+ size_t rx_bd_fill_idx;
+ size_t amount_rx_buf;
+ size_t amount_tx_buf;
+ ring_buffer tx_ring;
+
+ /*
+ * Statistics
+ */
+ unsigned rx_overrun_errors;
+ unsigned rx_interrupts;
+ unsigned tx_complete_int;
+ unsigned tx_tur_errors;
+ unsigned tx_rlex_errors;
+ unsigned tx_tfc_errors;
+ unsigned tx_hresp_errors;
+ unsigned tx_interrupts;
+} if_atsam_softc;
+
+static struct if_atsam_softc if_atsam_softc_inst;
+
+static struct mbuf *if_atsam_new_mbuf(struct ifnet *ifp)
+{
+ struct mbuf *m;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m != NULL) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) != 0) {
+ m->m_pkthdr.rcvif = ifp;
+ m->m_data = mtod(m, char *);
+ rtems_cache_invalidate_multiple_data_lines(mtod(m, void *),
+ GMAC_RX_BUFFER_SIZE);
+ } else {
+ m_free(m);
+ m = NULL;
+ }
+ }
+ return (m);
+}
+
+
+static uint8_t if_atsam_wait_phy(Gmac *pHw, uint32_t retry)
+{
+ volatile uint32_t retry_count = 0;
+
+ while (!GMAC_IsIdle(pHw)) {
+ if (retry == 0) {
+ continue;
+ }
+ retry_count++;
+
+ if (retry_count >= retry) {
+ return (1);
+ }
+ rtems_task_wake_after(1);
+ }
+
+ return (0);
+}
+
+
+static uint8_t
+if_atsam_write_phy(Gmac *pHw, uint8_t PhyAddress, uint8_t Address,
+ uint32_t Value, uint32_t retry)
+{
+ GMAC_PHYMaintain(pHw, PhyAddress, Address, 0, (uint16_t)Value);
+ if (if_atsam_wait_phy(pHw, retry) == 1) {
+ return (1);
+ }
+ return (0);
+}
+
+
+static uint8_t
+if_atsam_read_phy(Gmac *pHw,
+ uint8_t PhyAddress, uint8_t Address, uint32_t *pvalue, uint32_t retry)
+{
+ GMAC_PHYMaintain(pHw, PhyAddress, Address, 1, 0);
+ if (if_atsam_wait_phy(pHw, retry) == 1) {
+ return (1);
+ }
+ *pvalue = GMAC_PHYData(pHw);
+ return (0);
+}
+
+
+static void atsamv7_find_valid_phy(if_atsam_gmac *gmac_inst)
+{
+ Gmac *pHw = gmac_inst->gGmacd.pHw;
+ uint32_t value = 0;
+ uint8_t phy_address;
+ int i;
+
+ if (gmac_inst->phy_address != 0xFF) {
+ return;
+ }
+
+ /* Find another one */
+ phy_address = 0xFF;
+
+ for (i = 31; i >= 0; --i) {
+ int rv;
+
+ rv = if_atsam_read_phy(pHw, (uint8_t)i, MII_PHYIDR1,
+ &value, gmac_inst->retries);
+ if (rv == 0 && value != 0 && value < 0xffff) {
+ phy_address = (uint8_t)i;
+ break;
+ }
+ }
+
+ if (phy_address != 0xFF) {
+ if_atsam_read_phy(pHw, phy_address, MII_PHYIDR1, &value,
+ gmac_inst->retries);
+ if_atsam_read_phy(pHw, phy_address, MII_PHYIDR2, &value,
+ gmac_inst->retries);
+ gmac_inst->phy_address = phy_address;
+ }
+}
+
+
+static uint8_t if_atsam_reset_phy(if_atsam_gmac *gmac_inst)
+{
+ uint32_t retry_max;
+ uint32_t bmcr;
+ uint8_t phy_address;
+ uint32_t timeout = 10;
+ uint8_t ret = 0;
+
+ Gmac *pHw = gmac_inst->gGmacd.pHw;
+
+ phy_address = gmac_inst->phy_address;
+ retry_max = gmac_inst->retries;
+
+ bmcr = BMCR_RESET;
+ if_atsam_write_phy(pHw, phy_address, MII_BMCR, bmcr, retry_max);
+ do {
+ if_atsam_read_phy(pHw, phy_address, MII_BMCR, &bmcr,
+ retry_max);
+ timeout--;
+ } while ((bmcr & BMCR_RESET) && timeout);
+
+ if (!timeout) {
+ ret = 1;
+ }
+ return (ret);
+}
+
+
+static uint8_t
+if_atsam_init_phy(if_atsam_gmac *gmac_inst, uint32_t mck,
+ const Pin *pResetPins, uint32_t nbResetPins, const Pin *pGmacPins,
+ uint32_t nbGmacPins)
+{
+ uint8_t rc = 1;
+ Gmac *pHw = gmac_inst->gGmacd.pHw;
+
+ /* Perform RESET */
+ if (pResetPins) {
+ /* Configure PINS */
+ PIO_Configure(pResetPins, nbResetPins);
+ PIO_Clear(pResetPins);
+ rtems_task_wake_after(1);
+ PIO_Set(pResetPins);
+ }
+ /* Configure GMAC runtime pins */
+ if (rc) {
+ PIO_Configure(pGmacPins, nbGmacPins);
+ rc = GMAC_SetMdcClock(pHw, mck);
+
+ if (!rc) {
+ return (0);
+ }
+ if_atsam_reset_phy(gmac_inst);
+ }
+ return (rc);
+}
+
+static bool if_atsam_is_valid_phy(int phy)
+{
+ return phy >= 0 && phy <= 31;
+}
+
+static int if_atsam_mdio_read(int phy, void *arg, unsigned reg, uint32_t *pval)
+{
+ if_atsam_softc *sc = (if_atsam_softc *)arg;
+
+ if (!if_atsam_is_valid_phy(phy)) {
+ return (EINVAL);
+ }
+
+ return (if_atsam_read_phy(sc->Gmac_inst.gGmacd.pHw,
+ (uint8_t)phy, (uint8_t)reg, pval, sc->Gmac_inst.retries));
+}
+
+
+static int if_atsam_mdio_write(int phy, void *arg, unsigned reg, uint32_t pval)
+{
+ if_atsam_softc *sc = (if_atsam_softc *)arg;
+
+ if (!if_atsam_is_valid_phy(phy)) {
+ return (EINVAL);
+ }
+
+ return if_atsam_write_phy(sc->Gmac_inst.gGmacd.pHw,
+ (uint8_t)phy, (uint8_t)reg, pval, sc->Gmac_inst.retries);
+}
+
+
+/*
+ * Interrupt Handler for the network driver
+ */
+static void if_atsam_interrupt_handler(void *arg)
+{
+ if_atsam_softc *sc = (if_atsam_softc *)arg;
+ uint32_t irq_status_val;
+ rtems_event_set rx_event = 0;
+ rtems_event_set tx_event = 0;
+ Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
+
+ /* Get interrupt status */
+ irq_status_val = GMAC_GetItStatus(pHw, 0);
+
+ /* Check receive interrupts */
+ if ((irq_status_val & GMAC_IER_ROVR) != 0) {
+ ++sc->rx_overrun_errors;
+ rx_event = ATSAMV7_ETH_RX_EVENT_INTERRUPT;
+ }
+ if ((irq_status_val & GMAC_IER_RCOMP) != 0) {
+ rx_event = ATSAMV7_ETH_RX_EVENT_INTERRUPT;
+ }
+ /* Send events to receive task and switch off rx interrupts */
+ if (rx_event != 0) {
+ ++sc->rx_interrupts;
+ /* Erase the interrupts for RX completion and errors */
+ GMAC_DisableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0);
+ (void)rtems_bsdnet_event_send(sc->rx_daemon_tid, rx_event);
+ }
+ if ((irq_status_val & GMAC_IER_TUR) != 0) {
+ ++sc->tx_tur_errors;
+ tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT;
+ }
+ if ((irq_status_val & GMAC_IER_RLEX) != 0) {
+ ++sc->tx_rlex_errors;
+ tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT;
+ }
+ if ((irq_status_val & GMAC_IER_TFC) != 0) {
+ ++sc->tx_tfc_errors;
+ tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT;
+ }
+ if ((irq_status_val & GMAC_IER_HRESP) != 0) {
+ ++sc->tx_hresp_errors;
+ tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT;
+ }
+ if ((irq_status_val & GMAC_IER_TCOMP) != 0) {
+ ++sc->tx_complete_int;
+ tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT;
+ }
+ /* Send events to transmit task and switch off tx interrupts */
+ if (tx_event != 0) {
+ ++sc->tx_interrupts;
+ /* Erase the interrupts for TX completion and errors */
+ GMAC_DisableIt(pHw, GMAC_INT_TX_BITS, 0);
+ (void)rtems_bsdnet_event_send(sc->tx_daemon_tid, tx_event);
+ }
+}
+/*
+ * Receive daemon
+ */
+static void if_atsam_rx_daemon(void *arg)
+{
+ if_atsam_softc *sc = (if_atsam_softc *)arg;
+ rtems_event_set events = 0;
+ void *rx_bd_base;
+ struct mbuf *m;
+ struct mbuf *n;
+ volatile sGmacRxDescriptor *buffer_desc;
+ int frame_len;
+ struct ether_header *eh;
+ uint32_t tmp_rx_bd_address;
+
+ Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
+
+ /* Allocate memory space for priority queue descriptor list */
+ rx_bd_base = rtems_cache_coherent_allocate(sizeof(sGmacRxDescriptor),
+ GMAC_DESCRIPTOR_ALIGNMENT, 0);
+ assert(rx_bd_base != NULL);
+
+ buffer_desc = (sGmacRxDescriptor *)rx_bd_base;
+ buffer_desc->addr.val = GMAC_RX_SET_USED_WRAP;
+ buffer_desc->status.val = 0;
+
+ GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 1);
+ GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 2);
+
+ /* Allocate memory space for buffer descriptor list */
+ rx_bd_base = rtems_cache_coherent_allocate(
+ sc->amount_rx_buf * sizeof(sGmacRxDescriptor),
+ GMAC_DESCRIPTOR_ALIGNMENT, 0);
+ assert(rx_bd_base != NULL);
+ buffer_desc = (sGmacRxDescriptor *)rx_bd_base;
+
+ /* Create descriptor list and mark as empty */
+ for (sc->rx_bd_fill_idx = 0; sc->rx_bd_fill_idx < sc->amount_rx_buf;
+ ++sc->rx_bd_fill_idx) {
+ m = if_atsam_new_mbuf(&sc->arpcom.ac_if);
+ assert(m != NULL);
+ sc->rx_mbuf[sc->rx_bd_fill_idx] = m;
+ buffer_desc->addr.val = ((uint32_t)m->m_data) &
+ GMAC_RX_BUF_DESC_ADDR_MASK;
+ buffer_desc->status.val = 0;
+ if (sc->rx_bd_fill_idx == (sc->amount_rx_buf - 1)) {
+ buffer_desc->addr.bm.bWrap = 1;
+ } else {
+ buffer_desc++;
+ }
+ }
+ buffer_desc = (sGmacRxDescriptor *)rx_bd_base;
+
+ /* Set 2 Byte Receive Buffer Offset */
+ pHw->GMAC_NCFGR |= GMAC_RX_SET_OFFSET;
+
+ /* Write Buffer Queue Base Address Register */
+ GMAC_ReceiveEnable(pHw, 0);
+ GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 0);
+
+ /* Set address for address matching */
+ GMAC_SetAddress(pHw, 0, sc->GMacAddress);
+
+ /* Enable Receiving of data */
+ GMAC_ReceiveEnable(pHw, 1);
+
+ /* Setup the interrupts for RX completion and errors */
+ GMAC_EnableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0);
+
+ sc->rx_bd_fill_idx = 0;
+
+ while (true) {
+ /* Wait for events */
+ rtems_bsdnet_event_receive(ATSAMV7_ETH_RX_EVENT_INTERRUPT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT, &events);
+
+ /*
+ * Check for all packets with a set ownership bit
+ */
+ while (buffer_desc->addr.bm.bOwnership == 1) {
+ if (buffer_desc->status.bm.bEof == 1) {
+ m = sc->rx_mbuf[sc->rx_bd_fill_idx];
+
+ /* New mbuf for desc */
+ n = if_atsam_new_mbuf(&sc->arpcom.ac_if);
+ if (n != NULL) {
+ frame_len = (int)
+ (buffer_desc->status.bm.len);
+
+ /* Discard Ethernet header */
+ int sz = frame_len - ETHER_HDR_LEN;
+
+ /* Update mbuf */
+ eh = (struct ether_header *)
+ (mtod(m, char *) + 2);
+ m->m_len = sz;
+ m->m_pkthdr.len = sz;
+ m->m_data = (void *)(eh + 1);
+ ether_input(&sc->arpcom.ac_if, eh, m);
+ m = n;
+ } else {
+ (void)rtems_bsdnet_event_send(
+ sc->tx_daemon_tid, ATSAMV7_ETH_START_TRANSMIT_EVENT);
+ }
+ sc->rx_mbuf[sc->rx_bd_fill_idx] = m;
+ tmp_rx_bd_address = (uint32_t)m->m_data &
+ GMAC_RX_BUF_DESC_ADDR_MASK;
+
+ /* Switch pointer to next buffer descriptor */
+ if (sc->rx_bd_fill_idx ==
+ (sc->amount_rx_buf - 1)) {
+ tmp_rx_bd_address |= GMAC_RX_SET_WRAP;
+ sc->rx_bd_fill_idx = 0;
+ } else {
+ ++sc->rx_bd_fill_idx;
+ }
+
+ /*
+ * Give ownership to GMAC for further processing
+ */
+ tmp_rx_bd_address &= ~GMAC_RX_SET_USED;
+ _ARM_Data_synchronization_barrier();
+ buffer_desc->addr.val = tmp_rx_bd_address;
+
+ buffer_desc = (sGmacRxDescriptor *)rx_bd_base
+ + sc->rx_bd_fill_idx;
+ }
+ }
+ /* Setup the interrupts for RX completion and errors */
+ GMAC_EnableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0);
+ }
+}
+
+/*
+ * Update of current transmit buffer position.
+ */
+static void if_atsam_tx_bd_pos_update(size_t *pos, size_t amount_tx_buf)
+{
+ *pos = (*pos + 1) % amount_tx_buf;
+}
+
+/*
+ * Is RingBuffer empty
+ */
+static bool if_atsam_ring_buffer_empty(ring_buffer *ring_buffer)
+{
+ return (ring_buffer->tx_bd_used == ring_buffer->tx_bd_free);
+}
+
+/*
+ * Is RingBuffer full
+ */
+static bool if_atsam_ring_buffer_full(ring_buffer *ring_buffer)
+{
+ size_t tx_bd_used_next = ring_buffer->tx_bd_used;
+
+ if_atsam_tx_bd_pos_update(&tx_bd_used_next, ring_buffer->length);
+ return (tx_bd_used_next == ring_buffer->tx_bd_free);
+}
+
+/*
+ * Cleanup transmit file descriptors by freeing mbufs which are not needed any
+ * longer due to correct transmission.
+ */
+static void if_atsam_tx_bd_cleanup(if_atsam_softc *sc)
+{
+ struct mbuf *m;
+ volatile sGmacTxDescriptor *cur;
+ bool eof_needed = false;
+
+ while (!if_atsam_ring_buffer_empty(&sc->tx_ring)){
+ cur = sc->tx_bd_base + sc->tx_ring.tx_bd_free;
+ if (((cur->status.bm.bUsed == 1) && !eof_needed) || eof_needed) {
+ eof_needed = true;
+ cur->status.val |= GMAC_TX_SET_USED;
+ m = sc->tx_mbuf[sc->tx_ring.tx_bd_free];
+ m_free(m);
+ sc->tx_mbuf[sc->tx_ring.tx_bd_free] = 0;
+ if_atsam_tx_bd_pos_update(&sc->tx_ring.tx_bd_free,
+ sc->tx_ring.length);
+ if (cur->status.bm.bLastBuffer) {
+ eof_needed = false;
+ }
+ } else {
+ break;
+ }
+ }
+}
+
+/*
+ * Prepare Ethernet frame to start transmission.
+ */
+static bool if_atsam_send_packet(if_atsam_softc *sc, struct mbuf *m)
+{
+ volatile sGmacTxDescriptor *cur;
+ volatile sGmacTxDescriptor *start_packet_tx_bd = 0;
+ int pos = 0;
+ uint32_t tmp_val = 0;
+ Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
+ bool success;
+
+ if_atsam_tx_bd_cleanup(sc);
+ /* Wait for interrupt in case no buffer descriptors are available */
+ /* Wait for events */
+ while (true) {
+ if (if_atsam_ring_buffer_full(&sc->tx_ring)) {
+ /* Setup the interrupts for TX completion and errors */
+ GMAC_EnableIt(pHw, GMAC_INT_TX_BITS, 0);
+ success = false;
+ break;
+ }
+
+ /*
+ * Get current mbuf for data fill
+ */
+ cur = &sc->tx_bd_base[sc->tx_ring.tx_bd_used];
+ /* Set the transfer data */
+ if (m->m_len) {
+ uintptr_t cache_adjustment = mtod(m, uintptr_t) % 32;
+
+ rtems_cache_flush_multiple_data_lines(
+ mtod(m, const char *) - cache_adjustment,
+ (size_t)(m->m_len + cache_adjustment));
+
+ cur->addr = mtod(m, uint32_t);
+ tmp_val = (uint32_t)m->m_len | GMAC_TX_SET_USED;
+ if (sc->tx_ring.tx_bd_used == (sc->tx_ring.length - 1)) {
+ tmp_val |= GMAC_TX_SET_WRAP;
+ }
+ if (pos == 0) {
+ start_packet_tx_bd = cur;
+ }
+ sc->tx_mbuf[sc->tx_ring.tx_bd_used] = m;
+ m = m->m_next;
+ if_atsam_tx_bd_pos_update(&sc->tx_ring.tx_bd_used,
+ sc->tx_ring.length);
+ } else {
+ /* Discard empty mbufs */
+ m = m_free(m);
+ }
+
+ /*
+ * Send out the buffer once the complete mbuf_chain has been
+ * processed
+ */
+ if (m == NULL) {
+ tmp_val |= GMAC_TX_SET_EOF;
+ tmp_val &= ~GMAC_TX_SET_USED;
+ _ARM_Data_synchronization_barrier();
+ cur->status.val = tmp_val;
+ start_packet_tx_bd->status.val &= ~GMAC_TX_SET_USED;
+ _ARM_Data_synchronization_barrier();
+ GMAC_TransmissionStart(pHw);
+ success = true;
+ break;
+ } else {
+ if (pos > 0) {
+ tmp_val &= ~GMAC_TX_SET_USED;
+ }
+ pos++;
+ cur->status.val = tmp_val;
+ }
+ }
+ return success;
+}
+
+
+/*
+ * Transmit daemon
+ */
+static void if_atsam_tx_daemon(void *arg)
+{
+ if_atsam_softc *sc = (if_atsam_softc *)arg;
+ rtems_event_set events = 0;
+ sGmacTxDescriptor *buffer_desc;
+ int bd_number;
+ void *tx_bd_base;
+ struct mbuf *m;
+ bool success;
+
+ Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ GMAC_TransmitEnable(pHw, 0);
+
+ /* Allocate memory space for priority queue descriptor list */
+ tx_bd_base = rtems_cache_coherent_allocate(sizeof(sGmacTxDescriptor),
+ GMAC_DESCRIPTOR_ALIGNMENT, 0);
+ assert(tx_bd_base != NULL);
+
+ buffer_desc = (sGmacTxDescriptor *)tx_bd_base;
+ buffer_desc->addr = 0;
+ buffer_desc->status.val = GMAC_TX_SET_USED | GMAC_TX_SET_WRAP;
+
+ GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 1);
+ GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 2);
+
+ /* Allocate memory space for buffer descriptor list */
+ tx_bd_base = rtems_cache_coherent_allocate(
+ sc->amount_tx_buf * sizeof(sGmacTxDescriptor),
+ GMAC_DESCRIPTOR_ALIGNMENT, 0);
+ assert(tx_bd_base != NULL);
+ buffer_desc = (sGmacTxDescriptor *)tx_bd_base;
+
+ /* Create descriptor list and mark as empty */
+ for (bd_number = 0; bd_number < sc->amount_tx_buf; bd_number++) {
+ buffer_desc->addr = 0;
+ buffer_desc->status.val = GMAC_TX_SET_USED;
+ if (bd_number == (sc->amount_tx_buf - 1)) {
+ buffer_desc->status.bm.bWrap = 1;
+ } else {
+ buffer_desc++;
+ }
+ }
+ buffer_desc = (sGmacTxDescriptor *)tx_bd_base;
+
+ /* Write Buffer Queue Base Address Register */
+ GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 0);
+
+ /* Enable Transmission of data */
+ GMAC_TransmitEnable(pHw, 1);
+
+ /* Set variables in context */
+ sc->tx_bd_base = tx_bd_base;
+
+ while (true) {
+ /* Wait for events */
+ rtems_bsdnet_event_receive(ATSAMV7_ETH_START_TRANSMIT_EVENT | ATSAMV7_ETH_TX_EVENT_INTERRUPT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT, &events);
+ //printf("TX Transmit Event received\n");
+
+ /*
+ * Send packets till queue is empty
+ */
+ while (true) {
+ /*
+ * Get the mbuf chain to transmit
+ */
+ if_atsam_tx_bd_cleanup(sc);
+ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m);
+ if (!m) {
+ ifp->if_flags &= ~IFF_OACTIVE;
+ break;
+ }
+ success = if_atsam_send_packet(sc, m);
+ if (!success){
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Send packet (caller provides header).
+ */
+static void if_atsam_enet_start(struct ifnet *ifp)
+{
+ if_atsam_softc *sc = (if_atsam_softc *)ifp->if_softc;
+
+ ifp->if_flags |= IFF_OACTIVE;
+ rtems_bsdnet_event_send(sc->tx_daemon_tid,
+ ATSAMV7_ETH_START_TRANSMIT_EVENT);
+}
+
+
+/*
+ * Attach a watchdog for autonegotiation to the system
+ */
+static void if_atsam_interface_watchdog(struct ifnet *ifp)
+{
+ uint32_t anlpar;
+ uint8_t speed = GMAC_SPEED_100M;
+ uint8_t full_duplex = GMAC_DUPLEX_FULL;
+
+ if_atsam_softc *sc = (if_atsam_softc *)ifp->if_softc;
+ Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
+ uint8_t phy = sc->Gmac_inst.phy_address;
+ uint32_t retries = sc->Gmac_inst.retries;
+
+ if (if_atsam_read_phy(pHw, phy, MII_ANLPAR, &anlpar, retries)) {
+ anlpar = 0;
+ }
+ if (sc->anlpar != anlpar) {
+ /* Set up the GMAC link speed */
+ if (anlpar & ANLPAR_TX_FD) {
+ /* Set MII for 100BaseTx and Full Duplex */
+ speed = GMAC_SPEED_100M;
+ full_duplex = GMAC_DUPLEX_FULL;
+ } else if (anlpar & ANLPAR_10_FD) {
+ /* Set MII for 10BaseTx and Full Duplex */
+ speed = GMAC_SPEED_10M;
+ full_duplex = GMAC_DUPLEX_FULL;
+ } else if (anlpar & ANLPAR_TX) {
+ /* Set MII for 100BaseTx and half Duplex */
+ speed = GMAC_SPEED_100M;
+ full_duplex = GMAC_DUPLEX_HALF;
+ } else if (anlpar & ANLPAR_10) {
+ /* Set MII for 10BaseTx and half Duplex */
+ speed = GMAC_SPEED_10M;
+ full_duplex = GMAC_DUPLEX_HALF;
+ } else {
+ /* Set MII for 100BaseTx and Full Duplex */
+ speed = GMAC_SPEED_100M;
+ full_duplex = GMAC_DUPLEX_FULL;
+ }
+ GMAC_SetLinkSpeed(pHw, speed, full_duplex);
+ sc->anlpar = anlpar;
+ }
+ ifp->if_timer = WATCHDOG_TIMEOUT;
+}
+
+
+/*
+ * Sets up the hardware and chooses the interface to be used
+ */
+static void if_atsam_init(void *arg)
+{
+ rtems_status_code status;
+
+ if_atsam_softc *sc = (if_atsam_softc *)arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ uint32_t dmac_cfg = 0;
+ uint32_t gmii_val = 0;
+
+ if (sc->arpcom.ac_if.if_flags & IFF_RUNNING) {
+ return;
+ }
+ sc->arpcom.ac_if.if_flags |= IFF_RUNNING;
+ sc->interrupt_number = GMAC_IRQn;
+
+ /* Disable Watchdog */
+ WDT_Disable(WDT);
+
+ /* Enable Peripheral Clock */
+ if ((PMC->PMC_PCSR1 & (1u << 7)) != (1u << 7)) {
+ PMC->PMC_PCER1 = 1 << 7;
+ }
+ /* Setup interrupts */
+ NVIC_ClearPendingIRQ(GMAC_IRQn);
+ NVIC_EnableIRQ(GMAC_IRQn);
+
+ GMACD_Init(&sc->Gmac_inst.gGmacd, GMAC, ID_GMAC, GMAC_CAF_ENABLE,
+ GMAC_NBC_DISABLE);
+
+ /* Enable MDIO interface */
+ GMAC_EnableMdio(sc->Gmac_inst.gGmacd.pHw);
+
+ /* PHY initialize */
+ if_atsam_init_phy(&sc->Gmac_inst, BOARD_MCK, &gmacResetPin, 1,
+ gmacPins, PIO_LISTSIZE(gmacPins));
+ /* Find valid Phy */
+ atsamv7_find_valid_phy(&sc->Gmac_inst);
+
+ /* Set Link Speed */
+ sc->anlpar = 0xFFFFFFFF;
+ if_atsam_interface_watchdog(ifp);
+
+ /* Enable autonegotation */
+ if_atsam_read_phy(sc->Gmac_inst.gGmacd.pHw, sc->Gmac_inst.phy_address,
+ MII_BMCR, &gmii_val, sc->Gmac_inst.retries);
+ if_atsam_write_phy(sc->Gmac_inst.gGmacd.pHw, sc->Gmac_inst.phy_address,
+ MII_BMCR, (gmii_val | BMCR_AUTOEN), sc->Gmac_inst.retries);
+
+ /* Configuration of DMAC */
+ dmac_cfg = (GMAC_DCFGR_DRBS(GMAC_RX_BUFFER_SIZE >> 6)) |
+ GMAC_DCFGR_RXBMS(3) | GMAC_DCFGR_TXPBMS | GMAC_DCFGR_FBLDO_INCR16;
+ GMAC_SetDMAConfig(sc->Gmac_inst.gGmacd.pHw, dmac_cfg, 0);
+
+ /* Shut down Transmit and Receive */
+ GMAC_ReceiveEnable(sc->Gmac_inst.gGmacd.pHw, 0);
+ GMAC_TransmitEnable(sc->Gmac_inst.gGmacd.pHw, 0);
+
+ GMAC_StatisticsWriteEnable(sc->Gmac_inst.gGmacd.pHw, 1);
+
+ /*
+ * Allocate mbuf pointers
+ */
+ sc->rx_mbuf = malloc(sc->amount_rx_buf * sizeof *sc->rx_mbuf,
+ M_MBUF, M_NOWAIT);
+ sc->tx_mbuf = malloc(sc->amount_tx_buf * sizeof *sc->tx_mbuf,
+ M_MBUF, M_NOWAIT);
+
+ /* Install interrupt handler */
+ status = rtems_interrupt_handler_install(sc->interrupt_number,
+ "Ethernet",
+ RTEMS_INTERRUPT_UNIQUE,
+ if_atsam_interrupt_handler,
+ sc);
+ assert(status == RTEMS_SUCCESSFUL);
+
+ /*
+ * Start driver tasks
+ */
+ sc->rx_daemon_tid = rtems_bsdnet_newproc("SCrx", 4096,
+ if_atsam_rx_daemon, sc);
+ sc->tx_daemon_tid = rtems_bsdnet_newproc("SCtx", 4096,
+ if_atsam_tx_daemon, sc);
+
+ /* Start Watchdog Timer */
+ ifp->if_timer = 1;
+}
+
+
+/*
+ * Stop the device
+ */
+static void if_atsam_stop(struct if_atsam_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /* Disable MDIO interface and TX/RX */
+ pHw->GMAC_NCR &= ~(GMAC_NCR_RXEN | GMAC_NCR_TXEN);
+ pHw->GMAC_NCR &= ~GMAC_NCR_MPE;
+}
+
+
+/*
+ * Show interface statistics
+ */
+static void if_atsam_stats(struct if_atsam_softc *sc)
+{
+ int eno = EIO;
+ int media = 0;
+ Gmac *pHw;
+
+ media = (int)IFM_MAKEWORD(0, 0, 0, sc->Gmac_inst.phy_address);
+ eno = rtems_mii_ioctl(&sc->mdio, sc, SIOCGIFMEDIA, &media);
+
+ rtems_bsdnet_semaphore_release();
+
+ if (eno == 0) {
+ rtems_ifmedia2str(media, NULL, 0);
+ printf("\n");
+ }
+ pHw = sc->Gmac_inst.gGmacd.pHw;
+
+ printf("\n** Context Statistics **\n");
+ printf("Rx interrupts: %u\n", sc->rx_interrupts);
+ printf("Tx interrupts: %u\n", sc->tx_interrupts);
+ printf("Error Tur Tx interrupts: %u\n\n", sc->tx_tur_errors);
+ printf("Error Rlex Tx interrupts: %u\n\n", sc->tx_rlex_errors);
+ printf("Error Tfc Tx interrupts: %u\n\n", sc->tx_tfc_errors);
+ printf("Error Hresp Tx interrupts: %u\n\n", sc->tx_hresp_errors);
+ printf("Tx complete interrupts: %u\n\n", sc->tx_complete_int);
+ printf("\n** Statistics **\n");
+ printf("Octets Transmitted Low: %lu\n", pHw->GMAC_OTLO);
+ printf("Octets Transmitted High: %lu\n", pHw->GMAC_OTHI);
+ printf("Frames Transmitted: %lu\n", pHw->GMAC_FT);
+ printf("Broadcast Frames Transmitted: %lu\n", pHw->GMAC_BCFT);
+ printf("Multicast Frames Transmitted: %lu\n", pHw->GMAC_MFT);
+ printf("Pause Frames Transmitted: %lu\n", pHw->GMAC_PFT);
+ printf("64 Byte Frames Transmitted: %lu\n", pHw->GMAC_BFT64);
+ printf("65 to 127 Byte Frames Transmitted: %lu\n", pHw->GMAC_TBFT127);
+ printf("128 to 255 Byte Frames Transmitted: %lu\n", pHw->GMAC_TBFR255);
+ printf("256 to 511 Byte Frames Transmitted: %lu\n", pHw->GMAC_TBFT511);
+ printf("512 to 1023 Byte Frames Transmitted: %lu\n",
+ pHw->GMAC_TBFT1023);
+ printf("1024 to 1518 Byte Frames Transmitted: %lu\n",
+ pHw->GMAC_TBFT1518);
+ printf("Greater Than 1518 Byte Frames Transmitted: %lu\n",
+ pHw->GMAC_GTBFT1518);
+ printf("Transmit Underruns: %lu\n", pHw->GMAC_TUR);
+ printf("Single Collision Frames: %lu\n", pHw->GMAC_SCF);
+ printf("Multiple Collision Frames: %lu\n", pHw->GMAC_MCF);
+ printf("Excessive Collisions: %lu\n", pHw->GMAC_EC);
+ printf("Late Collisions: %lu\n", pHw->GMAC_LC);
+ printf("Deferred Transmission Frames: %lu\n", pHw->GMAC_DTF);
+ printf("Carrier Sense Errors: %lu\n", pHw->GMAC_CSE);
+ printf("Octets Received Low: %lu\n", pHw->GMAC_ORLO);
+ printf("Octets Received High: %lu\n", pHw->GMAC_ORHI);
+ printf("Frames Received: %lu\n", pHw->GMAC_FR);
+ printf("Broadcast Frames Received: %lu\n", pHw->GMAC_BCFR);
+ printf("Multicast Frames Received: %lu\n", pHw->GMAC_MFR);
+ printf("Pause Frames Received: %lu\n", pHw->GMAC_PFR);
+ printf("64 Byte Frames Received: %lu\n", pHw->GMAC_BFR64);
+ printf("65 to 127 Byte Frames Received: %lu\n", pHw->GMAC_TBFR127);
+ printf("128 to 255 Byte Frames Received: %lu\n", pHw->GMAC_TBFR255);
+ printf("256 to 511 Byte Frames Received: %lu\n", pHw->GMAC_TBFR511);
+ printf("512 to 1023 Byte Frames Received: %lu\n", pHw->GMAC_TBFR1023);
+ printf("1024 to 1518 Byte Frames Received: %lu\n", pHw->GMAC_TBFR1518);
+ printf("1519 to Maximum Byte Frames Received: %lu\n",
+ pHw->GMAC_TBFR1518);
+ printf("Undersize Frames Received: %lu\n", pHw->GMAC_UFR);
+ printf("Oversize Frames Received: %lu\n", pHw->GMAC_OFR);
+ printf("Jabbers Received: %lu\n", pHw->GMAC_JR);
+ printf("Frame Check Sequence Errors: %lu\n", pHw->GMAC_FCSE);
+ printf("Length Field Frame Errors: %lu\n", pHw->GMAC_LFFE);
+ printf("Receive Symbol Errors: %lu\n", pHw->GMAC_RSE);
+ printf("Alignment Errors: %lu\n", pHw->GMAC_AE);
+ printf("Receive Resource Errors: %lu\n", pHw->GMAC_RRE);
+ printf("Receive Overrun: %lu\n", pHw->GMAC_ROE);
+ printf("IP Header Checksum Errors: %lu\n", pHw->GMAC_IHCE);
+ printf("TCP Checksum Errors: %lu\n", pHw->GMAC_TCE);
+ printf("UDP Checksum Errors: %lu\n", pHw->GMAC_UCE);
+
+ rtems_bsdnet_semaphore_obtain();
+}
+
+
+/*
+ * Calculates the index that is to be sent into the hash registers
+ */
+static void if_atsam_get_hash_index(uint64_t addr, uint32_t *val)
+{
+ uint64_t tmp_val;
+ uint8_t i, j;
+ uint64_t idx;
+ int offset = 0;
+
+ addr &= MAC_ADDR_MASK;
+
+ for (i = 0; i < HASH_INDEX_AMOUNT; ++i) {
+ tmp_val = 0;
+ offset = 0;
+ for (j = 0; j < HASH_ELEMENTS_PER_INDEX; j++) {
+ idx = (addr >> (offset + i)) & MAC_IDX_MASK;
+ tmp_val ^= idx;
+ offset += HASH_INDEX_AMOUNT;
+ }
+ if (tmp_val > 0) {
+ *val |= (1u << i);
+ }
+ }
+}
+
+
+/*
+ * Dis/Enable promiscuous Mode
+ */
+static void if_atsam_promiscuous_mode(if_atsam_softc *sc, bool enable)
+{
+ Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
+
+ if (enable) {
+ pHw->GMAC_NCFGR |= GMAC_PROM_ENABLE;
+ } else {
+ pHw->GMAC_NCFGR &= ~GMAC_PROM_ENABLE;
+ }
+}
+
+
+/*
+ * Multicast handler
+ */
+static int
+if_atsam_multicast_control(bool add, struct ifreq *ifr, if_atsam_softc *sc)
+{
+ int eno = 0;
+ struct arpcom *ac = &sc->arpcom;
+ Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
+
+ /* Switch off Multicast Hashing */
+ pHw->GMAC_NCFGR &= ~GMAC_MC_ENABLE;
+
+ if (add) {
+ eno = ether_addmulti(ifr, ac);
+ } else {
+ eno = ether_delmulti(ifr, ac);
+ }
+
+ if (eno == ENETRESET) {
+ struct ether_multistep step;
+ struct ether_multi *enm;
+
+ eno = 0;
+
+ pHw->GMAC_HRB = 0;
+ pHw->GMAC_HRT = 0;
+
+ ETHER_FIRST_MULTI(step, ac, enm);
+ while (enm != NULL) {
+ uint64_t addrlo = 0;
+ uint64_t addrhi = 0;
+ uint32_t val = 0;
+
+ memcpy(&addrlo, enm->enm_addrlo, ETHER_ADDR_LEN);
+ memcpy(&addrhi, enm->enm_addrhi, ETHER_ADDR_LEN);
+ while (addrlo <= addrhi) {
+ if_atsam_get_hash_index(addrlo, &val);
+ if (val < 32) {
+ pHw->GMAC_HRB |= (1u << val);
+ } else {
+ pHw->GMAC_HRT |= (1u << (val - 32));
+ }
+ ++addrlo;
+ }
+ ETHER_NEXT_MULTI(step, enm);
+ }
+ }
+ /* Switch on Multicast Hashing */
+ pHw->GMAC_NCFGR |= GMAC_MC_ENABLE;
+ return (eno);
+}
+
+
+/*
+ * Driver ioctl handler
+ */
+static int
+if_atsam_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct if_atsam_softc *sc = (if_atsam_softc *)ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ int rv = 0;
+ bool prom_enable;
+
+ switch (command) {
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ rtems_mii_ioctl(&sc->mdio, sc, command, &ifr->ifr_media);
+ break;
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl(ifp, command, data);
+ break;
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_flags & IFF_RUNNING) {
+ /* Don't do anything */
+ } else {
+ if_atsam_init(sc);
+ }
+ prom_enable = ((ifp->if_flags & IFF_PROMISC) != 0);
+ if_atsam_promiscuous_mode(sc, prom_enable);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING) {
+ if_atsam_stop(sc);
+ }
+ }
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if_atsam_multicast_control(command == SIOCADDMULTI, ifr, sc);
+ break;
+ case SIO_RTEMS_SHOW_STATS:
+ if_atsam_stats(sc);
+ break;
+ default:
+ rv = EINVAL;
+ break;
+ }
+ return (rv);
+}
+
+
+/*
+ * Attach an SAMV71 driver to the system
+ */
+static int if_atsam_driver_attach(struct rtems_bsdnet_ifconfig *config)
+{
+ if_atsam_softc *sc = &if_atsam_softc_inst;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ const if_atsam_config *conf = config->drv_ctrl;
+ int unitNumber;
+ char *unitName;
+
+ if (conf != NULL) {
+ sc->Gmac_inst.retries = conf->mdio_retries;
+ sc->Gmac_inst.phy_address = conf->phy_addr;
+ } else {
+ sc->Gmac_inst.retries = 10;
+ sc->Gmac_inst.phy_address = 0xFF;
+ }
+
+ /* The MAC address used */
+ memcpy(sc->GMacAddress, config->hardware_address, ETHER_ADDR_LEN);
+ memcpy(sc->arpcom.ac_enaddr, sc->GMacAddress, ETHER_ADDR_LEN);
+
+ /*
+ * Parse driver name
+ */
+ unitNumber = rtems_bsdnet_parse_driver_name(config, &unitName);
+ assert(unitNumber == 0);
+
+ assert(ifp->if_softc == NULL);
+
+ /* MDIO */
+ sc->mdio.mdio_r = if_atsam_mdio_read;
+ sc->mdio.mdio_w = if_atsam_mdio_write;
+ sc->mdio.has_gmii = 1;
+
+ if (config->rbuf_count > 0) {
+ sc->amount_rx_buf = config->rbuf_count;
+ } else {
+ sc->amount_rx_buf = 8;
+ }
+
+ if (config->xbuf_count > 0) {
+ sc->amount_tx_buf = config->xbuf_count;
+ } else {
+ sc->amount_tx_buf = 64;
+ }
+
+ sc->tx_ring.tx_bd_used = 0;
+ sc->tx_ring.tx_bd_free = 0;
+ sc->tx_ring.length = sc->amount_tx_buf;
+
+ /*
+ * Set up network interface values
+ */
+ ifp->if_softc = sc;
+ ifp->if_unit = (short int)unitNumber;
+ ifp->if_name = unitName;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_init = if_atsam_init;
+ ifp->if_ioctl = if_atsam_ioctl;
+ ifp->if_start = if_atsam_enet_start;
+ ifp->if_output = ether_output;
+ ifp->if_watchdog = if_atsam_interface_watchdog;
+ ifp->if_flags = IFF_MULTICAST | IFF_BROADCAST | IFF_SIMPLEX;
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+ ifp->if_timer = 0;
+
+ /*
+ * Attach the interface
+ */
+ if_attach(ifp);
+ ether_ifattach(ifp);
+ return (1);
+}
+
+
+int if_atsam_attach(struct rtems_bsdnet_ifconfig *config, int attaching)
+{
+ (void)attaching;
+ return (if_atsam_driver_attach(config));
+}
diff --git a/bsps/arm/csb336/net/lan91c11x.c b/bsps/arm/csb336/net/lan91c11x.c
new file mode 100644
index 0000000000..37594776d1
--- /dev/null
+++ b/bsps/arm/csb336/net/lan91c11x.c
@@ -0,0 +1,260 @@
+/**
+ * @file
+ *
+ * @ingroup arm_csb336
+ *
+ * @brief Helper functions for SMSC LAN91C11x
+ */
+
+/*
+ * Helper functions for SMSC LAN91C11x
+ *
+ * Copyright (c) 2004 by Cogent Computer Systems
+ * Written by Jay Monkman <jtm@lopingdog.com>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <rtems.h>
+#include "lan91c11x.h"
+
+uint16_t lan91c11x_read_reg(int reg)
+{
+ volatile uint16_t *ptr = (uint16_t *)LAN91C11X_BASE_ADDR;
+ uint16_t old_bank;
+ uint16_t val;
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable(level);
+
+ /* save the bank register */
+ old_bank = ptr[7] & 0x7;
+
+ /* set the bank register */
+ ptr[7] = (reg >> 4) & 0x7;
+
+ val = ptr[((reg & 0xf) >> 1)];
+
+ /* restore the bank register */
+ ptr[7] = old_bank;
+
+ rtems_interrupt_enable(level);
+ return val;
+}
+
+void lan91c11x_write_reg(int reg, uint16_t value)
+{
+ volatile uint16_t *ptr = (uint16_t *)LAN91C11X_BASE_ADDR;
+ uint16_t old_bank;
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable(level);
+
+ /* save the bank register */
+ old_bank = ptr[7] & 0x7;
+
+ /* set the bank register */
+ ptr[7] = (reg >> 4) & 0x7;
+
+ ptr[((reg & 0xf) >> 1)] = value;
+
+ /* restore the bank register */
+ ptr[7] = old_bank;
+
+ rtems_interrupt_enable(level);
+}
+
+uint16_t lan91c11x_read_reg_fast(int reg)
+{
+ volatile uint16_t *ptr = (uint16_t *)LAN91C11X_BASE_ADDR;
+ uint16_t val;
+
+ val = ptr[((reg & 0xf) >> 1)];
+
+ return val;
+}
+
+void lan91c11x_write_reg_fast(int reg, uint16_t value)
+{
+ volatile uint16_t *ptr = (uint16_t *)LAN91C11X_BASE_ADDR;
+
+ ptr[((reg & 0xf) >> 1)] = value;
+}
+
+
+uint16_t lan91c11x_read_phy_reg(int reg)
+{
+ int i;
+ uint16_t mask;
+ uint16_t bits[64];
+ int clk_idx = 0;
+ int input_idx = 0;
+ uint16_t phydata;
+
+ /* 32 consecutive ones on MDO to establish sync */
+ for (i = 0; i < 32; ++i) {
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO;
+ }
+
+ /* Start code <01> */
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE;
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO;
+
+ /* Read command <10> */
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO;
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE;
+
+ /* Output the PHY address, msb first - Internal PHY is address 0 */
+ for (i = 0; i < 5; ++i) {
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE;
+ }
+
+ /* Output the phy register number, msb first */
+ mask = 0x10;
+ for (i = 0; i < 5; ++i) {
+ if (reg & mask) {
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO;
+ } else {
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE;
+ }
+
+
+ /* Shift to next lowest bit */
+ mask >>= 1;
+ }
+
+ /* 1 bit time for turnaround */
+ bits[clk_idx++] = 0;
+
+ /* Input starts at this bit time */
+ input_idx = clk_idx;
+
+ /* Will input 16 bits */
+ for (i = 0; i < 16; ++i) {
+ bits[clk_idx++] = 0;
+ }
+
+ /* Final clock bit */
+ bits[clk_idx++] = 0;
+
+ /* Turn off all MII Interface bits */
+ lan91c11x_write_reg(LAN91C11X_MGMT,
+ lan91c11x_read_reg(LAN91C11X_MGMT) & 0xfff0);
+
+ /* Clock all 64 cycles */
+ for (i = 0; i < sizeof bits; ++i) {
+ /* Clock Low - output data */
+ lan91c11x_write_reg(LAN91C11X_MGMT, bits[i]);
+ rtems_task_wake_after(1);
+
+ /* Clock Hi - input data */
+ lan91c11x_write_reg(LAN91C11X_MGMT, bits[i] | LAN91C11X_MGMT_MCLK);
+ rtems_task_wake_after(1);
+ bits[i] |= lan91c11x_read_reg(LAN91C11X_MGMT) & LAN91C11X_MGMT_MDI;
+ }
+
+ /* Return to idle state */
+ /* Set clock to low, data to low, and output tristated */
+ lan91c11x_write_reg(LAN91C11X_MGMT, lan91c11x_read_reg(LAN91C11X_MGMT) & 0xfff0);
+ rtems_task_wake_after(1);
+
+ /* Recover input data */
+ phydata = 0;
+ for (i = 0; i < 16; ++i) {
+ phydata <<= 1;
+
+ if (bits[input_idx++] & LAN91C11X_MGMT_MDI) {
+ phydata |= 0x0001;
+ }
+ }
+
+ return phydata;
+}
+
+
+
+void lan91c11x_write_phy_reg(int reg, uint16_t phydata)
+{
+ int i;
+ ushort mask;
+ ushort bits[64];
+ int clk_idx = 0;
+
+ /* 32 consecutive ones on MDO to establish sync */
+ for (i = 0; i < 32; ++i) {
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO;
+ }
+
+ /* Start code <01> */
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE;
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO;
+
+ /* Write command <01> */
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE;
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO;
+
+ /* Output the PHY address, msb first - Internal PHY is address 0 */
+ for (i = 0; i < 5; ++i) {
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE;
+ }
+
+ /* Output the phy register number, msb first */
+ mask = 0x10;
+ for (i = 0; i < 5; ++i) {
+ if (reg & mask) {
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO;
+ } else {
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE;
+ }
+
+ /* Shift to next lowest bit */
+ mask >>= 1;
+ }
+
+ /* 2 extra bit times for turnaround */
+ bits[clk_idx++] = 0;
+ bits[clk_idx++] = 0;
+
+ /* Write out 16 bits of data, msb first */
+ mask = 0x8000;
+ for (i = 0; i < 16; ++i) {
+ if (phydata & mask) {
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO;
+ } else {
+ bits[clk_idx++] = LAN91C11X_MGMT_MDOE;
+ }
+
+ /* Shift to next lowest bit */
+ mask >>= 1;
+ }
+
+ /* Turn off all MII Interface bits */
+ lan91c11x_write_reg(LAN91C11X_MGMT,
+ lan91c11x_read_reg(LAN91C11X_MGMT) & 0xfff0);
+
+ /* Clock all 64 cycles */
+ for (i = 0; i < sizeof bits; ++i) {
+ /* Clock Low - output data */
+ lan91c11x_write_reg(LAN91C11X_MGMT, bits[i]);
+ rtems_task_wake_after(1);
+
+ /* Clock Hi - input data */
+ lan91c11x_write_reg(LAN91C11X_MGMT, bits[i] | LAN91C11X_MGMT_MCLK);
+ rtems_task_wake_after(1);
+ bits[i] |= lan91c11x_read_reg(LAN91C11X_MGMT) & LAN91C11X_MGMT_MDI;
+ }
+
+ /* Return to idle state */
+ /* Set clock to low, data to low, and output tristated */
+ lan91c11x_write_reg(LAN91C11X_MGMT,
+ lan91c11x_read_reg(LAN91C11X_MGMT) & 0xfff0);
+ rtems_task_wake_after(1);
+
+}
+
+
+
diff --git a/bsps/arm/csb336/net/lan91c11x.h b/bsps/arm/csb336/net/lan91c11x.h
new file mode 100644
index 0000000000..8172874e0c
--- /dev/null
+++ b/bsps/arm/csb336/net/lan91c11x.h
@@ -0,0 +1,229 @@
+/**
+ * @file
+ *
+ * @ingroup arm_csb336
+ *
+ * @brief SMSC LAN91C11x ethernet devices definitions.
+ */
+
+/**
+ * @defgroup arm_csb336_lan91c11x SMSC LAN91C11x
+ *
+ * @ingroup arm_csb336
+ *
+ * @brief SMSC LAN91C11x ethernet devices definitions.
+ */
+
+/*
+ * Header file for SMSC LAN91C11x ethernet devices
+ *
+ * Copyright (c) 2004 by Cogent Computer Systems
+ * Written by Jay Monkman <jtm@lopingdog.com>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+#ifndef __LAN91C11X_H__
+#define __LAN91C11X_H__
+
+#include <rtems.h>
+#include <bsp.h>
+
+uint16_t lan91c11x_read_reg(int);
+void lan91c11x_write_reg(int, uint16_t);
+uint16_t lan91c11x_read_reg_fast(int);
+void lan91c11x_write_reg_fast(int, uint16_t);
+void lan91c11x_write_phy_reg(int , uint16_t);
+uint16_t lan91c11x_read_phy_reg(int);
+void lan91c11x_unlock(void);
+void lan91c11x_lock(void);
+
+#define LAN91C11X_BASE_ADDR 0x12000000
+
+#define LAN91C11X_REG(_b_, _r_) ((((_b_) & 0xf) << 4) | ((_r_) & 0xf))
+
+
+#define LAN91C11X_TCR (LAN91C11X_REG(0, 0x0))
+#define LAN91C11X_EPHSTAT (LAN91C11X_REG(0, 0x2))
+#define LAN91C11X_RCR (LAN91C11X_REG(0, 0x4))
+#define LAN91C11X_CNTR (LAN91C11X_REG(0, 0x6))
+#define LAN91C11X_MIR (LAN91C11X_REG(0, 0x8))
+#define LAN91C11X_RPCR (LAN91C11X_REG(0, 0xa))
+#define LAN91C11X_BANK (LAN91C11X_REG(0, 0xe))
+#define LAN91C11X_CONFIG (LAN91C11X_REG(1, 0x0))
+#define LAN91C11X_BASE (LAN91C11X_REG(1, 0x2))
+#define LAN91C11X_IA0 (LAN91C11X_REG(1, 0x4))
+#define LAN91C11X_IA2 (LAN91C11X_REG(1, 0x6))
+#define LAN91C11X_IA4 (LAN91C11X_REG(1, 0x8))
+#define LAN91C11X_GNRL (LAN91C11X_REG(1, 0xa))
+#define LAN91C11X_CTRL (LAN91C11X_REG(1, 0xc))
+#define LAN91C11X_MMUCMD (LAN91C11X_REG(2, 0x0))
+#define LAN91C11X_PNR (LAN91C11X_REG(2, 0x2))
+#define LAN91C11X_FIFO (LAN91C11X_REG(2, 0x4))
+#define LAN91C11X_PTR (LAN91C11X_REG(2, 0x6))
+#define LAN91C11X_DATA (LAN91C11X_REG(2, 0x8))
+#define LAN91C11X_INT (LAN91C11X_REG(2, 0xc))
+#define LAN91C11X_MT0 (LAN91C11X_REG(3, 0x0))
+#define LAN91C11X_MT2 (LAN91C11X_REG(3, 0x2))
+#define LAN91C11X_MT4 (LAN91C11X_REG(3, 0x4))
+#define LAN91C11X_MT6 (LAN91C11X_REG(3, 0x6))
+#define LAN91C11X_MGMT (LAN91C11X_REG(3, 0x8))
+#define LAN91C11X_REV (LAN91C11X_REG(3, 0xa))
+#define LAN91C11X_ERCV (LAN91C11X_REG(3, 0xc))
+
+
+#define LAN91C11X_TCR_TXENA (bit(0))
+#define LAN91C11X_TCR_LOOP (bit(1))
+#define LAN91C11X_TCR_FORCOL (bit(2))
+#define LAN91C11X_TCR_PADEN (bit(7))
+#define LAN91C11X_TCR_NOCRC (bit(8))
+#define LAN91C11X_TCR_MONCSN (bit(10))
+#define LAN91C11X_TCR_FDUPLX (bit(11))
+#define LAN91C11X_TCR_STPSQET (bit(12))
+#define LAN91C11X_TCR_EPHLOOP (bit(13))
+#define LAN91C11X_TCR_SWFDUP (bit(15))
+
+#define LAN91C11X_EPHSTAT_TXSUC (bit(0))
+#define LAN91C11X_EPHSTAT_SNGLCOL (bit(1))
+#define LAN91C11X_EPHSTAT_MULCOL (bit(2))
+#define LAN91C11X_EPHSTAT_LTXMUL (bit(3))
+#define LAN91C11X_EPHSTAT_16COL (bit(4))
+#define LAN91C11X_EPHSTAT_SQET (bit(5))
+#define LAN91C11X_EPHSTAT_LTXBRD (bit(6))
+#define LAN91C11X_EPHSTAT_TXDFR (bit(7))
+#define LAN91C11X_EPHSTAT_LATCOL (bit(9))
+#define LAN91C11X_EPHSTAT_LOST (bit(10))
+#define LAN91C11X_EPHSTAT_EXCDEF (bit(11))
+#define LAN91C11X_EPHSTAT_CTRROL (bit(12))
+#define LAN91C11X_EPHSTAT_LINK (bit(14))
+#define LAN91C11X_EPHSTAT_TXUNRN (bit(15))
+
+#define LAN91C11X_RCR_RXABT (bit(0))
+#define LAN91C11X_RCR_PRMS (bit(1))
+#define LAN91C11X_RCR_ALMUL (bit(2))
+#define LAN91C11X_RCR_RXEN (bit(8))
+#define LAN91C11X_RCR_STRIP (bit(9))
+#define LAN91C11X_RCR_ABTENB (bit(13))
+#define LAN91C11X_RCR_FILT (bit(14))
+#define LAN91C11X_RCR_RST (bit(15))
+
+#define LAN91C11X_RPCR_LS0B (bit(2))
+#define LAN91C11X_RPCR_LS1B (bit(3))
+#define LAN91C11X_RPCR_LS2B (bit(4))
+#define LAN91C11X_RPCR_LS0A (bit(5))
+#define LAN91C11X_RPCR_LS1A (bit(6))
+#define LAN91C11X_RPCR_LS2A (bit(7))
+#define LAN91C11X_RPCR_ANEG (bit(11))
+#define LAN91C11X_RPCR_DPLX (bit(12))
+#define LAN91C11X_RPCR_SPEED (bit(13))
+
+#define LAN91C11X_CONFIG_EXTPHY (bit(9))
+#define LAN91C11X_CONFIG_GPCTRL (bit(10))
+#define LAN91C11X_CONFIG_NOWAIT (bit(12))
+#define LAN91C11X_CONFIG_PWR (bit(15))
+
+#define LAN91C11X_CTRL_STORE (bit(0))
+#define LAN91C11X_CTRL_RELOAD (bit(1))
+#define LAN91C11X_CTRL_EEPROM (bit(2))
+#define LAN91C11X_CTRL_TEEN (bit(5))
+#define LAN91C11X_CTRL_CREN (bit(6))
+#define LAN91C11X_CTRL_LEEN (bit(7))
+#define LAN91C11X_CTRL_AUTO (bit(11))
+#define LAN91C11X_CTRL_RCVBAD (bit(14))
+
+#define LAN91C11X_MMUCMD_BUSY (bit(0))
+#define LAN91C11X_MMUCMD_NOOP (0 << 5)
+#define LAN91C11X_MMUCMD_ALLOCTX (1 << 5)
+#define LAN91C11X_MMUCMD_RESETMMU (2 << 5)
+#define LAN91C11X_MMUCMD_REMFRM (3 << 5)
+#define LAN91C11X_MMUCMD_REMTOP (4 << 5)
+#define LAN91C11X_MMUCMD_RELEASE (5 << 5)
+#define LAN91C11X_MMUCMD_ENQUEUE (6 << 5)
+#define LAN91C11X_MMUCMD_RESETTX (7 << 5)
+
+#define LAN91C11X_PTR_MASK (0x7ff)
+#define LAN91C11X_PTR_NE (bit(11))
+#define LAN91C11X_PTR_ETEN (bit(12))
+#define LAN91C11X_PTR_READ (bit(13))
+#define LAN91C11X_PTR_AUTOINC (bit(14))
+#define LAN91C11X_PTR_RCV (bit(15))
+
+#define LAN91C11X_INT_RX (bit(0))
+#define LAN91C11X_INT_TX (bit(1))
+#define LAN91C11X_INT_TXE (bit(2))
+#define LAN91C11X_INT_ALLOC (bit(3))
+#define LAN91C11X_INT_RXOV (bit(4))
+#define LAN91C11X_INT_EPH (bit(5))
+#define LAN91C11X_INT_ERX (bit(6))
+#define LAN91C11X_INT_MD (bit(7))
+#define LAN91C11X_INT_RXMASK (bit(8))
+#define LAN91C11X_INT_TXMASK (bit(9))
+#define LAN91C11X_INT_TXEMASK (bit(10))
+#define LAN91C11X_INT_ALLOCMASK (bit(11))
+#define LAN91C11X_INT_RXOVMASK (bit(12))
+#define LAN91C11X_INT_EPHMASK (bit(13))
+#define LAN91C11X_INT_ERXMASK (bit(14))
+#define LAN91C11X_INT_MDMASK (bit(15))
+
+#define LAN91C11X_MGMT_MDO (bit(0))
+#define LAN91C11X_MGMT_MDI (bit(1))
+#define LAN91C11X_MGMT_MCLK (bit(2))
+#define LAN91C11X_MGMT_MDOE (bit(3))
+#define LAN91C11X_MGMT_MSKCRS100 (bit(14))
+
+
+#define LAN91C11X_PKT_CTRL_CRC (bit(4))
+#define LAN91C11X_PKT_CTRL_ODD (bit(5))
+
+
+/* PHY Registers */
+#define PHY_CTRL 0x00 /* PHY Control */
+#define PHY_STAT 0x01 /* PHY Status */
+#define PHY_ID1 0x02 /* PHY Identifier 1 */
+#define PHY_ID2 0x03 /* PHY Identifier 2 */
+#define PHY_AD 0x04 /* PHY Auto-negotiate Control */
+#define PHY_RMT 0x05 /* PHY Auto-neg Remote End Cap Register */
+#define PHY_CFG1 0x10 /* PHY Configuration 1 */
+#define PHY_CFG2 0x11 /* PHY Configuration 2 */
+#define PHY_INT 0x12 /* Status Output (Interrupt Status) */
+#define PHY_MASK 0x13 /* Interrupt Mask */
+
+/* PHY Control Register Bit Defines */
+#define PHY_CTRL_RST 0x8000 /* PHY Reset */
+#define PHY_CTRL_LPBK 0x4000 /* PHY Loopback */
+#define PHY_CTRL_SPEED 0x2000 /* 100Mbps, 0=10Mpbs */
+#define PHY_CTRL_ANEGEN 0x1000 /* Enable Auto negotiation */
+#define PHY_CTRL_PDN 0x0800 /* PHY Power Down mode */
+#define PHY_CTRL_MIIDIS 0x0400 /* MII 4 bit interface disabled */
+#define PHY_CTRL_ANEGRST 0x0200 /* Reset Auto negotiate */
+#define PHY_CTRL_DPLX 0x0100 /* Full Duplex, 0=Half Duplex */
+#define PHY_CTRL_COLTST 0x0080 /* MII Colision Test */
+
+#define PHY_STAT_CAPT4 0x8000
+#define PHY_STAT_CAPTXF 0x4000
+#define PHY_STAT_CAPTXH 0x2000
+#define PHY_STAT_CAPTF 0x1000
+#define PHY_STAT_CAPTH 0x0800
+#define PHY_STAT_CAPSUPR 0x0040
+#define PHY_STAT_ANEGACK 0x0020
+#define PHY_STAT_REMFLT 0x0010
+#define PHY_STAT_CAPANEG 0x0008
+#define PHY_STAT_LINK 0x0004
+#define PHY_STAT_JAB 0x0002
+#define PHY_STAT_EXREG 0x0001
+
+#define PHY_ADV_NP 0x8000
+#define PHY_ADV_ACK 0x4000
+#define PHY_ADV_RF 0x2000
+#define PHY_ADV_T4 0x0200
+#define PHY_ADV_TXFDX 0x0100
+#define PHY_ADV_TXHDX 0x0080
+#define PHY_ADV_10FDX 0x0040
+#define PHY_ADV_10HDX 0x0020
+#define PHY_ADV_CSMA 0x0001
+
+
+
+
+#endif /* __LAN91C11X_H__ */
diff --git a/bsps/arm/csb336/net/network.c b/bsps/arm/csb336/net/network.c
new file mode 100644
index 0000000000..be3d3f7b3d
--- /dev/null
+++ b/bsps/arm/csb336/net/network.c
@@ -0,0 +1,708 @@
+/*
+ * MC9323MXL Ethernet driver
+ *
+ * Copyright (c) 2004 by Cogent Computer Systems
+ * Written by Jay Monkman <jtm@lopingdog.com>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <rtems.h>
+#include <rtems/rtems_bsdnet.h>
+#include <mc9328mxl.h>
+#include "lan91c11x.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <errno.h>
+#include <rtems/error.h>
+#include <rtems/bspIo.h>
+#include <assert.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 <bsp/irq.h>
+
+/* RTEMS event used by interrupt handler to start receive daemon. */
+#define START_RECEIVE_EVENT RTEMS_EVENT_1
+
+/* RTEMS event used to start transmit daemon. */
+#define START_TRANSMIT_EVENT RTEMS_EVENT_2
+
+static void enet_isr(void *);
+static void enet_isr_on(void);
+
+typedef struct {
+ unsigned long rx_packets; /* total packets received */
+ unsigned long tx_packets; /* total packets transmitted */
+ unsigned long rx_bytes; /* total bytes received */
+ unsigned long tx_bytes; /* total bytes transmitted */
+ unsigned long interrupts; /* total number of interrupts */
+ unsigned long rx_interrupts; /* total number of rx interrupts */
+ unsigned long tx_interrupts; /* total number of tx interrupts */
+ unsigned long txerr_interrupts; /* total number of tx error interrupts */
+
+} eth_stats_t;
+
+/*
+ * Hardware-specific storage
+ */
+typedef struct
+{
+ /*
+ * Connection to networking code
+ * This entry *must* be the first in the sonic_softc structure.
+ */
+ struct arpcom arpcom;
+
+ int accept_bcast;
+
+ /* Tasks waiting for interrupts */
+ rtems_id rx_task;
+ rtems_id tx_task;
+
+ eth_stats_t stats;
+
+} mc9328mxl_enet_softc_t;
+
+static mc9328mxl_enet_softc_t softc;
+
+
+/* function prototypes */
+int rtems_mc9328mxl_enet_attach(struct rtems_bsdnet_ifconfig *config,
+ void *chip);
+void mc9328mxl_enet_init(void *arg);
+void mc9328mxl_enet_init_hw(mc9328mxl_enet_softc_t *sc);
+void mc9328mxl_enet_start(struct ifnet *ifp);
+void mc9328mxl_enet_stop (mc9328mxl_enet_softc_t *sc);
+void mc9328mxl_enet_tx_task (void *arg);
+void mc9328mxl_enet_sendpacket (struct ifnet *ifp, struct mbuf *m);
+void mc9328mxl_enet_rx_task(void *arg);
+void mc9328mxl_enet_stats(mc9328mxl_enet_softc_t *sc);
+static int mc9328mxl_enet_ioctl(struct ifnet *ifp,
+ ioctl_command_t command, caddr_t data);
+
+
+int rtems_mc9328mxl_enet_attach (
+ struct rtems_bsdnet_ifconfig *config,
+ void *chip /* only one ethernet, so no chip number */
+ )
+{
+ struct ifnet *ifp;
+ int mtu;
+ int unitnumber;
+ char *unitname;
+ int tmp;
+
+ /*
+ * Parse driver name
+ */
+ unitnumber = rtems_bsdnet_parse_driver_name(config, &unitname);
+ if (unitnumber < 0) {
+ return 0;
+ }
+
+ /*
+ * Is driver free?
+ */
+ if (unitnumber != 0) {
+ printf ("Bad MC9328MXL unit number.\n");
+ return 0;
+ }
+
+ ifp = &softc.arpcom.ac_if;
+ if (ifp->if_softc != NULL) {
+ printf ("Driver already in use.\n");
+ return 0;
+ }
+
+ /* zero out the control structure */
+ memset( &softc, 0, sizeof(softc) );
+
+
+ /* set the MAC address */
+ tmp = lan91c11x_read_reg(LAN91C11X_IA0);
+ softc.arpcom.ac_enaddr[0] = tmp & 0xff;
+ softc.arpcom.ac_enaddr[1] = (tmp >> 8) & 0xff;
+
+ tmp = lan91c11x_read_reg(LAN91C11X_IA2);
+ softc.arpcom.ac_enaddr[2] = tmp & 0xff;
+ softc.arpcom.ac_enaddr[3] = (tmp >> 8) & 0xff;
+
+ tmp = lan91c11x_read_reg(LAN91C11X_IA4);
+ softc.arpcom.ac_enaddr[4] = tmp & 0xff;
+ softc.arpcom.ac_enaddr[5] = (tmp >> 8) & 0xff;
+
+ if (config->mtu) {
+ mtu = config->mtu;
+ } else {
+ mtu = ETHERMTU;
+ }
+
+ softc.accept_bcast = !config->ignore_broadcast;
+
+ /*
+ * Set up network interface values
+ */
+ ifp->if_softc = &softc;
+ ifp->if_unit = unitnumber;
+ ifp->if_name = unitname;
+ ifp->if_mtu = mtu;
+ ifp->if_init = mc9328mxl_enet_init;
+ ifp->if_ioctl = mc9328mxl_enet_ioctl;
+ ifp->if_start = mc9328mxl_enet_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST;
+ if (ifp->if_snd.ifq_maxlen == 0) {
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+ }
+
+ /* Attach the interface */
+ if_attach (ifp);
+ ether_ifattach (ifp);
+ return 1;
+}
+
+void mc9328mxl_enet_init(void *arg)
+{
+ mc9328mxl_enet_softc_t *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ /*
+ *This is for stuff that only gets done once (mc9328mxl_enet_init()
+ * gets called multiple times
+ */
+ if (sc->tx_task == 0)
+ {
+ /* Set up ENET hardware */
+ mc9328mxl_enet_init_hw(sc);
+
+ /* Start driver tasks */
+ sc->rx_task = rtems_bsdnet_newproc("ENrx",
+ 4096,
+ mc9328mxl_enet_rx_task,
+ sc);
+ sc->tx_task = rtems_bsdnet_newproc("ENtx",
+ 4096,
+ mc9328mxl_enet_tx_task,
+ sc);
+ } /* if tx_task */
+
+
+ /* Configure for promiscuous if needed */
+ if (ifp->if_flags & IFF_PROMISC) {
+ lan91c11x_write_reg(LAN91C11X_RCR,
+ (lan91c11x_read_reg(LAN91C11X_RCR) |
+ LAN91C11X_RCR_PRMS));
+ }
+
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+
+ /* Enable TX/RX */
+ lan91c11x_write_reg(LAN91C11X_TCR,
+ (lan91c11x_read_reg(LAN91C11X_TCR) |
+ LAN91C11X_TCR_TXENA));
+
+ lan91c11x_write_reg(LAN91C11X_RCR,
+ (lan91c11x_read_reg(LAN91C11X_RCR) |
+ LAN91C11X_RCR_RXEN));
+
+
+} /* mc9328mxl_enet_init() */
+
+void mc9328mxl_enet_init_hw(mc9328mxl_enet_softc_t *sc)
+{
+ uint16_t stat;
+ uint16_t my = 0;
+ rtems_status_code status = RTEMS_SUCCESSFUL;
+
+ lan91c11x_write_reg(LAN91C11X_RCR, LAN91C11X_RCR_RST);
+ lan91c11x_write_reg(LAN91C11X_RCR, 0);
+ rtems_task_wake_after(1);
+
+ /* Reset the PHY */
+ lan91c11x_write_phy_reg(PHY_CTRL, PHY_CTRL_RST);
+ while(lan91c11x_read_phy_reg(PHY_CTRL) & PHY_CTRL_RST) {
+ rtems_task_wake_after(1);
+ }
+
+
+ stat = lan91c11x_read_phy_reg(PHY_STAT);
+
+ if(stat & PHY_STAT_CAPT4) {
+ my |= PHY_ADV_T4;
+ }
+/* 100Mbs doesn't work, so we won't advertise it */
+
+ if(stat & PHY_STAT_CAPTXF) {
+ my |= PHY_ADV_TXFDX;
+ }
+ if(stat & PHY_STAT_CAPTXH) {
+ my |= PHY_ADV_TXHDX;
+ }
+
+ if(stat & PHY_STAT_CAPTF) {
+ my |= PHY_ADV_10FDX;
+ }
+
+ if(stat & PHY_STAT_CAPTH) {
+ my |= PHY_ADV_10HDX;
+ }
+
+ my |= PHY_ADV_CSMA;
+
+ lan91c11x_write_phy_reg(PHY_AD, my);
+
+
+ /* Enable Autonegotiation */
+#if 0
+ lan91c11x_write_phy_reg(PHY_CTRL,
+ (PHY_CTRL_ANEGEN | PHY_CTRL_ANEGRST));
+#endif
+
+ /* Enable full duplex, let MAC take care
+ * of padding and CRC.
+ */
+ lan91c11x_write_reg(LAN91C11X_TCR,
+ (LAN91C11X_TCR_PADEN |
+ LAN91C11X_TCR_SWFDUP));
+
+ /* Disable promisc, don'tstrip CRC */
+ lan91c11x_write_reg(LAN91C11X_RCR, 0);
+
+ /* Enable auto-negotiation, LEDA is link, LEDB is traffic */
+ lan91c11x_write_reg(LAN91C11X_RPCR,
+ (LAN91C11X_RPCR_ANEG |
+ LAN91C11X_RPCR_LS2B));
+
+ /* Don't add wait states, enable PHY power */
+ lan91c11x_write_reg(LAN91C11X_CONFIG,
+ (LAN91C11X_CONFIG_NOWAIT |
+ LAN91C11X_CONFIG_PWR));
+
+ /* Disable error interrupts, enable auto release */
+ lan91c11x_write_reg(LAN91C11X_CTRL, LAN91C11X_CTRL_AUTO);
+
+ /* Reset MMU */
+ lan91c11x_write_reg(LAN91C11X_MMUCMD,
+ LAN91C11X_MMUCMD_RESETMMU );
+
+
+ rtems_task_wake_after(100);
+ /* Enable Autonegotiation */
+ lan91c11x_write_phy_reg(PHY_CTRL, 0x3000);
+ rtems_task_wake_after(100);
+
+ /* Enable Interrupts for RX */
+ lan91c11x_write_reg(LAN91C11X_INT, LAN91C11X_INT_RXMASK);
+
+ /* Enable interrupts on GPIO Port A3 */
+ /* Make pin 3 an input */
+ MC9328MXL_GPIOA_DDIR &= ~bit(3);
+
+ /* Use GPIO function for pin 3 */
+ MC9328MXL_GPIOA_GIUS |= bit(3);
+
+ /* Set for active high, level triggered interupt */
+ MC9328MXL_GPIOA_ICR1 = ((MC9328MXL_GPIOA_ICR1 & ~(3 << 6)) |
+ (2 << 6));
+
+ /* Enable GPIO port A3 interrupt */
+ MC9328MXL_GPIOA_IMR |= bit(3);
+
+ /* Install the interrupt handler */
+ status = rtems_interrupt_handler_install(
+ BSP_INT_GPIO_PORTA,
+ "Network",
+ RTEMS_INTERRUPT_UNIQUE,
+ enet_isr,
+ (void *)BSP_INT_GPIO_PORTA
+ );
+ assert(status == RTEMS_SUCCESSFUL);
+ enet_isr_on();
+
+} /* mc9328mxl_enet_init_hw() */
+
+void mc9328mxl_enet_start(struct ifnet *ifp)
+{
+ mc9328mxl_enet_softc_t *sc = ifp->if_softc;
+
+ rtems_bsdnet_event_send(sc->tx_task, START_TRANSMIT_EVENT);
+ ifp->if_flags |= IFF_OACTIVE;
+}
+
+void mc9328mxl_enet_stop (mc9328mxl_enet_softc_t *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+
+ /* Stop the transmitter and receiver. */
+ lan91c11x_write_reg(LAN91C11X_TCR,
+ (lan91c11x_read_reg(LAN91C11X_TCR) &
+ ~LAN91C11X_TCR_TXENA));
+
+ lan91c11x_write_reg(LAN91C11X_RCR,
+ (lan91c11x_read_reg(LAN91C11X_RCR) &
+ ~LAN91C11X_RCR_RXEN));
+
+}
+
+/*
+ * Driver transmit daemon
+ */
+void mc9328mxl_enet_tx_task(void *arg)
+{
+ mc9328mxl_enet_softc_t *sc = (mc9328mxl_enet_softc_t *)arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ rtems_event_set events;
+
+ for (;;)
+ {
+ 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;
+ }
+ mc9328mxl_enet_sendpacket (ifp, m);
+ softc.stats.tx_packets++;
+
+ }
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+}
+
+/* Send packet */
+void mc9328mxl_enet_sendpacket (struct ifnet *ifp, struct mbuf *m)
+{
+ struct mbuf *l = NULL;
+ int size = 0;
+ int tmp;
+ int i;
+ int start;
+ uint16_t d;
+
+ /* How big is the packet ? */
+ l = m;
+ do {
+ size += l->m_len;
+ l = l->m_next;
+ } while (l != NULL);
+
+ /* Allocate a TX buffer */
+ lan91c11x_write_reg(LAN91C11X_MMUCMD,
+ (LAN91C11X_MMUCMD_ALLOCTX |
+ (size >> 8)));
+
+ /* Wait for the allocation */
+ while ((lan91c11x_read_reg(LAN91C11X_INT) & LAN91C11X_INT_ALLOC) == 0) {
+ continue;
+ }
+
+ tmp = lan91c11x_read_reg(LAN91C11X_PNR);
+ lan91c11x_write_reg(LAN91C11X_PNR, ((tmp >> 8) & 0xff));
+
+ /* Set the data pointer for auto increment */
+ lan91c11x_write_reg(LAN91C11X_PTR, LAN91C11X_PTR_AUTOINC);
+
+ /* A delay is needed between pointer and data access ?!? */
+ for (i = 0; i < 10; i++) {
+ continue;
+ }
+
+ /* Write status word */
+ lan91c11x_write_reg(LAN91C11X_DATA, 0);
+
+ /* Write byte count */
+ if (size & 1) {
+ size++;
+ }
+ lan91c11x_write_reg(LAN91C11X_DATA, size + 6);
+
+ lan91c11x_lock();
+
+ /* Copy the mbuf */
+ l = m;
+ start = 0;
+ d = 0;
+ while (l != NULL)
+ {
+ uint8_t *data;
+
+ data = mtod(l, uint8_t *);
+
+ for (i = start; i < l->m_len; i++) {
+ if ((i & 1) == 0) {
+ d = data[i] << 8;
+ } else {
+ d = d | data[i];
+ lan91c11x_write_reg_fast(LAN91C11X_DATA, htons(d));
+ }
+ }
+
+ /* If everything is 2 byte aligned, i will be even */
+ start = (i & 1);
+
+ l = l->m_next;
+ }
+
+ /* write control byte */
+ if (i & 1) {
+ lan91c11x_write_reg_fast(LAN91C11X_DATA,
+ htons(LAN91C11X_PKT_CTRL_ODD | d));
+ } else {
+ lan91c11x_write_reg_fast(LAN91C11X_DATA, 0);
+ }
+
+ lan91c11x_unlock();
+
+ /* Enable TX interrupts */
+ lan91c11x_write_reg(LAN91C11X_INT,
+ (lan91c11x_read_reg(LAN91C11X_INT) |
+ LAN91C11X_INT_TXMASK |
+ LAN91C11X_INT_TXEMASK));
+
+ /* Enqueue it */
+ lan91c11x_write_reg(LAN91C11X_MMUCMD,
+ LAN91C11X_MMUCMD_ENQUEUE);
+
+ /* free the mbuf chain we just copied */
+ m_freem(m);
+
+} /* mc9328mxl_enet_sendpacket () */
+
+
+/* reader task */
+void mc9328mxl_enet_rx_task(void *arg)
+{
+ mc9328mxl_enet_softc_t *sc = (mc9328mxl_enet_softc_t *)arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ struct ether_header *eh;
+ rtems_event_set events;
+ int pktlen;
+ uint16_t rsw;
+ uint16_t bc;
+ uint16_t cbyte;
+ int i;
+ uint16_t int_reg;
+
+ /* Input packet handling loop */
+ while (1) {
+ rtems_bsdnet_event_receive(
+ START_RECEIVE_EVENT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &events);
+
+ /* Configure for reads from RX data area */
+ lan91c11x_write_reg(LAN91C11X_PTR,
+ (LAN91C11X_PTR_AUTOINC |
+ LAN91C11X_PTR_RCV |
+ LAN91C11X_PTR_READ));
+
+ /* read the receive status word */
+ rsw = lan91c11x_read_reg(LAN91C11X_DATA);
+ /* TBD: Need to check rsw here */
+
+ /* read the byte count */
+ bc = lan91c11x_read_reg(LAN91C11X_DATA);
+ pktlen = (bc & 0x7ff) - 6;
+
+ /* get an mbuf for this packet */
+ MGETHDR(m, M_WAIT, MT_DATA);
+
+ /* now get a cluster pointed to by the mbuf */
+ /* since an mbuf by itself is too small */
+ MCLGET(m, M_WAIT);
+
+ lan91c11x_lock();
+
+ /* Copy the received packet into an mbuf */
+ for (i = 0; i < (pktlen / 2); i++) {
+ ((uint16_t*)m->m_ext.ext_buf)[i] =
+ lan91c11x_read_reg_fast(LAN91C11X_DATA);
+ }
+
+ cbyte = lan91c11x_read_reg_fast(LAN91C11X_DATA);
+ if (cbyte & LAN91C11X_PKT_CTRL_ODD) {
+ ((uint16_t*)m->m_ext.ext_buf)[i] = cbyte;
+ pktlen++;
+ }
+ lan91c11x_unlock();
+
+ /* Release the packets memory */
+ lan91c11x_write_reg(LAN91C11X_MMUCMD,
+ LAN91C11X_MMUCMD_REMTOP);
+
+ /* set the receiving interface */
+ m->m_pkthdr.rcvif = ifp;
+ m->m_nextpkt = 0;
+
+ /* set the length of the mbuf */
+ m->m_len = pktlen - (sizeof(struct ether_header));
+ m->m_pkthdr.len = m->m_len;
+
+ /* strip off the ethernet header from the mbuf */
+ /* but save the pointer to it */
+ eh = mtod (m, struct ether_header *);
+ m->m_data += sizeof(struct ether_header);
+
+
+ softc.stats.rx_packets++;
+
+ /* give all this stuff to the stack */
+ ether_input(ifp, eh, m);
+
+ /* renable RX interrupts */
+ int_reg = lan91c11x_read_reg(LAN91C11X_INT);
+ int_reg |= LAN91C11X_INT_RXMASK;
+ lan91c11x_write_reg(LAN91C11X_INT, int_reg);
+
+ }
+} /* mc9328mxl_enet_rx_task */
+
+
+/* Show interface statistics */
+void mc9328mxl_enet_stats (mc9328mxl_enet_softc_t *sc)
+{
+ printf (" Total Interrupts:%-8lu", sc->stats.interrupts);
+ printf (" Rx Interrupts:%-8lu", sc->stats.rx_interrupts);
+ printf (" Tx Interrupts:%-8lu\n", sc->stats.tx_interrupts);
+ printf (" Tx Error Interrupts:%-8lu\n", sc->stats.txerr_interrupts);
+ printf (" Rx Packets:%-8lu", sc->stats.rx_packets);
+ printf (" Tx Packets:%-8lu\n", sc->stats.tx_packets);
+}
+
+
+/* Enables mc9328mxl_enet interrupts. */
+static void enet_isr_on(void)
+{
+ /* Enable interrupts */
+ MC9328MXL_AITC_INTENNUM = MC9328MXL_INT_GPIO_PORTA;
+
+ return;
+}
+
+/* Driver ioctl handler */
+static int
+mc9328mxl_enet_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ mc9328mxl_enet_softc_t *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command) {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl (ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING))
+ {
+ case IFF_RUNNING:
+ mc9328mxl_enet_stop (sc);
+ break;
+
+ case IFF_UP:
+ mc9328mxl_enet_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ mc9328mxl_enet_stop (sc);
+ mc9328mxl_enet_init (sc);
+ break;
+
+ default:
+ break;
+ } /* switch (if_flags) */
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ mc9328mxl_enet_stats (sc);
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ } /* switch (command) */
+ return error;
+}
+
+/* interrupt handler */
+static void enet_isr(void * unused)
+{
+ uint16_t int_reg;
+
+ softc.stats.interrupts++;
+ /* get the ISR status and determine RX or TX */
+ int_reg = lan91c11x_read_reg(LAN91C11X_INT);
+
+ /* Handle RX interrupts */
+ if ((int_reg & LAN91C11X_INT_RX) && (int_reg & LAN91C11X_INT_RXMASK)) {
+ softc.stats.rx_interrupts++;
+
+ /* Disable the interrupt */
+ int_reg &= ~LAN91C11X_INT_RXMASK;
+
+ rtems_bsdnet_event_send (softc.rx_task, START_RECEIVE_EVENT);
+ }
+
+ /* Handle TX Empty interrupts */
+ if ((int_reg & LAN91C11X_INT_TXE) && (int_reg & LAN91C11X_INT_TXEMASK)) {
+ softc.stats.tx_interrupts++;
+
+ /* Disable the interrupt */
+ int_reg &= ~LAN91C11X_INT_TXEMASK;
+
+ /* Acknowledge the interrupt */
+ int_reg |= LAN91C11X_INT_TXE;
+
+ rtems_bsdnet_event_send(softc.tx_task, START_TRANSMIT_EVENT);
+
+ }
+
+ /* Handle interrupts for transmit errors */
+ if ((int_reg & LAN91C11X_INT_TX) && (int_reg & LAN91C11X_INT_TXMASK)) {
+ softc.stats.txerr_interrupts++;
+ printk("Caught TX interrupt - error on transmission\n");
+ }
+
+ /* Update the interrupt register on the 91c11x */
+ lan91c11x_write_reg(LAN91C11X_INT, int_reg);
+
+ /* clear GPIO Int. status */
+ MC9328MXL_GPIOA_ISR |= bit(3);
+}
+
diff --git a/bsps/arm/csb337/net/network.c b/bsps/arm/csb337/net/network.c
new file mode 100644
index 0000000000..af7150122d
--- /dev/null
+++ b/bsps/arm/csb337/net/network.c
@@ -0,0 +1,864 @@
+/*
+ * AT91RM9200 ethernet driver
+ *
+ * Copyright (c) 2003 by Cogent Computer Systems
+ * Written by Mike Kelly <mike@cogcomp.com>
+ * and Jay Monkman <jtm@lopingdog.com>
+ *
+ *
+ * July 2009: Joel Sherrill merged csb637 PHY differences from
+ * MicroMonitor 1.17.
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <rtems.h>
+#include <rtems/rtems_bsdnet.h>
+#include <rtems/bspIo.h>
+#include <at91rm9200.h>
+#include <at91rm9200_emac.h>
+#include <at91rm9200_gpio.h>
+#include <at91rm9200_pmc.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <errno.h>
+#include <rtems/error.h>
+#include <assert.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 <bsp/irq.h>
+#include <bspopts.h>
+
+/* enable debugging of the PHY code */
+#define PHY_DBG
+
+/* enable debugging of the EMAC code */
+/* #define EMAC_DBG */
+
+#if csb637
+ /* Bit defines for the PHY Status Register #1 (phy address 0x01) */
+ /* 1 = PHY able to perform 100BASE-T4 */
+ #define PHY_STAT_100BASE_T4 BIT15
+ /* 1 = PHY able to perform full-duplex 100BASE-X */
+ #define PHY_STAT_100BASE_X_FDX BIT14
+ /* 1 = PHY able to perform half-duplex 100BASE-X */
+ #define PHY_STAT_100BASE_X_HDX BIT13
+ /* 1 = PHY able to operate at 10 Mbps in full-duplex mode */
+ #define PHY_STAT_10BASE_FDX BIT12
+ /* 1 = PHY able to operate at 10 Mbps in half-duplex mode */
+ #define PHY_STAT_10BASE_HDX BIT11
+ /* 1 = PHY will accept management frames with preamble suppressed */
+ #define PHY_STAT_MF_PREAMBLE BIT6
+ /* 1 = Auto-negotiation complete */
+ #define PHY_STAT_AUTO_NEG_DONE BIT5
+ /* 1 = Remote fault condition detected */
+ #define PHY_STAT_REM_FLT BIT4
+ /* 1 = PHY is able to perform Auto-Negotiation */
+ #define PHY_STAT_AUTO_NEG_ABLE BIT3
+ /* 1 = Link is up */
+ #define PHY_STAT_LINK_UP BIT2
+ /* 1 = Jabber condition detected */
+ #define PHY_STAT_JABBER BIT1
+ /* 1 = Extended register capabilities */
+ #define PHY_STAT_EXT_REG BIT0
+
+ /* Bit defines for the Auxillary Mode 3 register */
+ #define PHY_AUX_MODE2_TRAFFIC_LED BIT6
+#endif
+
+ /* interrupt stuff */
+ #define EMAC_INT_PRIORITY 0 /* lowest priority */
+
+ /* RTEMS event used by interrupt handler to start receive daemon. */
+ #define START_RECEIVE_EVENT RTEMS_EVENT_1
+
+ /* RTEMS event used to start transmit daemon. */
+ #define START_TRANSMIT_EVENT RTEMS_EVENT_2
+
+static void at91rm9200_emac_isr (void *);
+static void at91rm9200_emac_isr_on(void);
+
+/* use the values defined in linkcmds for our use of SRAM */
+extern void * at91rm9200_emac_rxbuf_hdrs;
+extern void * at91rm9200_emac_txbuf;
+extern void * at91rm9200_emac_rxbufs;
+
+/* Set up EMAC hardware */
+/* Number of Receive and Transmit Buffers and Buffer Descriptors */
+#define NUM_RXBDS 8
+#define NUM_TXBDS 1
+#define RX_BUFFER_SIZE 0x600
+
+/* use internal SRAM for buffers and descriptors
+ * also insure that the receive descriptors
+ * start on a 64byte boundary
+ * Receive Buffer Descriptor Header
+ */
+
+typedef struct
+{
+ unsigned long address;
+ unsigned long status;
+} RXBUF_HDR;
+
+RXBUF_HDR *rxbuf_hdrs;
+unsigned char *txbuf;
+unsigned char *rxbuf;
+
+int delay_cnt;
+
+/*
+ * Hardware-specific storage
+ */
+typedef struct
+{
+ /*
+ * Connection to networking code
+ * This entry *must* be the first in the sonic_softc structure.
+ */
+ struct arpcom arpcom;
+
+ /*
+ * Interrupt vector
+ */
+ rtems_vector_number vector;
+
+ /*
+ * Indicates configuration
+ */
+ int acceptBroadcast;
+
+ /*
+ * Task waiting for interrupts
+ */
+ rtems_id rxDaemonTid;
+ rtems_id txDaemonTid;
+
+ /*
+ * current receive header
+ */
+ int rx_buf_idx;
+
+
+
+ /*
+ * Statistics
+ */
+ unsigned long Interrupts;
+ unsigned long rxInterrupts;
+ unsigned long rxMissed;
+ unsigned long rxGiant;
+ unsigned long rxNonOctet;
+ unsigned long rxBadCRC;
+ unsigned long rxCollision;
+
+ unsigned long txInterrupts;
+ unsigned long txSingleCollision;
+ unsigned long txMultipleCollision;
+ unsigned long txCollision;
+ unsigned long txDeferred;
+ unsigned long txUnderrun;
+ unsigned long txLateCollision;
+ unsigned long txExcessiveCollision;
+ unsigned long txExcessiveDeferral;
+ unsigned long txLostCarrier;
+ unsigned long txRawWait;
+} at91rm9200_emac_softc_t;
+
+static at91rm9200_emac_softc_t softc;
+
+
+/* The AT91RM9200 ethernet fifos are very undersized. Therefore
+ * we use the internal SRAM to hold 4 receive packets and one
+ * transmit packet. Note that the AT91RM9200 can only queue
+ * one transmit packet at a time.
+ */
+
+/* function prototypes */
+int rtems_at91rm9200_emac_attach (struct rtems_bsdnet_ifconfig *config,
+ void *chip);
+void at91rm9200_emac_init(void *arg);
+void at91rm9200_emac_init_hw(at91rm9200_emac_softc_t *sc);
+void at91rm9200_emac_start(struct ifnet *ifp);
+void at91rm9200_emac_stop (at91rm9200_emac_softc_t *sc);
+void at91rm9200_emac_txDaemon (void *arg);
+void at91rm9200_emac_sendpacket (struct ifnet *ifp, struct mbuf *m);
+void at91rm9200_emac_rxDaemon(void *arg);
+void at91rm9200_emac_stats (at91rm9200_emac_softc_t *sc);
+static int at91rm9200_emac_ioctl (struct ifnet *ifp,
+ ioctl_command_t command,
+ caddr_t data);
+
+#if csb637
+/*
+ * phyread(): Read the PHY
+ */
+static uint32_t phyread(uint8_t reg)
+{
+ EMAC_REG(EMAC_MAN) = (0x01 << 30 /* Start of Frame Delimiter */
+ | 0x02 << 28 /* Operation, 0x01 = Write, 0x02 = Read */
+ | 0x00 << 23 /* Phy Number, 0 as we only have one */
+ | reg << 18 /* Phy Register */
+ | 0x02 << 16); /* must be 0x02 for turn around field */
+
+ /* wait for phy read to complete (was udelay(5000)) */
+ rtems_task_wake_after(1);
+
+ #if defined(EMAC_DBG)
+ printk(
+ "EMAC: Phy 0, Reg %d, Read 0x%04lx.\n",
+ reg,
+ (EMAC_REG(EMAC_MAN) & 0xffff)
+ );
+ #endif
+
+ return EMAC_REG(EMAC_MAN) & 0xffff;
+}
+#endif
+
+/*
+ * phywrite(): Write the PHY
+ */
+static void phywrite(uint8_t reg, uint16_t data)
+{
+ EMAC_REG(EMAC_MAN) = (0x01 << 30 /* Start of Frame Delimiter */
+ | 0x01 << 28 /* Operation, 0x01 = Write, 0x02 = Read */
+ | 0x00 << 23 /* Phy Number, BCM5221 is address 0 */
+ | reg << 18 /* Phy Register */
+ | 0x02 << 16 /* must be 0x02 for turn around field */
+ | data);
+ #if defined(EMAC_DBG)
+ printk("EMAC: Phy 0, Reg %d, Write 0x%04x.\n", reg, data);
+ #endif
+
+ /* wait for phy write to complete (was udelay(5000)) */
+ rtems_task_wake_after(1);
+}
+
+
+int rtems_at91rm9200_emac_attach (
+ struct rtems_bsdnet_ifconfig *config,
+ void *chip /* only one ethernet, so no chip number */
+ )
+{
+ struct ifnet *ifp;
+ int mtu;
+ int unitnumber;
+ char *unitname;
+ void *p;
+
+ /* an array of receive buffer descriptors -- avoid type punned warning */
+ p = (void *)&at91rm9200_emac_rxbuf_hdrs;
+ rxbuf_hdrs = (RXBUF_HDR *)p;
+
+ /* one transmit buffer, 1536 bytes maximum */
+ txbuf = (unsigned char *)&at91rm9200_emac_txbuf;
+
+ /* receive buffers starting address */
+ rxbuf = (unsigned char *)&at91rm9200_emac_rxbufs;
+ /*
+ * Parse driver name
+ */
+ if ((unitnumber = rtems_bsdnet_parse_driver_name (config, &unitname)) < 0)
+ return 0;
+
+ /*
+ * Is driver free?
+ */
+ if (unitnumber != 0) {
+ printk ("Bad AT91RM9200 EMAC unit number.\n");
+ return 0;
+ }
+ ifp = &softc.arpcom.ac_if;
+ if (ifp->if_softc != NULL) {
+ printk ("Driver already in use.\n");
+ return 0;
+ }
+
+ /*
+ * zero out the control structure
+ */
+
+ memset( &softc, 0, sizeof(softc) );
+
+
+ /* get the MAC address from the chip */
+ softc.arpcom.ac_enaddr[0] = (EMAC_REG(EMAC_SA1L) >> 0) & 0xff;
+ softc.arpcom.ac_enaddr[1] = (EMAC_REG(EMAC_SA1L) >> 8) & 0xff;
+ softc.arpcom.ac_enaddr[2] = (EMAC_REG(EMAC_SA1L) >> 16) & 0xff;
+ softc.arpcom.ac_enaddr[3] = (EMAC_REG(EMAC_SA1L) >> 24) & 0xff;
+ softc.arpcom.ac_enaddr[4] = (EMAC_REG(EMAC_SA1H) >> 0) & 0xff;
+ softc.arpcom.ac_enaddr[5] = (EMAC_REG(EMAC_SA1H) >> 8) & 0xff;
+
+ #if 0
+ printk( "MAC=%02x:%02x:%02x:%02x:%02x:%02x\n",
+ softc.arpcom.ac_enaddr[0],
+ softc.arpcom.ac_enaddr[1],
+ softc.arpcom.ac_enaddr[2],
+ softc.arpcom.ac_enaddr[3],
+ softc.arpcom.ac_enaddr[4],
+ softc.arpcom.ac_enaddr[5]
+ );
+ #endif
+
+ if (config->mtu) {
+ mtu = config->mtu;
+ } else {
+ mtu = ETHERMTU;
+ }
+
+ softc.acceptBroadcast = !config->ignore_broadcast;
+
+ /*
+ * Set up network interface values
+ */
+ ifp->if_softc = &softc;
+ ifp->if_unit = unitnumber;
+ ifp->if_name = unitname;
+ ifp->if_mtu = mtu;
+ ifp->if_init = at91rm9200_emac_init;
+ ifp->if_ioctl = at91rm9200_emac_ioctl;
+ ifp->if_start = at91rm9200_emac_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST;
+ if (ifp->if_snd.ifq_maxlen == 0) {
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+ }
+
+ softc.rx_buf_idx = 0;
+
+ /*
+ * Attach the interface
+ */
+ if_attach (ifp);
+ ether_ifattach (ifp);
+ return 1;
+}
+
+void at91rm9200_emac_init(void *arg)
+{
+ at91rm9200_emac_softc_t *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ rtems_status_code status = RTEMS_SUCCESSFUL;
+
+ /*
+ *This is for stuff that only gets done once (at91rm9200_emac_init()
+ * gets called multiple times
+ */
+ if (sc->txDaemonTid == 0) {
+ /* Set up EMAC hardware */
+ at91rm9200_emac_init_hw(sc);
+
+ /* Start driver tasks */
+ sc->rxDaemonTid = rtems_bsdnet_newproc("ENrx",
+ 4096,
+ at91rm9200_emac_rxDaemon,
+ sc);
+ sc->txDaemonTid = rtems_bsdnet_newproc("ENtx",
+ 4096,
+ at91rm9200_emac_txDaemon,
+ sc);
+ } /* if txDaemonTid */
+
+ /* set our priority in the AIC */
+ AIC_SMR_REG(AIC_SMR_EMAC) = AIC_SMR_PRIOR(EMAC_INT_PRIORITY);
+
+ /* install the interrupt handler */
+ status = rtems_interrupt_handler_install(
+ AT91RM9200_INT_EMAC,
+ "Network",
+ RTEMS_INTERRUPT_UNIQUE,
+ at91rm9200_emac_isr,
+ NULL
+ );
+ assert(status == RTEMS_SUCCESSFUL);
+ at91rm9200_emac_isr_on();
+
+ /* EMAC doesn't support promiscuous, so ignore requests */
+ if (ifp->if_flags & IFF_PROMISC) {
+ printk ("Warning - AT91RM9200 Ethernet driver"
+ " doesn't support Promiscuous Mode!\n");
+ }
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+
+ /* Enable TX/RX and clear the statistics counters */
+ EMAC_REG(EMAC_CTL) = (EMAC_CTL_TE | EMAC_CTL_RE | EMAC_CTL_CSR);
+
+ /* clear any pending interrupts */
+ EMAC_REG(EMAC_TSR) = 0xffffffff;
+ EMAC_REG(EMAC_RSR) = 0xffffffff;
+
+} /* at91rm9200_emac_init() */
+
+void at91rm9200_emac_init_hw(at91rm9200_emac_softc_t *sc)
+{
+ int i;
+
+ /* Configure shared pins for Ethernet, not GPIO */
+ PIOA_REG(PIO_PDR) = ( BIT7 | /* tx clock */
+ BIT8 | /* tx enable */
+ BIT9 | /* tx data 0 */
+ BIT10 | /* tx data 1 */
+ BIT11 | /* carrier sense */
+ BIT12 | /* rx data 0 */
+ BIT13 | /* rx data 1 */
+ BIT14 | /* rx error */
+ BIT15 | /* MII clock */
+ BIT16 ); /* MII data */
+
+ PIOB_REG(PIO_PDR) = ( BIT12 | /* tx data 2 */
+ BIT13 | /* tx data 3 */
+ BIT14 | /* tx error */
+ BIT15 | /* rx data 2 */
+ BIT16 | /* rx data 3 */
+ BIT17 | /* rx data valid */
+ BIT18 | /* rx collistion */
+ BIT19 ); /* rx clock */
+
+ PIOB_REG(PIO_BSR) = ( BIT12 | /* tx data 2 */
+ BIT13 | /* tx data 3 */
+ BIT14 | /* tx error */
+ BIT15 | /* rx data 2 */
+ BIT16 | /* rx data 3 */
+ BIT17 | /* rx data valid */
+ BIT18 | /* rx collistion */
+ BIT19 ); /* rx clock */
+
+
+ /* Enable the clock to the EMAC */
+ PMC_REG(PMC_PCER) |= PMC_PCR_PID_EMAC;
+
+ /* initialize our receive buffer descriptors */
+ for (i = 0; i < NUM_RXBDS-1; i++) {
+ rxbuf_hdrs[i].address = (unsigned long)(&rxbuf[i * RX_BUFFER_SIZE]);
+ rxbuf_hdrs[i].status = 0x00000000;
+ }
+
+ /* last one needs the wrapbit set as well */
+ rxbuf_hdrs[i].address = ((unsigned long)(&rxbuf[i * RX_BUFFER_SIZE]) |
+ RXBUF_ADD_WRAP);
+ rxbuf_hdrs[i].status = 0x00000000;
+
+ /* point to our receive buffer queue */
+ EMAC_REG(EMAC_RBQP) = (unsigned long)rxbuf_hdrs;
+
+ /* clear any left over status bits */
+ EMAC_REG(EMAC_RSR) &= ~(EMAC_RSR_OVR | EMAC_RSR_REC | EMAC_RSR_BNA);
+
+ /* set the MII clock divder to MCK/64 */
+ EMAC_REG(EMAC_CFG) &= EMAC_CFG_CLK_MASK;
+ EMAC_REG(EMAC_CFG) = (EMAC_CFG_CLK_64 | EMAC_CFG_BIG | EMAC_CFG_FD);
+
+ /* enable the MII interface */
+ EMAC_REG(EMAC_CTL) = EMAC_CTL_MPE;
+
+ #if csb637
+ {
+ int timeout;
+ uint32_t emac_link_status;
+
+ #if defined(PHY_DBG)
+ printk("EMAC: Getting Link Status.\n");
+ #endif
+ /* read the PHY ID registers */
+ emac_link_status = phyread(0x02);
+ emac_link_status = phyread(0x03);
+
+ /* Get the link status - wait for done with a timeout */
+ for (timeout = 10000 ; timeout ; ) {
+ for (i = 0; i < 100; i++)
+ ;
+ emac_link_status = phyread(0x01);
+ if (!(emac_link_status & PHY_STAT_AUTO_NEG_ABLE)) {
+ #if defined(PHY_DBG)
+ printk("EMAC: PHY is unable to Auto-Negotatiate!\n");
+ #endif
+ timeout = 0;
+ break;
+ }
+ if (emac_link_status & PHY_STAT_AUTO_NEG_DONE) {
+ #if defined(PHY_DBG)
+ printk("EMAC: Auto-Negotiate Complete, Link = ");
+ #endif
+ break;
+ }
+ timeout-- ;
+ }
+ if (!timeout) {
+ #if defined(PHY_DBG)
+ printk(
+ "EMAC: Auto-Negotatiate Failed, Status = 0x%04lx!\n"
+ "EMAC: Initialization Halted.\n",
+ emac_link_status
+ );
+ #endif
+ /* if autonegotiation fails, just force to 10HD... */
+ emac_link_status = PHY_STAT_10BASE_HDX;
+ }
+
+ /* Set SPD and FD based on the return link status */
+ if (emac_link_status & (PHY_STAT_100BASE_X_FDX | PHY_STAT_100BASE_X_HDX)){
+ EMAC_REG(EMAC_CFG) |= EMAC_CFG_SPD;
+ #if defined(PHY_DBG)
+ printk("100MBIT, ");
+ #endif
+ } else {
+ EMAC_REG(EMAC_CFG) &= ~EMAC_CFG_SPD;
+ #if defined(PHY_DBG)
+ printk("10MBIT, ");
+ #endif
+ }
+
+ if (emac_link_status & (PHY_STAT_100BASE_X_FDX | PHY_STAT_10BASE_FDX)) {
+ EMAC_REG(EMAC_CFG) |= EMAC_CFG_FD;
+ #if defined(PHY_DBG)
+ printk("Full Duplex.\n");
+ #endif
+ } else {
+ EMAC_REG(EMAC_CFG) &= ~EMAC_CFG_FD;
+ #if defined(PHY_DBG)
+ printk("Half Duplex.\n");
+ #endif
+ }
+
+ /* Set PHY LED modes. Traffic Meter Mode for ACTLED
+ * Set Bit 6 - Traffic Mode on
+ */
+ phywrite(0x1b, PHY_AUX_MODE2_TRAFFIC_LED);
+ }
+ #else
+ /* must be csb337 */
+ /* Set PHY LED2 to combined Link/Activity and enable pulse stretching */
+ phywrite( 18, 0x0d0a );
+ #endif
+
+ #if 0
+ EMAC_REG(EMAC_MAN) = (0x01 << 30 | /* Start of Frame Delimiter */
+ 0x01 << 28 | /* Operation, 0x01 = Write */
+ 0x00 << 23 | /* Phy Number */
+ 0x14 << 18 | /* Phy Register */
+ 0x02 << 16 | /* must be 0x02 */
+ 0x0D0A); /* Write data (0x0000 if read) */
+ #endif
+
+} /* at91rm9200_emac_init_hw() */
+
+void at91rm9200_emac_start(struct ifnet *ifp)
+{
+ at91rm9200_emac_softc_t *sc = ifp->if_softc;
+
+ rtems_bsdnet_event_send(sc->txDaemonTid, START_TRANSMIT_EVENT);
+ ifp->if_flags |= IFF_OACTIVE;
+}
+
+void at91rm9200_emac_stop (at91rm9200_emac_softc_t *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /*
+ * Stop the transmitter and receiver.
+ */
+ EMAC_REG(EMAC_CTL) &= ~(EMAC_CTL_TE | EMAC_CTL_RE);
+}
+
+/*
+ * Driver transmit daemon
+ */
+void at91rm9200_emac_txDaemon (void *arg)
+{
+ at91rm9200_emac_softc_t *sc = (at91rm9200_emac_softc_t *)arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ rtems_event_set events;
+
+ for (;;)
+ {
+ /* turn on TX interrupt, then wait for one */
+ EMAC_REG(EMAC_IER) = EMAC_INT_TCOM; /* Transmit complete */
+
+ 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;
+ at91rm9200_emac_sendpacket (ifp, m);
+ }
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+}
+
+/* Send packet */
+void at91rm9200_emac_sendpacket (struct ifnet *ifp, struct mbuf *m)
+{
+ struct mbuf *l = NULL;
+ unsigned int pkt_offset = 0;
+ delay_cnt = 0;
+ /* printk("at91rm9200_emac_sendpacket %p\n", m); */
+
+ /* Wait for EMAC Transmit Queue to become available. */
+ while (((EMAC_REG(EMAC_TSR) & EMAC_TSR_COMP) == 0) &&
+ ((EMAC_REG(EMAC_TSR) & EMAC_TSR_TXIDLE) == 0))
+
+ {
+ delay_cnt++;
+/* sleep(0); make sure we don't hog the cpu */
+ continue;
+ }
+
+ /* copy the mbuf chain into the transmit buffer */
+ l = m;
+ while (l != NULL) {
+ memcpy(((char *)txbuf + pkt_offset), /* offset into pkt for mbuf */
+ (char *)mtod(l, void *), /* cast to void */
+ l->m_len); /* length of this mbuf */
+
+ pkt_offset += l->m_len; /* update offset */
+ l = l->m_next; /* get next mbuf, if any */
+ }
+
+ /* free the mbuf chain we just copied */
+ m_freem(m);
+
+ /* clear any pending status */
+ EMAC_REG(EMAC_TSR) = (EMAC_TSR_OVR | EMAC_TSR_COL | EMAC_TSR_RLE
+ | EMAC_TSR_COMP | EMAC_TSR_UND);
+
+ /* tell the EMAC about our buffer */
+ EMAC_REG(EMAC_TAR) = (unsigned long)txbuf;
+ EMAC_REG(EMAC_TCR) = (unsigned long)pkt_offset;
+} /* at91rm9200_emac_sendpacket () */
+
+
+/* SONIC reader task */
+void at91rm9200_emac_rxDaemon(void *arg)
+{
+ at91rm9200_emac_softc_t *sc = (at91rm9200_emac_softc_t *)arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ struct ether_header *eh;
+ rtems_event_set events;
+ int pktlen;
+
+ /* Input packet handling loop */
+ for (;;) {
+ /* turn on RX interrupts, then wait for one */
+ EMAC_REG(EMAC_IER) = (EMAC_INT_RCOM | /* Receive complete */
+ EMAC_INT_RBNA | /* Receive buf not available */
+ EMAC_INT_ROVR); /* Receive overrun */
+
+ rtems_bsdnet_event_receive(
+ START_RECEIVE_EVENT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &events);
+
+ if (EMAC_REG(EMAC_RSR) & EMAC_RSR_BNA) {
+ printk("1: EMAC_BNA\n");
+ }
+
+ if (EMAC_REG(EMAC_RSR) & EMAC_RSR_OVR) {
+ printk("1: EMAC_OVR\n");
+ }
+
+ /* clear the receive status as we do not use it anyway */
+ EMAC_REG(EMAC_RSR) = (EMAC_RSR_REC | EMAC_RSR_OVR | EMAC_RSR_BNA);
+
+ /* scan the buffer descriptors looking for any with data in them */
+ while (rxbuf_hdrs[sc->rx_buf_idx].address & RXBUF_ADD_OWNED) {
+ pktlen = rxbuf_hdrs[sc->rx_buf_idx].status & RXBUF_STAT_LEN_MASK;
+
+ /* get an mbuf this packet */
+ MGETHDR(m, M_WAIT, MT_DATA);
+
+ /* now get a cluster pointed to by the mbuf */
+ /* since an mbuf by itself is too small */
+ MCLGET(m, M_WAIT);
+
+ /* set the type of mbuf to ifp (ethernet I/F) */
+ m->m_pkthdr.rcvif = ifp;
+ m->m_nextpkt = 0;
+
+ /* copy the packet into the cluster pointed to by the mbuf */
+ memcpy((char *)m->m_ext.ext_buf,
+ (char *)(rxbuf_hdrs[sc->rx_buf_idx].address & 0xfffffffc),
+ pktlen);
+
+ /* Release the buffer ASAP back to the EMAC */
+ rxbuf_hdrs[sc->rx_buf_idx].address &= ~RXBUF_ADD_OWNED;
+
+ /* set the length of the mbuf */
+ m->m_len = pktlen - (sizeof(struct ether_header) + 4);
+ m->m_pkthdr.len = m->m_len;
+
+ /* strip off the ethernet header from the mbuf */
+ /* but save the pointer to it */
+ eh = mtod (m, struct ether_header *);
+ m->m_data += sizeof(struct ether_header);
+
+ /* increment the buffer index */
+ sc->rx_buf_idx++;
+ if (sc->rx_buf_idx >= NUM_RXBDS) {
+ sc->rx_buf_idx = 0;
+ }
+
+ /* give all this stuff to the stack */
+ ether_input(ifp, eh, m);
+
+ } /* while ADD_OWNED = 0 */
+
+ if (EMAC_REG(EMAC_RSR) & EMAC_RSR_BNA) {
+ printk("2:EMAC_BNA\n");
+ }
+ if (EMAC_REG(EMAC_RSR) & EMAC_RSR_OVR) {
+ printk("2:EMAC_OVR\n");
+ }
+
+
+ } /* for (;;) */
+} /* at91rm9200_emac_rxDaemon() */
+
+
+/* Show interface statistics */
+void at91rm9200_emac_stats (at91rm9200_emac_softc_t *sc)
+{
+ printf (" Total Interrupts:%-8lu", sc->Interrupts);
+ printf (" Rx Interrupts:%-8lu", sc->rxInterrupts);
+ printf (" Giant:%-8lu", sc->rxGiant);
+ printf (" Non-octet:%-8lu\n", sc->rxNonOctet);
+ printf (" Bad CRC:%-8lu", sc->rxBadCRC);
+ printf (" Collision:%-8lu", sc->rxCollision);
+ printf (" Missed:%-8lu\n", sc->rxMissed);
+
+ printf ( " Tx Interrupts:%-8lu", sc->txInterrupts);
+ printf ( " Deferred:%-8lu", sc->txDeferred);
+ printf (" Lost Carrier:%-8lu\n", sc->txLostCarrier);
+ printf ( "Single Collisions:%-8lu", sc->txSingleCollision);
+ printf ( "Multiple Collisions:%-8lu", sc->txMultipleCollision);
+ printf ("Excessive Collisions:%-8lu\n", sc->txExcessiveCollision);
+ printf ( " Total Collisions:%-8lu", sc->txCollision);
+ printf ( " Late Collision:%-8lu", sc->txLateCollision);
+ printf (" Underrun:%-8lu\n", sc->txUnderrun);
+ printf ( " Raw output wait:%-8lu\n", sc->txRawWait);
+}
+
+
+/* Enables at91rm9200_emac interrupts. */
+static void at91rm9200_emac_isr_on(void)
+{
+ /* Enable various TX/RX interrupts */
+ EMAC_REG(EMAC_IER) = (EMAC_INT_RCOM | /* Receive complete */
+ EMAC_INT_RBNA | /* Receive buffer not available */
+ EMAC_INT_TCOM | /* Transmit complete */
+ EMAC_INT_ROVR | /* Receive overrun */
+ EMAC_INT_ABT); /* Abort on DMA transfer */
+
+ return;
+}
+
+/* Driver ioctl handler */
+static int
+at91rm9200_emac_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ at91rm9200_emac_softc_t *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command) {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl (ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING))
+ {
+ case IFF_RUNNING:
+ at91rm9200_emac_stop (sc);
+ break;
+
+ case IFF_UP:
+ at91rm9200_emac_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ at91rm9200_emac_stop (sc);
+ at91rm9200_emac_init (sc);
+ break;
+
+ default:
+ break;
+ } /* switch (if_flags) */
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ at91rm9200_emac_stats (sc);
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ } /* switch (command) */
+ return error;
+}
+
+/* interrupt handler */
+static void at91rm9200_emac_isr (void * unused)
+{
+ unsigned long status32;
+
+ /* get the ISR status and determine RX or TX */
+ status32 = EMAC_REG(EMAC_ISR);
+
+ if (status32 & EMAC_INT_ABT) {
+ EMAC_REG(EMAC_IDR) = EMAC_INT_ABT; /* disable it */
+ rtems_panic("AT91RM9200 Ethernet MAC has received an Abort.\n");
+ }
+
+ if (status32 & (EMAC_INT_RCOM | /* Receive complete */
+ EMAC_INT_RBNA | /* Receive buffer not available */
+ EMAC_INT_ROVR)) { /* Receive overrun */
+
+ /* disable the RX interrupts */
+ EMAC_REG(EMAC_IDR) = (EMAC_INT_RCOM | /* Receive complete */
+ EMAC_INT_RBNA | /* Receive buf not available */
+ EMAC_INT_ROVR); /* Receive overrun */
+
+ rtems_bsdnet_event_send (softc.rxDaemonTid, START_RECEIVE_EVENT);
+ }
+
+ if (status32 & EMAC_INT_TCOM) { /* Transmit buffer register empty */
+
+ /* disable the TX interrupts */
+ EMAC_REG(EMAC_IDR) = EMAC_INT_TCOM;
+
+ rtems_bsdnet_event_send (softc.txDaemonTid, START_TRANSMIT_EVENT);
+ }
+}
+
diff --git a/bsps/arm/edb7312/net/network.c b/bsps/arm/edb7312/net/network.c
new file mode 100644
index 0000000000..480808aeaa
--- /dev/null
+++ b/bsps/arm/edb7312/net/network.c
@@ -0,0 +1,126 @@
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <rtems.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <bsp/irq.h>
+#include <libchip/cs8900.h>
+#include <assert.h>
+
+#define CS8900_BASE 0x20000300
+unsigned int bsp_cs8900_io_base = 0;
+unsigned int bsp_cs8900_memory_base = 0;
+static void cs8900_isr(void *);
+
+char g_enetbuf[1520];
+
+static void cs8900_isr(void *arg)
+{
+ cs8900_interrupt(BSP_EINT3, arg);
+}
+
+/* cs8900_io_set_reg - set one of the I/O addressed registers */
+void cs8900_io_set_reg (cs8900_device *cs, unsigned short reg, unsigned short data)
+{
+ /* works the same for all values of dev */
+/*
+ printf("cs8900_io_set_reg: reg: %#6x, val %#6x\n",
+ CS8900_BASE + reg,
+ data);
+*/
+ *(unsigned short *)(CS8900_BASE + reg) = data;
+}
+
+/* cs8900_io_get_reg - reads one of the I/O addressed registers */
+unsigned short cs8900_io_get_reg (cs8900_device *cs, unsigned short reg)
+{
+ unsigned short val;
+ /* works the same for all values of dev */
+ val = *(unsigned short *)(CS8900_BASE + reg);
+/*
+ printf("cs8900_io_get_reg: reg: %#6x, val %#6x\n", reg, val);
+*/
+ return val;
+}
+
+/* cs8900_mem_set_reg - sets one of the registers mapped through
+ * PacketPage
+ */
+void cs8900_mem_set_reg (cs8900_device *cs, unsigned long reg, unsigned short data)
+{
+ /* works the same for all values of dev */
+ cs8900_io_set_reg(cs, CS8900_IO_PACKET_PAGE_PTR, reg);
+ cs8900_io_set_reg(cs, CS8900_IO_PP_DATA_PORT0, data);
+}
+
+/* cs8900_mem_get_reg - reads one of the registers mapped through
+ * PacketPage
+ */
+unsigned short cs8900_mem_get_reg (cs8900_device *cs, unsigned long reg)
+{
+ /* works the same for all values of dev */
+ cs8900_io_set_reg(cs, CS8900_IO_PACKET_PAGE_PTR, reg);
+ return cs8900_io_get_reg(cs, CS8900_IO_PP_DATA_PORT0);
+}
+
+void cs8900_attach_interrupt (cs8900_device *cs)
+{
+ rtems_status_code status = RTEMS_SUCCESSFUL;
+
+ status = rtems_interrupt_handler_install(
+ BSP_EINT3,
+ "Network",
+ RTEMS_INTERRUPT_UNIQUE,
+ cs8900_isr,
+ cs
+ );
+ assert(status == RTEMS_SUCCESSFUL);
+}
+
+void cs8900_detach_interrupt (cs8900_device *cs)
+{
+ rtems_status_code status = RTEMS_SUCCESSFUL;
+
+ status = rtems_interrupt_handler_remove(
+ BSP_EINT3,
+ cs8900_isr,
+ cs
+ );
+ assert(status == RTEMS_SUCCESSFUL);
+}
+
+unsigned short cs8900_get_data_block (cs8900_device *cs, unsigned char *data)
+{
+ int len;
+ int i;
+
+ len = cs8900_mem_get_reg(cs, CS8900_PP_RxLength);
+
+ for (i = 0; i < ((len + 1) / 2); i++) {
+ ((short *)data)[i] = cs8900_io_get_reg(cs,
+ CS8900_IO_RX_TX_DATA_PORT0);
+ }
+ return len;
+}
+
+void cs8900_tx_load (cs8900_device *cs, struct mbuf *m)
+{
+ int len;
+ unsigned short *data;
+ int i;
+
+ len = 0;
+
+ do {
+ memcpy(&g_enetbuf[len], mtod(m, const void *), m->m_len);
+ len += m->m_len;
+ m = m->m_next;
+ } while (m != 0);
+
+ data = (unsigned short *) &g_enetbuf[0];
+ for (i = 0; i < ((len + 1) / 2); i++) {
+ cs8900_io_set_reg(cs,
+ CS8900_IO_RX_TX_DATA_PORT0,
+ data[i]);
+ }
+}
diff --git a/bsps/arm/gumstix/net/rtl8019.c b/bsps/arm/gumstix/net/rtl8019.c
new file mode 100644
index 0000000000..995835c689
--- /dev/null
+++ b/bsps/arm/gumstix/net/rtl8019.c
@@ -0,0 +1,1196 @@
+/*
+ *By Yang Xi <hiyangxi@gmail.com>.
+ *RTL8019 ethernet driver for Gumstix on SKYEYE simulator
+ *Based on NE2000 driver for pc386 bsp
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <bsp.h>
+#include <bsp/irq.h>
+#include "wd80x3.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+
+/* Define this to force byte-wide data transfers with the NIC. This
+ is needed for boards like the TS-1325 386EX PC, which support only
+ an 8-bit PC/104 bus. Undefine this on a normal PC.*/
+
+#define NE2000_BYTE_TRANSFERS
+
+/* Define this to print debugging messages with printk. */
+
+/*#define DEBUG_NE2000
+ #define DEBUG_NE*/
+
+/* We expect to be able to read a complete packet into an mbuf. */
+
+#if (MCLBYTES < 1520)
+# error "Driver must have MCLBYTES >= 1520"
+#endif
+
+/* The 8390 macro definitions in wd80x3.h expect RO to be defined. */
+#define RO 0
+
+/* Minimum size of Ethernet packet. */
+#define ET_MINLEN 60
+
+/* The number of NE2000 devices supported by this driver. */
+
+#define NNEDRIVER 1
+
+/* RTEMS event number used by the interrupt handler to signal the
+ driver task. This must not be any of the events used by the
+ network task synchronization. */
+#define INTERRUPT_EVENT RTEMS_EVENT_1
+
+/* RTEMS event number used to start the transmit daemon. This must
+ not be the same as INTERRUPT_EVENT. */
+#define START_TRANSMIT_EVENT RTEMS_EVENT_2
+
+/* Interrupts we want to handle from the device. */
+
+#define NE_INTERRUPTS \
+ (MSK_PRX | MSK_PTX | MSK_RXE | MSK_TXE | MSK_OVW | MSK_CNT)
+
+/* The size of a page in device memory. */
+
+#define NE_PAGE_SIZE (256)
+
+/* The first page address in device memory. */
+
+#define NE_START_PAGE (0x40)
+
+/* The last page address, plus 1. */
+
+#define NE_STOP_PAGE (0x80)
+
+/* The number of pages used for a single transmit buffer. This is
+ 1536 bytes, enough for a full size packet. */
+
+#define NE_TX_PAGES (6)
+
+/* The number of transmit buffers. We use two, so we can load one
+ packet while the other is being sent. */
+
+#define NE_TX_BUFS (2)
+
+/* We use the first pages in memory as transmit buffers, and the
+ remaining ones as receive buffers. */
+
+#define NE_FIRST_TX_PAGE (NE_START_PAGE)
+
+#define NE_FIRST_RX_PAGE (NE_FIRST_TX_PAGE + NE_TX_PAGES * NE_TX_BUFS)
+
+/* Data we store for each NE2000 device. */
+
+struct ne_softc {
+ /* The bsdnet information structure. */
+ struct arpcom arpcom;
+
+ /* The interrupt request number. */
+ unsigned int irno;
+ /* The base IO port number. */
+ unsigned int port;
+
+ /* Whether we accept broadcasts. */
+ int accept_broadcasts;
+
+ /* The thread ID of the transmit task. */
+ rtems_id tx_daemon_tid;
+ /* The thread ID of the receive task. */
+ rtems_id rx_daemon_tid;
+
+ /* Whether we use byte-transfers with the device. */
+ bool byte_transfers;
+
+ /* The number of memory buffers which the transmit daemon has loaded
+ with data to be sent, but which have not yet been completely
+ sent. */
+ int inuse;
+ /* The index of the next available transmit memory buffer. */
+ int nextavail;
+ /* The index of the next transmit buffer to send. */
+ int nextsend;
+ /* Nonzero if the device is currently transmitting a packet. */
+ int transmitting;
+ /* The length of the data stored in each transmit buffer. */
+ int sendlen[NE_TX_BUFS];
+
+ /* Set if we have a packet overrun while receiving. */
+ int overrun;
+ /* Set if we should resend after an overrun. */
+ int resend;
+
+ /* Statistics. */
+ struct {
+ /* Number of packets received. */
+ unsigned long rx_packets;
+ /* Number of packets sent. */
+ unsigned long tx_packets;
+ /* Number of interrupts. */
+ unsigned long interrupts;
+ /* Number of receive acknowledgements. */
+ unsigned long rx_acks;
+ /* Number of transmit acknowledgements. */
+ unsigned long tx_acks;
+ /* Number of packet overruns. */
+ unsigned long overruns;
+ /* Number of frame errors. */
+ unsigned long rx_frame_errors;
+ /* Number of CRC errors. */
+ unsigned long rx_crc_errors;
+ /* Number of missed packets. */
+ unsigned long rx_missed_errors;
+ } stats;
+};
+
+/* The list of NE2000 devices on this system. */
+
+static struct ne_softc ne_softc[NNEDRIVER];
+
+/*
+ * receive ring descriptor
+ *
+ * The National Semiconductor DS8390 Network interface controller uses
+ * the following receive ring headers. The way this works is that the
+ * memory on the interface card is chopped up into 256 bytes blocks.
+ * A contiguous portion of those blocks are marked for receive packets
+ * by setting start and end block #'s in the NIC. For each packet that
+ * is put into the receive ring, one of these headers (4 bytes each) is
+ * tacked onto the front. The first byte is a copy of the receiver status
+ * register at the time the packet was received.
+ */
+struct ne_ring
+{
+ unsigned char rsr; /* receiver status */
+ unsigned char next; /* pointer to next packet */
+ unsigned short count; /* bytes in packet (length + 4) */
+};
+
+/* Forward declarations to avoid warnings */
+
+static void ne_init_irq_handler (struct ne_softc *sc);
+static void ne_stop (struct ne_softc *sc);
+static void ne_stop_hardware (struct ne_softc *sc);
+static void ne_init (void *arg);
+static void ne_init_hardware (struct ne_softc *sc);
+
+static void ne_reset(struct ne_softc *sc);
+#ifdef DEBUG_NE
+static void ne_dump(struct ne_softc *sc);
+#endif
+
+/* Read data from an NE2000 device. Read LEN bytes at ADDR, storing
+ them into P. */
+
+static void
+ne_read_data (struct ne_softc *sc, int addr, int len, unsigned char *p)
+{
+ unsigned int port = sc->port;
+ unsigned int dport = port + DATAPORT;
+ outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STA);
+ outport_byte (port + RBCR0, len);
+ outport_byte (port + RBCR1, len >> 8);
+ outport_byte (port + RSAR0, addr);
+ outport_byte (port + RSAR1, addr >> 8);
+ outport_byte (port + CMDR, MSK_PG0 | MSK_RRE | MSK_STA);
+
+ if (sc->byte_transfers)
+ {
+ unsigned char d;
+ while (len > 0)
+ {
+ inport_byte(dport, d);
+ *p++ = d;
+ len--;
+ }
+ }
+ else /* word transfers */
+ {
+ unsigned short d;
+ while (len > 1)
+ {
+ inport_word(dport, d);
+ *p++ = d;
+ *p++ = d >> 8;
+ len -= 2;
+ }
+ if (len)
+ {
+ inport_word(dport, d);
+ *p++ = d;
+ }
+ }
+
+ outport_byte (port + ISR, MSK_RDC);
+}
+
+/* Handle the current NE2000 status. This is called when the device
+ signals an interrupt. It is also called at other times while
+ NE2000 interrupts have been disabled. */
+
+static void
+ne_check_status (struct ne_softc *sc, int from_irq_handler)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ unsigned int port = sc->port;
+ unsigned char status;
+
+ /* It seems that we need to use a loop here, because if the NE2000
+ signals an interrupt because packet transmission is complete, and
+ then receives a packet while interrupts are disabled, it seems to
+ sometimes fail to signal the interrupt for the received packet
+ when interrupts are reenabled. (Based on the behaviour of the
+ Realtek 8019AS chip). */
+
+ /* int count = 0; */
+ while (1)
+ {
+ inport_byte (port + ISR, status);
+#ifdef DEBUG_NE2000
+ printk ("NE2000 status 0x%x\n", status);
+#endif
+ if (status == 0)
+ break;
+
+ /* ack */
+ outport_byte (port + ISR, status);
+
+ /* Check for incoming packet overwrite. */
+ if (status & MSK_OVW)
+ {
+ ifp->if_timer = 0;
+#ifdef DEBUG_NE
+ printk("^\n");
+#endif
+ ++sc->stats.overruns;
+ ne_reset(sc);
+ /* Reenable device interrupts. */
+ if (from_irq_handler)
+ outport_byte(port + IMR, NE_INTERRUPTS);
+ return;
+ }
+
+ /* Check for transmitted packet. The transmit daemon may now be
+ able to send another packet to the device. */
+ if ((status & (MSK_PTX | MSK_TXE)) != 0)
+ {
+ ifp->if_timer = 0;
+ ++sc->stats.tx_acks;
+ --sc->inuse;
+ sc->transmitting = 0;
+ if (sc->inuse > 0 || (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) != 0)
+ rtems_bsdnet_event_send (sc->tx_daemon_tid, START_TRANSMIT_EVENT);
+ }
+
+ /* Check for received packet. */
+ if ((status & (MSK_PRX | MSK_RXE)) != 0)
+ {
+ ++sc->stats.rx_acks;
+ rtems_bsdnet_event_send (sc->rx_daemon_tid, INTERRUPT_EVENT);
+ }
+
+ /* Check for counter change. */
+ if ((status & MSK_CNT) != 0)
+ {
+ unsigned char add;
+ inport_byte (port + CNTR0, add);
+ sc->stats.rx_frame_errors += add;
+ inport_byte (port + CNTR1, add);
+ sc->stats.rx_crc_errors += add;
+ inport_byte (port + CNTR2, add);
+ sc->stats.rx_missed_errors += add;
+ }
+
+ break;
+ }
+
+ outport_byte (port + CMDR, MSK_PG0 | MSK_STA | MSK_RD2);
+
+}
+
+/* Handle an NE2000 interrupt. */
+
+static void
+ne_interrupt_handler (void *arg)
+{
+ struct ne_softc *sc = arg;
+
+ if (sc == NULL)
+ return;
+
+ ++sc->stats.interrupts;
+
+#ifdef DEBUG_NE
+ printk("!");
+#endif
+ ne_check_status(sc, 1);
+}
+
+/* Turn NE2000 interrupts on. */
+
+static void
+ne_interrupt_on (struct ne_softc *sc)
+{
+#ifdef DEBUG_NE
+ printk ("ne_interrupt_on()\n");
+#endif
+ if (sc != NULL)
+ outport_byte (sc->port + IMR, NE_INTERRUPTS);
+}
+
+/* Initialize the NE2000 hardware. */
+
+static void
+ne_init_hardware (struct ne_softc *sc)
+{
+ unsigned int port = sc->port;
+ int i;
+
+#ifdef DEBUG_NE2000
+ printk ("ne_init_hardware()\n");
+#endif
+
+ /* Initialize registers. */
+
+ /* Set interface for page 0, Remote DMA complete, Stopped */
+ outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP);
+
+ /* Set FIFO threshold to 8, No auto-init Remote DMA, byte order=80x86 */
+ /* byte-wide DMA xfers */
+ if (sc->byte_transfers)
+ outport_byte (port + DCR, MSK_FT10 | MSK_BMS);
+ /* word-wide DMA xfers */
+ else
+ outport_byte (port + DCR, MSK_FT10 | MSK_BMS | MSK_WTS);
+
+ /* Clear Remote Byte Count Registers */
+ outport_byte (port + RBCR0, 0);
+ outport_byte (port + RBCR1, 0);
+
+ /* For the moment, don't store incoming packets in memory. */
+ outport_byte (port + RCR, MSK_MON);
+
+ /* Place NIC in internal loopback mode */
+ outport_byte (port + TCR, MSK_LOOP);
+
+ /* Initialize transmit/receive (ring-buffer) Page Start */
+ outport_byte (port + TPSR, NE_FIRST_TX_PAGE);
+ outport_byte (port + PSTART, NE_FIRST_RX_PAGE);
+
+ /* Initialize Receiver (ring-buffer) Page Stop and Boundary */
+ outport_byte (port + PSTOP, NE_STOP_PAGE);
+ outport_byte (port + BNRY, NE_STOP_PAGE - 1);
+
+ /* Clear all interrupts */
+ outport_byte (port + ISR, 0xff);
+ /* Disable all interrupts */
+ outport_byte (port + IMR, 0);
+
+ /* Program Command Register for page 1 */
+ outport_byte (port + CMDR, MSK_PG1 | MSK_RD2 | MSK_STP);
+
+ /* Set the Ethernet hardware address. */
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ outport_byte (port + PAR + i, sc->arpcom.ac_enaddr[i]);
+
+ /* Set Current Page pointer to next_packet */
+ outport_byte (port + CURR, NE_FIRST_RX_PAGE);
+
+ /* Clear the multicast address. */
+ for (i = 0; i < MARsize; ++i)
+ outport_byte (port + MAR + i, 0);
+
+ /* Set page 0 registers */
+ outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP);
+
+ /* accept broadcast */
+ outport_byte (port + RCR, (sc->accept_broadcasts ? MSK_AB : 0));
+
+ /* Start interface */
+ outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STA);
+
+ /* Take interface out of loopback */
+ outport_byte (port + TCR, 0);
+}
+
+/* Set up interrupts.
+*/
+static void
+ne_init_irq_handler(struct ne_softc *sc)
+{
+ rtems_status_code status = RTEMS_SUCCESSFUL;
+
+#ifdef DEBUG_NE
+ printk("ne_init_irq_handler(%d)\n", sc->irno);
+#endif
+
+ status = rtems_interrupt_handler_install(
+ sc->irno,
+ "RTL8019",
+ RTEMS_INTERRUPT_UNIQUE,
+ ne_interrupt_handler,
+ sc
+ );
+ assert(status == RTEMS_SUCCESSFUL);
+ ne_interrupt_on(sc);
+}
+
+/* The NE2000 packet receive daemon. This task is started when the
+ NE2000 driver is initialized. */
+
+#ifdef DEBUG_NE
+static int ccc = 0; /* experinent! */
+#endif
+
+static void
+ne_rx_daemon (void *arg)
+{
+ struct ne_softc *sc = (struct ne_softc *) arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ unsigned int port = sc->port;
+
+ while (1)
+ {
+ rtems_event_set events;
+
+ /* Wait for the interrupt handler to tell us that there is a
+ packet ready to receive. */
+ rtems_bsdnet_event_receive (INTERRUPT_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events);
+
+ /* Don't let the device interrupt us now. */
+ outport_byte (port + IMR, 0);
+
+ while (1)
+ {
+ unsigned char startpage, currpage;
+ unsigned short len;
+ unsigned char next, cnt1, cnt2;
+ struct mbuf *m = NULL;
+ unsigned char *p;
+ int startaddr;
+ int toend;
+ struct ether_header *eh;
+ struct ne_ring hdr; /* ring buffer header */
+ int reset;
+
+ inport_byte (port + BNRY, startpage);
+
+ outport_byte (port + CMDR, MSK_PG1 | MSK_RD2);
+ inport_byte (port + CURR, currpage);
+ outport_byte (port + CMDR, MSK_PG0 | MSK_RD2);
+
+ ++startpage;
+ if (startpage >= NE_STOP_PAGE)
+ startpage = NE_FIRST_RX_PAGE;
+
+ if (startpage == currpage)
+ break;
+
+#ifdef DEBUG_NE2000
+ printk ("ne_rx_daemon: start page %x; current page %x\n",
+ startpage, currpage);
+#endif
+
+ reset = 0;
+
+ /* Read the buffer header */
+ startaddr = startpage * NE_PAGE_SIZE;
+ ne_read_data(sc, startaddr, sizeof(hdr), (unsigned char *)&hdr);
+ next = hdr.next;
+
+ if (next >= NE_STOP_PAGE)
+ next = NE_FIRST_RX_PAGE;
+
+ /* check packet length */
+ len = hdr.count;
+ if (currpage < startpage)
+ cnt1 = currpage + (NE_STOP_PAGE - NE_FIRST_RX_PAGE) - startpage;
+ else
+ cnt1 = currpage - startpage;
+ cnt2 = len / NE_PAGE_SIZE;
+ if (len % NE_PAGE_SIZE)
+ cnt2++;
+ if (cnt1 < cnt2)
+ {
+#ifdef DEBUG_NE
+ printk("(%x<%x:%x)\n", cnt1, cnt2, len);
+/*
+ printk("start page 0x%x; current page 0x%x\n",
+ startpage, currpage);
+ printk("cnt1 < cnt2 (0x%x, 0x%x); len 0x%x\n",
+ cnt1, cnt2, len);
+*/
+#endif
+ reset = 1;
+ }
+ if (len > (ETHER_MAX_LEN - ETHER_CRC_LEN + sizeof(struct ne_ring)) ||
+ len < (ETHER_MIN_LEN - ETHER_CRC_LEN + sizeof(struct ne_ring)) ||
+ len > MCLBYTES)
+ {
+#ifdef DEBUG_NE
+ printk("(%x)", len);
+
+ printk("start page 0x%x; current page 0x%x\n",
+ startpage, currpage);
+ printk("len out of range: 0x%x\n", len);
+ printk("stat: 0x%x, next: 0x%x\n", hdr.rsr, hdr.next);
+#endif
+ reset = 1;
+ }
+#ifdef DEBUG_NE
+ if (++ccc == 100)
+ { ccc = 0; reset = 1;
+ printk("T");
+ }
+#endif
+
+ /* reset interface */
+ if (reset)
+ {
+ printk("Reset in RX\n");
+ ne_reset(sc);
+ goto Next;
+ }
+
+ /* The first four bytes of the length are the buffer header
+ * Just decrease them by 2 since in ARM, we have to make sure
+ * 4bytes memory access align on 4bytes
+ */
+ len -= 2;
+ startaddr += 2;
+
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+
+ if (m == NULL)
+ panic ("ne_rx_daemon");
+
+ m->m_pkthdr.rcvif = ifp;
+ m->m_nextpkt = 0;
+
+ p = mtod (m, unsigned char *);
+ m->m_len = m->m_pkthdr.len = len - (sizeof(struct ether_header) + 2);
+
+ toend = NE_STOP_PAGE * NE_PAGE_SIZE - startaddr;
+ if (toend < len)
+ {
+ ne_read_data (sc, startaddr, toend, p);
+ p += toend;
+ len -= toend;
+ startaddr = NE_FIRST_RX_PAGE * NE_PAGE_SIZE;
+ }
+
+ if (len > 0)
+ ne_read_data (sc, startaddr, len, p);
+
+ m->m_data +=2;
+ eh = mtod(m, struct ether_header *);
+ m->m_data += sizeof (struct ether_header);
+
+#ifdef DEBUG_NE
+ /*printk("[r%d]", hdr.count - sizeof(hdr));*/
+ printk("<\n");
+#endif
+ ether_input (ifp, eh, m);
+ ++sc->stats.rx_packets;
+ outport_byte (port + BNRY, next - 1);
+ }
+
+ if (sc->overrun) {
+ outport_byte (port + ISR, MSK_OVW);
+ outport_byte (port + TCR, 0);
+ if (sc->resend)
+ outport_byte (port + CMDR, MSK_PG0 | MSK_TXP | MSK_RD2 | MSK_STA);
+ sc->resend = 0;
+ sc->overrun = 0;
+ }
+
+ Next:
+ /* Reenable device interrupts. */
+ outport_byte (port + IMR, NE_INTERRUPTS);
+ }
+}
+
+/* Load an NE2000 packet onto the device. */
+
+static void
+ne_loadpacket (struct ne_softc *sc, struct mbuf *m)
+{
+ unsigned int port = sc->port;
+ unsigned int dport = port + DATAPORT;
+ struct mbuf *mhold = m;
+ int leftover;
+ unsigned char leftover_data;
+ int timeout;
+ int send_cnt = 0;
+
+#ifdef DEBUG_NE2000
+ printk ("Uploading NE2000 packet\n");
+#endif
+
+ /* Reset remote DMA complete flag. */
+ outport_byte (port + ISR, MSK_RDC);
+
+ /* Write out the count. */
+ outport_byte (port + RBCR0, m->m_pkthdr.len);
+ outport_byte (port + RBCR1, m->m_pkthdr.len >> 8);
+
+ sc->sendlen[sc->nextavail] = m->m_pkthdr.len;
+
+ /* Tell the device which address we want to write to. */
+ outport_byte (port + RSAR0, 0);
+ outport_byte (port + RSAR1,
+ NE_FIRST_TX_PAGE + (sc->nextavail * NE_TX_PAGES));
+
+ /* Set up the write. */
+ outport_byte (port + CMDR, MSK_PG0 | MSK_RWR | MSK_STA);
+
+ /* Transfer the mbuf chain to device memory. NE2000 devices require
+ that the data be transferred as words, so we need to handle odd
+ length mbufs. Never occurs if we force byte transfers. */
+
+ leftover = 0;
+ leftover_data = '\0';
+
+ for (; m != NULL; m = m->m_next) {
+ int len;
+ unsigned char *data;
+
+ len = m->m_len;
+ if (len == 0)
+ continue;
+
+ data = mtod (m, unsigned char *);
+
+ if (leftover) {
+ unsigned char next;
+
+ /* Data left over from previous mbuf in chain. */
+ next = *data++;
+ --len;
+ outport_word (dport, leftover_data | (next << 8));
+ send_cnt += 2;
+ leftover = 0;
+ }
+
+ /* If using byte transfers, len always ends up as zero so
+ there are no leftovers. */
+
+ if (sc->byte_transfers)
+ while (len > 0) {
+ outport_byte (dport, *data++);
+ len--;
+ }
+ else
+ while (len > 1) {
+ outport_word (dport, data[0] | (data[1] << 8));
+ data += 2;
+ len -= 2;
+ send_cnt += 2;
+ }
+
+ if (len > 0)
+ {
+ leftover = 1;
+ leftover_data = *data++;
+ }
+ }
+
+ if (leftover)
+ {
+ outport_word (dport, leftover_data);
+ send_cnt += 2;
+ }
+
+#ifdef DEBUG_NE
+ /* printk("{l%d|%d}", send_cnt, sc->nextavail); */
+ printk("v");
+#endif
+ m_freem (mhold);
+
+ /* Wait for the device to complete accepting the data, with a
+ limiting counter so that we don't wait too long. */
+ for (timeout = 0; timeout < 1000; ++timeout)
+ {
+ unsigned char status;
+
+ inport_byte (port + ISR, status);
+
+#ifdef DEBUG_NE2000
+ if ((status &~ MSK_RDC) != 0)
+ printk ("Status 0x%x while waiting for acknowledgement of uploaded packet\n",
+ status);
+#endif
+
+ if ((status & MSK_RDC) != 0) {
+ outport_byte (port + ISR, MSK_RDC);
+ break;
+ }
+ }
+
+ if (timeout >= 1000)
+ printk ("Timed out waiting for acknowledgement of uploaded NE2000 packet\n");
+
+ ++sc->nextavail;
+ if (sc->nextavail == NE_TX_BUFS)
+ sc->nextavail = 0;
+}
+
+/* Tell the NE2000 to transmit a buffer whose contents we have already
+ loaded onto the device. */
+
+static void
+ne_transmit (struct ne_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ unsigned int port = sc->port;
+ int len;
+
+#ifdef DEBUG_NE2000
+ printk ("Transmitting NE2000 packet\n");
+#endif
+
+ len = sc->sendlen[sc->nextsend];
+ if (len < ET_MINLEN)
+ len = ET_MINLEN;
+ outport_byte (port + TBCR0, len);
+ outport_byte (port + TBCR1, len >> 8);
+
+ outport_byte (port + TPSR, NE_FIRST_TX_PAGE + (sc->nextsend * NE_TX_PAGES));
+
+ outport_byte (port + CMDR, MSK_PG0 | MSK_TXP | MSK_RD2 | MSK_STA);
+
+#ifdef DEBUG_NE
+ /* printk("{s%d|%d}", len, sc->nextsend); */
+ printk(">");
+#endif
+ ++sc->nextsend;
+ if (sc->nextsend == NE_TX_BUFS)
+ sc->nextsend = 0;
+
+ ++sc->stats.tx_packets;
+
+ /* set watchdog timer */
+ ifp->if_timer = 2;
+}
+
+/* The NE2000 packet transmit daemon. This task is started when the
+ NE2000 driver is initialized. */
+
+static void
+ne_tx_daemon (void *arg)
+{
+ struct ne_softc *sc = (struct ne_softc *) arg;
+ unsigned int port = sc->port;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ while (1) {
+ rtems_event_set events;
+
+ /* Wait for a packet to be ready for sending, or for there to be
+ room for another packet in the device memory. */
+ rtems_bsdnet_event_receive (START_TRANSMIT_EVENT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &events);
+
+#ifdef DEBUG_NE2000
+ printk ("ne_tx_daemon\n");
+#endif
+
+ /* This daemon handles both uploading data onto the device and
+ telling the device to transmit data which has been uploaded.
+ These are separate tasks, because while the device is
+ transmitting one buffer we will upload another. */
+
+ /* Don't let the device interrupt us now. */
+ outport_byte (port + IMR, 0);
+
+ while (1) {
+ struct mbuf *m;
+
+ /* If the device is not transmitting a packet, and we have
+ uploaded a packet, tell the device to transmit it. */
+ if (! sc->transmitting && sc->inuse > 0) {
+ sc->transmitting = 1;
+ ne_transmit (sc);
+ }
+
+ /* If we don't have any more buffers to send, quit now. */
+ if (ifp->if_snd.ifq_head == NULL) {
+ ifp->if_flags &= ~IFF_OACTIVE;
+ break;
+ }
+
+ /* Allocate a buffer to load data into. If there are none
+ available, quit until a buffer has been transmitted. */
+ if (sc->inuse >= NE_TX_BUFS)
+ break;
+
+ ++sc->inuse;
+
+ IF_DEQUEUE (&ifp->if_snd, m);
+ if (m == NULL)
+ panic ("ne_tx_daemon");
+
+ ne_loadpacket (sc, m);
+
+ /* Check the device status. It may have finished transmitting
+ the last packet. */
+ ne_check_status(sc, 0);
+ }
+
+ /* Reenable device interrupts. */
+ outport_byte (port + IMR, NE_INTERRUPTS);
+ }
+}
+
+/* Start sending an NE2000 packet. */
+
+static void
+ne_start (struct ifnet *ifp)
+{
+ struct ne_softc *sc = ifp->if_softc;
+
+#ifdef DEBUG_NE
+ printk("S");
+#endif
+ /* Tell the transmit daemon to wake up and send a packet. */
+ rtems_bsdnet_event_send (sc->tx_daemon_tid, START_TRANSMIT_EVENT);
+ ifp->if_flags |= IFF_OACTIVE;
+}
+
+/* Initialize and start and NE2000. */
+
+static void
+ne_init (void *arg)
+{
+ struct ne_softc *sc = (struct ne_softc *) arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+#ifdef DEBUG_NE
+ printk("ne_init()\n");
+ ne_dump(sc);
+#endif
+
+ /* only once... */
+ if (sc->tx_daemon_tid == 0)
+ {
+ sc->inuse = 0;
+ sc->nextavail = 0;
+ sc->nextsend = 0;
+ sc->transmitting = 0;
+
+ ne_init_hardware (sc);
+
+ sc->tx_daemon_tid = rtems_bsdnet_newproc ("SCtx", 4096, ne_tx_daemon, sc);
+ sc->rx_daemon_tid = rtems_bsdnet_newproc ("SCrx", 4096, ne_rx_daemon, sc);
+
+ /* install rtems irq handler */
+ ne_init_irq_handler(sc);
+ }
+
+ ifp->if_flags |= IFF_RUNNING;
+}
+
+/* Stop an NE2000. */
+
+static void
+ne_stop (struct ne_softc *sc)
+{
+ sc->arpcom.ac_if.if_flags &= ~IFF_RUNNING;
+
+ ne_stop_hardware(sc);
+
+ sc->inuse = 0;
+ sc->nextavail = 0;
+ sc->nextsend = 0;
+ sc->transmitting = 0;
+ sc->overrun = 0;
+ sc->resend = 0;
+}
+
+static void
+ne_stop_hardware (struct ne_softc *sc)
+{
+ unsigned int port = sc->port;
+ int i;
+
+ /* Stop everything. */
+ outport_byte (port + CMDR, MSK_STP | MSK_RD2);
+
+ /* Wait for the interface to stop, using I as a time limit. */
+ for (i = 0; i < 5000; ++i)
+ {
+ unsigned char status;
+
+ inport_byte (port + ISR, status);
+ if ((status & MSK_RST) != 0)
+ break;
+ }
+}
+
+/* reinitializing interface
+*/
+static void
+ne_reset(struct ne_softc *sc)
+{
+ ne_stop(sc);
+ ne_init_hardware(sc);
+ sc->arpcom.ac_if.if_flags |= IFF_RUNNING;
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+#ifdef DEBUG_NE
+ printk("*");
+#endif
+}
+
+#ifdef DEBUG_NE
+/* show anything about ne
+*/
+static void
+ne_dump(struct ne_softc *sc)
+{
+ int i;
+ printk("\nne configuration:\n");
+ printk("ethernet addr:");
+ for (i=0; i<ETHER_ADDR_LEN; i++)
+ printk(" %x", sc->arpcom.ac_enaddr[i]);
+ printk("\n");
+ printk("irq = %d\n", sc->irno);
+ printk("port = 0x%x\n", sc->port);
+ printk("accept_broadcasts = %d\n", sc->accept_broadcasts);
+ printk("byte_transfers = %d\n", sc->byte_transfers);
+}
+#endif
+
+/* Show NE2000 interface statistics. */
+
+static void
+ne_stats (struct ne_softc *sc)
+{
+ printf (" Received packets: %-8lu", sc->stats.rx_packets);
+ printf (" Transmitted packets: %-8lu\n", sc->stats.tx_packets);
+ printf (" Receive acks: %-8lu", sc->stats.rx_acks);
+ printf (" Transmit acks: %-8lu\n", sc->stats.tx_acks);
+ printf (" Packet overruns: %-8lu", sc->stats.overruns);
+ printf (" Frame errors: %-8lu\n", sc->stats.rx_frame_errors);
+ printf (" CRC errors: %-8lu", sc->stats.rx_crc_errors);
+ printf (" Missed packets: %-8lu\n", sc->stats.rx_missed_errors);
+ printf (" Interrupts: %-8lu\n", sc->stats.interrupts);
+}
+
+/* NE2000 driver ioctl handler. */
+
+static int
+ne_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct ne_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command) {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ error = ether_ioctl (ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
+ case IFF_RUNNING:
+ ne_stop (sc);
+ break;
+
+ case IFF_UP:
+ printk("IFF_UP\n");
+ ne_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ ne_stop (sc);
+ ne_init (sc);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ ne_stats (sc);
+ break;
+
+ /* FIXME: Multicast commands must be added here. */
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+/*
+ * Device timeout/watchdog routine. Entered if the device neglects to
+ * generate an interrupt after a transmit has been started on it.
+ */
+static void
+ne_watchdog(struct ifnet *ifp)
+{
+ struct ne_softc *sc = ifp->if_softc;
+
+ printk("ne2000: device timeout\n");
+ ifp->if_oerrors++;
+
+ ne_reset(sc);
+}
+
+static void
+print_byte(unsigned char b)
+{
+ printk("%x%x", b >> 4, b & 0x0f);
+}
+
+/* Attach an NE2000 driver to the system. */
+
+int
+rtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
+{
+ int i;
+ struct ne_softc *sc;
+ struct ifnet *ifp;
+ int mtu;
+
+ /* dettach ... */
+ if (!attach)
+ return 0;
+
+ /* Find a free driver. */
+ sc = NULL;
+ for (i = 0; i < NNEDRIVER; ++i) {
+ sc = &ne_softc[i];
+ ifp = &sc->arpcom.ac_if;
+ if (ifp->if_softc == NULL)
+ break;
+ }
+
+ if (sc == NULL) {
+ printf ("Too many NE2000 drivers.\n");
+ return 0;
+ }
+
+ memset (sc, 0, sizeof *sc);
+
+ /* Check whether we do byte-wide or word-wide transfers. */
+
+#ifdef NE2000_BYTE_TRANSFERS
+ sc->byte_transfers = true;
+#else
+ sc->byte_transfers = false;
+#endif
+
+ /* Handle the options passed in by the caller. */
+
+ if (config->mtu != 0)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ /* We use 16 as the default IRQ. */
+ sc->irno = XSCALE_IRQ_NETWORK;
+
+
+
+ /*IO prts are mapped to 0X40000600 */
+ sc->port = 0x40000600;
+
+ sc->accept_broadcasts = ! config->ignore_broadcast;
+
+ if (config->hardware_address != NULL)
+ memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
+ ETHER_ADDR_LEN);
+ else
+ {
+ unsigned char prom[16];
+ int ia;
+
+ /* Read the PROM to get the Ethernet hardware address. */
+
+ outport_byte (sc->port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP);
+
+ if (sc->byte_transfers) {
+ outport_byte (sc->port + DCR, MSK_FT10 | MSK_BMS);
+ }
+ else {
+ outport_byte (sc->port + DCR, MSK_FT10 | MSK_BMS | MSK_WTS);
+ }
+
+ outport_byte (sc->port + RBCR0, 0);
+ outport_byte (sc->port + RBCR1, 0);
+ outport_byte (sc->port + RCR, MSK_MON);
+ outport_byte (sc->port + TCR, MSK_LOOP);
+ outport_byte (sc->port + IMR, 0);
+ outport_byte (sc->port + ISR, 0xff);
+
+ ne_read_data (sc, 0, sizeof prom, prom);
+
+ outport_byte (sc->port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP);
+
+ for (ia = 0; ia < ETHER_ADDR_LEN; ++ia)
+ sc->arpcom.ac_enaddr[ia] = prom[ia * 2];
+ }
+
+ /* Set up the network interface. */
+
+ ifp->if_softc = sc;
+ ifp->if_unit = i + 1;
+ ifp->if_name = "ne";
+ ifp->if_mtu = mtu;
+ ifp->if_init = ne_init;
+ ifp->if_ioctl = ne_ioctl;
+ ifp->if_watchdog = ne_watchdog;
+ ifp->if_start = ne_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ /* Attach the interface. */
+
+ if_attach (ifp);
+ ether_ifattach (ifp);
+
+ printk("network device '%s' <", config->name);
+ print_byte(sc->arpcom.ac_enaddr[0]);
+ for (i=1; i<ETHER_ADDR_LEN; i++)
+ { printk(":");
+ print_byte(sc->arpcom.ac_enaddr[i]);
+ }
+ printk("> initialized on port 0x%x, irq %d\n", sc->port, sc->irno);
+
+ return 1;
+}
diff --git a/bsps/arm/gumstix/net/wd80x3.h b/bsps/arm/gumstix/net/wd80x3.h
new file mode 100644
index 0000000000..c4a3aba758
--- /dev/null
+++ b/bsps/arm/gumstix/net/wd80x3.h
@@ -0,0 +1,303 @@
+/**
+ * @file
+ * @ingroup gumstix_dp8390
+ * @brief DP8390 Ethernet Controller Support
+ */
+
+/*
+ * Information about the DP8390 Ethernet controller.
+ */
+
+#ifndef __BSP_WD80x3_h
+#define __BSP_WD80x3_h
+
+/* Register descriptions */
+
+/**
+ * @defgroup gumstix_dp8390 DP8390 Support
+ * @ingroup arm_gumstix
+ * @brief DP8390 Ethernet Controller Support
+ * @{
+ */
+
+/**
+ * @name Controller DP8390.
+ * @{
+ */
+
+/** @brief Port Window. */
+#define DATAPORT 0x10
+/** @brief Issue a read for reset */
+#define RESET 0x1f
+/** @brief I/O port definition */
+#define W83CREG 0x00
+#define ADDROM 0x08
+
+/** @} */
+
+/**
+ * @name page 0 read or read/write registers
+ * @{
+ */
+
+#define CMDR 0x00+RO
+/** @brief current local dma addr 0 for read */
+#define CLDA0 0x01+RO
+/** @brief current local dma addr 1 for read */
+#define CLDA1 0x02+RO
+/** @brief boundary reg for rd and wr */
+#define BNRY 0x03+RO
+/** @brief tx status reg for rd */
+#define TSR 0x04+RO
+/** @brief number of collision reg for rd */
+#define NCR 0x05+RO
+/** @breif FIFO for rd */
+#define FIFO 0x06+RO
+/** @brief interrupt status reg for rd and wr */
+#define ISR 0x07+RO
+/** @brief current remote dma address 0 for rd */
+#define CRDA0 0x08+RO
+/** @brief current remote dma address 1 for rd */
+#define CRDA1 0x09+RO
+/** @brief rx status reg for rd */
+#define RSR 0x0C+RO
+/** @brief tally cnt 0 for frm alg err for rd */
+#define CNTR0 0x0D+RO
+/** @brief tally cnt 1 for crc err for rd */
+#define CNTR1 RO+0x0E
+/** @brief tally cnt 2 for missed pkt for rd */
+#define CNTR2 0x0F+RO
+
+/** @} */
+
+/**
+ * @name page 0 write registers
+ * @{
+ */
+
+/** @brief page start register */
+#define PSTART 0x01+RO
+/** @brief page stop register */
+#define PSTOP 0x02+RO
+/** @breif tx start page start reg */
+#define TPSR 0x04+RO
+/** @brief tx byte count 0 reg */
+#define TBCR0 0x05+RO
+/** @brief tx byte count 1 reg */
+#define TBCR1 0x06+RO
+/** @brief remote start address reg 0 */
+#define RSAR0 0x08+RO
+/** @brief remote start address reg 1 */
+#define RSAR1 0x09+RO
+/** @brief remote byte count reg 0 */
+#define RBCR0 0x0A+RO
+/** @brief remote byte count reg 1 */
+#define RBCR1 0x0B+RO
+/** @brief rx configuration reg */
+#define RCR 0x0C+RO
+/** @brief tx configuration reg */
+#define TCR 0x0D+RO
+/** @brief data configuration reg */
+#define DCR RO+0x0E
+/** @brief interrupt mask reg */
+#define IMR 0x0F+RO
+
+/** @} */
+
+/**
+ * @name page 1 registers
+ * @{
+ */
+
+/** @brief physical addr reg base for rd and wr */
+#define PAR 0x01+RO
+/** @brief current page reg for rd and wr */
+#define CURR 0x07+RO
+/** @brief multicast addr reg base fro rd and WR */
+#define MAR 0x08+RO
+/** @brief size of multicast addr space */
+#define MARsize 8
+
+/** @} */
+
+/**
+ * @name W83CREG command bits
+ * @{
+ */
+
+/** @brief W83CREG masks */
+#define MSK_RESET 0x80
+#define MSK_ENASH 0x40
+/** @brief memory decode bits, corresponding */
+#define MSK_DECOD 0x3F
+
+/** @} */
+
+/**
+ * @name CMDR command bits
+ * @{
+ */
+
+/** @brief stop the chip */
+#define MSK_STP 0x01
+/** @brief start the chip */
+#define MSK_STA 0x02
+/** @brief initial txing of a frm */
+#define MSK_TXP 0x04
+/** @brief remote read */
+#define MSK_RRE 0x08
+/** @brief remote write */
+#define MSK_RWR 0x10
+/** @brief no DMA used */
+#define MSK_RD2 0x20
+/** @brief select register page 0 */
+#define MSK_PG0 0x00
+/** @brief select register page 1 */
+#define MSK_PG1 0x40
+/** @brief select register page 2 */
+#define MSK_PG2 0x80
+
+/** @} */
+
+/**
+ * @name ISR and TSR status bits
+ * @{
+ */
+
+/* @brief rx with no error */
+#define MSK_PRX 0x01
+/* @brief tx with no error */
+#define MSK_PTX 0x02
+/* @brief rx with error */
+#define MSK_RXE 0x04
+/* @brief tx with error */
+#define MSK_TXE 0x08
+/* @brief overwrite warning */
+#define MSK_OVW 0x10
+/* @brief MSB of one of the tally counters is set */
+#define MSK_CNT 0x20
+/* @brief remote dma completed */
+#define MSK_RDC 0x40
+/* @brief reset state indicator */
+#define MSK_RST 0x80
+
+/** @} */
+
+/**
+ * @name DCR command bits
+ * @{
+ */
+
+/** @brief word transfer mode selection */
+#define MSK_WTS 0x01
+/** @brief byte order selection */
+#define MSK_BOS 0x02
+/** @brief long addr selection */
+#define MSK_LAS 0x04
+/** @brief burst mode selection */
+#define MSK_BMS 0x08
+/** @brief autoinitialize remote */
+#define MSK_ARM 0x10
+/** @brief burst lrngth selection */
+#define MSK_FT00 0x00
+/** @brief burst lrngth selection */
+#define MSK_FT01 0x20
+/** @brief burst lrngth selection */
+#define MSK_FT10 0x40
+/** @brief burst lrngth selection */
+#define MSK_FT11 0x60
+
+/** @} */
+
+/**
+ * @name RCR command bits
+ * @{
+ */
+
+/** @brief save error pkts */
+#define MSK_SEP 0x01
+/** @brief accept runt pkt */
+#define MSK_AR 0x02
+/** @brief 8390 RCR */
+#define MSK_AB 0x04
+/** @brief accept multicast */
+#define MSK_AM 0x08
+/** @brief accept all pkt with physical adr */
+#define MSK_PRO 0x10
+/** @brief monitor mode */
+#define MSK_MON 0x20
+
+/** @} */
+
+/**
+ * @name TCR command bits
+ * @{
+ */
+
+/** @brief inhibit CRC, do not append crc */
+#define MSK_CRC 0x01
+/** @brief set loopback mode */
+#define MSK_LOOP 0x02
+/** @brief Accept broadcasts */
+#define MSK_BCST 0x04
+/** @brief encoded loopback control */
+#define MSK_LB01 0x06
+/** @brief auto tx disable */
+#define MSK_ATD 0x08
+/** @brief collision offset enable */
+#define MSK_OFST 0x10
+
+/** @} */
+
+/**
+ * @name receive status bits
+ * @{
+ */
+
+/** @brief rx without error */
+#define SMK_PRX 0x01
+/** @brief CRC error */
+#define SMK_CRC 0x02
+/** @brief frame alignment error */
+#define SMK_FAE 0x04
+/** @brief FIFO overrun */
+#define SMK_FO 0x08
+/** @brief missed pkt */
+#define SMK_MPA 0x10
+/** @brief physical/multicase address */
+#define SMK_PHY 0x20
+/** @brief receiver disable. set in monitor mode */
+#define SMK_DIS 0x40
+/** @brief deferring */
+#define SMK_DEF 0x80
+
+/** @} */
+
+/**
+ * @name transmit status bits
+ * @{
+ */
+
+/** @brief tx without error */
+#define SMK_PTX 0x01
+/** @brief non deferred tx */
+#define SMK_DFR 0x02
+/** @brief tx collided */
+#define SMK_COL 0x04
+/** @brief tx abort because of excessive collisions */
+#define SMK_ABT 0x08
+/** @brief carrier sense lost */
+#define SMK_CRS 0x10
+/** @brief FIFO underrun */
+#define SMK_FU 0x20
+/** @brief collision detect heartbeat */
+#define SMK_CDH 0x40
+/** @brief out of window collision */
+#define SMK_OWC 0x80
+
+/** @} */
+
+/** @} */
+
+#endif
+/* end of include */
diff --git a/bsps/arm/rtl22xx/net/network.c b/bsps/arm/rtl22xx/net/network.c
new file mode 100644
index 0000000000..b269a6ddfc
--- /dev/null
+++ b/bsps/arm/rtl22xx/net/network.c
@@ -0,0 +1,126 @@
+/*Note: this file is copy from 7312 BSP, and untested yet*/
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <rtems.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <bsp/irq.h>
+#include <libchip/cs8900.h>
+#include <assert.h>
+
+#define CS8900_BASE 0x20000300
+unsigned int bsp_cs8900_io_base = 0;
+unsigned int bsp_cs8900_memory_base = 0;
+static void cs8900_isr(void *);
+
+char g_enetbuf[1520];
+
+static void cs8900_isr(void *arg)
+{
+ cs8900_interrupt(LPC22xx_INTERRUPT_EINT2, arg);
+}
+
+/* cs8900_io_set_reg - set one of the I/O addressed registers */
+void cs8900_io_set_reg (cs8900_device *cs, unsigned short reg, unsigned short data)
+{
+ /* works the same for all values of dev */
+/*
+ printf("cs8900_io_set_reg: reg: %#6x, val %#6x\n",
+ CS8900_BASE + reg,
+ data);
+*/
+ *(unsigned short *)(CS8900_BASE + reg) = data;
+}
+
+/* cs8900_io_get_reg - reads one of the I/O addressed registers */
+unsigned short cs8900_io_get_reg (cs8900_device *cs, unsigned short reg)
+{
+ unsigned short val;
+ /* works the same for all values of dev */
+ val = *(unsigned short *)(CS8900_BASE + reg);
+/*
+ printf("cs8900_io_get_reg: reg: %#6x, val %#6x\n", reg, val);
+*/
+ return val;
+}
+
+/* cs8900_mem_set_reg - sets one of the registers mapped through
+ * PacketPage
+ */
+void cs8900_mem_set_reg (cs8900_device *cs, unsigned long reg, unsigned short data)
+{
+ /* works the same for all values of dev */
+ cs8900_io_set_reg(cs, CS8900_IO_PACKET_PAGE_PTR, reg);
+ cs8900_io_set_reg(cs, CS8900_IO_PP_DATA_PORT0, data);
+}
+
+/* cs8900_mem_get_reg - reads one of the registers mapped through
+ * PacketPage
+ */
+unsigned short cs8900_mem_get_reg (cs8900_device *cs, unsigned long reg)
+{
+ /* works the same for all values of dev */
+ cs8900_io_set_reg(cs, CS8900_IO_PACKET_PAGE_PTR, reg);
+ return cs8900_io_get_reg(cs, CS8900_IO_PP_DATA_PORT0);
+}
+
+void cs8900_attach_interrupt (cs8900_device *cs)
+{
+ rtems_status_code status = RTEMS_SUCCESSFUL;
+ status = rtems_interrupt_handler_install(
+ LPC22xx_INTERRUPT_EINT2,
+ "Network",
+ RTEMS_INTERRUPT_UNIQUE,
+ cs8900_isr,
+ cs
+ );
+ assert(status == RTEMS_SUCCESSFUL);
+}
+
+void cs8900_detach_interrupt (cs8900_device *cs)
+{
+ rtems_status_code status = RTEMS_SUCCESSFUL;
+ status = rtems_interrupt_handler_remove(
+ LPC22xx_INTERRUPT_EINT2,
+ cs8900_isr,
+ cs
+ );
+ assert(status == RTEMS_SUCCESSFUL);
+}
+
+unsigned short cs8900_get_data_block (cs8900_device *cs, unsigned char *data)
+{
+ int len;
+ int i;
+
+ len = cs8900_mem_get_reg(cs, CS8900_PP_RxLength);
+
+ for (i = 0; i < ((len + 1) / 2); i++) {
+ ((short *)data)[i] = cs8900_io_get_reg(cs,
+ CS8900_IO_RX_TX_DATA_PORT0);
+ }
+ return len;
+}
+
+void cs8900_tx_load (cs8900_device *cs, struct mbuf *m)
+{
+ int len;
+ unsigned short *data;
+ int i;
+
+ len = 0;
+
+ do {
+ memcpy(&g_enetbuf[len], mtod(m, const void *), m->m_len);
+ len += m->m_len;
+ m = m->m_next;
+ } while (m != 0);
+
+ data = (unsigned short *) &g_enetbuf[0];
+ for (i = 0; i < ((len + 1) / 2); i++) {
+ cs8900_io_set_reg(cs,
+ CS8900_IO_RX_TX_DATA_PORT0,
+ data[i]);
+ }
+}
diff --git a/bsps/arm/shared/net/lpc-ethernet.c b/bsps/arm/shared/net/lpc-ethernet.c
new file mode 100644
index 0000000000..fb8f014963
--- /dev/null
+++ b/bsps/arm/shared/net/lpc-ethernet.c
@@ -0,0 +1,1839 @@
+/**
+ * @file
+ *
+ * @ingroup lpc_eth
+ *
+ * @brief Ethernet driver.
+ */
+
+/*
+ * Copyright (c) 2009-2012 embedded brains GmbH. All rights reserved.
+ *
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * 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.
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <rtems.h>
+#include <rtems/rtems_bsdnet.h>
+#include <rtems/rtems_mii_ioctl.h>
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+#include <bsp.h>
+#include <bsp/irq.h>
+#include <bsp/lpc-ethernet-config.h>
+#include <bsp/utility.h>
+
+#if MCLBYTES > (2 * 1024)
+ #error "MCLBYTES to large"
+#endif
+
+#ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
+ #define LPC_ETH_CONFIG_TX_BUF_SIZE sizeof(struct mbuf *)
+#else
+ #define LPC_ETH_CONFIG_TX_BUF_SIZE 1518U
+#endif
+
+#define DEFAULT_PHY 0
+#define WATCHDOG_TIMEOUT 5
+
+typedef struct {
+ uint32_t start;
+ uint32_t control;
+} lpc_eth_transfer_descriptor;
+
+typedef struct {
+ uint32_t info;
+ uint32_t hash_crc;
+} lpc_eth_receive_status;
+
+typedef struct {
+ uint32_t mac1;
+ uint32_t mac2;
+ uint32_t ipgt;
+ uint32_t ipgr;
+ uint32_t clrt;
+ uint32_t maxf;
+ uint32_t supp;
+ uint32_t test;
+ uint32_t mcfg;
+ uint32_t mcmd;
+ uint32_t madr;
+ uint32_t mwtd;
+ uint32_t mrdd;
+ uint32_t mind;
+ uint32_t reserved_0 [2];
+ uint32_t sa0;
+ uint32_t sa1;
+ uint32_t sa2;
+ uint32_t reserved_1 [45];
+ uint32_t command;
+ uint32_t status;
+ uint32_t rxdescriptor;
+ uint32_t rxstatus;
+ uint32_t rxdescriptornum;
+ uint32_t rxproduceindex;
+ uint32_t rxconsumeindex;
+ uint32_t txdescriptor;
+ uint32_t txstatus;
+ uint32_t txdescriptornum;
+ uint32_t txproduceindex;
+ uint32_t txconsumeindex;
+ uint32_t reserved_2 [10];
+ uint32_t tsv0;
+ uint32_t tsv1;
+ uint32_t rsv;
+ uint32_t reserved_3 [3];
+ uint32_t flowcontrolcnt;
+ uint32_t flowcontrolsts;
+ uint32_t reserved_4 [34];
+ uint32_t rxfilterctrl;
+ uint32_t rxfilterwolsts;
+ uint32_t rxfilterwolclr;
+ uint32_t reserved_5 [1];
+ uint32_t hashfilterl;
+ uint32_t hashfilterh;
+ uint32_t reserved_6 [882];
+ uint32_t intstatus;
+ uint32_t intenable;
+ uint32_t intclear;
+ uint32_t intset;
+ uint32_t reserved_7 [1];
+ uint32_t powerdown;
+} lpc_eth_controller;
+
+static volatile lpc_eth_controller *const lpc_eth =
+ (volatile lpc_eth_controller *) LPC_ETH_CONFIG_REG_BASE;
+
+/* ETH_RX_CTRL */
+
+#define ETH_RX_CTRL_SIZE_MASK 0x000007ffU
+#define ETH_RX_CTRL_INTERRUPT 0x80000000U
+
+/* ETH_RX_STAT */
+
+#define ETH_RX_STAT_RXSIZE_MASK 0x000007ffU
+#define ETH_RX_STAT_BYTES 0x00000100U
+#define ETH_RX_STAT_CONTROL_FRAME 0x00040000U
+#define ETH_RX_STAT_VLAN 0x00080000U
+#define ETH_RX_STAT_FAIL_FILTER 0x00100000U
+#define ETH_RX_STAT_MULTICAST 0x00200000U
+#define ETH_RX_STAT_BROADCAST 0x00400000U
+#define ETH_RX_STAT_CRC_ERROR 0x00800000U
+#define ETH_RX_STAT_SYMBOL_ERROR 0x01000000U
+#define ETH_RX_STAT_LENGTH_ERROR 0x02000000U
+#define ETH_RX_STAT_RANGE_ERROR 0x04000000U
+#define ETH_RX_STAT_ALIGNMENT_ERROR 0x08000000U
+#define ETH_RX_STAT_OVERRUN 0x10000000U
+#define ETH_RX_STAT_NO_DESCRIPTOR 0x20000000U
+#define ETH_RX_STAT_LAST_FLAG 0x40000000U
+#define ETH_RX_STAT_ERROR 0x80000000U
+
+/* ETH_TX_CTRL */
+
+#define ETH_TX_CTRL_SIZE_MASK 0x7ffU
+#define ETH_TX_CTRL_SIZE_SHIFT 0
+#define ETH_TX_CTRL_OVERRIDE 0x04000000U
+#define ETH_TX_CTRL_HUGE 0x08000000U
+#define ETH_TX_CTRL_PAD 0x10000000U
+#define ETH_TX_CTRL_CRC 0x20000000U
+#define ETH_TX_CTRL_LAST 0x40000000U
+#define ETH_TX_CTRL_INTERRUPT 0x80000000U
+
+/* ETH_TX_STAT */
+
+#define ETH_TX_STAT_COLLISION_COUNT_MASK 0x01e00000U
+#define ETH_TX_STAT_DEFER 0x02000000U
+#define ETH_TX_STAT_EXCESSIVE_DEFER 0x04000000U
+#define ETH_TX_STAT_EXCESSIVE_COLLISION 0x08000000U
+#define ETH_TX_STAT_LATE_COLLISION 0x10000000U
+#define ETH_TX_STAT_UNDERRUN 0x20000000U
+#define ETH_TX_STAT_NO_DESCRIPTOR 0x40000000U
+#define ETH_TX_STAT_ERROR 0x80000000U
+
+/* ETH_INT */
+
+#define ETH_INT_RX_OVERRUN 0x00000001U
+#define ETH_INT_RX_ERROR 0x00000002U
+#define ETH_INT_RX_FINISHED 0x00000004U
+#define ETH_INT_RX_DONE 0x00000008U
+#define ETH_INT_TX_UNDERRUN 0x00000010U
+#define ETH_INT_TX_ERROR 0x00000020U
+#define ETH_INT_TX_FINISHED 0x00000040U
+#define ETH_INT_TX_DONE 0x00000080U
+#define ETH_INT_SOFT 0x00001000U
+#define ETH_INT_WAKEUP 0x00002000U
+
+/* ETH_RX_FIL_CTRL */
+
+#define ETH_RX_FIL_CTRL_ACCEPT_UNICAST 0x00000001U
+#define ETH_RX_FIL_CTRL_ACCEPT_BROADCAST 0x00000002U
+#define ETH_RX_FIL_CTRL_ACCEPT_MULTICAST 0x00000004U
+#define ETH_RX_FIL_CTRL_ACCEPT_UNICAST_HASH 0x00000008U
+#define ETH_RX_FIL_CTRL_ACCEPT_MULTICAST_HASH 0x00000010U
+#define ETH_RX_FIL_CTRL_ACCEPT_PERFECT 0x00000020U
+#define ETH_RX_FIL_CTRL_MAGIC_PACKET_WOL 0x00001000U
+#define ETH_RX_FIL_CTRL_RX_FILTER_WOL 0x00002000U
+
+/* ETH_CMD */
+
+#define ETH_CMD_RX_ENABLE 0x00000001U
+#define ETH_CMD_TX_ENABLE 0x00000002U
+#define ETH_CMD_REG_RESET 0x00000008U
+#define ETH_CMD_TX_RESET 0x00000010U
+#define ETH_CMD_RX_RESET 0x00000020U
+#define ETH_CMD_PASS_RUNT_FRAME 0x00000040U
+#define ETH_CMD_PASS_RX_FILTER 0X00000080U
+#define ETH_CMD_TX_FLOW_CONTROL 0x00000100U
+#define ETH_CMD_RMII 0x00000200U
+#define ETH_CMD_FULL_DUPLEX 0x00000400U
+
+/* ETH_STAT */
+
+#define ETH_STAT_RX_ACTIVE 0x00000001U
+#define ETH_STAT_TX_ACTIVE 0x00000002U
+
+/* ETH_MAC2 */
+
+#define ETH_MAC2_FULL_DUPLEX BSP_BIT32(8)
+
+/* ETH_SUPP */
+
+#define ETH_SUPP_SPEED BSP_BIT32(8)
+
+/* ETH_MCFG */
+
+#define ETH_MCFG_CLOCK_SELECT(val) BSP_FLD32(val, 2, 4)
+
+#define ETH_MCFG_RESETMIIMGMT BSP_BIT32(15)
+
+/* ETH_MCMD */
+
+#define ETH_MCMD_READ BSP_BIT32(0)
+#define ETH_MCMD_SCAN BSP_BIT32(1)
+
+/* ETH_MADR */
+
+#define ETH_MADR_REG(val) BSP_FLD32(val, 0, 4)
+#define ETH_MADR_PHY(val) BSP_FLD32(val, 8, 12)
+
+/* ETH_MIND */
+
+#define ETH_MIND_BUSY BSP_BIT32(0)
+#define ETH_MIND_SCANNING BSP_BIT32(1)
+#define ETH_MIND_NOT_VALID BSP_BIT32(2)
+#define ETH_MIND_MII_LINK_FAIL BSP_BIT32(3)
+
+/* Events */
+
+#define LPC_ETH_EVENT_INITIALIZE RTEMS_EVENT_1
+
+#define LPC_ETH_EVENT_TXSTART RTEMS_EVENT_2
+
+#define LPC_ETH_EVENT_INTERRUPT RTEMS_EVENT_3
+
+#define LPC_ETH_EVENT_STOP RTEMS_EVENT_4
+
+/* Status */
+
+#define LPC_ETH_INTERRUPT_RECEIVE \
+ (ETH_INT_RX_ERROR | ETH_INT_RX_FINISHED | ETH_INT_RX_DONE)
+
+#define LPC_ETH_INTERRUPT_TRANSMIT \
+ (ETH_INT_TX_DONE | ETH_INT_TX_FINISHED | ETH_INT_TX_ERROR)
+
+#define LPC_ETH_RX_STAT_ERRORS \
+ (ETH_RX_STAT_CRC_ERROR \
+ | ETH_RX_STAT_SYMBOL_ERROR \
+ | ETH_RX_STAT_LENGTH_ERROR \
+ | ETH_RX_STAT_ALIGNMENT_ERROR \
+ | ETH_RX_STAT_OVERRUN \
+ | ETH_RX_STAT_NO_DESCRIPTOR)
+
+#define LPC_ETH_LAST_FRAGMENT_FLAGS \
+ (ETH_TX_CTRL_OVERRIDE \
+ | ETH_TX_CTRL_PAD \
+ | ETH_TX_CTRL_CRC \
+ | ETH_TX_CTRL_INTERRUPT \
+ | ETH_TX_CTRL_LAST)
+
+/* Debug */
+
+#ifdef DEBUG
+ #define LPC_ETH_PRINTF(...) printf(__VA_ARGS__)
+ #define LPC_ETH_PRINTK(...) printk(__VA_ARGS__)
+#else
+ #define LPC_ETH_PRINTF(...)
+ #define LPC_ETH_PRINTK(...)
+#endif
+
+typedef enum {
+ LPC_ETH_STATE_NOT_INITIALIZED = 0,
+ LPC_ETH_STATE_DOWN,
+ LPC_ETH_STATE_UP
+} lpc_eth_state;
+
+typedef struct {
+ struct arpcom arpcom;
+ lpc_eth_state state;
+ struct rtems_mdio_info mdio;
+ uint32_t anlpar;
+ rtems_id receive_task;
+ rtems_id transmit_task;
+ unsigned rx_unit_count;
+ unsigned tx_unit_count;
+ volatile lpc_eth_transfer_descriptor *rx_desc_table;
+ volatile lpc_eth_receive_status *rx_status_table;
+ struct mbuf **rx_mbuf_table;
+ volatile lpc_eth_transfer_descriptor *tx_desc_table;
+ volatile uint32_t *tx_status_table;
+ void *tx_buf_table;
+ unsigned received_frames;
+ unsigned receive_interrupts;
+ unsigned transmitted_frames;
+ unsigned transmit_interrupts;
+ unsigned receive_drop_errors;
+ unsigned receive_overrun_errors;
+ unsigned receive_fragment_errors;
+ unsigned receive_crc_errors;
+ unsigned receive_symbol_errors;
+ unsigned receive_length_errors;
+ unsigned receive_alignment_errors;
+ unsigned receive_no_descriptor_errors;
+ unsigned receive_fatal_errors;
+ unsigned transmit_underrun_errors;
+ unsigned transmit_late_collision_errors;
+ unsigned transmit_excessive_collision_errors;
+ unsigned transmit_excessive_defer_errors;
+ unsigned transmit_no_descriptor_errors;
+ unsigned transmit_overflow_errors;
+ unsigned transmit_fatal_errors;
+ uint32_t phy_id;
+ int phy;
+ rtems_vector_number interrupt_number;
+ rtems_id control_task;
+} lpc_eth_driver_entry;
+
+static lpc_eth_driver_entry lpc_eth_driver_data;
+
+static void lpc_eth_control_request_complete(const lpc_eth_driver_entry *e)
+{
+ rtems_status_code sc = rtems_event_transient_send(e->control_task);
+ assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static void lpc_eth_control_request(
+ lpc_eth_driver_entry *e,
+ rtems_id task,
+ rtems_event_set event
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ uint32_t nest_count = 0;
+
+ e->control_task = rtems_task_self();
+
+ sc = rtems_bsdnet_event_send(task, event);
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ nest_count = rtems_bsdnet_semaphore_release_recursive();
+ sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ assert(sc == RTEMS_SUCCESSFUL);
+ rtems_bsdnet_semaphore_obtain_recursive(nest_count);
+
+ e->control_task = 0;
+}
+
+static inline uint32_t lpc_eth_increment(
+ uint32_t value,
+ uint32_t cycle
+)
+{
+ if (value < cycle) {
+ return ++value;
+ } else {
+ return 0;
+ }
+}
+
+static void lpc_eth_enable_promiscous_mode(bool enable)
+{
+ if (enable) {
+ lpc_eth->rxfilterctrl = ETH_RX_FIL_CTRL_ACCEPT_UNICAST
+ | ETH_RX_FIL_CTRL_ACCEPT_MULTICAST
+ | ETH_RX_FIL_CTRL_ACCEPT_BROADCAST;
+ } else {
+ lpc_eth->rxfilterctrl = ETH_RX_FIL_CTRL_ACCEPT_PERFECT
+ | ETH_RX_FIL_CTRL_ACCEPT_MULTICAST_HASH
+ | ETH_RX_FIL_CTRL_ACCEPT_BROADCAST;
+ }
+}
+
+static void lpc_eth_interrupt_handler(void *arg)
+{
+ lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg;
+ rtems_event_set re = 0;
+ rtems_event_set te = 0;
+ uint32_t ie = 0;
+
+ /* Get interrupt status */
+ uint32_t im = lpc_eth->intenable;
+ uint32_t is = lpc_eth->intstatus & im;
+
+ /* Check receive interrupts */
+ if ((is & ETH_INT_RX_OVERRUN) != 0) {
+ re = LPC_ETH_EVENT_INITIALIZE;
+ ++e->receive_fatal_errors;
+ } else if ((is & LPC_ETH_INTERRUPT_RECEIVE) != 0) {
+ re = LPC_ETH_EVENT_INTERRUPT;
+ ie |= LPC_ETH_INTERRUPT_RECEIVE;
+ }
+
+ /* Send events to receive task */
+ if (re != 0) {
+ ++e->receive_interrupts;
+ (void) rtems_bsdnet_event_send(e->receive_task, re);
+ }
+
+ /* Check transmit interrupts */
+ if ((is & ETH_INT_TX_UNDERRUN) != 0) {
+ te = LPC_ETH_EVENT_INITIALIZE;
+ ++e->transmit_fatal_errors;
+ } else if ((is & LPC_ETH_INTERRUPT_TRANSMIT) != 0) {
+ te = LPC_ETH_EVENT_INTERRUPT;
+ ie |= LPC_ETH_INTERRUPT_TRANSMIT;
+ }
+
+ /* Send events to transmit task */
+ if (te != 0) {
+ ++e->transmit_interrupts;
+ (void) rtems_bsdnet_event_send(e->transmit_task, te);
+ }
+
+ LPC_ETH_PRINTK("interrupt: rx = 0x%08x, tx = 0x%08x\n", re, te);
+
+ /* Update interrupt mask */
+ lpc_eth->intenable = im & ~ie;
+
+ /* Clear interrupts */
+ lpc_eth->intclear = is;
+}
+
+static void lpc_eth_enable_receive_interrupts(void)
+{
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable(level);
+ lpc_eth->intenable |= LPC_ETH_INTERRUPT_RECEIVE;
+ rtems_interrupt_enable(level);
+}
+
+static void lpc_eth_disable_receive_interrupts(void)
+{
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable(level);
+ lpc_eth->intenable &= ~LPC_ETH_INTERRUPT_RECEIVE;
+ rtems_interrupt_enable(level);
+}
+
+static void lpc_eth_enable_transmit_interrupts(void)
+{
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable(level);
+ lpc_eth->intenable |= LPC_ETH_INTERRUPT_TRANSMIT;
+ rtems_interrupt_enable(level);
+}
+
+static void lpc_eth_disable_transmit_interrupts(void)
+{
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable(level);
+ lpc_eth->intenable &= ~LPC_ETH_INTERRUPT_TRANSMIT;
+ rtems_interrupt_enable(level);
+}
+
+#define LPC_ETH_RX_DATA_OFFSET 2
+
+static struct mbuf *lpc_eth_new_mbuf(struct ifnet *ifp, bool wait)
+{
+ struct mbuf *m = NULL;
+ int mw = wait ? M_WAIT : M_DONTWAIT;
+
+ MGETHDR(m, mw, MT_DATA);
+ if (m != NULL) {
+ MCLGET(m, mw);
+ if ((m->m_flags & M_EXT) != 0) {
+ /* Set receive interface */
+ m->m_pkthdr.rcvif = ifp;
+
+ /* Adjust by two bytes for proper IP header alignment */
+ m->m_data = mtod(m, char *) + LPC_ETH_RX_DATA_OFFSET;
+
+ return m;
+ } else {
+ m_free(m);
+ }
+ }
+
+ return NULL;
+}
+
+static bool lpc_eth_add_new_mbuf(
+ struct ifnet *ifp,
+ volatile lpc_eth_transfer_descriptor *desc,
+ struct mbuf **mbufs,
+ uint32_t i,
+ bool wait
+)
+{
+ /* New mbuf */
+ struct mbuf *m = lpc_eth_new_mbuf(ifp, wait);
+
+ /* Check mbuf */
+ if (m != NULL) {
+ /* Cache invalidate */
+ rtems_cache_invalidate_multiple_data_lines(
+ mtod(m, void *),
+ MCLBYTES - LPC_ETH_RX_DATA_OFFSET
+ );
+
+ /* Add mbuf to queue */
+ desc [i].start = mtod(m, uint32_t);
+ desc [i].control = (MCLBYTES - LPC_ETH_RX_DATA_OFFSET - 1)
+ | ETH_RX_CTRL_INTERRUPT;
+
+ /* Cache flush of descriptor */
+ rtems_cache_flush_multiple_data_lines(
+ (void *) &desc [i],
+ sizeof(desc [0])
+ );
+
+ /* Add mbuf to table */
+ mbufs [i] = m;
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static void lpc_eth_receive_task(void *arg)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_event_set events = 0;
+ lpc_eth_driver_entry *const e = (lpc_eth_driver_entry *) arg;
+ struct ifnet *const ifp = &e->arpcom.ac_if;
+ volatile lpc_eth_transfer_descriptor *const desc = e->rx_desc_table;
+ volatile lpc_eth_receive_status *const status = e->rx_status_table;
+ struct mbuf **const mbufs = e->rx_mbuf_table;
+ uint32_t const index_max = e->rx_unit_count - 1;
+ uint32_t produce_index = 0;
+ uint32_t consume_index = 0;
+
+ LPC_ETH_PRINTF("%s\n", __func__);
+
+ /* Main event loop */
+ while (true) {
+ /* Wait for events */
+ sc = rtems_bsdnet_event_receive(
+ LPC_ETH_EVENT_INITIALIZE
+ | LPC_ETH_EVENT_STOP
+ | LPC_ETH_EVENT_INTERRUPT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &events
+ );
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ LPC_ETH_PRINTF("rx: wake up: 0x%08" PRIx32 "\n", events);
+
+ /* Stop receiver? */
+ if ((events & LPC_ETH_EVENT_STOP) != 0) {
+ lpc_eth_control_request_complete(e);
+
+ /* Wait for events */
+ continue;
+ }
+
+ /* Initialize receiver? */
+ if ((events & LPC_ETH_EVENT_INITIALIZE) != 0) {
+ /* Disable receive interrupts */
+ lpc_eth_disable_receive_interrupts();
+
+ /* Disable receiver */
+ lpc_eth->command &= ~ETH_CMD_RX_ENABLE;
+
+ /* Wait for inactive status */
+ while ((lpc_eth->status & ETH_STAT_RX_ACTIVE) != 0) {
+ /* Wait */
+ }
+
+ /* Reset */
+ lpc_eth->command |= ETH_CMD_RX_RESET;
+
+ /* Clear receive interrupts */
+ lpc_eth->intclear = LPC_ETH_INTERRUPT_RECEIVE;
+
+ /* Move existing mbufs to the front */
+ consume_index = 0;
+ for (produce_index = 0; produce_index <= index_max; ++produce_index) {
+ if (mbufs [produce_index] != NULL) {
+ mbufs [consume_index] = mbufs [produce_index];
+ ++consume_index;
+ }
+ }
+
+ /* Fill receive queue */
+ for (
+ produce_index = consume_index;
+ produce_index <= index_max;
+ ++produce_index
+ ) {
+ lpc_eth_add_new_mbuf(ifp, desc, mbufs, produce_index, true);
+ }
+
+ /* Receive descriptor table */
+ lpc_eth->rxdescriptornum = index_max;
+ lpc_eth->rxdescriptor = (uint32_t) desc;
+ lpc_eth->rxstatus = (uint32_t) status;
+
+ /* Initialize indices */
+ produce_index = lpc_eth->rxproduceindex;
+ consume_index = lpc_eth->rxconsumeindex;
+
+ /* Enable receiver */
+ lpc_eth->command |= ETH_CMD_RX_ENABLE;
+
+ /* Enable receive interrupts */
+ lpc_eth_enable_receive_interrupts();
+
+ lpc_eth_control_request_complete(e);
+
+ /* Wait for events */
+ continue;
+ }
+
+ while (true) {
+ /* Clear receive interrupt status */
+ lpc_eth->intclear = LPC_ETH_INTERRUPT_RECEIVE;
+
+ /* Get current produce index */
+ produce_index = lpc_eth->rxproduceindex;
+
+ if (consume_index != produce_index) {
+ uint32_t stat = 0;
+
+ /* Fragment status */
+ rtems_cache_invalidate_multiple_data_lines(
+ (void *) &status [consume_index],
+ sizeof(status [0])
+ );
+ stat = status [consume_index].info;
+
+ if (
+ (stat & ETH_RX_STAT_LAST_FLAG) != 0
+ && (stat & LPC_ETH_RX_STAT_ERRORS) == 0
+ ) {
+ /* Received mbuf */
+ struct mbuf *m = mbufs [consume_index];
+
+ if (lpc_eth_add_new_mbuf(ifp, desc, mbufs, consume_index, false)) {
+ /* Ethernet header */
+ struct ether_header *eh = mtod(m, struct ether_header *);
+
+ /* Discard Ethernet header and CRC */
+ int sz = (int) (stat & ETH_RX_STAT_RXSIZE_MASK) + 1
+ - ETHER_HDR_LEN - ETHER_CRC_LEN;
+
+ /* Update mbuf */
+ m->m_len = sz;
+ m->m_pkthdr.len = sz;
+ m->m_data = mtod(m, char *) + ETHER_HDR_LEN;
+
+ LPC_ETH_PRINTF("rx: %02" PRIu32 ": %u\n", consume_index, sz);
+
+ /* Hand over */
+ ether_input(ifp, eh, m);
+
+ /* Increment received frames counter */
+ ++e->received_frames;
+ } else {
+ ++e->receive_drop_errors;
+ }
+ } else {
+ /* Update error counters */
+ if ((stat & ETH_RX_STAT_OVERRUN) != 0) {
+ ++e->receive_overrun_errors;
+ }
+ if ((stat & ETH_RX_STAT_LAST_FLAG) == 0) {
+ ++e->receive_fragment_errors;
+ }
+ if ((stat & ETH_RX_STAT_CRC_ERROR) != 0) {
+ ++e->receive_crc_errors;
+ }
+ if ((stat & ETH_RX_STAT_SYMBOL_ERROR) != 0) {
+ ++e->receive_symbol_errors;
+ }
+ if ((stat & ETH_RX_STAT_LENGTH_ERROR) != 0) {
+ ++e->receive_length_errors;
+ }
+ if ((stat & ETH_RX_STAT_ALIGNMENT_ERROR) != 0) {
+ ++e->receive_alignment_errors;
+ }
+ if ((stat & ETH_RX_STAT_NO_DESCRIPTOR) != 0) {
+ ++e->receive_no_descriptor_errors;
+ }
+ }
+
+ /* Increment and update consume index */
+ consume_index = lpc_eth_increment(consume_index, index_max);
+ lpc_eth->rxconsumeindex = consume_index;
+ } else {
+ /* Nothing to do, enable receive interrupts */
+ lpc_eth_enable_receive_interrupts();
+ break;
+ }
+ }
+ }
+}
+
+static struct mbuf *lpc_eth_next_fragment(
+ struct ifnet *ifp,
+ struct mbuf *m,
+ uint32_t *ctrl
+)
+{
+ struct mbuf *n = NULL;
+ int size = 0;
+
+ while (true) {
+ if (m == NULL) {
+ /* Dequeue first fragment of the next frame */
+ IF_DEQUEUE(&ifp->if_snd, m);
+
+ /* Empty queue? */
+ if (m == NULL) {
+ return m;
+ }
+ }
+
+ /* Get fragment size */
+ size = m->m_len;
+
+ if (size > 0) {
+ /* Now we have a not empty fragment */
+ break;
+ } else {
+ /* Discard empty fragments */
+ m = m_free(m);
+ }
+ }
+
+ /* Set fragment size */
+ *ctrl = (uint32_t) (size - 1);
+
+ /* Discard empty successive fragments */
+ n = m->m_next;
+ while (n != NULL && n->m_len <= 0) {
+ n = m_free(n);
+ }
+ m->m_next = n;
+
+ /* Is our fragment the last in the frame? */
+ if (n == NULL) {
+ *ctrl |= LPC_ETH_LAST_FRAGMENT_FLAGS;
+ }
+
+ return m;
+}
+
+static void lpc_eth_transmit_task(void *arg)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_event_set events = 0;
+ lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg;
+ struct ifnet *ifp = &e->arpcom.ac_if;
+ volatile lpc_eth_transfer_descriptor *const desc = e->tx_desc_table;
+ volatile uint32_t *const status = e->tx_status_table;
+ #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
+ struct mbuf **const mbufs = e->tx_buf_table;
+ #else
+ char *const buf = e->tx_buf_table;
+ #endif
+ struct mbuf *m = NULL;
+ uint32_t const index_max = e->tx_unit_count - 1;
+ uint32_t produce_index = 0;
+ uint32_t consume_index = 0;
+ uint32_t ctrl = 0;
+ #ifndef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
+ uint32_t frame_length = 0;
+ char *frame_buffer = NULL;
+ #endif
+
+ LPC_ETH_PRINTF("%s\n", __func__);
+
+ #ifndef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
+ /* Initialize descriptor table */
+ for (produce_index = 0; produce_index <= index_max; ++produce_index) {
+ desc [produce_index].start =
+ (uint32_t) (buf + produce_index * LPC_ETH_CONFIG_TX_BUF_SIZE);
+ }
+ #endif
+
+ /* Main event loop */
+ while (true) {
+ /* Wait for events */
+ sc = rtems_bsdnet_event_receive(
+ LPC_ETH_EVENT_INITIALIZE
+ | LPC_ETH_EVENT_STOP
+ | LPC_ETH_EVENT_TXSTART
+ | LPC_ETH_EVENT_INTERRUPT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &events
+ );
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ LPC_ETH_PRINTF("tx: wake up: 0x%08" PRIx32 "\n", events);
+
+ /* Stop transmitter? */
+ if ((events & LPC_ETH_EVENT_STOP) != 0) {
+ lpc_eth_control_request_complete(e);
+
+ /* Wait for events */
+ continue;
+ }
+
+ /* Initialize transmitter? */
+ if ((events & LPC_ETH_EVENT_INITIALIZE) != 0) {
+ /* Disable transmit interrupts */
+ lpc_eth_disable_transmit_interrupts();
+
+ /* Disable transmitter */
+ lpc_eth->command &= ~ETH_CMD_TX_ENABLE;
+
+ /* Wait for inactive status */
+ while ((lpc_eth->status & ETH_STAT_TX_ACTIVE) != 0) {
+ /* Wait */
+ }
+
+ /* Reset */
+ lpc_eth->command |= ETH_CMD_TX_RESET;
+
+ /* Clear transmit interrupts */
+ lpc_eth->intclear = LPC_ETH_INTERRUPT_TRANSMIT;
+
+ /* Transmit descriptors */
+ lpc_eth->txdescriptornum = index_max;
+ lpc_eth->txdescriptor = (uint32_t) desc;
+ lpc_eth->txstatus = (uint32_t) status;
+
+ #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
+ /* Discard outstanding fragments (= data loss) */
+ for (produce_index = 0; produce_index <= index_max; ++produce_index) {
+ struct mbuf *victim = mbufs [produce_index];
+
+ if (victim != NULL) {
+ m_free(victim);
+ mbufs [produce_index] = NULL;
+ }
+ }
+ #endif
+
+ /* Initialize indices */
+ produce_index = lpc_eth->txproduceindex;
+ consume_index = lpc_eth->txconsumeindex;
+
+ #ifndef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
+ /* Fresh frame length and buffer start */
+ frame_length = 0;
+ frame_buffer = (char *) desc [produce_index].start;
+ #endif
+
+ /* Enable transmitter */
+ lpc_eth->command |= ETH_CMD_TX_ENABLE;
+
+ lpc_eth_control_request_complete(e);
+ }
+
+ /* Free consumed fragments */
+ while (true) {
+ /* Save last known consume index */
+ uint32_t c = consume_index;
+
+ /* Clear transmit interrupt status */
+ lpc_eth->intclear = LPC_ETH_INTERRUPT_TRANSMIT;
+
+ /* Get new consume index */
+ consume_index = lpc_eth->txconsumeindex;
+
+ /* Nothing consumed in the meantime? */
+ if (c == consume_index) {
+ break;
+ }
+
+ while (c != consume_index) {
+ uint32_t s = status [c];
+
+ /* Update error counters */
+ if ((s & (ETH_TX_STAT_ERROR | ETH_TX_STAT_NO_DESCRIPTOR)) != 0) {
+ if ((s & ETH_TX_STAT_UNDERRUN) != 0) {
+ ++e->transmit_underrun_errors;
+ }
+ if ((s & ETH_TX_STAT_LATE_COLLISION) != 0) {
+ ++e->transmit_late_collision_errors;
+ }
+ if ((s & ETH_TX_STAT_EXCESSIVE_COLLISION) != 0) {
+ ++e->transmit_excessive_collision_errors;
+ }
+ if ((s & ETH_TX_STAT_EXCESSIVE_DEFER) != 0) {
+ ++e->transmit_excessive_defer_errors;
+ }
+ if ((s & ETH_TX_STAT_NO_DESCRIPTOR) != 0) {
+ ++e->transmit_no_descriptor_errors;
+ }
+ }
+
+ #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
+ /* Release mbuf */
+ m_free(mbufs [c]);
+ mbufs [c] = NULL;
+ #endif
+
+ /* Next consume index */
+ c = lpc_eth_increment(c, index_max);
+ }
+ }
+
+ /* Transmit new fragments */
+ while (true) {
+ /* Compute next produce index */
+ uint32_t p = lpc_eth_increment(produce_index, index_max);
+
+ /* Get next fragment and control value */
+ m = lpc_eth_next_fragment(ifp, m, &ctrl);
+
+ /* Queue full? */
+ if (p == consume_index) {
+ LPC_ETH_PRINTF("tx: full queue: 0x%08x\n", m);
+
+ /* The queue is full, wait for transmit interrupt */
+ break;
+ }
+
+ /* New fragment? */
+ if (m != NULL) {
+ #ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
+ /* Set the transfer data */
+ rtems_cache_flush_multiple_data_lines(
+ mtod(m, const void *),
+ (size_t) m->m_len
+ );
+ desc [produce_index].start = mtod(m, uint32_t);
+ desc [produce_index].control = ctrl;
+ rtems_cache_flush_multiple_data_lines(
+ (void *) &desc [produce_index],
+ sizeof(desc [0])
+ );
+ mbufs [produce_index] = m;
+
+ LPC_ETH_PRINTF(
+ "tx: %02" PRIu32 ": %u %s\n",
+ produce_index, m->m_len,
+ (ctrl & ETH_TX_CTRL_LAST) != 0 ? "L" : ""
+ );
+
+ /* Next produce index */
+ produce_index = p;
+
+ /* Last fragment of a frame? */
+ if ((ctrl & ETH_TX_CTRL_LAST) != 0) {
+ /* Update the produce index */
+ lpc_eth->txproduceindex = produce_index;
+
+ /* Increment transmitted frames counter */
+ ++e->transmitted_frames;
+ }
+
+ /* Next fragment of the frame */
+ m = m->m_next;
+ #else
+ size_t fragment_length = (size_t) m->m_len;
+ void *fragment_start = mtod(m, void *);
+ uint32_t new_frame_length = frame_length + fragment_length;
+
+ /* Check buffer size */
+ if (new_frame_length > LPC_ETH_CONFIG_TX_BUF_SIZE) {
+ LPC_ETH_PRINTF("tx: overflow\n");
+
+ /* Discard overflow data */
+ new_frame_length = LPC_ETH_CONFIG_TX_BUF_SIZE;
+ fragment_length = new_frame_length - frame_length;
+
+ /* Finalize frame */
+ ctrl |= LPC_ETH_LAST_FRAGMENT_FLAGS;
+
+ /* Update error counter */
+ ++e->transmit_overflow_errors;
+ }
+
+ LPC_ETH_PRINTF(
+ "tx: copy: %" PRIu32 "%s%s\n",
+ fragment_length,
+ (m->m_flags & M_EXT) != 0 ? ", E" : "",
+ (m->m_flags & M_PKTHDR) != 0 ? ", H" : ""
+ );
+
+ /* Copy fragment to buffer in Ethernet RAM */
+ memcpy(frame_buffer, fragment_start, fragment_length);
+
+ if ((ctrl & ETH_TX_CTRL_LAST) != 0) {
+ /* Finalize descriptor */
+ desc [produce_index].control = (ctrl & ~ETH_TX_CTRL_SIZE_MASK)
+ | (new_frame_length - 1);
+
+ LPC_ETH_PRINTF(
+ "tx: %02" PRIu32 ": %" PRIu32 "\n",
+ produce_index,
+ new_frame_length
+ );
+
+ /* Cache flush of data */
+ rtems_cache_flush_multiple_data_lines(
+ (const void *) desc [produce_index].start,
+ new_frame_length
+ );
+
+ /* Cache flush of descriptor */
+ rtems_cache_flush_multiple_data_lines(
+ (void *) &desc [produce_index],
+ sizeof(desc [0])
+ );
+
+ /* Next produce index */
+ produce_index = p;
+
+ /* Update the produce index */
+ lpc_eth->txproduceindex = produce_index;
+
+ /* Fresh frame length and buffer start */
+ frame_length = 0;
+ frame_buffer = (char *) desc [produce_index].start;
+
+ /* Increment transmitted frames counter */
+ ++e->transmitted_frames;
+ } else {
+ /* New frame length */
+ frame_length = new_frame_length;
+
+ /* Update current frame buffer start */
+ frame_buffer += fragment_length;
+ }
+
+ /* Free mbuf and get next */
+ m = m_free(m);
+ #endif
+ } else {
+ /* Nothing to transmit */
+ break;
+ }
+ }
+
+ /* No more fragments? */
+ if (m == NULL) {
+ /* Interface is now inactive */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ } else {
+ LPC_ETH_PRINTF("tx: enable interrupts\n");
+
+ /* Enable transmit interrupts */
+ lpc_eth_enable_transmit_interrupts();
+ }
+ }
+}
+
+static int lpc_eth_mdio_wait_for_not_busy(void)
+{
+ rtems_interval one_second = rtems_clock_get_ticks_per_second();
+ rtems_interval i = 0;
+
+ while ((lpc_eth->mind & ETH_MIND_BUSY) != 0 && i < one_second) {
+ rtems_task_wake_after(1);
+ ++i;
+ }
+
+ LPC_ETH_PRINTK("tx: lpc_eth_mdio_wait %s after %d\n",
+ i != one_second? "succeed": "timeout", i);
+
+ return i != one_second ? 0 : ETIMEDOUT;
+}
+
+static uint32_t lpc_eth_mdio_read_anlpar(int phy)
+{
+ uint32_t madr = ETH_MADR_REG(MII_ANLPAR) | ETH_MADR_PHY(phy);
+ uint32_t anlpar = 0;
+ int eno = 0;
+
+ if (lpc_eth->madr != madr) {
+ lpc_eth->madr = madr;
+ }
+
+ if (lpc_eth->mcmd != ETH_MCMD_READ) {
+ lpc_eth->mcmd = 0;
+ lpc_eth->mcmd = ETH_MCMD_READ;
+ }
+
+ eno = lpc_eth_mdio_wait_for_not_busy();
+ if (eno == 0) {
+ anlpar = lpc_eth->mrdd;
+ }
+
+ /* Start next read */
+ lpc_eth->mcmd = 0;
+ lpc_eth->mcmd = ETH_MCMD_READ;
+
+ return anlpar;
+}
+
+static int lpc_eth_mdio_read(
+ int phy,
+ void *arg RTEMS_UNUSED,
+ unsigned reg,
+ uint32_t *val
+)
+{
+ int eno = 0;
+
+ if (0 <= phy && phy <= 31) {
+ lpc_eth->madr = ETH_MADR_REG(reg) | ETH_MADR_PHY(phy);
+ lpc_eth->mcmd = 0;
+ lpc_eth->mcmd = ETH_MCMD_READ;
+ eno = lpc_eth_mdio_wait_for_not_busy();
+
+ if (eno == 0) {
+ *val = lpc_eth->mrdd;
+ }
+ } else {
+ eno = EINVAL;
+ }
+
+ return eno;
+}
+
+static int lpc_eth_mdio_write(
+ int phy,
+ void *arg RTEMS_UNUSED,
+ unsigned reg,
+ uint32_t val
+)
+{
+ int eno = 0;
+
+ if (0 <= phy && phy <= 31) {
+ lpc_eth->madr = ETH_MADR_REG(reg) | ETH_MADR_PHY(phy);
+ lpc_eth->mwtd = val;
+ eno = lpc_eth_mdio_wait_for_not_busy();
+ } else {
+ eno = EINVAL;
+ }
+
+ return eno;
+}
+
+static int lpc_eth_phy_get_id(int phy, uint32_t *id)
+{
+ uint32_t id1 = 0;
+ int eno = lpc_eth_mdio_read(phy, NULL, MII_PHYIDR1, &id1);
+
+ if (eno == 0) {
+ uint32_t id2 = 0;
+
+ eno = lpc_eth_mdio_read(phy, NULL, MII_PHYIDR2, &id2);
+ if (eno == 0) {
+ *id = (id1 << 16) | (id2 & 0xfff0);
+ }
+ }
+
+ return eno;
+}
+
+#define PHY_KSZ80X1RNL 0x221550
+#define PHY_DP83848 0x20005c90
+
+typedef struct {
+ unsigned reg;
+ uint32_t set;
+ uint32_t clear;
+} lpc_eth_phy_action;
+
+static int lpc_eth_phy_set_and_clear(
+ lpc_eth_driver_entry *e,
+ const lpc_eth_phy_action *actions,
+ size_t n
+)
+{
+ int eno = 0;
+ size_t i;
+
+ for (i = 0; eno == 0 && i < n; ++i) {
+ const lpc_eth_phy_action *action = &actions [i];
+ uint32_t val;
+
+ eno = lpc_eth_mdio_read(e->phy, NULL, action->reg, &val);
+ if (eno == 0) {
+ val |= action->set;
+ val &= ~action->clear;
+ eno = lpc_eth_mdio_write(e->phy, NULL, action->reg, val);
+ }
+ }
+
+ return eno;
+}
+
+static const lpc_eth_phy_action lpc_eth_phy_up_action_default [] = {
+ { MII_BMCR, 0, BMCR_PDOWN },
+ { MII_BMCR, BMCR_RESET, 0 },
+ { MII_BMCR, BMCR_AUTOEN, 0 }
+};
+
+static const lpc_eth_phy_action lpc_eth_phy_up_pre_action_KSZ80X1RNL [] = {
+ /* Disable slow oscillator mode */
+ { 0x11, 0, 0x10 }
+};
+
+static const lpc_eth_phy_action lpc_eth_phy_up_post_action_KSZ80X1RNL [] = {
+ /* Enable energy detect power down (EDPD) mode */
+ { 0x18, 0x0800, 0 },
+ /* Turn PLL of automatically in EDPD mode */
+ { 0x10, 0x10, 0 }
+};
+
+static int lpc_eth_phy_up(lpc_eth_driver_entry *e)
+{
+ int eno;
+ int retries = 64;
+ uint32_t val;
+
+ e->phy = DEFAULT_PHY - 1;
+ while (true) {
+ e->phy = (e->phy + 1) % 32;
+
+ --retries;
+ eno = lpc_eth_phy_get_id(e->phy, &e->phy_id);
+ if (
+ (eno == 0 && e->phy_id != 0xfffffff0 && e->phy_id != 0)
+ || retries <= 0
+ ) {
+ break;
+ }
+
+ rtems_task_wake_after(1);
+ }
+
+ LPC_ETH_PRINTF("lpc_eth_phy_get_id: 0x%08" PRIx32 " from phy %d retries %d\n",
+ e->phy_id, e->phy, retries);
+
+ if (eno == 0) {
+ switch (e->phy_id) {
+ case PHY_KSZ80X1RNL:
+ eno = lpc_eth_phy_set_and_clear(
+ e,
+ &lpc_eth_phy_up_pre_action_KSZ80X1RNL [0],
+ RTEMS_ARRAY_SIZE(lpc_eth_phy_up_pre_action_KSZ80X1RNL)
+ );
+ break;
+ case PHY_DP83848:
+ eno = lpc_eth_mdio_read(e->phy, NULL, 0x17, &val);
+ LPC_ETH_PRINTF("phy PHY_DP83848 RBR 0x%08" PRIx32 "\n", val);
+ /* val = 0x21; */
+ val = 0x32 ;
+ eno = lpc_eth_mdio_write(e->phy, NULL, 0x17, val);
+ break;
+ case 0:
+ case 0xfffffff0:
+ eno = EIO;
+ e->phy = DEFAULT_PHY;
+ break;
+ default:
+ break;
+ }
+
+ if (eno == 0) {
+ eno = lpc_eth_phy_set_and_clear(
+ e,
+ &lpc_eth_phy_up_action_default [0],
+ RTEMS_ARRAY_SIZE(lpc_eth_phy_up_action_default)
+ );
+ }
+
+ if (eno == 0) {
+ switch (e->phy_id) {
+ case PHY_KSZ80X1RNL:
+ eno = lpc_eth_phy_set_and_clear(
+ e,
+ &lpc_eth_phy_up_post_action_KSZ80X1RNL [0],
+ RTEMS_ARRAY_SIZE(lpc_eth_phy_up_post_action_KSZ80X1RNL)
+ );
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ e->phy_id = 0;
+ }
+
+ return eno;
+}
+
+static const lpc_eth_phy_action lpc_eth_phy_down_action_default [] = {
+ { MII_BMCR, BMCR_PDOWN, 0 }
+};
+
+static const lpc_eth_phy_action lpc_eth_phy_down_post_action_KSZ80X1RNL [] = {
+ /* Enable slow oscillator mode */
+ { 0x11, 0x10, 0 }
+};
+
+static void lpc_eth_phy_down(lpc_eth_driver_entry *e)
+{
+ int eno = lpc_eth_phy_set_and_clear(
+ e,
+ &lpc_eth_phy_down_action_default [0],
+ RTEMS_ARRAY_SIZE(lpc_eth_phy_down_action_default)
+ );
+
+ if (eno == 0) {
+ switch (e->phy_id) {
+ case PHY_KSZ80X1RNL:
+ eno = lpc_eth_phy_set_and_clear(
+ e,
+ &lpc_eth_phy_down_post_action_KSZ80X1RNL [0],
+ RTEMS_ARRAY_SIZE(lpc_eth_phy_down_post_action_KSZ80X1RNL)
+ );
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void lpc_eth_soft_reset(void)
+{
+ lpc_eth->command = 0x38;
+ lpc_eth->mac1 = 0xcf00;
+ lpc_eth->mac1 = 0x0;
+}
+
+static int lpc_eth_up_or_down(lpc_eth_driver_entry *e, bool up)
+{
+ int eno = 0;
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ struct ifnet *ifp = &e->arpcom.ac_if;
+
+ if (up && e->state == LPC_ETH_STATE_DOWN) {
+
+ lpc_eth_config_module_enable();
+
+ /* Enable RX/TX reset and disable soft reset */
+ lpc_eth->mac1 = 0xf00;
+
+ /* Initialize PHY */
+ /* Clock value 10 (divide by 44 ) is safe on LPC178x up to 100 MHz AHB clock */
+ lpc_eth->mcfg = ETH_MCFG_CLOCK_SELECT(10) | ETH_MCFG_RESETMIIMGMT;
+ rtems_task_wake_after(1);
+ lpc_eth->mcfg = ETH_MCFG_CLOCK_SELECT(10);
+ rtems_task_wake_after(1);
+ eno = lpc_eth_phy_up(e);
+
+ if (eno == 0) {
+ /*
+ * We must have a valid external clock from the PHY at this point,
+ * otherwise the system bus hangs and only a watchdog reset helps.
+ */
+ lpc_eth_soft_reset();
+
+ /* Reinitialize registers */
+ lpc_eth->mac2 = 0x31;
+ lpc_eth->ipgt = 0x15;
+ lpc_eth->ipgr = 0x12;
+ lpc_eth->clrt = 0x370f;
+ lpc_eth->maxf = 0x0600;
+ lpc_eth->supp = ETH_SUPP_SPEED;
+ lpc_eth->test = 0;
+ #ifdef LPC_ETH_CONFIG_RMII
+ lpc_eth->command = 0x0600;
+ #else
+ lpc_eth->command = 0x0400;
+ #endif
+ lpc_eth->intenable = ETH_INT_RX_OVERRUN | ETH_INT_TX_UNDERRUN;
+ lpc_eth->intclear = 0x30ff;
+ lpc_eth->powerdown = 0;
+
+ /* MAC address */
+ lpc_eth->sa0 = ((uint32_t) e->arpcom.ac_enaddr [5] << 8)
+ | (uint32_t) e->arpcom.ac_enaddr [4];
+ lpc_eth->sa1 = ((uint32_t) e->arpcom.ac_enaddr [3] << 8)
+ | (uint32_t) e->arpcom.ac_enaddr [2];
+ lpc_eth->sa2 = ((uint32_t) e->arpcom.ac_enaddr [1] << 8)
+ | (uint32_t) e->arpcom.ac_enaddr [0];
+
+ /* Enable receiver */
+ lpc_eth->mac1 = 0x03;
+
+ /* Initialize tasks */
+ lpc_eth_control_request(e, e->receive_task, LPC_ETH_EVENT_INITIALIZE);
+ lpc_eth_control_request(e, e->transmit_task, LPC_ETH_EVENT_INITIALIZE);
+
+ /* Install interrupt handler */
+ sc = rtems_interrupt_handler_install(
+ e->interrupt_number,
+ "Ethernet",
+ RTEMS_INTERRUPT_UNIQUE,
+ lpc_eth_interrupt_handler,
+ e
+ );
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ /* Start watchdog timer */
+ ifp->if_timer = 1;
+
+ /* Change state */
+ e->state = LPC_ETH_STATE_UP;
+ }
+
+ if (eno != 0) {
+ ifp->if_flags &= ~IFF_UP;
+ }
+ } else if (!up && e->state == LPC_ETH_STATE_UP) {
+ /* Remove interrupt handler */
+ sc = rtems_interrupt_handler_remove(
+ e->interrupt_number,
+ lpc_eth_interrupt_handler,
+ e
+ );
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ /* Stop tasks */
+ lpc_eth_control_request(e, e->receive_task, LPC_ETH_EVENT_STOP);
+ lpc_eth_control_request(e, e->transmit_task, LPC_ETH_EVENT_STOP);
+
+ lpc_eth_soft_reset();
+ lpc_eth_phy_down(e);
+ lpc_eth_config_module_disable();
+
+ /* Stop watchdog timer */
+ ifp->if_timer = 0;
+
+ /* Change state */
+ e->state = LPC_ETH_STATE_DOWN;
+ }
+
+ return eno;
+}
+
+static void lpc_eth_interface_init(void *arg)
+{
+ /* Nothing to do */
+}
+
+static void lpc_eth_interface_stats(lpc_eth_driver_entry *e)
+{
+ int eno = EIO;
+ int media = 0;
+
+ if (e->state == LPC_ETH_STATE_UP) {
+ media = IFM_MAKEWORD(0, 0, 0, 0);
+ eno = rtems_mii_ioctl(&e->mdio, e, SIOCGIFMEDIA, &media);
+ }
+
+ rtems_bsdnet_semaphore_release();
+
+ if (eno == 0) {
+ rtems_ifmedia2str(media, NULL, 0);
+ printf("\n");
+ }
+
+ printf("received frames: %u\n", e->received_frames);
+ printf("receive interrupts: %u\n", e->receive_interrupts);
+ printf("transmitted frames: %u\n", e->transmitted_frames);
+ printf("transmit interrupts: %u\n", e->transmit_interrupts);
+ printf("receive drop errors: %u\n", e->receive_drop_errors);
+ printf("receive overrun errors: %u\n", e->receive_overrun_errors);
+ printf("receive fragment errors: %u\n", e->receive_fragment_errors);
+ printf("receive CRC errors: %u\n", e->receive_crc_errors);
+ printf("receive symbol errors: %u\n", e->receive_symbol_errors);
+ printf("receive length errors: %u\n", e->receive_length_errors);
+ printf("receive alignment errors: %u\n", e->receive_alignment_errors);
+ printf("receive no descriptor errors: %u\n", e->receive_no_descriptor_errors);
+ printf("receive fatal errors: %u\n", e->receive_fatal_errors);
+ printf("transmit underrun errors: %u\n", e->transmit_underrun_errors);
+ printf("transmit late collision errors: %u\n", e->transmit_late_collision_errors);
+ printf("transmit excessive collision errors: %u\n", e->transmit_excessive_collision_errors);
+ printf("transmit excessive defer errors: %u\n", e->transmit_excessive_defer_errors);
+ printf("transmit no descriptor errors: %u\n", e->transmit_no_descriptor_errors);
+ printf("transmit overflow errors: %u\n", e->transmit_overflow_errors);
+ printf("transmit fatal errors: %u\n", e->transmit_fatal_errors);
+
+ rtems_bsdnet_semaphore_obtain();
+}
+
+static int lpc_eth_multicast_control(
+ bool add,
+ struct ifreq *ifr,
+ struct arpcom *ac
+)
+{
+ int eno = 0;
+
+ if (add) {
+ eno = ether_addmulti(ifr, ac);
+ } else {
+ eno = ether_delmulti(ifr, ac);
+ }
+
+ if (eno == ENETRESET) {
+ struct ether_multistep step;
+ struct ether_multi *enm;
+
+ eno = 0;
+
+ lpc_eth->hashfilterl = 0;
+ lpc_eth->hashfilterh = 0;
+
+ ETHER_FIRST_MULTI(step, ac, enm);
+ while (enm != NULL) {
+ uint64_t addrlo = 0;
+ uint64_t addrhi = 0;
+
+ memcpy(&addrlo, enm->enm_addrlo, ETHER_ADDR_LEN);
+ memcpy(&addrhi, enm->enm_addrhi, ETHER_ADDR_LEN);
+ while (addrlo <= addrhi) {
+ /* XXX: ether_crc32_le() does not work, why? */
+ uint32_t crc = ether_crc32_be((uint8_t *) &addrlo, ETHER_ADDR_LEN);
+ uint32_t index = (crc >> 23) & 0x3f;
+
+ if (index < 32) {
+ lpc_eth->hashfilterl |= 1U << index;
+ } else {
+ lpc_eth->hashfilterh |= 1U << (index - 32);
+ }
+ ++addrlo;
+ }
+ ETHER_NEXT_MULTI(step, enm);
+ }
+ }
+
+ return eno;
+}
+
+static int lpc_eth_interface_ioctl(
+ struct ifnet *ifp,
+ ioctl_command_t cmd,
+ caddr_t data
+)
+{
+ lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int eno = 0;
+
+ LPC_ETH_PRINTF("%s\n", __func__);
+
+ switch (cmd) {
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ rtems_mii_ioctl(&e->mdio, e, cmd, &ifr->ifr_media);
+ break;
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl(ifp, cmd, data);
+ break;
+ case SIOCSIFFLAGS:
+ eno = lpc_eth_up_or_down(e, (ifp->if_flags & IFF_UP) != 0);
+ if (eno == 0 && (ifp->if_flags & IFF_UP) != 0) {
+ lpc_eth_enable_promiscous_mode((ifp->if_flags & IFF_PROMISC) != 0);
+ }
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ eno = lpc_eth_multicast_control(cmd == SIOCADDMULTI, ifr, &e->arpcom);
+ break;
+ case SIO_RTEMS_SHOW_STATS:
+ lpc_eth_interface_stats(e);
+ break;
+ default:
+ eno = EINVAL;
+ break;
+ }
+
+ return eno;
+}
+
+static void lpc_eth_interface_start(struct ifnet *ifp)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) ifp->if_softc;
+
+ ifp->if_flags |= IFF_OACTIVE;
+
+ if (e->state == LPC_ETH_STATE_UP) {
+ sc = rtems_bsdnet_event_send(e->transmit_task, LPC_ETH_EVENT_TXSTART);
+ assert(sc == RTEMS_SUCCESSFUL);
+ }
+}
+
+static void lpc_eth_interface_watchdog(struct ifnet *ifp)
+{
+ lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) ifp->if_softc;
+
+ if (e->state == LPC_ETH_STATE_UP) {
+ uint32_t anlpar = lpc_eth_mdio_read_anlpar(e->phy);
+
+ if (e->anlpar != anlpar) {
+ bool full_duplex = false;
+ bool speed = false;
+
+ e->anlpar = anlpar;
+
+ if ((anlpar & ANLPAR_TX_FD) != 0) {
+ full_duplex = true;
+ speed = true;
+ } else if ((anlpar & ANLPAR_T4) != 0) {
+ speed = true;
+ } else if ((anlpar & ANLPAR_TX) != 0) {
+ speed = true;
+ } else if ((anlpar & ANLPAR_10_FD) != 0) {
+ full_duplex = true;
+ }
+
+ if (full_duplex) {
+ lpc_eth->mac2 |= ETH_MAC2_FULL_DUPLEX;
+ } else {
+ lpc_eth->mac2 &= ~ETH_MAC2_FULL_DUPLEX;
+ }
+
+ if (speed) {
+ lpc_eth->supp |= ETH_SUPP_SPEED;
+ } else {
+ lpc_eth->supp &= ~ETH_SUPP_SPEED;
+ }
+ }
+
+ ifp->if_timer = WATCHDOG_TIMEOUT;
+ }
+}
+
+static unsigned lpc_eth_fixup_unit_count(int count, int default_value, int max)
+{
+ if (count <= 0) {
+ count = default_value;
+ } else if (count > max) {
+ count = max;
+ }
+
+ return LPC_ETH_CONFIG_UNIT_MULTIPLE
+ + (((unsigned) count - 1U) & ~(LPC_ETH_CONFIG_UNIT_MULTIPLE - 1U));
+}
+
+static int lpc_eth_attach(struct rtems_bsdnet_ifconfig *config)
+{
+ lpc_eth_driver_entry *e = &lpc_eth_driver_data;
+ struct ifnet *ifp = &e->arpcom.ac_if;
+ char *unit_name = NULL;
+ int unit_index = rtems_bsdnet_parse_driver_name(config, &unit_name);
+ size_t table_area_size = 0;
+ char *table_area = NULL;
+ char *table_location = NULL;
+
+ /* Check parameter */
+ if (unit_index < 0) {
+ return 0;
+ }
+ if (unit_index != 0) {
+ goto cleanup;
+ }
+ if (config->hardware_address == NULL) {
+ goto cleanup;
+ }
+ if (e->state != LPC_ETH_STATE_NOT_INITIALIZED) {
+ goto cleanup;
+ }
+
+ /* MDIO */
+ e->mdio.mdio_r = lpc_eth_mdio_read;
+ e->mdio.mdio_w = lpc_eth_mdio_write;
+ e->mdio.has_gmii = 0;
+ e->anlpar = 0;
+
+ /* Interrupt number */
+ config->irno = LPC_ETH_CONFIG_INTERRUPT;
+
+ /* Device control */
+ config->drv_ctrl = e;
+
+ /* Receive unit count */
+ e->rx_unit_count = lpc_eth_fixup_unit_count(
+ config->rbuf_count,
+ LPC_ETH_CONFIG_RX_UNIT_COUNT_DEFAULT,
+ LPC_ETH_CONFIG_RX_UNIT_COUNT_MAX
+ );
+ config->rbuf_count = (int) e->rx_unit_count;
+
+ /* Transmit unit count */
+ e->tx_unit_count = lpc_eth_fixup_unit_count(
+ config->xbuf_count,
+ LPC_ETH_CONFIG_TX_UNIT_COUNT_DEFAULT,
+ LPC_ETH_CONFIG_TX_UNIT_COUNT_MAX
+ );
+ config->xbuf_count = (int) e->tx_unit_count;
+
+ /* Remember interrupt number */
+ e->interrupt_number = config->irno;
+
+ /* Copy MAC address */
+ memcpy(e->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
+
+ /* Allocate and clear table area */
+ table_area_size =
+ e->rx_unit_count
+ * (sizeof(lpc_eth_transfer_descriptor)
+ + sizeof(lpc_eth_receive_status)
+ + sizeof(struct mbuf *))
+ + e->tx_unit_count
+ * (sizeof(lpc_eth_transfer_descriptor)
+ + sizeof(uint32_t)
+ + LPC_ETH_CONFIG_TX_BUF_SIZE);
+ table_area = lpc_eth_config_alloc_table_area(table_area_size);
+ if (table_area == NULL) {
+ goto cleanup;
+ }
+ memset(table_area, 0, table_area_size);
+
+ table_location = table_area;
+
+ /*
+ * The receive status table must be the first one since it has the strictest
+ * alignment requirements.
+ */
+ e->rx_status_table = (volatile lpc_eth_receive_status *) table_location;
+ table_location += e->rx_unit_count * sizeof(e->rx_status_table [0]);
+
+ e->rx_desc_table = (volatile lpc_eth_transfer_descriptor *) table_location;
+ table_location += e->rx_unit_count * sizeof(e->rx_desc_table [0]);
+
+ e->rx_mbuf_table = (struct mbuf **) table_location;
+ table_location += e->rx_unit_count * sizeof(e->rx_mbuf_table [0]);
+
+ e->tx_desc_table = (volatile lpc_eth_transfer_descriptor *) table_location;
+ table_location += e->tx_unit_count * sizeof(e->tx_desc_table [0]);
+
+ e->tx_status_table = (volatile uint32_t *) table_location;
+ table_location += e->tx_unit_count * sizeof(e->tx_status_table [0]);
+
+ e->tx_buf_table = table_location;
+
+ /* Set interface data */
+ ifp->if_softc = e;
+ ifp->if_unit = (short) unit_index;
+ ifp->if_name = unit_name;
+ ifp->if_mtu = (config->mtu > 0) ? (u_long) config->mtu : ETHERMTU;
+ ifp->if_init = lpc_eth_interface_init;
+ ifp->if_ioctl = lpc_eth_interface_ioctl;
+ ifp->if_start = lpc_eth_interface_start;
+ ifp->if_output = ether_output;
+ ifp->if_watchdog = lpc_eth_interface_watchdog;
+ ifp->if_flags = IFF_MULTICAST | IFF_BROADCAST | IFF_SIMPLEX;
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+ ifp->if_timer = 0;
+
+ /* Create tasks */
+ e->receive_task = rtems_bsdnet_newproc(
+ "ntrx",
+ 4096,
+ lpc_eth_receive_task,
+ e
+ );
+ e->transmit_task = rtems_bsdnet_newproc(
+ "nttx",
+ 4096,
+ lpc_eth_transmit_task,
+ e
+ );
+
+ /* Change status */
+ ifp->if_flags |= IFF_RUNNING;
+ e->state = LPC_ETH_STATE_DOWN;
+
+ /* Attach the interface */
+ if_attach(ifp);
+ ether_ifattach(ifp);
+
+ return 1;
+
+cleanup:
+
+ lpc_eth_config_free_table_area(table_area);
+
+ /* FIXME: Type */
+ free(unit_name, (int) 0xdeadbeef);
+
+ return 0;
+}
+
+static int lpc_eth_detach(
+ struct rtems_bsdnet_ifconfig *config RTEMS_UNUSED
+)
+{
+ /* FIXME: Detach the interface from the upper layers? */
+
+ /* Module soft reset */
+ lpc_eth->command = 0x38;
+ lpc_eth->mac1 = 0xcf00;
+
+ /* FIXME: More cleanup */
+
+ return 0;
+}
+
+int lpc_eth_attach_detach(
+ struct rtems_bsdnet_ifconfig *config,
+ int attaching
+)
+{
+ /* FIXME: Return value */
+
+ if (attaching) {
+ return lpc_eth_attach(config);
+ } else {
+ return lpc_eth_detach(config);
+ }
+}