summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVijay Kumar Banerjee <vijay@rtems.org>2021-02-18 19:31:10 -0700
committerVijay Kumar Banerjee <vijay@rtems.org>2021-03-02 14:32:11 -0700
commitd1517076487ea677799764efcb4313760b68c05e (patch)
tree55fd6d0645f9a2ac277275852e7411d8f703f9b8
parent22f3e3c64addcefee992eed572d758a6eab2313b (diff)
bsps: Add support to build from shared bsp folder for specific BSPs
-rw-r--r--bsp_drivers.py22
-rw-r--r--bsps/arm/shared/lpc-ethernet.c1839
-rw-r--r--bsps/include/libchip/i82586var.h319
-rw-r--r--bsps/include/libchip/if_dcreg.h1120
-rw-r--r--bsps/include/libchip/if_fxpvar.h203
-rw-r--r--bsps/include/libchip/smc91111.h558
-rw-r--r--bsps/lm32/include/system_conf.h329
-rw-r--r--bsps/shared/grlib/net/README7
-rw-r--r--bsps/shared/grlib/net/greth.c1655
-rw-r--r--bsps/shared/grlib/net/network_interface_add.c62
-rw-r--r--bsps/shared/net/README12
-rw-r--r--bsps/shared/net/README.3com3
-rw-r--r--bsps/shared/net/README.cs890026
-rw-r--r--bsps/shared/net/README.dec21140116
-rw-r--r--bsps/shared/net/README.i825861
-rw-r--r--bsps/shared/net/README.open_eth72
-rw-r--r--bsps/shared/net/README.sonic135
-rw-r--r--bsps/shared/net/README.tulipclone101
-rw-r--r--bsps/shared/net/cs8900.c1216
-rw-r--r--bsps/shared/net/cs8900.c.bsp510
-rw-r--r--bsps/shared/net/cs8900.h.bsp38
-rw-r--r--bsps/shared/net/dec21140.c1112
-rw-r--r--bsps/shared/net/elnk.c3553
-rw-r--r--bsps/shared/net/greth2.c1200
-rw-r--r--bsps/shared/net/i82586.c2198
-rw-r--r--bsps/shared/net/i82586reg.h448
-rw-r--r--bsps/shared/net/if_dc.c3844
-rw-r--r--bsps/shared/net/if_fxp.c2339
-rw-r--r--bsps/shared/net/if_fxpreg.h370
-rw-r--r--bsps/shared/net/open_eth.c767
-rw-r--r--bsps/shared/net/smc91111.c1653
-rw-r--r--bsps/shared/net/smc91111config.h118
-rw-r--r--bsps/shared/net/sonic.c1685
-rw-r--r--lnetworking.py21
34 files changed, 27641 insertions, 11 deletions
diff --git a/bsp_drivers.py b/bsp_drivers.py
index 910dcde..9d6631d 100644
--- a/bsp_drivers.py
+++ b/bsp_drivers.py
@@ -33,16 +33,28 @@ import waflib.ConfigSet
def bsp_files(bld):
source_files = {}
include_dirs = {}
- bsp_archs = {}
include_files = []
+ special_case_dirs = {'atsamv': './bsps/arm/atsam',
+ 'lm32_evr': './bsps/lm32',
+ 'lpc24xx_ea': './bsps/arm/shared/'}
+ special_case_sources = {'leon2': ['./bsps/shared/grlib/net/network_interface_add.c',
+ './bsps/shared/grlib/net/greth.c'],
+ 'leon3': ['./bsps/shared/grlib/net/network_interface_add.c',
+ './bsps/shared/grlib/net/greth.c'],
+ 'griscv':['./bsps/shared/grlib/net/network_interface_add.c',
+ './bsps/shared/grlib/net/greth.c']}
+
bsp_list = bld.env.RTEMS_ARCH_BSP_LIST
for bl in bsp_list:
bsp = bl.split('-')[-1]
arch = bl.split('-')[0]
- bsp_archs[bsp] = bl
- for root, dirs, files in os.walk(os.path.join('./bsps', arch, bsp)):
+ if bsp not in special_case_dirs:
+ source_dir = os.walk(os.path.join('./bsps', arch, bsp))
+ else:
+ source_dir = os.walk(special_case_dirs[bsp])
+ for root, dirs, files in source_dir:
include_dirs[bsp] = []
source_files[bsp] = []
for name in files:
@@ -51,4 +63,6 @@ def bsp_files(bld):
if name[-2:] == '.h':
if root not in include_dirs[bsp]:
include_dirs[bsp].append(root)
- return (include_dirs, source_files, bsp_archs)
+ if bsp in special_case_sources:
+ source_files[bsp].extend(special_case_sources[bsp])
+ return (include_dirs, source_files)
diff --git a/bsps/arm/shared/lpc-ethernet.c b/bsps/arm/shared/lpc-ethernet.c
new file mode 100644
index 0000000..ccfe169
--- /dev/null
+++ b/bsps/arm/shared/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.
+ */
+
+#include <machine/rtems-bsd-kernel-space.h>
+
+#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);
+ }
+}
diff --git a/bsps/include/libchip/i82586var.h b/bsps/include/libchip/i82586var.h
new file mode 100644
index 0000000..c9421a6
--- /dev/null
+++ b/bsps/include/libchip/i82586var.h
@@ -0,0 +1,319 @@
+/* $NetBSD: i82586var.h,v 1.15 2001/01/22 22:28:45 bjh21 Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Paul Kranenburg and Charles M. Hannum.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*-
+ * Copyright (c) 1992, 1993, University of Vermont and State
+ * Agricultural College.
+ * Copyright (c) 1992, 1993, Garrett A. Wollman.
+ *
+ * Portions:
+ * Copyright (c) 1994, 1995, Rafal K. Boni
+ * Copyright (c) 1990, 1991, William F. Jolitz
+ * Copyright (c) 1990, The Regents of the University of California
+ *
+ * All rights reserved.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of Vermont
+ * and State Agricultural College and Garrett A. Wollman, by William F.
+ * Jolitz, and by the University of California, Berkeley, Lawrence
+ * Berkeley Laboratory, and its contributors.
+ * 4. Neither the names of the Universities nor the names of the authors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 UNIVERSITY OR AUTHORS 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.
+ */
+
+/*
+ * Intel 82586 Ethernet chip
+ * Register, bit, and structure definitions.
+ *
+ * Original StarLAN driver written by Garrett Wollman with reference to the
+ * Clarkson Packet Driver code for this chip written by Russ Nelson and others.
+ *
+ * BPF support code taken from hpdev/if_le.c, supplied with tcpdump.
+ *
+ * 3C507 support is loosely based on code donated to NetBSD by Rafal Boni.
+ *
+ * Majorly cleaned up and 3C507 code merged by Charles Hannum.
+ *
+ * Converted to SUN ie driver by Charles D. Cranor,
+ * October 1994, January 1995.
+ * This sun version based on i386 version 1.30.
+ */
+
+#ifndef I82586_DEBUG
+#define I82586_DEBUG 0
+#endif
+
+/* Debug elements */
+#define IED_RINT 0x01
+#define IED_TINT 0x02
+#define IED_RNR 0x04
+#define IED_CNA 0x08
+#define IED_READFRAME 0x10
+#define IED_ENQ 0x20
+#define IED_XMIT 0x40
+#define IED_ALL 0x7f
+
+#define B_PER_F 3 /* recv buffers per frame */
+#define IE_RBUF_SIZE 256 /* size of each receive buffer;
+ MUST BE POWER OF TWO */
+#define NTXBUF 2 /* number of transmit commands */
+#define IE_TBUF_SIZE ETHER_MAX_LEN /* length of transmit buffer */
+
+#define IE_MAXMCAST (IE_TBUF_SIZE/6)/* must fit in transmit buffer */
+
+
+#define INTR_ENTER 0 /* intr hook called on ISR entry */
+#define INTR_EXIT 1 /* intr hook called on ISR exit */
+#define INTR_LOOP 2 /* intr hook called on ISR loop */
+#define INTR_ACK 3 /* intr hook called on ie_ack */
+
+#define CHIP_PROBE 0 /* reset called from chip probe */
+#define CARD_RESET 1 /* reset called from card reset */
+
+#if I82586_DEBUG
+#define I82586_INTS_REQ 0
+#define I82586_INTS_IN 1
+#define I82586_INTS_LOOPS 2
+#define I82586_INTS_OUT 3
+#define I82586_RX_INT 4
+#define I82586_RX_DROP 5
+#define I82586_RX_ERR 6
+#define I82586_RX_OK 7
+#define I82586_RX_START 8
+#define I82586_START_TX 9
+#define I82586_TX_START 10
+#define I82586_TX_INT 11
+#define I82586_TX_REQ 12
+#define I82586_TX_EVT 13
+#define I82586_TX_EMIT 14
+#define I82586_TX_BAD 15
+#define I82586_TX_ACTIVE 16
+#define I82586_TRACE_CNT 17
+
+#define I82586_TRACE_FLOW (10000)
+#endif
+
+/*
+ * Ethernet status, per interface.
+ *
+ * The chip uses two types of pointers: 16 bit and 24 bit
+ * 24 bit pointers cover the board's memory.
+ * 16 bit pointers are offsets from the ISCP's `ie_base'
+ *
+ * The board's memory is represented by the bus handle `bh'. The MI
+ * i82586 driver deals exclusively with offsets relative to the
+ * board memory bus handle. The `ie_softc' fields below that are marked
+ * `MD' are in the domain of the front-end driver; they opaque to the
+ * MI driver part.
+ *
+ * The front-end is required to manage the SCP and ISCP structures. i.e.
+ * allocate room for them on the board's memory, and arrange to point the
+ * chip at the SCB stucture, the offset of which is passed to the MI
+ * driver in `sc_scb'.
+ *
+ * The following functions provide the glue necessary to deal with
+ * host and bus idiosyncracies:
+ *
+ * hwreset - board reset
+ * hwinit - board initialization
+ * chan_attn - get chip to look at prepared commands
+ * intrhook - board dependent interrupt processing
+ *
+ * All of the following shared-memory access function use an offset
+ * relative to the bus handle to indicate the shared memory location.
+ * The bus_{read/write}N function take or return offset into the
+ * shared memory in the host's byte-order.
+ *
+ * memcopyin - copy device memory: board to KVA
+ * memcopyout - copy device memory: KVA to board
+ * bus_read16 - read a 16-bit i82586 pointer
+ `offset' argument will be 16-bit aligned
+ * bus_write16 - write a 16-bit i82586 pointer
+ `offset' argument will be 16-bit aligned
+ * bus_write24 - write a 24-bit i82586 pointer
+ `offset' argument will be 32-bit aligned
+ * bus_barrier - perform a bus barrier operation, forcing
+ all outstanding reads/writes to complete
+ *
+ */
+
+struct ie_softc {
+ struct arpcom arpcom;
+
+ /*
+ * For RTEMS we run the tx and rx handlers under a task due to the
+ * network semaphore stuff.
+ */
+
+ rtems_id intr_task;
+ rtems_id tx_task;
+
+ void *sc_iobase; /* (MD) KVA of base of 24 bit addr space */
+ void *sc_maddr; /* (MD) KVA of base of chip's RAM
+ (16bit addr space) */
+ u_int sc_msize; /* (MD) how much RAM we have/use */
+
+ /* Bus glue */
+ void (*hwreset) (struct ie_softc *, int);
+ void (*hwinit) (struct ie_softc *);
+ void (*chan_attn) (struct ie_softc *, int);
+ int (*intrhook) (struct ie_softc *, int where);
+
+ void (*memcopyin) (struct ie_softc *, void *, int, size_t);
+ void (*memcopyout) (struct ie_softc *, const void *,
+ int, size_t);
+ u_int16_t (*ie_bus_read16) (struct ie_softc *, int offset);
+ void (*ie_bus_write16) (struct ie_softc *, int offset,
+ u_int16_t value);
+ void (*ie_bus_write24) (struct ie_softc *, int offset,
+ int addr);
+ void (*ie_bus_barrier) (struct ie_softc *, int offset,
+ int length, int flags);
+
+ /* Media management */
+ int (*sc_mediachange) (struct ie_softc *);
+ /* card dependent media change */
+ void (*sc_mediastatus) (struct ie_softc *, struct ifmediareq *);
+ /* card dependent media status */
+
+ /*
+ * Offsets (relative to bus handle) of the i82586 SYSTEM structures.
+ */
+ int scp; /* Offset to the SCP (set by front-end) */
+ int iscp; /* Offset to the ISCP (set by front-end) */
+ int scb; /* Offset to SCB (set by front-end) */
+
+ /*
+ * Offset and size of a block of board memory where the buffers
+ * are to be allocated from (initialized by front-end).
+ */
+ int buf_area; /* Start of descriptors and buffers */
+ int buf_area_sz; /* Size of above */
+
+ /*
+ * The buffers & descriptors (recv and xmit)
+ */
+ int rframes; /* Offset to `nrxbuf' frame descriptors */
+ int rbds; /* Offset to `nrxbuf' buffer descriptors */
+ int rbufs; /* Offset to `nrxbuf' receive buffers */
+#define IE_RBUF_ADDR(sc, i) (sc->rbufs + ((i) * IE_RBUF_SIZE))
+ int rfhead, rftail;
+ int rbhead, rbtail;
+ int nframes; /* number of frames in use */
+ int nrxbuf; /* number of recv buffs in use */
+ int rnr_expect; /* XXX - expect a RCVR not ready interrupt */
+
+ int nop_cmds; /* Offset to NTXBUF no-op commands */
+ int xmit_cmds; /* Offset to NTXBUF transmit commands */
+ int xbds; /* Offset to NTXBUF buffer descriptors */
+ int xbufs; /* Offset to NTXBUF transmit buffers */
+#define IE_XBUF_ADDR(sc, i) (sc->xbufs + ((i) * IE_TBUF_SIZE))
+
+ int xchead, xctail;
+ int xmit_busy;
+ int do_xmitnopchain; /* Controls use of xmit NOP chains */
+ int xmit_req;
+
+ /* Multicast addresses */
+ char *mcast_addrs; /* Current MC filter addresses */
+ int mcast_addrs_size; /* Current size of MC buffer */
+ int mcast_count; /* Current # of addrs in buffer */
+ int want_mcsetup; /* run mcsetup at next opportunity */
+
+ int promisc; /* are we in promisc mode? */
+ int async_cmd_inprogress; /* we didn't wait for 586 to accept
+ a command */
+
+#if I82586_DEBUG
+#define I82586_TRACE(s, e, d) \
+do { rtems_interrupt_level level; rtems_interrupt_disable (level); \
+ (s)->trace_flow[(s)->trace_flow_in++] = (e); \
+ (s)->trace_flow[(s)->trace_flow_in++] = (unsigned int)(d); \
+ if ((s)->trace_flow_in >= I82586_TRACE_FLOW) { \
+ (s)->trace_flow_in = 0; \
+ (s)->trace_flow_wrap = 1; \
+ } \
+ rtems_interrupt_enable (level); \
+ } while (0)
+
+ int sc_debug;
+ unsigned int trace_flow[I82586_TRACE_FLOW * 2];
+ unsigned int trace_flow_wrap;
+#endif
+ unsigned int trace_flow_in;
+};
+
+/* Exported functions */
+rtems_isr i82586_intr (rtems_vector_number , void *);
+int i82586_proberam (struct ie_softc *);
+int i82586_attach (struct rtems_bsdnet_ifconfig *config, int attaching);
+
+/* Shortcut macros to optional (driver uses default if unspecified) callbacks */
+#define xIE_BUS_BARRIER(sc, offset, length, flags) \
+do { \
+ if ((sc)->ie_bus_barrier) \
+ ((sc)->ie_bus_barrier)((sc), (offset), (length), (flags));\
+ else \
+ bus_space_barrier((sc)->bt, (sc)->bh, (offset), (length), \
+ (flags)); \
+} while (0)
+
+#define IE_BUS_BARRIER(sc, offset, length, flags)
diff --git a/bsps/include/libchip/if_dcreg.h b/bsps/include/libchip/if_dcreg.h
new file mode 100644
index 0000000..07395c1
--- /dev/null
+++ b/bsps/include/libchip/if_dcreg.h
@@ -0,0 +1,1120 @@
+/*
+ * Copyright (c) 1997, 1998, 1999
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ *
+ * $FreeBSD: src/sys/pci/if_dcreg.h,v 1.4.2.21 2003/02/12 22:19:34 mbr Exp $
+ */
+
+/*
+ * 21143 and clone common register definitions.
+ */
+
+#define DC_BUSCTL 0x00 /* bus control */
+#define DC_TXSTART 0x08 /* tx start demand */
+#define DC_RXSTART 0x10 /* rx start demand */
+#define DC_RXADDR 0x18 /* rx descriptor list start addr */
+#define DC_TXADDR 0x20 /* tx descriptor list start addr */
+#define DC_ISR 0x28 /* interrupt status register */
+#define DC_NETCFG 0x30 /* network config register */
+#define DC_IMR 0x38 /* interrupt mask */
+#define DC_FRAMESDISCARDED 0x40 /* # of discarded frames */
+#define DC_SIO 0x48 /* MII and ROM/EEPROM access */
+#define DC_ROM 0x50 /* ROM programming address */
+#define DC_TIMER 0x58 /* general timer */
+#define DC_10BTSTAT 0x60 /* SIA status */
+#define DC_SIARESET 0x68 /* SIA connectivity */
+#define DC_10BTCTRL 0x70 /* SIA transmit and receive */
+#define DC_WATCHDOG 0x78 /* SIA and general purpose port */
+
+/*
+ * There are two general 'types' of MX chips that we need to be
+ * concerned with. One is the original 98713, which has its internal
+ * NWAY support controlled via the MDIO bits in the serial I/O
+ * register. The other is everything else (from the 98713A on up),
+ * which has its internal NWAY controlled via CSR13, CSR14 and CSR15,
+ * just like the 21143. This type setting also governs which of the
+ * 'magic' numbers we write to CSR16. The PNIC II falls into the
+ * 98713A/98715/98715A/98725 category.
+ */
+#define DC_TYPE_98713 0x1
+#define DC_TYPE_98713A 0x2
+#define DC_TYPE_987x5 0x3
+
+/* Other type of supported chips. */
+#define DC_TYPE_21143 0x4 /* Intel 21143 */
+#define DC_TYPE_ASIX 0x5 /* ASIX AX88140A/AX88141 */
+#define DC_TYPE_AL981 0x6 /* ADMtek AL981 Comet */
+#define DC_TYPE_AN985 0x7 /* ADMtek AN985 Centaur */
+#define DC_TYPE_DM9102 0x8 /* Davicom DM9102 */
+#define DC_TYPE_PNICII 0x9 /* 82c115 PNIC II */
+#define DC_TYPE_PNIC 0xA /* 82c168/82c169 PNIC I */
+#define DC_TYPE_CONEXANT 0xC /* Conexant LANfinity RS7112 */
+
+#define DC_IS_MACRONIX(x) \
+ (x->dc_type == DC_TYPE_98713 || \
+ x->dc_type == DC_TYPE_98713A || \
+ x->dc_type == DC_TYPE_987x5)
+
+#define DC_IS_ADMTEK(x) \
+ (x->dc_type == DC_TYPE_AL981 || \
+ x->dc_type == DC_TYPE_AN985)
+
+#define DC_IS_INTEL(x) (x->dc_type == DC_TYPE_21143)
+#define DC_IS_ASIX(x) (x->dc_type == DC_TYPE_ASIX)
+#define DC_IS_COMET(x) (x->dc_type == DC_TYPE_AL981)
+#define DC_IS_CENTAUR(x) (x->dc_type == DC_TYPE_AN985)
+#define DC_IS_DAVICOM(x) (x->dc_type == DC_TYPE_DM9102)
+#define DC_IS_PNICII(x) (x->dc_type == DC_TYPE_PNICII)
+#define DC_IS_PNIC(x) (x->dc_type == DC_TYPE_PNIC)
+#define DC_IS_CONEXANT(x) (x->dc_type == DC_TYPE_CONEXANT)
+
+/* MII/symbol mode port types */
+#define DC_PMODE_MII 0x1
+#define DC_PMODE_SYM 0x2
+#define DC_PMODE_SIA 0x3
+
+/*
+ * Bus control bits.
+ */
+#define DC_BUSCTL_RESET 0x00000001
+#define DC_BUSCTL_ARBITRATION 0x00000002
+#define DC_BUSCTL_SKIPLEN 0x0000007C
+#define DC_BUSCTL_BUF_BIGENDIAN 0x00000080
+#define DC_BUSCTL_BURSTLEN 0x00003F00
+#define DC_BUSCTL_CACHEALIGN 0x0000C000
+#define DC_BUSCTL_TXPOLL 0x000E0000
+#define DC_BUSCTL_DBO 0x00100000
+#define DC_BUSCTL_MRME 0x00200000
+#define DC_BUSCTL_MRLE 0x00800000
+#define DC_BUSCTL_MWIE 0x01000000
+#define DC_BUSCTL_ONNOW_ENB 0x04000000
+
+#define DC_SKIPLEN_1LONG 0x00000004
+#define DC_SKIPLEN_2LONG 0x00000008
+#define DC_SKIPLEN_3LONG 0x00000010
+#define DC_SKIPLEN_4LONG 0x00000020
+#define DC_SKIPLEN_5LONG 0x00000040
+
+#define DC_CACHEALIGN_NONE 0x00000000
+#define DC_CACHEALIGN_8LONG 0x00004000
+#define DC_CACHEALIGN_16LONG 0x00008000
+#define DC_CACHEALIGN_32LONG 0x0000C000
+
+#define DC_BURSTLEN_USECA 0x00000000
+#define DC_BURSTLEN_1LONG 0x00000100
+#define DC_BURSTLEN_2LONG 0x00000200
+#define DC_BURSTLEN_4LONG 0x00000400
+#define DC_BURSTLEN_8LONG 0x00000800
+#define DC_BURSTLEN_16LONG 0x00001000
+#define DC_BURSTLEN_32LONG 0x00002000
+
+#define DC_TXPOLL_OFF 0x00000000
+#define DC_TXPOLL_1 0x00020000
+#define DC_TXPOLL_2 0x00040000
+#define DC_TXPOLL_3 0x00060000
+#define DC_TXPOLL_4 0x00080000
+#define DC_TXPOLL_5 0x000A0000
+#define DC_TXPOLL_6 0x000C0000
+#define DC_TXPOLL_7 0x000E0000
+
+/*
+ * Interrupt status bits.
+ */
+#define DC_ISR_TX_OK 0x00000001
+#define DC_ISR_TX_IDLE 0x00000002
+#define DC_ISR_TX_NOBUF 0x00000004
+#define DC_ISR_TX_JABBERTIMEO 0x00000008
+#define DC_ISR_LINKGOOD 0x00000010
+#define DC_ISR_TX_UNDERRUN 0x00000020
+#define DC_ISR_RX_OK 0x00000040
+#define DC_ISR_RX_NOBUF 0x00000080
+#define DC_ISR_RX_READ 0x00000100
+#define DC_ISR_RX_WATDOGTIMEO 0x00000200
+#define DC_ISR_TX_EARLY 0x00000400
+#define DC_ISR_TIMER_EXPIRED 0x00000800
+#define DC_ISR_LINKFAIL 0x00001000
+#define DC_ISR_BUS_ERR 0x00002000
+#define DC_ISR_RX_EARLY 0x00004000
+#define DC_ISR_ABNORMAL 0x00008000
+#define DC_ISR_NORMAL 0x00010000
+#define DC_ISR_RX_STATE 0x000E0000
+#define DC_ISR_TX_STATE 0x00700000
+#define DC_ISR_BUSERRTYPE 0x03800000
+#define DC_ISR_100MBPSLINK 0x08000000
+#define DC_ISR_MAGICKPACK 0x10000000
+
+#define DC_RXSTATE_STOPPED 0x00000000 /* 000 - Stopped */
+#define DC_RXSTATE_FETCH 0x00020000 /* 001 - Fetching descriptor */
+#define DC_RXSTATE_ENDCHECK 0x00040000 /* 010 - check for rx end */
+#define DC_RXSTATE_WAIT 0x00060000 /* 011 - waiting for packet */
+#define DC_RXSTATE_SUSPEND 0x00080000 /* 100 - suspend rx */
+#define DC_RXSTATE_CLOSE 0x000A0000 /* 101 - close tx desc */
+#define DC_RXSTATE_FLUSH 0x000C0000 /* 110 - flush from FIFO */
+#define DC_RXSTATE_DEQUEUE 0x000E0000 /* 111 - dequeue from FIFO */
+
+#define DC_TXSTATE_RESET 0x00000000 /* 000 - reset */
+#define DC_TXSTATE_FETCH 0x00100000 /* 001 - fetching descriptor */
+#define DC_TXSTATE_WAITEND 0x00200000 /* 010 - wait for tx end */
+#define DC_TXSTATE_READING 0x00300000 /* 011 - read and enqueue */
+#define DC_TXSTATE_RSVD 0x00400000 /* 100 - reserved */
+#define DC_TXSTATE_SETUP 0x00500000 /* 101 - setup packet */
+#define DC_TXSTATE_SUSPEND 0x00600000 /* 110 - suspend tx */
+#define DC_TXSTATE_CLOSE 0x00700000 /* 111 - close tx desc */
+
+/*
+ * Network config bits.
+ */
+#define DC_NETCFG_RX_HASHPERF 0x00000001
+#define DC_NETCFG_RX_ON 0x00000002
+#define DC_NETCFG_RX_HASHONLY 0x00000004
+#define DC_NETCFG_RX_BADFRAMES 0x00000008
+#define DC_NETCFG_RX_INVFILT 0x00000010
+#define DC_NETCFG_BACKOFFCNT 0x00000020
+#define DC_NETCFG_RX_PROMISC 0x00000040
+#define DC_NETCFG_RX_ALLMULTI 0x00000080
+#define DC_NETCFG_FULLDUPLEX 0x00000200
+#define DC_NETCFG_LOOPBACK 0x00000C00
+#define DC_NETCFG_FORCECOLL 0x00001000
+#define DC_NETCFG_TX_ON 0x00002000
+#define DC_NETCFG_TX_THRESH 0x0000C000
+#define DC_NETCFG_TX_BACKOFF 0x00020000
+#define DC_NETCFG_PORTSEL 0x00040000 /* 0 == 10, 1 == 100 */
+#define DC_NETCFG_HEARTBEAT 0x00080000
+#define DC_NETCFG_STORENFWD 0x00200000
+#define DC_NETCFG_SPEEDSEL 0x00400000 /* 1 == 10, 0 == 100 */
+#define DC_NETCFG_PCS 0x00800000
+#define DC_NETCFG_SCRAMBLER 0x01000000
+#define DC_NETCFG_NO_RXCRC 0x02000000
+#define DC_NETCFG_RX_ALL 0x40000000
+#define DC_NETCFG_CAPEFFECT 0x80000000
+
+#define DC_OPMODE_NORM 0x00000000
+#define DC_OPMODE_INTLOOP 0x00000400
+#define DC_OPMODE_EXTLOOP 0x00000800
+
+#if 0
+#define DC_TXTHRESH_72BYTES 0x00000000
+#define DC_TXTHRESH_96BYTES 0x00004000
+#define DC_TXTHRESH_128BYTES 0x00008000
+#define DC_TXTHRESH_160BYTES 0x0000C000
+#endif
+
+#define DC_TXTHRESH_MIN 0x00000000
+#define DC_TXTHRESH_INC 0x00004000
+#define DC_TXTHRESH_MAX 0x0000C000
+
+
+/*
+ * Interrupt mask bits.
+ */
+#define DC_IMR_TX_OK 0x00000001
+#define DC_IMR_TX_IDLE 0x00000002
+#define DC_IMR_TX_NOBUF 0x00000004
+#define DC_IMR_TX_JABBERTIMEO 0x00000008
+#define DC_IMR_LINKGOOD 0x00000010
+#define DC_IMR_TX_UNDERRUN 0x00000020
+#define DC_IMR_RX_OK 0x00000040
+#define DC_IMR_RX_NOBUF 0x00000080
+#define DC_IMR_RX_READ 0x00000100
+#define DC_IMR_RX_WATDOGTIMEO 0x00000200
+#define DC_IMR_TX_EARLY 0x00000400
+#define DC_IMR_TIMER_EXPIRED 0x00000800
+#define DC_IMR_LINKFAIL 0x00001000
+#define DC_IMR_BUS_ERR 0x00002000
+#define DC_IMR_RX_EARLY 0x00004000
+#define DC_IMR_ABNORMAL 0x00008000
+#define DC_IMR_NORMAL 0x00010000
+#define DC_IMR_100MBPSLINK 0x08000000
+#define DC_IMR_MAGICKPACK 0x10000000
+
+#define DC_INTRS \
+ (DC_IMR_RX_OK|DC_IMR_TX_OK|DC_IMR_RX_NOBUF|DC_IMR_RX_WATDOGTIMEO|\
+ DC_IMR_TX_NOBUF|DC_IMR_TX_UNDERRUN|DC_IMR_BUS_ERR| \
+ DC_IMR_ABNORMAL|DC_IMR_NORMAL/*|DC_IMR_TX_EARLY*/)
+/*
+ * Serial I/O (EEPROM/ROM) bits.
+ */
+#define DC_SIO_EE_CS 0x00000001 /* EEPROM chip select */
+#define DC_SIO_EE_CLK 0x00000002 /* EEPROM clock */
+#define DC_SIO_EE_DATAIN 0x00000004 /* EEPROM data output */
+#define DC_SIO_EE_DATAOUT 0x00000008 /* EEPROM data input */
+#define DC_SIO_ROMDATA4 0x00000010
+#define DC_SIO_ROMDATA5 0x00000020
+#define DC_SIO_ROMDATA6 0x00000040
+#define DC_SIO_ROMDATA7 0x00000080
+#define DC_SIO_EESEL 0x00000800
+#define DC_SIO_ROMSEL 0x00001000
+#define DC_SIO_ROMCTL_WRITE 0x00002000
+#define DC_SIO_ROMCTL_READ 0x00004000
+#define DC_SIO_MII_CLK 0x00010000 /* MDIO clock */
+#define DC_SIO_MII_DATAOUT 0x00020000 /* MDIO data out */
+#define DC_SIO_MII_DIR 0x00040000 /* MDIO dir */
+#define DC_SIO_MII_DATAIN 0x00080000 /* MDIO data in */
+
+#define DC_EECMD_WRITE 0x140
+#define DC_EECMD_READ 0x180
+#define DC_EECMD_ERASE 0x1c0
+
+#define DC_EE_NODEADDR_OFFSET 0x70
+#define DC_EE_NODEADDR 10
+
+/*
+ * General purpose timer register
+ */
+#define DC_TIMER_VALUE 0x0000FFFF
+#define DC_TIMER_CONTINUOUS 0x00010000
+
+/*
+ * 10baseT status register
+ */
+#define DC_TSTAT_MIIACT 0x00000001 /* MII port activity */
+#define DC_TSTAT_LS100 0x00000002 /* link status of 100baseTX */
+#define DC_TSTAT_LS10 0x00000004 /* link status of 10baseT */
+#define DC_TSTAT_AUTOPOLARITY 0x00000008
+#define DC_TSTAT_AUIACT 0x00000100 /* AUI activity */
+#define DC_TSTAT_10BTACT 0x00000200 /* 10baseT activity */
+#define DC_TSTAT_NSN 0x00000400 /* non-stable FLPs detected */
+#define DC_TSTAT_REMFAULT 0x00000800
+#define DC_TSTAT_ANEGSTAT 0x00007000
+#define DC_TSTAT_LP_CAN_NWAY 0x00008000 /* link partner supports NWAY */
+#define DC_TSTAT_LPCODEWORD 0xFFFF0000 /* link partner's code word */
+
+#define DC_ASTAT_DISABLE 0x00000000
+#define DC_ASTAT_TXDISABLE 0x00001000
+#define DC_ASTAT_ABDETECT 0x00002000
+#define DC_ASTAT_ACKDETECT 0x00003000
+#define DC_ASTAT_CMPACKDETECT 0x00004000
+#define DC_ASTAT_AUTONEGCMP 0x00005000
+#define DC_ASTAT_LINKCHECK 0x00006000
+
+/*
+ * PHY reset register
+ */
+#define DC_SIA_RESET 0x00000001
+#define DC_SIA_AUI 0x00000008 /* AUI or 10baseT */
+
+/*
+ * 10baseT control register
+ */
+#define DC_TCTL_ENCODER_ENB 0x00000001
+#define DC_TCTL_LOOPBACK 0x00000002
+#define DC_TCTL_DRIVER_ENB 0x00000004
+#define DC_TCTL_LNKPULSE_ENB 0x00000008
+#define DC_TCTL_HALFDUPLEX 0x00000040
+#define DC_TCTL_AUTONEGENBL 0x00000080
+#define DC_TCTL_RX_SQUELCH 0x00000100
+#define DC_TCTL_COLL_SQUELCH 0x00000200
+#define DC_TCTL_COLL_DETECT 0x00000400
+#define DC_TCTL_SQE_ENB 0x00000800
+#define DC_TCTL_LINKTEST 0x00001000
+#define DC_TCTL_AUTOPOLARITY 0x00002000
+#define DC_TCTL_SET_POL_PLUS 0x00004000
+#define DC_TCTL_AUTOSENSE 0x00008000 /* 10bt/AUI autosense */
+#define DC_TCTL_100BTXHALF 0x00010000
+#define DC_TCTL_100BTXFULL 0x00020000
+#define DC_TCTL_100BT4 0x00040000
+
+/*
+ * Watchdog timer register
+ */
+#define DC_WDOG_JABBERDIS 0x00000001
+#define DC_WDOG_HOSTUNJAB 0x00000002
+#define DC_WDOG_JABBERCLK 0x00000004
+#define DC_WDOG_RXWDOGDIS 0x00000010
+#define DC_WDOG_RXWDOGCLK 0x00000020
+#define DC_WDOG_MUSTBEZERO 0x00000100
+#define DC_WDOG_AUIBNC 0x00100000
+#define DC_WDOG_ACTIVITY 0x00200000
+#define DC_WDOG_RX_MATCH 0x00400000
+#define DC_WDOG_LINK 0x00800000
+#define DC_WDOG_CTLWREN 0x08000000
+
+/*
+ * Size of a setup frame.
+ */
+#define DC_SFRAME_LEN 192
+
+/*
+ * 21x4x TX/RX list structure.
+ */
+
+struct dc_desc {
+ u_int32_t dc_status;
+ u_int32_t dc_ctl;
+ u_int32_t dc_ptr1;
+ u_int32_t dc_ptr2;
+};
+
+#define dc_data dc_ptr1
+#define dc_next dc_ptr2
+
+#define DC_RXSTAT_FIFOOFLOW 0x00000001
+#define DC_RXSTAT_CRCERR 0x00000002
+#define DC_RXSTAT_DRIBBLE 0x00000004
+#define DC_RXSTAT_MIIERE 0x00000008
+#define DC_RXSTAT_WATCHDOG 0x00000010
+#define DC_RXSTAT_FRAMETYPE 0x00000020 /* 0 == IEEE 802.3 */
+#define DC_RXSTAT_COLLSEEN 0x00000040
+#define DC_RXSTAT_GIANT 0x00000080
+#define DC_RXSTAT_LASTFRAG 0x00000100
+#define DC_RXSTAT_FIRSTFRAG 0x00000200
+#define DC_RXSTAT_MULTICAST 0x00000400
+#define DC_RXSTAT_RUNT 0x00000800
+#define DC_RXSTAT_RXTYPE 0x00003000
+#define DC_RXSTAT_DE 0x00004000
+#define DC_RXSTAT_RXERR 0x00008000
+#define DC_RXSTAT_RXLEN 0x3FFF0000
+#define DC_RXSTAT_OWN 0x80000000
+
+#define DC_RXBYTES(x) ((x & DC_RXSTAT_RXLEN) >> 16)
+#define DC_RXSTAT (DC_RXSTAT_FIRSTFRAG|DC_RXSTAT_LASTFRAG|DC_RXSTAT_OWN)
+
+#define DC_RXCTL_BUFLEN1 0x00000FFF
+#define DC_RXCTL_BUFLEN2 0x00FFF000
+#define DC_RXCTL_RLINK 0x01000000
+#define DC_RXCTL_RLAST 0x02000000
+
+#define DC_TXSTAT_DEFER 0x00000001
+#define DC_TXSTAT_UNDERRUN 0x00000002
+#define DC_TXSTAT_LINKFAIL 0x00000003
+#define DC_TXSTAT_COLLCNT 0x00000078
+#define DC_TXSTAT_SQE 0x00000080
+#define DC_TXSTAT_EXCESSCOLL 0x00000100
+#define DC_TXSTAT_LATECOLL 0x00000200
+#define DC_TXSTAT_NOCARRIER 0x00000400
+#define DC_TXSTAT_CARRLOST 0x00000800
+#define DC_TXSTAT_JABTIMEO 0x00004000
+#define DC_TXSTAT_ERRSUM 0x00008000
+#define DC_TXSTAT_OWN 0x80000000
+
+#define DC_TXCTL_BUFLEN1 0x000007FF
+#define DC_TXCTL_BUFLEN2 0x003FF800
+#define DC_TXCTL_FILTTYPE0 0x00400000
+#define DC_TXCTL_PAD 0x00800000
+#define DC_TXCTL_TLINK 0x01000000
+#define DC_TXCTL_TLAST 0x02000000
+#define DC_TXCTL_NOCRC 0x04000000
+#define DC_TXCTL_SETUP 0x08000000
+#define DC_TXCTL_FILTTYPE1 0x10000000
+#define DC_TXCTL_FIRSTFRAG 0x20000000
+#define DC_TXCTL_LASTFRAG 0x40000000
+#define DC_TXCTL_FINT 0x80000000
+
+#define DC_FILTER_PERFECT 0x00000000
+#define DC_FILTER_HASHPERF 0x00400000
+#define DC_FILTER_INVERSE 0x10000000
+#define DC_FILTER_HASHONLY 0x10400000
+
+#define DC_MAXFRAGS 16
+#ifdef DEVICE_POLLING
+#define DC_RX_LIST_CNT 192
+#else
+#define DC_RX_LIST_CNT 64
+#endif
+#define DC_TX_LIST_CNT 256
+#define DC_MIN_FRAMELEN 60
+#define DC_RXLEN 1536
+
+#define DC_INC(x, y) (x) = (x + 1) % y
+
+struct dc_list_data {
+ struct dc_desc dc_rx_list[DC_RX_LIST_CNT];
+ struct dc_desc dc_tx_list[DC_TX_LIST_CNT];
+};
+
+struct dc_chain_data {
+ struct mbuf *dc_rx_chain[DC_RX_LIST_CNT];
+ struct mbuf *dc_tx_chain[DC_TX_LIST_CNT];
+ u_int32_t dc_sbuf[DC_SFRAME_LEN/sizeof(u_int32_t)];
+ u_int8_t dc_pad[DC_MIN_FRAMELEN];
+ int dc_tx_prod;
+ int dc_tx_cons;
+ int dc_tx_cnt;
+ int dc_rx_prod;
+};
+
+struct dc_mediainfo {
+ int dc_media;
+ u_int8_t *dc_gp_ptr;
+ u_int8_t dc_gp_len;
+ u_int8_t *dc_reset_ptr;
+ u_int8_t dc_reset_len;
+ struct dc_mediainfo *dc_next;
+};
+
+
+struct dc_type {
+ u_int16_t dc_vid;
+ u_int16_t dc_did;
+ char *dc_name;
+ int dc_devsig;
+ int dc_bus;
+ int dc_dev;
+ int dc_fun;
+};
+
+struct dc_mii_frame {
+ u_int8_t mii_stdelim;
+ u_int8_t mii_opcode;
+ u_int8_t mii_phyaddr;
+ u_int8_t mii_regaddr;
+ u_int8_t mii_turnaround;
+ u_int16_t mii_data;
+};
+
+/*
+ * MII constants
+ */
+#define DC_MII_STARTDELIM 0x01
+#define DC_MII_READOP 0x02
+#define DC_MII_WRITEOP 0x01
+#define DC_MII_TURNAROUND 0x02
+
+
+/*
+ * Registers specific to clone devices.
+ * This mainly relates to RX filter programming: not all 21x4x clones
+ * use the standard DEC filter programming mechanism.
+ */
+
+/*
+ * ADMtek specific registers and constants for the AL981 and AN985.
+ * The AN985 doesn't use the magic PHY registers.
+ */
+#define DC_AL_CR 0x88 /* command register */
+#define DC_AL_PAR0 0xA4 /* station address */
+#define DC_AL_PAR1 0xA8 /* station address */
+#define DC_AL_MAR0 0xAC /* multicast hash filter */
+#define DC_AL_MAR1 0xB0 /* multicast hash filter */
+#define DC_AL_BMCR 0xB4 /* built in PHY control */
+#define DC_AL_BMSR 0xB8 /* built in PHY status */
+#define DC_AL_VENID 0xBC /* built in PHY ID0 */
+#define DC_AL_DEVID 0xC0 /* built in PHY ID1 */
+#define DC_AL_ANAR 0xC4 /* built in PHY autoneg advert */
+#define DC_AL_LPAR 0xC8 /* bnilt in PHY link part. ability */
+#define DC_AL_ANER 0xCC /* built in PHY autoneg expansion */
+
+#define DC_AL_CR_ATUR 0x00000001 /* automatic TX underrun recovery */
+#define DC_ADMTEK_PHYADDR 0x1
+#define DC_AL_EE_NODEADDR 4
+/* End of ADMtek specific registers */
+
+/*
+ * ASIX specific registers.
+ */
+#define DC_AX_FILTIDX 0x68 /* RX filter index */
+#define DC_AX_FILTDATA 0x70 /* RX filter data */
+
+/*
+ * Special ASIX-specific bits in the ASIX NETCFG register (CSR6).
+ */
+#define DC_AX_NETCFG_RX_BROAD 0x00000100
+
+/*
+ * RX Filter Index Register values
+ */
+#define DC_AX_FILTIDX_PAR0 0x00000000
+#define DC_AX_FILTIDX_PAR1 0x00000001
+#define DC_AX_FILTIDX_MAR0 0x00000002
+#define DC_AX_FILTIDX_MAR1 0x00000003
+/* End of ASIX specific registers */
+
+/*
+ * Macronix specific registers. The Macronix chips have a special
+ * register for reading the NWAY status, which we don't use, plus
+ * a magic packet register, which we need to tweak a bit per the
+ * Macronix application notes.
+ */
+#define DC_MX_MAGICPACKET 0x80
+#define DC_MX_NWAYSTAT 0xA0
+
+/*
+ * Magic packet register
+ */
+#define DC_MX_MPACK_DISABLE 0x00400000
+
+/*
+ * NWAY status register.
+ */
+#define DC_MX_NWAY_10BTHALF 0x08000000
+#define DC_MX_NWAY_10BTFULL 0x10000000
+#define DC_MX_NWAY_100BTHALF 0x20000000
+#define DC_MX_NWAY_100BTFULL 0x40000000
+#define DC_MX_NWAY_100BT4 0x80000000
+
+/*
+ * These are magic values that must be written into CSR16
+ * (DC_MX_MAGICPACKET) in order to put the chip into proper
+ * operating mode. The magic numbers are documented in the
+ * Macronix 98715 application notes.
+ */
+#define DC_MX_MAGIC_98713 0x0F370000
+#define DC_MX_MAGIC_98713A 0x0B3C0000
+#define DC_MX_MAGIC_98715 0x0B3C0000
+#define DC_MX_MAGIC_98725 0x0B3C0000
+/* End of Macronix specific registers */
+
+/*
+ * PNIC 82c168/82c169 specific registers.
+ * The PNIC has its own special NWAY support, which doesn't work,
+ * and shortcut ways of reading the EEPROM and MII bus.
+ */
+#define DC_PN_GPIO 0x60 /* general purpose pins control */
+#define DC_PN_PWRUP_CFG 0x90 /* config register, set by EEPROM */
+#define DC_PN_SIOCTL 0x98 /* serial EEPROM control register */
+#define DC_PN_MII 0xA0 /* MII access register */
+#define DC_PN_NWAY 0xB8 /* Internal NWAY register */
+
+/* Serial I/O EEPROM register */
+#define DC_PN_SIOCTL_DATA 0x0000003F
+#define DC_PN_SIOCTL_OPCODE 0x00000300
+#define DC_PN_SIOCTL_BUSY 0x80000000
+
+#define DC_PN_EEOPCODE_ERASE 0x00000300
+#define DC_PN_EEOPCODE_READ 0x00000600
+#define DC_PN_EEOPCODE_WRITE 0x00000100
+
+/*
+ * The first two general purpose pins control speed selection and
+ * 100Mbps loopback on the 82c168 chip. The control bits should always
+ * be set (to make the data pins outputs) and the speed selction and
+ * loopback bits set accordingly when changing media. Physically, this
+ * will set the state of a relay mounted on the card.
+ */
+#define DC_PN_GPIO_DATA0 0x000000001
+#define DC_PN_GPIO_DATA1 0x000000002
+#define DC_PN_GPIO_DATA2 0x000000004
+#define DC_PN_GPIO_DATA3 0x000000008
+#define DC_PN_GPIO_CTL0 0x000000010
+#define DC_PN_GPIO_CTL1 0x000000020
+#define DC_PN_GPIO_CTL2 0x000000040
+#define DC_PN_GPIO_CTL3 0x000000080
+#define DC_PN_GPIO_SPEEDSEL DC_PN_GPIO_DATA0/* 1 == 100Mbps, 0 == 10Mbps */
+#define DC_PN_GPIO_100TX_LOOP DC_PN_GPIO_DATA1/* 1 == normal, 0 == loop */
+#define DC_PN_GPIO_BNC_ENB DC_PN_GPIO_DATA2
+#define DC_PN_GPIO_100TX_LNK DC_PN_GPIO_DATA3
+#define DC_PN_GPIO_SETBIT(sc, r) \
+ DC_SETBIT(sc, DC_PN_GPIO, ((r) | (r << 4)))
+#define DC_PN_GPIO_CLRBIT(sc, r) \
+ { \
+ DC_SETBIT(sc, DC_PN_GPIO, ((r) << 4)); \
+ DC_CLRBIT(sc, DC_PN_GPIO, (r)); \
+ }
+
+/* shortcut MII access register */
+#define DC_PN_MII_DATA 0x0000FFFF
+#define DC_PN_MII_RESERVER 0x00020000
+#define DC_PN_MII_REGADDR 0x007C0000
+#define DC_PN_MII_PHYADDR 0x0F800000
+#define DC_PN_MII_OPCODE 0x30000000
+#define DC_PN_MII_BUSY 0x80000000
+
+#define DC_PN_MIIOPCODE_READ 0x60020000
+#define DC_PN_MIIOPCODE_WRITE 0x50020000
+
+/* Internal NWAY bits */
+#define DC_PN_NWAY_RESET 0x00000001 /* reset */
+#define DC_PN_NWAY_PDOWN 0x00000002 /* power down */
+#define DC_PN_NWAY_BYPASS 0x00000004 /* bypass */
+#define DC_PN_NWAY_AUILOWCUR 0x00000008 /* AUI low current */
+#define DC_PN_NWAY_TPEXTEND 0x00000010 /* low squelch voltage */
+#define DC_PN_NWAY_POLARITY 0x00000020 /* 0 == on, 1 == off */
+#define DC_PN_NWAY_TP 0x00000040 /* 1 == tp, 0 == AUI */
+#define DC_PN_NWAY_AUIVOLT 0x00000080 /* 1 == full, 0 == half */
+#define DC_PN_NWAY_DUPLEX 0x00000100 /* LED, 1 == full, 0 == half */
+#define DC_PN_NWAY_LINKTEST 0x00000200 /* 0 == on, 1 == off */
+#define DC_PN_NWAY_AUTODETECT 0x00000400 /* 1 == off, 0 == on */
+#define DC_PN_NWAY_SPEEDSEL 0x00000800 /* LED, 0 = 10, 1 == 100 */
+#define DC_PN_NWAY_NWAY_ENB 0x00001000 /* 0 == off, 1 == on */
+#define DC_PN_NWAY_CAP10HDX 0x00002000
+#define DC_PN_NWAY_CAP10FDX 0x00004000
+#define DC_PN_NWAY_CAP100FDX 0x00008000
+#define DC_PN_NWAY_CAP100HDX 0x00010000
+#define DC_PN_NWAY_CAP100T4 0x00020000
+#define DC_PN_NWAY_ANEGRESTART 0x02000000 /* resets when aneg done */
+#define DC_PN_NWAY_REMFAULT 0x04000000
+#define DC_PN_NWAY_LPAR10HDX 0x08000000
+#define DC_PN_NWAY_LPAR10FDX 0x10000000
+#define DC_PN_NWAY_LPAR100FDX 0x20000000
+#define DC_PN_NWAY_LPAR100HDX 0x40000000
+#define DC_PN_NWAY_LPAR100T4 0x80000000
+
+/* End of PNIC specific registers */
+
+/*
+ * CONEXANT specific registers.
+ */
+
+#define DC_CONEXANT_PHYADDR 0x1
+#define DC_CONEXANT_EE_NODEADDR 0x19A
+
+/* End of CONEXANT specific registers */
+
+
+struct dc_softc {
+ struct arpcom arpcom; /* interface info */
+ rtems_irq_connect_data irqInfo;
+ volatile u_int32_t membase;
+ rtems_id daemontid;
+#if 0
+ bus_space_handle_t dc_bhandle; /* bus space handle */
+ bus_space_tag_t dc_btag; /* bus space tag */
+ void *dc_intrhand;
+ struct resource *dc_irq;
+ struct resource *dc_res;
+#endif
+ struct dc_type *dc_info; /* adapter info */
+/* device_t dc_miibus; */
+ u_int8_t dc_unit; /* interface number */
+ char *dc_name;
+ u_int8_t dc_type;
+ u_int8_t dc_pmode;
+ u_int8_t dc_link;
+ u_int8_t dc_cachesize;
+ int dc_romwidth;
+ int dc_pnic_rx_bug_save;
+ unsigned char *dc_pnic_rx_buf;
+ int dc_if_flags;
+ int dc_if_media;
+ u_int32_t dc_flags;
+ u_int32_t dc_txthresh;
+ u_int8_t *dc_srom;
+ struct dc_mediainfo *dc_mi;
+/*
+ struct callout_handle dc_stat_ch;
+*/
+ struct dc_list_data *dc_ldata;
+ struct dc_chain_data dc_cdata;
+#ifdef __alpha__
+ int dc_srm_media;
+#endif
+#ifdef DEVICE_POLLING
+ int rxcycles; /* ... when polling */
+#endif
+ int suspended; /* 0 = normal 1 = suspended */
+
+ u_int32_t saved_maps[5]; /* pci data */
+ u_int32_t saved_biosaddr;
+ u_int8_t saved_intline;
+ u_int8_t saved_cachelnsz;
+ u_int8_t saved_lattimer;
+};
+
+#define DC_TX_POLL 0x00000001
+#define DC_TX_COALESCE 0x00000002
+#define DC_TX_ADMTEK_WAR 0x00000004
+#define DC_TX_USE_TX_INTR 0x00000008
+#define DC_RX_FILTER_TULIP 0x00000010
+#define DC_TX_INTR_FIRSTFRAG 0x00000020
+#define DC_PNIC_RX_BUG_WAR 0x00000040
+#define DC_TX_FIXED_RING 0x00000080
+#define DC_TX_STORENFWD 0x00000100
+#define DC_REDUCED_MII_POLL 0x00000200
+#define DC_TX_INTR_ALWAYS 0x00000400
+#define DC_21143_NWAY 0x00000800
+#define DC_128BIT_HASH 0x00001000
+#define DC_64BIT_HASH 0x00002000
+#define DC_TULIP_LEDS 0x00004000
+#define DC_TX_ONE 0x00008000
+
+/*
+ * register space access macros
+ */
+#define _readl_(addr) (*(volatile unsigned int *)((void *)(addr)))
+#define _writel_(b, addr) (*(volatile unsigned int *)((void *)(addr)) = (b))
+
+#define CSR_READ_4(sc, reg) _readl_(((sc->membase)+(reg)))
+#define CSR_WRITE_4(sc, reg, val) _writel_(val, ((sc->membase)+(reg)))
+
+
+
+
+
+
+#define DC_TIMEOUT 1000
+#define ETHER_ALIGN 2
+
+/*
+ * General constants that are fun to know.
+ */
+
+/*
+ * DEC PCI vendor ID
+ */
+#define DC_VENDORID_DEC 0x1011
+
+/*
+ * DEC/Intel 21143 PCI device ID
+ */
+#define DC_DEVICEID_21143 0x0019
+
+/*
+ * Macronix PCI vendor ID
+ */
+#define DC_VENDORID_MX 0x10D9
+
+/*
+ * Macronix PMAC device IDs.
+ */
+#define DC_DEVICEID_98713 0x0512
+#define DC_DEVICEID_987x5 0x0531
+#define DC_DEVICEID_98727 0x0532
+#define DC_DEVICEID_98732 0x0532
+
+/* Macronix PCI revision codes. */
+#define DC_REVISION_98713 0x00
+#define DC_REVISION_98713A 0x10
+#define DC_REVISION_98715 0x20
+#define DC_REVISION_98715AEC_C 0x25
+#define DC_REVISION_98725 0x30
+
+/*
+ * Compex PCI vendor ID.
+ */
+#define DC_VENDORID_CP 0x11F6
+
+/*
+ * Compex PMAC PCI device IDs.
+ */
+#define DC_DEVICEID_98713_CP 0x9881
+
+/*
+ * Lite-On PNIC PCI vendor ID
+ */
+#define DC_VENDORID_LO 0x11AD
+
+/*
+ * 82c168/82c169 PNIC device IDs. Both chips have the same device
+ * ID but different revisions. Revision 0x10 is the 82c168, and
+ * 0x20 is the 82c169.
+ */
+#define DC_DEVICEID_82C168 0x0002
+
+#define DC_REVISION_82C168 0x10
+#define DC_REVISION_82C169 0x20
+
+/*
+ * Lite-On PNIC II device ID. Note: this is actually a Macronix 98715A
+ * with wake on lan/magic packet support.
+ */
+#define DC_DEVICEID_82C115 0xc115
+
+/*
+ * Davicom vendor ID.
+ */
+#define DC_VENDORID_DAVICOM 0x1282
+
+/*
+ * Davicom device IDs.
+ */
+#define DC_DEVICEID_DM9009 0x9009
+#define DC_DEVICEID_DM9100 0x9100
+#define DC_DEVICEID_DM9102 0x9102
+
+/*
+ * The DM9102A has the same PCI device ID as the DM9102,
+ * but a higher revision code.
+ */
+#define DC_REVISION_DM9102 0x10
+#define DC_REVISION_DM9102A 0x30
+
+/*
+ * ADMtek vendor ID.
+ */
+#define DC_VENDORID_ADMTEK 0x1317
+
+/*
+ * ADMtek device IDs.
+ */
+#define DC_DEVICEID_AL981 0x0981
+#define DC_DEVICEID_AN985 0x0985
+
+/*
+ * ASIX vendor ID.
+ */
+#define DC_VENDORID_ASIX 0x125B
+
+/*
+ * ASIX device IDs.
+ */
+#define DC_DEVICEID_AX88140A 0x1400
+
+/*
+ * The ASIX AX88140 and ASIX AX88141 have the same vendor and
+ * device IDs but different revision values.
+ */
+#define DC_REVISION_88140 0x00
+#define DC_REVISION_88141 0x10
+
+/*
+ * Accton vendor ID.
+ */
+#define DC_VENDORID_ACCTON 0x1113
+
+/*
+ * Accton device IDs.
+ */
+#define DC_DEVICEID_EN1217 0x1217
+#define DC_DEVICEID_EN2242 0x1216
+
+/*
+ * Conexant vendor ID.
+ */
+#define DC_VENDORID_CONEXANT 0x14f1
+
+/*
+ * Conexant device IDs.
+ */
+#define DC_DEVICEID_RS7112 0x1803
+
+/*
+ * PCI low memory base and low I/O base register, and
+ * other PCI registers.
+ */
+
+#define DC_PCI_CFID 0x00 /* Id */
+#define DC_PCI_CFCS 0x04 /* Command and status */
+#define DC_PCI_CFRV 0x08 /* Revision */
+#define DC_PCI_CFLT 0x0C /* Latency timer */
+#define DC_PCI_CFBIO 0x10 /* Base I/O address */
+#define DC_PCI_CFBMA 0x14 /* Base memory address */
+#define DC_PCI_CCIS 0x28 /* Card info struct */
+#define DC_PCI_CSID 0x2C /* Subsystem ID */
+#define DC_PCI_CBER 0x30 /* Expansion ROM base address */
+#define DC_PCI_CCAP 0x34 /* Caps pointer - PD/TD chip only */
+#define DC_PCI_CFIT 0x3C /* Interrupt */
+#define DC_PCI_CFDD 0x40 /* Device and driver area */
+#define DC_PCI_CWUA0 0x44 /* Wake-Up LAN addr 0 */
+#define DC_PCI_CWUA1 0x48 /* Wake-Up LAN addr 1 */
+#define DC_PCI_SOP0 0x4C /* SecureON passwd 0 */
+#define DC_PCI_SOP1 0x50 /* SecureON passwd 1 */
+#define DC_PCI_CWUC 0x54 /* Configuration Wake-Up cmd */
+#define DC_PCI_CCID 0xDC /* Capability ID - PD/TD only */
+#define DC_PCI_CPMC 0xE0 /* Pwrmgmt ctl & sts - PD/TD only */
+
+/* PCI ID register */
+#define DC_CFID_VENDOR 0x0000FFFF
+#define DC_CFID_DEVICE 0xFFFF0000
+
+/* PCI command/status register */
+#define DC_CFCS_IOSPACE 0x00000001 /* I/O space enable */
+#define DC_CFCS_MEMSPACE 0x00000002 /* memory space enable */
+#define DC_CFCS_BUSMASTER 0x00000004 /* bus master enable */
+#define DC_CFCS_MWI_ENB 0x00000010 /* mem write and inval enable */
+#define DC_CFCS_PARITYERR_ENB 0x00000040 /* parity error enable */
+#define DC_CFCS_SYSERR_ENB 0x00000100 /* system error enable */
+#define DC_CFCS_NEWCAPS 0x00100000 /* new capabilities */
+#define DC_CFCS_FAST_B2B 0x00800000 /* fast back-to-back capable */
+#define DC_CFCS_DATAPARITY 0x01000000 /* Parity error report */
+#define DC_CFCS_DEVSELTIM 0x06000000 /* devsel timing */
+#define DC_CFCS_TGTABRT 0x10000000 /* received target abort */
+#define DC_CFCS_MASTERABRT 0x20000000 /* received master abort */
+#define DC_CFCS_SYSERR 0x40000000 /* asserted system error */
+#define DC_CFCS_PARITYERR 0x80000000 /* asserted parity error */
+
+/* PCI revision register */
+#define DC_CFRV_STEPPING 0x0000000F
+#define DC_CFRV_REVISION 0x000000F0
+#define DC_CFRV_SUBCLASS 0x00FF0000
+#define DC_CFRV_BASECLASS 0xFF000000
+
+#define DC_21143_PB_REV 0x00000030
+#define DC_21143_TB_REV 0x00000030
+#define DC_21143_PC_REV 0x00000030
+#define DC_21143_TC_REV 0x00000030
+#define DC_21143_PD_REV 0x00000041
+#define DC_21143_TD_REV 0x00000041
+
+/* PCI latency timer register */
+#define DC_CFLT_CACHELINESIZE 0x000000FF
+#define DC_CFLT_LATENCYTIMER 0x0000FF00
+
+/* PCI subsystem ID register */
+#define DC_CSID_VENDOR 0x0000FFFF
+#define DC_CSID_DEVICE 0xFFFF0000
+
+/* PCI cababilities pointer */
+#define DC_CCAP_OFFSET 0x000000FF
+
+/* PCI interrupt config register */
+#define DC_CFIT_INTLINE 0x000000FF
+#define DC_CFIT_INTPIN 0x0000FF00
+#define DC_CFIT_MIN_GNT 0x00FF0000
+#define DC_CFIT_MAX_LAT 0xFF000000
+
+/* PCI capability register */
+#define DC_CCID_CAPID 0x000000FF
+#define DC_CCID_NEXTPTR 0x0000FF00
+#define DC_CCID_PM_VERS 0x00070000
+#define DC_CCID_PME_CLK 0x00080000
+#define DC_CCID_DVSPEC_INT 0x00200000
+#define DC_CCID_STATE_D1 0x02000000
+#define DC_CCID_STATE_D2 0x04000000
+#define DC_CCID_PME_D0 0x08000000
+#define DC_CCID_PME_D1 0x10000000
+#define DC_CCID_PME_D2 0x20000000
+#define DC_CCID_PME_D3HOT 0x40000000
+#define DC_CCID_PME_D3COLD 0x80000000
+
+/* PCI power management control/status register */
+#define DC_CPMC_STATE 0x00000003
+#define DC_CPMC_PME_ENB 0x00000100
+#define DC_CPMC_PME_STS 0x00008000
+
+#define DC_PSTATE_D0 0x0
+#define DC_PSTATE_D1 0x1
+#define DC_PSTATE_D2 0x2
+#define DC_PSTATE_D3 0x3
+
+/* Device specific region */
+/* Configuration and driver area */
+#define DC_CFDD_DRVUSE 0x0000FFFF
+#define DC_CFDD_SNOOZE_MODE 0x40000000
+#define DC_CFDD_SLEEP_MODE 0x80000000
+
+/* Configuration wake-up command register */
+#define DC_CWUC_MUST_BE_ZERO 0x00000001
+#define DC_CWUC_SECUREON_ENB 0x00000002
+#define DC_CWUC_FORCE_WUL 0x00000004
+#define DC_CWUC_BNC_ABILITY 0x00000008
+#define DC_CWUC_AUI_ABILITY 0x00000010
+#define DC_CWUC_TP10_ABILITY 0x00000020
+#define DC_CWUC_MII_ABILITY 0x00000040
+#define DC_CWUC_SYM_ABILITY 0x00000080
+#define DC_CWUC_LOCK 0x00000100
+
+/*
+ * SROM nonsense.
+ */
+
+#define DC_IB_CTLRCNT 0x13
+#define DC_IB_LEAF0_CNUM 0x1A
+#define DC_IB_LEAF0_OFFSET 0x1B
+
+struct dc_info_leaf {
+ u_int16_t dc_conntype;
+ u_int8_t dc_blkcnt;
+ u_int8_t dc_rsvd;
+ u_int16_t dc_infoblk;
+};
+
+#define DC_CTYPE_10BT 0x0000
+#define DC_CTYPE_10BT_NWAY 0x0100
+#define DC_CTYPE_10BT_FDX 0x0204
+#define DC_CTYPE_10B2 0x0001
+#define DC_CTYPE_10B5 0x0002
+#define DC_CTYPE_100BT 0x0003
+#define DC_CTYPE_100BT_FDX 0x0205
+#define DC_CTYPE_100T4 0x0006
+#define DC_CTYPE_100FX 0x0007
+#define DC_CTYPE_100FX_FDX 0x0208
+#define DC_CTYPE_MII_10BT 0x0009
+#define DC_CTYPE_MII_10BT_FDX 0x020A
+#define DC_CTYPE_MII_100BT 0x000D
+#define DC_CTYPE_MII_100BT_FDX 0x020E
+#define DC_CTYPE_MII_100T4 0x000F
+#define DC_CTYPE_MII_100FX 0x0010
+#define DC_CTYPE_MII_100FX_FDX 0x0211
+#define DC_CTYPE_DYN_PUP_AUTOSENSE 0x0800
+#define DC_CTYPE_PUP_AUTOSENSE 0x8800
+#define DC_CTYPE_NOMEDIA 0xFFFF
+
+#define DC_EBLOCK_SIA 0x0002
+#define DC_EBLOCK_MII 0x0003
+#define DC_EBLOCK_SYM 0x0004
+#define DC_EBLOCK_RESET 0x0005
+#define DC_EBLOCK_PHY_SHUTDOWN 0x0006
+
+struct dc_leaf_hdr {
+ u_int16_t dc_mtype;
+ u_int8_t dc_mcnt;
+ u_int8_t dc_rsvd;
+};
+
+struct dc_eblock_hdr {
+ u_int8_t dc_len;
+ u_int8_t dc_type;
+};
+
+struct dc_eblock_sia {
+ struct dc_eblock_hdr dc_sia_hdr;
+ u_int8_t dc_sia_code;
+ u_int8_t dc_sia_mediaspec[6]; /* CSR13, CSR14, CSR15 */
+ u_int8_t dc_sia_gpio_ctl[2];
+ u_int8_t dc_sia_gpio_dat[2];
+};
+
+#define DC_SIA_CODE_10BT 0x00
+#define DC_SIA_CODE_10B2 0x01
+#define DC_SIA_CODE_10B5 0x02
+#define DC_SIA_CODE_10BT_FDX 0x04
+#define DC_SIA_CODE_EXT 0x40
+
+/*
+ * Note that the first word in the gpr and reset
+ * sequences is always a control word.
+ */
+struct dc_eblock_mii {
+ struct dc_eblock_hdr dc_mii_hdr;
+ u_int8_t dc_mii_phynum;
+ u_int8_t dc_gpr_len;
+/* u_int16_t dc_gpr_dat[n]; */
+/* u_int8_t dc_reset_len; */
+/* u_int16_t dc_reset_dat[n]; */
+/* There are other fields after these, but we don't
+ * care about them since they can be determined by looking
+ * at the PHY.
+ */
+};
+
+struct dc_eblock_sym {
+ struct dc_eblock_hdr dc_sym_hdr;
+ u_int8_t dc_sym_code;
+ u_int8_t dc_sym_gpio_ctl[2];
+ u_int8_t dc_sym_gpio_dat[2];
+ u_int8_t dc_sym_cmd[2];
+};
+
+#define DC_SYM_CODE_100BT 0x03
+#define DC_SYM_CODE_100BT_FDX 0x05
+#define DC_SYM_CODE_100T4 0x06
+#define DC_SYM_CODE_100FX 0x07
+#define DC_SYM_CODE_100FX_FDX 0x08
+
+struct dc_eblock_reset {
+ struct dc_eblock_hdr dc_reset_hdr;
+ u_int8_t dc_reset_len;
+/* u_int16_t dc_reset_dat[n]; */
+};
+
+#ifdef __alpha__
+#undef vtophys
+#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va)
+#endif
diff --git a/bsps/include/libchip/if_fxpvar.h b/bsps/include/libchip/if_fxpvar.h
new file mode 100644
index 0000000..f29f52c
--- /dev/null
+++ b/bsps/include/libchip/if_fxpvar.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 1995, David Greenman
+ * All rights reserved.
+ *
+ * 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 unmodified, 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 AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD: src/sys/dev/fxp/if_fxpvar.h,v 1.17.2.3 2001/06/08 20:36:58 jlemon Exp $
+ */
+
+/*
+ * Misc. defintions for the Intel EtherExpress Pro/100B PCI Fast
+ * Ethernet driver
+ */
+
+/*
+ * Number of transmit control blocks. This determines the number
+ * of transmit buffers that can be chained in the CB list.
+ * This must be a power of two.
+ */
+#define FXP_NTXCB 128
+
+/*
+ * Number of completed TX commands at which point an interrupt
+ * will be generated to garbage collect the attached buffers.
+ * Must be at least one less than FXP_NTXCB, and should be
+ * enough less so that the transmitter doesn't becomes idle
+ * during the buffer rundown (which would reduce performance).
+ */
+#define FXP_CXINT_THRESH 120
+
+/*
+ * TxCB list index mask. This is used to do list wrap-around.
+ */
+#define FXP_TXCB_MASK (FXP_NTXCB - 1)
+
+/*
+ * Number of receive frame area buffers. These are large so chose
+ * wisely.
+ */
+#if 0
+#define FXP_NRFABUFS 64
+#else
+#define FXP_NRFABUFS 16
+#endif
+/*
+ * Maximum number of seconds that the receiver can be idle before we
+ * assume it's dead and attempt to reset it by reprogramming the
+ * multicast filter. This is part of a work-around for a bug in the
+ * NIC. See fxp_stats_update().
+ */
+#define FXP_MAX_RX_IDLE 15
+
+#if __FreeBSD_version < 500000
+#define FXP_LOCK(_sc)
+#define FXP_UNLOCK(_sc)
+#define mtx_init(a, b, c)
+#define mtx_destroy(a)
+struct mtx { int dummy; };
+#else
+#define FXP_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define FXP_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#endif
+
+#ifdef __alpha__
+#undef vtophys
+#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)(va))
+#endif /* __alpha__ */
+
+/*
+ * NOTE: Elements are ordered for optimal cacheline behavior, and NOT
+ * for functional grouping.
+ */
+struct fxp_softc {
+ struct arpcom arpcom; /* per-interface network data */
+#ifdef NOTUSED
+ struct resource *mem; /* resource descriptor for registers */
+ int rtp; /* register resource type */
+ int rgd; /* register descriptor in use */
+ struct resource *irq; /* resource descriptor for interrupt */
+#endif
+ void *ih; /* interrupt handler cookie */
+ struct mtx sc_mtx;
+#ifdef NOTUSED /* change for RTEMS */
+ bus_space_tag_t sc_st; /* bus space tag */
+ bus_space_handle_t sc_sh; /* bus space handle */
+#else
+ unsigned char pci_bus; /* RTEMS PCI bus number */
+ unsigned char pci_dev; /* RTEMS PCI slot/device number */
+ unsigned char pci_fun; /* RTEMS PCI function number */
+ bool pci_regs_are_io; /* RTEMS dev regs are I/O mapped */
+ u_int32_t pci_regs_base; /* RTEMS i386 register base */
+ rtems_id daemonTid; /* Task ID of deamon */
+ rtems_vector_number irq_num;
+
+#endif
+ struct mbuf *rfa_headm; /* first mbuf in receive frame area */
+ struct mbuf *rfa_tailm; /* last mbuf in receive frame area */
+ struct fxp_cb_tx *cbl_first; /* first active TxCB in list */
+ int tx_queued; /* # of active TxCB's */
+ int need_mcsetup; /* multicast filter needs programming */
+ struct fxp_cb_tx *cbl_last; /* last active TxCB in list */
+ struct fxp_stats *fxp_stats; /* Pointer to interface stats */
+ int rx_idle_secs; /* # of seconds RX has been idle */
+ enum {fxp_timeout_stopped,fxp_timeout_running,fxp_timeout_stop_rq}
+ stat_ch; /* status of status updater */
+ struct fxp_cb_tx *cbl_base; /* base of TxCB list */
+ struct fxp_cb_mcs *mcsp; /* Pointer to mcast setup descriptor */
+#ifdef NOTUSED
+ struct ifmedia sc_media; /* media information */
+ device_t miibus;
+ device_t dev;
+#endif
+ int eeprom_size; /* size of serial EEPROM */
+ int suspended; /* 0 = normal 1 = suspended (APM) */
+ int cu_resume_bug;
+ int chip;
+ int flags;
+ u_int32_t saved_maps[5]; /* pci data */
+ u_int32_t saved_biosaddr;
+ u_int8_t saved_intline;
+ u_int8_t saved_cachelnsz;
+ u_int8_t saved_lattimer;
+};
+
+#define FXP_CHIP_82557 1 /* 82557 chip type */
+
+#define FXP_FLAG_MWI_ENABLE 0x0001 /* MWI enable */
+#define FXP_FLAG_READ_ALIGN 0x0002 /* align read access with cacheline */
+#define FXP_FLAG_WRITE_ALIGN 0x0004 /* end write on cacheline */
+#define FXP_FLAG_EXT_TXCB 0x0008 /* enable use of extended TXCB */
+#define FXP_FLAG_SERIAL_MEDIA 0x0010 /* 10Mbps serial interface */
+#define FXP_FLAG_LONG_PKT_EN 0x0020 /* enable long packet reception */
+#define FXP_FLAG_ALL_MCAST 0x0040 /* accept all multicast frames */
+#define FXP_FLAG_CU_RESUME_BUG 0x0080 /* requires workaround for CU_RESUME */
+
+/* Macros to ease CSR access. */
+#if 0
+#define CSR_READ_1(sc, reg) \
+ bus_space_read_1((sc)->sc_st, (sc)->sc_sh, (reg))
+#define CSR_READ_2(sc, reg) \
+ bus_space_read_2((sc)->sc_st, (sc)->sc_sh, (reg))
+#define CSR_READ_4(sc, reg) \
+ bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg))
+#define CSR_WRITE_1(sc, reg, val) \
+ bus_space_write_1((sc)->sc_st, (sc)->sc_sh, (reg), (val))
+#define CSR_WRITE_2(sc, reg, val) \
+ bus_space_write_2((sc)->sc_st, (sc)->sc_sh, (reg), (val))
+#define CSR_WRITE_4(sc, reg, val) \
+ bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val))
+#else
+#define CSR_READ_1(sc, reg) fxp_csr_read_1(sc,reg)
+#define CSR_READ_2(sc, reg) fxp_csr_read_2(sc,reg)
+#define CSR_READ_4(sc, reg) fxp_csr_read_4(sc,reg)
+
+#define CSR_WRITE_1(sc, reg, val) \
+ do { \
+ if ((sc)->pci_regs_are_io) \
+ outport_byte((sc)->pci_regs_base+(reg),val); \
+ else \
+ *((volatile u_int8_t*)((sc)->pci_regs_base)+(reg)) = val; \
+ }while (0)
+
+#define CSR_WRITE_2(sc, reg, val) \
+ do { \
+ if ((sc)->pci_regs_are_io) \
+ outport_word((sc)->pci_regs_base+(reg),val); \
+ else \
+ *((volatile u_int16_t*)((u_int8_t*)((sc)->pci_regs_base)+(reg))) = val; \
+ }while (0)
+
+#define CSR_WRITE_4(sc, reg, val) \
+ do { \
+ if ((sc)->pci_regs_are_io) \
+ outport_long((sc)->pci_regs_base+(reg),val); \
+ else \
+ *((volatile u_int32_t*)((u_int8_t*)((sc)->pci_regs_base)+(reg))) = val; \
+ }while (0)
+
+#endif
+
+#define sc_if arpcom.ac_if
+
+#define FXP_UNIT(_sc) (_sc)->arpcom.ac_if.if_unit
diff --git a/bsps/include/libchip/smc91111.h b/bsps/include/libchip/smc91111.h
new file mode 100644
index 0000000..7ec8371
--- /dev/null
+++ b/bsps/include/libchip/smc91111.h
@@ -0,0 +1,558 @@
+#ifndef _SMC91111_H_
+#define _SMC91111_H_
+
+#include <libchip/smc91111exp.h>
+#include <rtems/bspIo.h>
+
+#define LAN91CXX_TCR 0x00
+#define LAN91CXX_EPH_STATUS 0x01
+#define LAN91CXX_RCR 0x02
+#define LAN91CXX_COUNTER 0x03
+#define LAN91CXX_MIR 0x04
+#define LAN91CXX_MCR 0x05 /* Other than 91C111*/
+#define LAN91CXX_RPCR 0x05 /* 91C111 only*/
+#define LAN91CXX_RESERVED_0 0x06
+#define LAN91CXX_BS 0x07
+#define LAN91CXX_CONFIG 0x08
+#define LAN91CXX_BASE_REG 0x09
+#define LAN91CXX_IA01 0x0a
+#define LAN91CXX_IA23 0x0b
+#define LAN91CXX_IA45 0x0c
+#define LAN91CXX_GENERAL 0x0d /* 91C96 - was "RESERVED_1" for others*/
+#define LAN91CXX_CONTROL 0x0e
+#define LAN91CXX_BS2 0x0f
+#define LAN91CXX_MMU_COMMAND 0x10
+#define LAN91CXX_PNR 0x11
+#define LAN91CXX_FIFO_PORTS 0x12
+#define LAN91CXX_POINTER 0x13
+#define LAN91CXX_DATA_HIGH 0x14
+#define LAN91CXX_DATA 0x15
+#define LAN91CXX_INTERRUPT 0x16
+#define LAN91CXX_BS3 0x17
+#define LAN91CXX_MT01 0x18
+#define LAN91CXX_MT23 0x19
+#define LAN91CXX_MT45 0x1a
+#define LAN91CXX_MT67 0x1b
+#define LAN91CXX_MGMT 0x1c
+#define LAN91CXX_REVISION 0x1d
+#define LAN91CXX_ERCV 0x1e
+#define LAN91CXX_BS4 0x1f
+
+#define LAN91CXX_RCR_SOFT_RST 0x8000 /* soft reset*/
+#define LAN91CXX_RCR_FILT_CAR 0x4000 /* filter carrier*/
+#define LAN91CXX_RCR_ABORT_ENB 0x2000 /* abort on collision*/
+#define LAN91CXX_RCR_STRIP_CRC 0x0200 /* strip CRC*/
+#define LAN91CXX_RCR_RXEN 0x0100 /* enable RX*/
+#define LAN91CXX_RCR_ALMUL 0x0004 /* receive all muticasts*/
+#define LAN91CXX_RCR_PRMS 0x0002 /* promiscuous*/
+#define LAN91CXX_RCR_RX_ABORT 0x0001 /* set when abort due to long frame*/
+
+#define LAN91CXX_TCR_SWFDUP 0x8000 /* Switched Full Duplex mode*/
+#define LAN91CXX_TCR_ETEN_TYPE 0x4000 /* ETEN type (91C96) 0 <=> like a 91C94*/
+#define LAN91CXX_TCR_EPH_LOOP 0x2000 /* loopback mode*/
+#define LAN91CXX_TCR_STP_SQET 0x1000 /* Stop transmission on SQET error*/
+#define LAN91CXX_TCR_FDUPLX 0x0800 /* full duplex*/
+#define LAN91CXX_TCR_MON_CSN 0x0400 /* monitor carrier during tx (91C96)*/
+#define LAN91CXX_TCR_NOCRC 0x0100 /* does not append CRC to frames*/
+#define LAN91CXX_TCR_PAD_EN 0x0080 /* pads frames with 00 to min length*/
+#define LAN91CXX_TCR_FORCOL 0x0004 /* force collision*/
+#define LAN91CXX_TCR_LLOOP 0x0002 /* local loopback (91C96)*/
+#define LAN91CXX_TCR_TXENA 0x0001 /* enable*/
+
+#define LAN91CXX_POINTER_RCV 0x8000
+#define LAN91CXX_POINTER_AUTO_INCR 0x4000
+#define LAN91CXX_POINTER_READ 0x2000
+#define LAN91CXX_POINTER_ETEN 0x1000
+#define LAN91CXX_POINTER_NOT_EMPTY 0x0800
+
+
+#define LAN91CXX_INTERRUPT_TX_IDLE_M 0x8000 /* (91C96)*/
+#define LAN91CXX_INTERRUPT_ERCV_INT_M 0x4000
+#define LAN91CXX_INTERRUPT_EPH_INT_M 0x2000
+#define LAN91CXX_INTERRUPT_RX_OVRN_INT_M 0x1000
+#define LAN91CXX_INTERRUPT_ALLOC_INT_M 0x0800
+#define LAN91CXX_INTERRUPT_TX_EMPTY_INT_M 0x0400
+#define LAN91CXX_INTERRUPT_TX_INT_M 0x0200
+#define LAN91CXX_INTERRUPT_RCV_INT_M 0x0100
+#define LAN91CXX_INTERRUPT_TX_IDLE 0x0080 /* (91C96)*/
+#define LAN91CXX_INTERRUPT_ERCV_INT 0x0040 /* also ack*/
+#define LAN91CXX_INTERRUPT_EPH_INT 0x0020
+#define LAN91CXX_INTERRUPT_RX_OVRN_INT 0x0010 /* also ack*/
+#define LAN91CXX_INTERRUPT_ALLOC_INT 0x0008
+#define LAN91CXX_INTERRUPT_TX_EMPTY_INT 0x0004 /* also ack*/
+#define LAN91CXX_INTERRUPT_TX_INT 0x0002 /* also ack*/
+#define LAN91CXX_INTERRUPT_RCV_INT 0x0001
+
+#define LAN91CXX_INTERRUPT_TX_SET 0x0006 /* TX_EMPTY + TX*/
+#define LAN91CXX_INTERRUPT_TX_SET_ACK 0x0004 /* TX_EMPTY and not plain TX*/
+#define LAN91CXX_INTERRUPT_TX_FIFO_ACK 0x0002 /* TX alone*/
+#define LAN91CXX_INTERRUPT_TX_SET_M 0x0600 /* TX_EMPTY + TX*/
+
+#define LAN91CXX_CONTROL_RCV_BAD 0x4000
+#define LAN91CXX_CONTROL_AUTO_RELEASE 0x0800
+#define LAN91CXX_CONTROL_LE_ENABLE 0x0080
+#define LAN91CXX_CONTROL_CR_ENABLE 0x0040
+#define LAN91CXX_CONTROL_TE_ENABLE 0x0020
+
+/* These are for setting the MAC address in the 91C96 serial EEPROM*/
+#define LAN91CXX_CONTROL_EEPROM_SELECT 0x0004
+#define LAN91CXX_CONTROL_RELOAD 0x0002
+#define LAN91CXX_CONTROL_STORE 0x0001
+#define LAN91CXX_CONTROL_EEPROM_BUSY 0x0003
+#define LAN91CXX_ESA_EEPROM_OFFSET 0x0020
+
+#define LAN91CXX_STATUS_TX_UNRN 0x8000
+#define LAN91CXX_STATUS_LINK_OK 0x4000
+#define LAN91CXX_STATUS_CTR_ROL 0x1000
+#define LAN91CXX_STATUS_EXC_DEF 0x0800
+#define LAN91CXX_STATUS_LOST_CARR 0x0400
+#define LAN91CXX_STATUS_LATCOL 0x0200
+#define LAN91CXX_STATUS_WAKEUP 0x0100
+#define LAN91CXX_STATUS_TX_DEFR 0x0080
+#define LAN91CXX_STATUS_LTX_BRD 0x0040
+#define LAN91CXX_STATUS_SQET 0x0020
+#define LAN91CXX_STATUS_16COL 0x0010
+#define LAN91CXX_STATUS_LTX_MULT 0x0008
+#define LAN91CXX_STATUS_MUL_COL 0x0004
+#define LAN91CXX_STATUS_SNGL_COL 0x0002
+#define LAN91CXX_STATUS_TX_SUC 0x0001
+
+#define LAN91CXX_MMU_COMMAND_BUSY 0x0001
+
+#define LAN91CXX_MMU_noop 0x0000
+#define LAN91CXX_MMU_alloc_for_tx 0x0020
+#define LAN91CXX_MMU_reset_mmu 0x0040
+#define LAN91CXX_MMU_rem_rx_frame 0x0060
+#define LAN91CXX_MMU_rem_tx_frame 0x0070 /* (91C96) only when TX stopped*/
+#define LAN91CXX_MMU_remrel_rx_frame 0x0080
+#define LAN91CXX_MMU_rel_packet 0x00a0
+#define LAN91CXX_MMU_enq_packet 0x00c0
+#define LAN91CXX_MMU_reset_tx_fifo 0x00e0
+
+#define LAN91CXX_CONTROLBYTE_CRC 0x1000
+#define LAN91CXX_CONTROLBYTE_ODD 0x2000
+#define LAN91CXX_CONTROLBYTE_RX 0x4000
+
+#define LAN91CXX_RX_STATUS_ALIGNERR 0x8000
+#define LAN91CXX_RX_STATUS_BCAST 0x4000
+#define LAN91CXX_RX_STATUS_BADCRC 0x2000
+#define LAN91CXX_RX_STATUS_ODDFRM 0x1000
+#define LAN91CXX_RX_STATUS_TOOLONG 0x0800
+#define LAN91CXX_RX_STATUS_TOOSHORT 0x0400
+#define LAN91CXX_RX_STATUS_HASHVALMASK 0x007e /* MASK*/
+#define LAN91CXX_RX_STATUS_MCAST 0x0001
+#define LAN91CXX_RX_STATUS_BAD \
+ (LAN91CXX_RX_STATUS_ALIGNERR | \
+ LAN91CXX_RX_STATUS_BADCRC | \
+ LAN91CXX_RX_STATUS_TOOLONG | \
+ LAN91CXX_RX_STATUS_TOOSHORT)
+
+#define LAN91CXX_RX_STATUS_IS_ODD(__cpd,__stat) ((__stat) & LAN91CXX_RX_STATUS_ODDFRM)
+#define LAN91CXX_CONTROLBYTE_IS_ODD(__cpd,__val) ((__val) & LAN91CXX_CONTROLBYTE_ODD)
+
+/* Attribute memory registers in PCMCIA mode*/
+#define LAN91CXX_ECOR 0x8000
+#define LAN91CXX_ECOR_RESET (1<<7)
+#define LAN91CXX_ECOR_LEVIRQ (1<<6)
+#define LAN91CXX_ECOR_ATTWR (1<<2)
+#define LAN91CXX_ECOR_ENABLE (1<<0)
+
+#define LAN91CXX_ECSR 0x8002
+#define LAN91CXX_ECSR_IOIS8 (1<<5)
+#define LAN91CXX_ECSR_PWRDWN (1<<2)
+#define LAN91CXX_ECSR_INTR (1<<1)
+
+/* These are for manipulating the MII interface*/
+#define LAN91CXX_MGMT_MDO 0x0001
+#define LAN91CXX_MGMT_MDI 0x0002
+#define LAN91CXX_MGMT_MCLK 0x0004
+#define LAN91CXX_MGMT_MDOE 0x0008
+
+/* Internal PHY registers (91c111)*/
+#define LAN91CXX_PHY_CTRL 0
+#define LAN91CXX_PHY_STAT 1
+#define LAN91CXX_PHY_ID1 2
+#define LAN91CXX_PHY_ID2 3
+#define LAN91CXX_PHY_AUTO_AD 4
+#define LAN91CXX_PHY_AUTO_CAP 5
+#define LAN91CXX_PHY_CONFIG1 16
+#define LAN91CXX_PHY_CONFIG2 17
+#define LAN91CXX_PHY_STATUS_OUT 18
+#define LAN91CXX_PHY_MASK 19
+
+/* PHY control bits*/
+#define LAN91CXX_PHY_CTRL_COLTST (1 << 7)
+#define LAN91CXX_PHY_CTRL_DPLX (1 << 8)
+#define LAN91CXX_PHY_CTRL_ANEG_RST (1 << 9)
+#define LAN91CXX_PHY_CTRL_MII_DIS (1 << 10)
+#define LAN91CXX_PHY_CTRL_PDN (1 << 11)
+#define LAN91CXX_PHY_CTRL_ANEG_EN (1 << 12)
+#define LAN91CXX_PHY_CTRL_SPEED (1 << 13)
+#define LAN91CXX_PHY_CTRL_LPBK (1 << 14)
+#define LAN91CXX_PHY_CTRL_RST (1 << 15)
+
+/* PHY Configuration Register 1 */
+#define PHY_CFG1_LNKDIS 0x8000 /* 1=Rx Link Detect Function disabled */
+#define PHY_CFG1_XMTDIS 0x4000 /* 1=TP Transmitter Disabled */
+#define PHY_CFG1_XMTPDN 0x2000 /* 1=TP Transmitter Powered Down */
+#define PHY_CFG1_BYPSCR 0x0400 /* 1=Bypass scrambler/descrambler */
+#define PHY_CFG1_UNSCDS 0x0200 /* 1=Unscramble Idle Reception Disable */
+#define PHY_CFG1_EQLZR 0x0100 /* 1=Rx Equalizer Disabled */
+#define PHY_CFG1_CABLE 0x0080 /* 1=STP(150ohm), 0=UTP(100ohm) */
+#define PHY_CFG1_RLVL0 0x0040 /* 1=Rx Squelch level reduced by 4.5db */
+#define PHY_CFG1_TLVL_SHIFT 2 /* Transmit Output Level Adjust */
+#define PHY_CFG1_TLVL_MASK 0x003C
+#define PHY_CFG1_TRF_MASK 0x0003 /* Transmitter Rise/Fall time */
+
+/* PHY Configuration Register 2 */
+#define PHY_CFG2_REG 0x11
+#define PHY_CFG2_APOLDIS 0x0020 /* 1=Auto Polarity Correction disabled */
+#define PHY_CFG2_JABDIS 0x0010 /* 1=Jabber disabled */
+#define PHY_CFG2_MREG 0x0008 /* 1=Multiple register access (MII mgt) */
+#define PHY_CFG2_INTMDIO 0x0004 /* 1=Interrupt signaled with MDIO pulseo */
+
+/* PHY Status Output (and Interrupt status) Register */
+#define PHY_INT_REG 0x12 /* Status Output (Interrupt Status) */
+#define PHY_INT_INT 0x8000 /* 1=bits have changed since last read */
+#define PHY_INT_LNKFAIL 0x4000 /* 1=Link Not detected */
+#define PHY_INT_LOSSSYNC 0x2000 /* 1=Descrambler has lost sync */
+#define PHY_INT_CWRD 0x1000 /* 1=Invalid 4B5B code detected on rx */
+#define PHY_INT_SSD 0x0800 /* 1=No Start Of Stream detected on rx */
+#define PHY_INT_ESD 0x0400 /* 1=No End Of Stream detected on rx */
+#define PHY_INT_RPOL 0x0200 /* 1=Reverse Polarity detected */
+#define PHY_INT_JAB 0x0100 /* 1=Jabber detected */
+#define PHY_INT_SPDDET 0x0080 /* 1=100Base-TX mode, 0=10Base-T mode */
+#define PHY_INT_DPLXDET 0x0040 /* 1=Device in Full Duplex */
+
+/* PHY Interrupt/Status Mask Register */
+#define PHY_MASK_REG 0x13 /* Interrupt Mask */
+
+#define LAN91CXX_RPCR_LEDA_LINK (0 << 2)
+#define LAN91CXX_RPCR_LEDA_TXRX (4 << 2)
+#define LAN91CXX_RPCR_LEDA_RX (6 << 2)
+#define LAN91CXX_RPCR_LEDA_TX (7 << 2)
+#define LAN91CXX_RPCR_LEDB_LINK (0 << 5)
+#define LAN91CXX_RPCR_LEDB_TXRX (4 << 5)
+#define LAN91CXX_RPCR_LEDB_RX (6 << 5)
+#define LAN91CXX_RPCR_LEDB_TX (7 << 5)
+#define LAN91CXX_RPCR_ANEG (1 << 11)
+#define LAN91CXX_RPCR_DPLX (1 << 12)
+#define LAN91CXX_RPCR_SPEED (1 << 13)
+
+/* PHY Control Register */
+#define PHY_CNTL_REG 0x00
+#define PHY_CNTL_RST 0x8000 /* 1=PHY Reset */
+#define PHY_CNTL_LPBK 0x4000 /* 1=PHY Loopback */
+#define PHY_CNTL_SPEED 0x2000 /* 1=100Mbps, 0=10Mpbs */
+#define PHY_CNTL_ANEG_EN 0x1000 /* 1=Enable Auto negotiation */
+#define PHY_CNTL_PDN 0x0800 /* 1=PHY Power Down mode */
+#define PHY_CNTL_MII_DIS 0x0400 /* 1=MII 4 bit interface disabled */
+#define PHY_CNTL_ANEG_RST 0x0200 /* 1=Reset Auto negotiate */
+#define PHY_CNTL_DPLX 0x0100 /* 1=Full Duplex, 0=Half Duplex */
+#define PHY_CNTL_COLTST 0x0080 /* 1= MII Colision Test */
+
+/* PHY Status Register */
+#define PHY_STAT_REG 0x01
+#define PHY_STAT_CAP_T4 0x8000 /* 1=100Base-T4 capable */
+#define PHY_STAT_CAP_TXF 0x4000 /* 1=100Base-X full duplex capable */
+#define PHY_STAT_CAP_TXH 0x2000 /* 1=100Base-X half duplex capable */
+#define PHY_STAT_CAP_TF 0x1000 /* 1=10Mbps full duplex capable */
+#define PHY_STAT_CAP_TH 0x0800 /* 1=10Mbps half duplex capable */
+#define PHY_STAT_CAP_SUPR 0x0040 /* 1=recv mgmt frames with not preamble */
+#define PHY_STAT_ANEG_ACK 0x0020 /* 1=ANEG has completed */
+#define PHY_STAT_REM_FLT 0x0010 /* 1=Remote Fault detected */
+#define PHY_STAT_CAP_ANEG 0x0008 /* 1=Auto negotiate capable */
+#define PHY_STAT_LINK 0x0004 /* 1=valid link */
+#define PHY_STAT_JAB 0x0002 /* 1=10Mbps jabber condition */
+#define PHY_STAT_EXREG 0x0001 /* 1=extended registers implemented */
+#define PHY_STAT_RESERVED 0x0780 /* Reserved bits mask. */
+
+/* PHY Identifier Registers */
+#define PHY_ID1_REG 0x02 /* PHY Identifier 1 */
+#define PHY_ID2_REG 0x03 /* PHY Identifier 2 */
+
+/* PHY Auto-Negotiation Advertisement Register */
+#define PHY_AD_REG 0x04
+#define PHY_AD_NP 0x8000 /* 1=PHY requests exchange of Next Page */
+#define PHY_AD_ACK 0x4000 /* 1=got link code word from remote */
+#define PHY_AD_RF 0x2000 /* 1=advertise remote fault */
+#define PHY_AD_T4 0x0200 /* 1=PHY is capable of 100Base-T4 */
+#define PHY_AD_TX_FDX 0x0100 /* 1=PHY is capable of 100Base-TX FDPLX */
+#define PHY_AD_TX_HDX 0x0080 /* 1=PHY is capable of 100Base-TX HDPLX */
+#define PHY_AD_10_FDX 0x0040 /* 1=PHY is capable of 10Base-T FDPLX */
+#define PHY_AD_10_HDX 0x0020 /* 1=PHY is capable of 10Base-T HDPLX */
+#define PHY_AD_CSMA 0x0001 /* 1=PHY is capable of 802.3 CMSA */
+
+
+static int debugflag_out = 0;
+
+#define dbc_printf(lvl,format, args...) do { \
+ if (!debugflag_out) { \
+ if (lvl & DEBUG) { \
+ printk(format,##args); \
+ } \
+ } \
+} while(0)
+
+#define db64_printf(format, args...) dbc_printf(64,format,##args);
+#define db16_printf(format, args...) dbc_printf(16,format,##args);
+#define db9_printf(format, args...) dbc_printf(9,format,##args);
+#define db4_printf(format, args...) dbc_printf(4,format,##args);
+#define db2_printf(format, args...) dbc_printf(2,format,##args);
+#define db1_printf(format, args...) dbc_printf(1,format,##args);
+#define db_printf(format, args...) dbc_printf(0xffff,format,##args);
+
+#if DEBUG & 1
+#define DEBUG_FUNCTION() do { db_printf("# %s\n", __FUNCTION__); } while (0)
+#else
+#define DEBUG_FUNCTION() do {} while(0)
+#endif
+
+
+/* ------------------------------------------------------------------------*/
+
+struct smsc_lan91cxx_stats {
+ unsigned int tx_good ;
+ unsigned int tx_max_collisions ;
+ unsigned int tx_late_collisions ;
+ unsigned int tx_underrun ;
+ unsigned int tx_carrier_loss ;
+ unsigned int tx_deferred ;
+ unsigned int tx_sqetesterrors ;
+ unsigned int tx_single_collisions;
+ unsigned int tx_mult_collisions ;
+ unsigned int tx_total_collisions ;
+ unsigned int rx_good ;
+ unsigned int rx_crc_errors ;
+ unsigned int rx_align_errors ;
+ unsigned int rx_resource_errors ;
+ unsigned int rx_overrun_errors ;
+ unsigned int rx_collisions ;
+ unsigned int rx_short_frames ;
+ unsigned int rx_too_long_frames ;
+ unsigned int rx_symbol_errors ;
+ unsigned int interrupts ;
+ unsigned int rx_count ;
+ unsigned int rx_deliver ;
+ unsigned int rx_resource ;
+ unsigned int rx_restart ;
+ unsigned int tx_count ;
+ unsigned int tx_complete ;
+ unsigned int tx_dropped ;
+};
+#define INCR_STAT(c,n) (((c)->stats.n)++)
+
+struct lan91cxx_priv_data;
+
+typedef struct lan91cxx_priv_data {
+
+ /* frontend */
+ struct arpcom arpcom;
+ rtems_id rxDaemonTid;
+ rtems_id txDaemonTid;
+
+ scmv91111_configuration_t config;
+
+ /* backend */
+ int rpc_cur_mode;
+ int autoneg_active;
+ int phyaddr;
+ unsigned int lastPhy18;
+
+ int txbusy; /* A packet has been sent*/
+ unsigned long txkey; /* Used to ack when packet sent*/
+ unsigned short* base; /* Base I/O address of controller*/
+ /* (as it comes out of reset)*/
+ int interrupt; /* Interrupt vector used by controller*/
+ unsigned char enaddr[6]; /* Controller ESA*/
+ /* Function to configure the ESA - may fetch ESA from EPROM or */
+ /* RedBoot config option. Use of the 'config_enaddr()' function*/
+ /* is depreciated in favor of the 'provide_esa()' function and*/
+ /* 'hardwired_esa' boolean*/
+ void (*config_enaddr)(struct lan91cxx_priv_data* cpd);
+ int hardwired_esa;
+ int txpacket;
+ int rxpacket;
+ int within_send;
+ int c111_reva; /* true if this is a revA LAN91C111*/
+ struct smsc_lan91cxx_stats stats;
+} lan91cxx_priv_data;
+
+/* ------------------------------------------------------------------------*/
+
+#ifdef LAN91CXX_32BIT_RX
+typedef unsigned int rxd_t;
+#else
+typedef unsigned short rxd_t;
+#endif
+
+typedef struct _debug_regs_pair {
+ int reg; char *name; struct _debug_regs_pair *bits;
+} debug_regs_pair;
+
+static debug_regs_pair debug_regs[] = {
+ {LAN91CXX_TCR , "LAN91CXX_TCR" ,0},
+ {LAN91CXX_EPH_STATUS , "LAN91CXX_EPH_STATUS",0},
+ {LAN91CXX_RCR , "LAN91CXX_RCR" ,0},
+ {LAN91CXX_COUNTER , "LAN91CXX_COUNTER" ,0},
+ {LAN91CXX_MIR , "LAN91CXX_MIR" ,0},
+ {LAN91CXX_MCR , "LAN91CXX_MCR" ,0},
+ {LAN91CXX_RPCR , "LAN91CXX_RPCR" ,0},
+ {LAN91CXX_RESERVED_0 , "LAN91CXX_RESERVED_0",0},
+ {LAN91CXX_BS , "LAN91CXX_BS" ,0},
+ {LAN91CXX_CONFIG , "LAN91CXX_CONFIG" ,0},
+ {LAN91CXX_BASE_REG , "LAN91CXX_BASE_REG" ,0},
+ {LAN91CXX_IA01 , "LAN91CXX_IA01" ,0},
+ {LAN91CXX_IA23 , "LAN91CXX_IA23" ,0},
+ {LAN91CXX_IA45 , "LAN91CXX_IA45" ,0},
+ {LAN91CXX_GENERAL , "LAN91CXX_GENERAL" ,0},
+ {LAN91CXX_CONTROL , "LAN91CXX_CONTROL" ,0},
+ {LAN91CXX_BS2 , "LAN91CXX_BS2" ,0},
+ {LAN91CXX_MMU_COMMAND, "LAN91CXX_MMU_COMMAND",0},
+ {LAN91CXX_PNR , "LAN91CXX_PNR" ,0},
+ {LAN91CXX_FIFO_PORTS , "LAN91CXX_FIFO_PORTS" ,0},
+ {LAN91CXX_POINTER , "LAN91CXX_POINTER" ,0},
+ {LAN91CXX_DATA_HIGH , "LAN91CXX_DATA_HIGH" ,0},
+ {LAN91CXX_DATA , "LAN91CXX_DATA" ,0},
+ {LAN91CXX_INTERRUPT , "LAN91CXX_INTERRUPT" ,0},
+ {LAN91CXX_BS3 , "LAN91CXX_BS3" ,0},
+ {LAN91CXX_MT01 , "LAN91CXX_MT01" ,0},
+ {LAN91CXX_MT23 , "LAN91CXX_MT23" ,0},
+ {LAN91CXX_MT45 , "LAN91CXX_MT45" ,0},
+ {LAN91CXX_MT67 , "LAN91CXX_MT67" ,0},
+/*{LAN91CXX_MGMT , "LAN91CXX_MGMT" ,0}, */
+ {LAN91CXX_REVISION , "LAN91CXX_REVISION" ,0},
+ {LAN91CXX_ERCV , "LAN91CXX_ERCV" ,0},
+ {LAN91CXX_BS4 , "LAN91CXX_BS4" ,0},
+
+
+
+ {-1,0}
+};
+
+static char *dbg_prefix = "";
+
+#ifndef SMSC_PLATFORM_DEFINED_GET_REG
+static __inline__ unsigned short
+get_reg(struct lan91cxx_priv_data *cpd, int regno)
+{
+ unsigned short val; debug_regs_pair *dbg = debug_regs; int c;
+ uint32_t Irql;
+
+ /*rtems_interrupt_disable(Irql);*/
+
+ HAL_WRITE_UINT16(cpd->base+(LAN91CXX_BS), CYG_CPU_TO_LE16(regno>>3));
+ HAL_READ_UINT16(cpd->base+((regno&0x7)), val);
+ val = CYG_LE16_TO_CPU(val);
+
+ /*rtems_interrupt_enable(Irql);*/
+
+#if DEBUG & 32
+ while ((c = dbg->reg) != -1) {
+ if (c == regno) {
+ db_printf("%sread reg [%d:%x] -> 0x%04x (%-20s)\n", dbg_prefix, regno>>3,(regno&0x7)*2, val, dbg->name);
+ break;
+ }
+ dbg++;
+ }
+#else
+ db2_printf("%sread reg %d:%x -> 0x%04x\n", dbg_prefix, regno>>3,(regno&0x7)*2, val);
+#endif
+
+ return val;
+}
+#endif /* SMSC_PLATFORM_DEFINED_GET_REG*/
+
+#ifndef SMSC_PLATFORM_DEFINED_PUT_REG
+static __inline__ void
+put_reg(struct lan91cxx_priv_data *cpd, int regno, unsigned short val)
+{
+ debug_regs_pair *dbg = debug_regs; int c;
+ uint32_t Irql;
+
+#if DEBUG & 32
+ while ((c = dbg->reg) != -1) {
+ if (c == regno) {
+ db_printf("%swrite reg [%d:%x] <- 0x%04x (%-20s)\n", dbg_prefix, regno>>3, (regno&0x07)*2, val, dbg->name);
+ break;
+ }
+ dbg++;
+ }
+#else
+ db2_printf("%swrite reg %d:%x <- 0x%04x\n", dbg_prefix, regno>>3,(regno&0x7)*2, val);
+#endif
+
+ /*rtems_interrupt_disable(Irql);*/
+
+ HAL_WRITE_UINT16(cpd->base+(LAN91CXX_BS), CYG_CPU_TO_LE16(regno>>3));
+ HAL_WRITE_UINT16(cpd->base+((regno&0x7)), CYG_CPU_TO_LE16(val));
+
+ /*rtems_interrupt_enable(Irql);*/
+
+}
+#endif /* SMSC_PLATFORM_DEFINED_PUT_REG*/
+
+#ifndef SMSC_PLATFORM_DEFINED_PUT_DATA
+/* ------------------------------------------------------------------------*/
+/* Assumes bank2 has been selected*/
+static __inline__ void
+put_data(struct lan91cxx_priv_data *cpd, unsigned short val)
+{
+ db2_printf("%s[wdata] <- 0x%04x\n", dbg_prefix, val);
+
+ HAL_WRITE_UINT16(cpd->base+((LAN91CXX_DATA & 0x7)), val);
+
+}
+
+/* Assumes bank2 has been selected*/
+static __inline__ void
+put_data8(struct lan91cxx_priv_data *cpd, unsigned char val)
+{
+ db2_printf("%s[bdata] <- 0x%02x\n", dbg_prefix, val);
+
+ HAL_WRITE_UINT8(((unsigned char *)(cpd->base+((LAN91CXX_DATA & 0x7))))+1, val);
+
+}
+
+#endif /* SMSC_PLATFORM_DEFINED_PUT_DATA*/
+
+#ifndef SMSC_PLATFORM_DEFINED_GET_DATA
+/* Assumes bank2 has been selected*/
+static __inline__ rxd_t
+get_data(struct lan91cxx_priv_data *cpd)
+{
+ rxd_t val;
+
+#ifdef LAN91CXX_32BIT_RX
+ HAL_READ_UINT32(cpd->base+((LAN91CXX_DATA_HIGH & 0x7)), val);
+#else
+ HAL_READ_UINT16(cpd->base+((LAN91CXX_DATA & 0x7)), val);
+#endif
+
+ db2_printf("%s[rdata] -> 0x%08x\n", dbg_prefix, val);
+ return val;
+}
+#endif /* SMSC_PLATFORM_DEFINED_GET_DATA*/
+
+/* ------------------------------------------------------------------------*/
+/* Read the bank register (this one is bank-independent)*/
+#ifndef SMSC_PLATFORM_DEFINED_GET_BANKSEL
+static __inline__ unsigned short
+get_banksel(struct lan91cxx_priv_data *cpd)
+{
+ unsigned short val;
+
+ HAL_READ_UINT16(cpd->base+(LAN91CXX_BS), val);
+ val = CYG_LE16_TO_CPU(val);
+ db2_printf("read bank sel val 0x%04x\n", val);
+ return val;
+}
+#endif
+
+
+
+
+
+#endif /* _SMC_91111_H_ */
+
+
diff --git a/bsps/lm32/include/system_conf.h b/bsps/lm32/include/system_conf.h
new file mode 100644
index 0000000..8ba4a1c
--- /dev/null
+++ b/bsps/lm32/include/system_conf.h
@@ -0,0 +1,329 @@
+/**
+ * @file
+ *
+ * @ingroup lm32_milkymist
+ *
+ * @brief System configuration.
+ */
+
+/* system_conf.h
+ * Global System conf
+ *
+ * Milkymist port of RTEMS
+ *
+ * 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.
+ *
+ * COPYRIGHT (c) 2010, 2011 Sebastien Bourdeauducq
+ */
+
+#ifndef __SYSTEM_CONFIG_H_
+#define __SYSTEM_CONFIG_H_
+
+#define UART_BAUD_RATE (115200)
+
+/* Clock frequency */
+#define MM_FREQUENCY (0xe0001074)
+
+/* FML bridge */
+#define FMLBRG_FLUSH_BASE (0xc8000000)
+#define FMLBRG_LINE_LENGTH (32)
+#define FMLBRG_LINE_COUNT (512)
+
+/* UART */
+#define MM_UART_RXTX (0xe0000000)
+#define MM_UART_DIV (0xe0000004)
+#define MM_UART_STAT (0xe0000008)
+#define MM_UART_CTRL (0xe000000c)
+
+#define UART_STAT_THRE (0x1)
+#define UART_STAT_RX_EVT (0x2)
+#define UART_STAT_TX_EVT (0x4)
+
+#define UART_CTRL_RX_INT (0x1)
+#define UART_CTRL_TX_INT (0x2)
+#define UART_CTRL_THRU (0x4)
+
+/* Timers */
+#define MM_TIMER1_COMPARE (0xe0001024)
+#define MM_TIMER1_COUNTER (0xe0001028)
+#define MM_TIMER1_CONTROL (0xe0001020)
+
+#define MM_TIMER0_COMPARE (0xe0001014)
+#define MM_TIMER0_COUNTER (0xe0001018)
+#define MM_TIMER0_CONTROL (0xe0001010)
+
+#define TIMER_ENABLE (0x01)
+#define TIMER_AUTORESTART (0x02)
+
+/* GPIO */
+#define MM_GPIO_IN (0xe0001000)
+#define MM_GPIO_OUT (0xe0001004)
+#define MM_GPIO_INTEN (0xe0001008)
+
+#define GPIO_BTN1 (0x00000001)
+#define GPIO_BTN2 (0x00000002)
+#define GPIO_BTN3 (0x00000004)
+#define GPIO_PCBREV0 (0x00000008)
+#define GPIO_PCBREV1 (0x00000010)
+#define GPIO_PCBREV2 (0x00000020)
+#define GPIO_PCBREV3 (0x00000040)
+#define GPIO_LED1 (0x00000001)
+#define GPIO_LED2 (0x00000002)
+
+/* System ID and reset */
+#define MM_SYSTEM_ID (0xe000107c)
+
+/* ICAP */
+#define MM_ICAP (0xe0001040)
+
+#define ICAP_READY (0x01)
+#define ICAP_CE (0x10000)
+#define ICAP_WRITE (0x20000)
+
+/* VGA */
+#define MM_VGA_RESET (0xe0003000)
+
+#define MM_VGA_HRES (0xe0003004)
+#define MM_VGA_HSYNC_START (0xe0003008)
+#define MM_VGA_HSYNC_END (0xe000300C)
+#define MM_VGA_HSCAN (0xe0003010)
+
+#define MM_VGA_VRES (0xe0003014)
+#define MM_VGA_VSYNC_START (0xe0003018)
+#define MM_VGA_VSYNC_END (0xe000301C)
+#define MM_VGA_VSCAN (0xe0003020)
+
+#define MM_VGA_BASEADDRESS (0xe0003024)
+#define MM_VGA_BASEADDRESS_ACT (0xe0003028)
+
+#define MM_VGA_BURST_COUNT (0xe000302C)
+
+#define MM_VGA_DDC (0xe0003030)
+
+#define MM_VGA_CLKSEL (0xe0003034)
+
+#define VGA_RESET (0x01)
+#define VGA_DDC_SDAIN (0x1)
+#define VGA_DDC_SDAOUT (0x2)
+#define VGA_DDC_SDAOE (0x4)
+#define VGA_DDC_SDC (0x8)
+
+/* Ethernet */
+#define MM_MINIMAC_SETUP (0xe0008000)
+#define MM_MINIMAC_MDIO (0xe0008004)
+
+#define MM_MINIMAC_STATE0 (0xe0008008)
+#define MM_MINIMAC_COUNT0 (0xe000800C)
+#define MM_MINIMAC_STATE1 (0xe0008010)
+#define MM_MINIMAC_COUNT1 (0xe0008014)
+
+#define MM_MINIMAC_TXCOUNT (0xe0008018)
+
+#define MINIMAC_RX0_BASE (0xb0000000)
+#define MINIMAC_RX1_BASE (0xb0000800)
+#define MINIMAC_TX_BASE (0xb0001000)
+
+#define MINIMAC_SETUP_PHYRST (0x1)
+
+#define MINIMAC_STATE_EMPTY (0x0)
+#define MINIMAC_STATE_LOADED (0x1)
+#define MINIMAC_STATE_PENDING (0x2)
+
+/* AC97 */
+#define MM_AC97_CRCTL (0xe0005000)
+
+#define AC97_CRCTL_RQEN (0x01)
+#define AC97_CRCTL_WRITE (0x02)
+
+#define MM_AC97_CRADDR (0xe0005004)
+#define MM_AC97_CRDATAOUT (0xe0005008)
+#define MM_AC97_CRDATAIN (0xe000500C)
+
+#define MM_AC97_DCTL (0xe0005010)
+#define MM_AC97_DADDRESS (0xe0005014)
+#define MM_AC97_DREMAINING (0xe0005018)
+
+#define MM_AC97_UCTL (0xe0005020)
+#define MM_AC97_UADDRESS (0xe0005024)
+#define MM_AC97_UREMAINING (0xe0005028)
+
+#define AC97_SCTL_EN (0x01)
+
+#define AC97_MAX_DMASIZE (0x3fffc)
+
+/* SoftUSB */
+#define MM_SOFTUSB_CONTROL (0xe000f000)
+
+#define SOFTUSB_CONTROL_RESET (0x1)
+
+#define MM_SOFTUSB_PMEM_BASE (0xa0000000)
+#define MM_SOFTUSB_DMEM_BASE (0xa0020000)
+
+#define SOFTUSB_PMEM_SIZE (1 << 13)
+#define SOFTUSB_DMEM_SIZE (1 << 13)
+
+/* PFPU */
+#define MM_PFPU_CTL (0xe0006000)
+#define PFPU_CTL_START (0x01)
+#define PFPU_CTL_BUSY (0x01)
+
+#define MM_PFPU_MESHBASE (0xe0006004)
+#define MM_PFPU_HMESHLAST (0xe0006008)
+#define MM_PFPU_VMESHLAST (0xe000600C)
+
+#define MM_PFPU_CODEPAGE (0xe0006010)
+
+#define MM_PFPU_DREGBASE (0xe0006400)
+#define MM_PFPU_CODEBASE (0xe0006800)
+
+#define PFPU_PAGESIZE (512)
+#define PFPU_SPREG_COUNT (2)
+#define PFPU_REG_X (0)
+#define PFPU_REG_Y (1)
+
+/* TMU */
+#define MM_TMU_CTL (0xe0007000)
+#define TMU_CTL_START (0x01)
+#define TMU_CTL_BUSY (0x01)
+#define TMU_CTL_CHROMAKEY (0x02)
+
+#define MM_TMU_HMESHLAST (0xe0007004)
+#define MM_TMU_VMESHLAST (0xe0007008)
+#define MM_TMU_BRIGHTNESS (0xe000700C)
+#define MM_TMU_CHROMAKEY (0xe0007010)
+
+#define MM_TMU_VERTICESADR (0xe0007014)
+#define MM_TMU_TEXFBUF (0xe0007018)
+#define MM_TMU_TEXHRES (0xe000701C)
+#define MM_TMU_TEXVRES (0xe0007020)
+#define MM_TMU_TEXHMASK (0xe0007024)
+#define MM_TMU_TEXVMASK (0xe0007028)
+
+#define MM_TMU_DSTFBUF (0xe000702C)
+#define MM_TMU_DSTHRES (0xe0007030)
+#define MM_TMU_DSTVRES (0xe0007034)
+#define MM_TMU_DSTHOFFSET (0xe0007038)
+#define MM_TMU_DSTVOFFSET (0xe000703C)
+#define MM_TMU_DSTSQUAREW (0xe0007040)
+#define MM_TMU_DSTSQUAREH (0xe0007044)
+
+#define MM_TMU_ALPHA (0xe0007048)
+
+/* Memory card */
+#define MM_MEMCARD_CLK2XDIV (0xe0004000)
+
+#define MM_MEMCARD_ENABLE (0xe0004004)
+
+#define MEMCARD_ENABLE_CMD_TX (0x1)
+#define MEMCARD_ENABLE_CMD_RX (0x2)
+#define MEMCARD_ENABLE_DAT_TX (0x4)
+#define MEMCARD_ENABLE_DAT_RX (0x8)
+
+#define MM_MEMCARD_PENDING (0xe0004008)
+
+#define MEMCARD_PENDING_CMD_TX (0x1)
+#define MEMCARD_PENDING_CMD_RX (0x2)
+#define MEMCARD_PENDING_DAT_TX (0x4)
+#define MEMCARD_PENDING_DAT_RX (0x8)
+
+#define MM_MEMCARD_START (0xe000400c)
+
+#define MEMCARD_START_CMD_RX (0x1)
+#define MEMCARD_START_DAT_RX (0x2)
+
+#define MM_MEMCARD_CMD (0xe0004010)
+#define MM_MEMCARD_DAT (0xe0004014)
+
+/* DMX */
+#define MM_DMX_TX(x) (0xe000c000+4*(x))
+#define MM_DMX_THRU (0xe000c800)
+#define MM_DMX_RX(x) (0xe000d000+4*(x))
+
+/* MIDI */
+#define MM_MIDI_RXTX (0xe000b000)
+#define MM_MIDI_DIV (0xe000b004)
+#define MM_MIDI_STAT (0xe000b008)
+#define MM_MIDI_CTRL (0xe000b00c)
+
+#define MIDI_STAT_THRE (0x1)
+#define MIDI_STAT_RX_EVT (0x2)
+#define MIDI_STAT_TX_EVT (0x4)
+
+#define MIDI_CTRL_RX_INT (0x1)
+#define MIDI_CTRL_TX_INT (0x2)
+#define MIDI_CTRL_THRU (0x4)
+
+/* IR */
+#define MM_IR_RX (0xe000e000)
+
+/* Video input */
+#define MM_BT656_I2C (0xe000a000)
+#define MM_BT656_FILTERSTATUS (0xe000a004)
+#define MM_BT656_BASE (0xe000a008)
+#define MM_BT656_MAXBURSTS (0xe000a00c)
+#define MM_BT656_DONEBURSTS (0xe000a010)
+
+#define BT656_I2C_SDAIN (0x1)
+#define BT656_I2C_SDAOUT (0x2)
+#define BT656_I2C_SDAOE (0x4)
+#define BT656_I2C_SDC (0x8)
+
+#define BT656_FILTER_FIELD1 (0x1)
+#define BT656_FILTER_FIELD2 (0x2)
+#define BT656_FILTER_INFRAME (0x4)
+
+/* Interrupts */
+#define MM_IRQ_UART (0)
+#define MM_IRQ_GPIO (1)
+#define MM_IRQ_TIMER0 (2)
+#define MM_IRQ_TIMER1 (3)
+#define MM_IRQ_AC97CRREQUEST (4)
+#define MM_IRQ_AC97CRREPLY (5)
+#define MM_IRQ_AC97DMAR (6)
+#define MM_IRQ_AC97DMAW (7)
+#define MM_IRQ_PFPU (8)
+#define MM_IRQ_TMU (9)
+#define MM_IRQ_ETHRX (10)
+#define MM_IRQ_ETHTX (11)
+#define MM_IRQ_VIDEOIN (12)
+#define MM_IRQ_MIDI (13)
+#define MM_IRQ_IR (14)
+#define MM_IRQ_USB (15)
+
+/* Flash layout */
+#define FLASH_BASE (0x80000000)
+
+#define FLASH_OFFSET_STANDBY_BITSTREAM (0x80000000)
+
+#define FLASH_OFFSET_RESCUE_BITSTREAM (0x800A0000)
+#define FLASH_OFFSET_RESCUE_BIOS (0x80220000)
+#define FLASH_OFFSET_MAC_ADDRESS (0x802200E0)
+#define FLASH_OFFSET_RESCUE_SPLASH (0x80240000)
+#define FLASH_OFFSET_RESCUE_APP (0x802E0000)
+
+#define FLASH_OFFSET_REGULAR_BITSTREAM (0x806E0000)
+#define FLASH_OFFSET_REGULAR_BIOS (0x80860000)
+#define FLASH_OFFSET_REGULAR_SPLASH (0x80880000)
+#define FLASH_OFFSET_REGULAR_APP (0x80920000)
+
+/* MMIO */
+#define MM_READ(reg) (*((volatile unsigned int *)(reg)))
+#define MM_WRITE(reg, val) *((volatile unsigned int *)(reg)) = val
+
+/* Flash partitions */
+
+#define FLASH_SECTOR_SIZE (128*1024)
+
+#define FLASH_PARTITION_COUNT (5)
+
+#define FLASH_PARTITIONS { \
+ { .start_address = 0x806E0000, .length = 0x0180000 }, \
+ { .start_address = 0x80860000, .length = 0x0020000 }, \
+ { .start_address = 0x80880000, .length = 0x00A0000 }, \
+ { .start_address = 0x80920000, .length = 0x0400000 }, \
+ { .start_address = 0x80D20000, .length = 0x12E0000 }, \
+}
+
+#endif /* __SYSTEM_CONFIG_H_ */
diff --git a/bsps/shared/grlib/net/README b/bsps/shared/grlib/net/README
new file mode 100644
index 0000000..3ef086f
--- /dev/null
+++ b/bsps/shared/grlib/net/README
@@ -0,0 +1,7 @@
+A non Driver Manager GRETH driver is located in libchip/network/greth.c. This
+version requires the driver manager.
+
+network_interface_add is used to assign IP/NETMASK and MAC address to
+GRETH interfaces dynamically according to in which order devices are
+registered. The function takes the settings from the user defined
+interface_configs[] array, defined in the project configuration.
diff --git a/bsps/shared/grlib/net/greth.c b/bsps/shared/grlib/net/greth.c
new file mode 100644
index 0000000..8b19b48
--- /dev/null
+++ b/bsps/shared/grlib/net/greth.c
@@ -0,0 +1,1655 @@
+/*
+ * Gaisler Research ethernet MAC driver
+ * adapted from Opencores driver by Marko Isomaki
+ *
+ * The license and distribution terms for this file may be
+ * found in found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ *
+ *
+ * 2008-12-10, Converted to driver manager and added support for
+ * multiple GRETH cores. <daniel@gaisler.com>
+ * 2007-09-07, Ported GBIT support from 4.6.5
+ */
+
+#include <machine/rtems-bsd-kernel-space.h>
+
+#include <rtems.h>
+#define CPU_U32_FIX
+#include <bsp.h>
+
+#ifdef GRETH_SUPPORTED
+
+#include <inttypes.h>
+#include <errno.h>
+#include <rtems/bspIo.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+
+#include <grlib/greth.h>
+#include <drvmgr/drvmgr.h>
+#include <grlib/ambapp_bus.h>
+#include <grlib/ambapp.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>
+
+#ifdef malloc
+#undef malloc
+#endif
+#ifdef free
+#undef free
+#endif
+
+#include <grlib/grlib_impl.h>
+
+#if defined(__m68k__)
+extern m68k_isr_entry set_vector( rtems_isr_entry, rtems_vector_number, int );
+#else
+extern rtems_isr_entry set_vector( rtems_isr_entry, rtems_vector_number, int );
+#endif
+
+
+/* #define GRETH_DEBUG */
+
+#ifdef GRETH_DEBUG
+#define DBG(args...) printk(args)
+#else
+#define DBG(args...)
+#endif
+
+/* #define GRETH_DEBUG_MII */
+
+#ifdef GRETH_DEBUG_MII
+#define MIIDBG(args...) printk(args)
+#else
+#define MIIDBG(args...)
+#endif
+
+#ifdef CPU_U32_FIX
+extern void ipalign(struct mbuf *m);
+#endif
+
+/* Used when reading from memory written by GRETH DMA unit */
+#ifndef GRETH_MEM_LOAD
+#define GRETH_MEM_LOAD(addr) (*(volatile unsigned int *)(addr))
+#endif
+
+/*
+ * Number of OCs supported by this driver
+ */
+#define NOCDRIVER 1
+
+/*
+ * Receive buffer size -- Allow for a full ethernet packet including CRC
+ */
+#define RBUF_SIZE 1518
+
+#define ET_MINLEN 64 /* minimum message length */
+
+/*
+ * RTEMS event used by interrupt handler to signal driver tasks.
+ * This must not be any of the events used by the network task synchronization.
+ */
+#define INTERRUPT_EVENT RTEMS_EVENT_1
+
+/*
+ * RTEMS event used to start transmit daemon.
+ * This must not be the same as INTERRUPT_EVENT.
+ */
+#define START_TRANSMIT_EVENT RTEMS_EVENT_2
+
+ /* event to send when tx buffers become available */
+#define GRETH_TX_WAIT_EVENT RTEMS_EVENT_3
+
+#if (MCLBYTES < RBUF_SIZE)
+# error "Driver must have MCLBYTES > RBUF_SIZE"
+#endif
+
+/* 4s Autonegotiation Timeout */
+#ifndef GRETH_AUTONEGO_TIMEOUT_MS
+#define GRETH_AUTONEGO_TIMEOUT_MS 4000
+#endif
+const struct timespec greth_tan = {
+ GRETH_AUTONEGO_TIMEOUT_MS/1000,
+ (GRETH_AUTONEGO_TIMEOUT_MS % 1000) * 1000000
+};
+
+/* For optimizing the autonegotiation time */
+#define GRETH_AUTONEGO_PRINT_TIME
+
+/* Ethernet buffer descriptor */
+
+typedef struct _greth_rxtxdesc {
+ volatile uint32_t ctrl; /* Length and status */
+ uint32_t *addr; /* Buffer pointer */
+} greth_rxtxdesc;
+
+
+/*
+ * Per-device data
+ */
+struct greth_softc
+{
+
+ struct arpcom arpcom;
+ struct drvmgr_dev *dev; /* Driver manager device */
+ char devName[32];
+
+ greth_regs *regs;
+ int minor;
+ int phyaddr; /* PHY Address configured by user (or -1 to autodetect) */
+ unsigned int edcl_dis;
+ int greth_rst;
+
+ int acceptBroadcast;
+ rtems_id daemonTid;
+
+ unsigned int tx_ptr;
+ unsigned int tx_dptr;
+ unsigned int tx_cnt;
+ unsigned int rx_ptr;
+ unsigned int txbufs;
+ unsigned int rxbufs;
+ greth_rxtxdesc *txdesc;
+ greth_rxtxdesc *rxdesc;
+ unsigned int txdesc_remote;
+ unsigned int rxdesc_remote;
+ struct mbuf **rxmbuf;
+ struct mbuf **txmbuf;
+ rtems_vector_number vector;
+
+ /* TX descriptor interrupt generation */
+ int tx_int_gen;
+ int tx_int_gen_cur;
+ struct mbuf *next_tx_mbuf;
+ int max_fragsize;
+
+ /*Status*/
+ struct phy_device_info phydev;
+ int phy_read_access;
+ int phy_write_access;
+ int fd;
+ int sp;
+ int gb;
+ int gbit_mac;
+ int auto_neg;
+ unsigned int advmodes; /* advertise ethernet speed modes. 0 = all modes. */
+ struct timespec auto_neg_time;
+ int mc_available;
+
+ /*
+ * Statistics
+ */
+ unsigned long rxInterrupts;
+
+ unsigned long rxPackets;
+ unsigned long rxLengthError;
+ unsigned long rxNonOctet;
+ unsigned long rxBadCRC;
+ unsigned long rxOverrun;
+
+ unsigned long txInterrupts;
+
+ unsigned long txDeferred;
+ unsigned long txHeartbeat;
+ unsigned long txLateCollision;
+ unsigned long txRetryLimit;
+ unsigned long txUnderrun;
+
+ /* Spin-lock ISR protection */
+ SPIN_DECLARE(devlock);
+};
+
+int greth_process_tx_gbit(struct greth_softc *sc);
+int greth_process_tx(struct greth_softc *sc);
+
+static char *almalloc(int sz, int alignment)
+{
+ char *tmp;
+ tmp = grlib_calloc(1, sz + (alignment-1));
+ tmp = (char *) (((int)tmp+alignment) & ~(alignment -1));
+ return(tmp);
+}
+
+/* GRETH interrupt handler */
+
+static void greth_interrupt (void *arg)
+{
+ uint32_t status;
+ uint32_t ctrl;
+ rtems_event_set events = 0;
+ struct greth_softc *greth = arg;
+ SPIN_ISR_IRQFLAGS(flags);
+
+ /* read and clear interrupt cause */
+ status = greth->regs->status;
+ greth->regs->status = status;
+
+ SPIN_LOCK(&greth->devlock, flags);
+ ctrl = greth->regs->ctrl;
+
+ /* Frame received? */
+ if ((ctrl & GRETH_CTRL_RXIRQ) && (status & (GRETH_STATUS_RXERR | GRETH_STATUS_RXIRQ)))
+ {
+ greth->rxInterrupts++;
+ /* Stop RX-Error and RX-Packet interrupts */
+ ctrl &= ~GRETH_CTRL_RXIRQ;
+ events |= INTERRUPT_EVENT;
+ }
+
+ if ( (ctrl & GRETH_CTRL_TXIRQ) && (status & (GRETH_STATUS_TXERR | GRETH_STATUS_TXIRQ)) )
+ {
+ greth->txInterrupts++;
+ ctrl &= ~GRETH_CTRL_TXIRQ;
+ events |= GRETH_TX_WAIT_EVENT;
+ }
+
+ /* Clear interrupt sources */
+ greth->regs->ctrl = ctrl;
+ SPIN_UNLOCK(&greth->devlock, flags);
+
+ /* Send the event(s) */
+ if ( events )
+ rtems_bsdnet_event_send(greth->daemonTid, events);
+}
+
+static uint32_t read_mii(struct greth_softc *sc, uint32_t phy_addr, uint32_t reg_addr)
+{
+ sc->phy_read_access++;
+ while (sc->regs->mdio_ctrl & GRETH_MDIO_BUSY) {}
+ sc->regs->mdio_ctrl = (phy_addr << 11) | (reg_addr << 6) | GRETH_MDIO_READ;
+ while (sc->regs->mdio_ctrl & GRETH_MDIO_BUSY) {}
+ if (!(sc->regs->mdio_ctrl & GRETH_MDIO_LINKFAIL)) {
+ MIIDBG("greth%d: mii read[%d] OK to %" PRIx32 ".%" PRIx32
+ " (0x%08" PRIx32 ",0x%08" PRIx32 ")\n",
+ sc->minor, sc->phy_read_access, phy_addr, reg_addr,
+ sc->regs->ctrl, sc->regs->mdio_ctrl);
+ return((sc->regs->mdio_ctrl >> 16) & 0xFFFF);
+ } else {
+ printf("greth%d: mii read[%d] failed to %" PRIx32 ".%" PRIx32
+ " (0x%08" PRIx32 ",0x%08" PRIx32 ")\n",
+ sc->minor, sc->phy_read_access, phy_addr, reg_addr,
+ sc->regs->ctrl, sc->regs->mdio_ctrl);
+ return (0xffff);
+ }
+}
+
+static void write_mii(struct greth_softc *sc, uint32_t phy_addr, uint32_t reg_addr, uint32_t data)
+{
+ sc->phy_write_access++;
+ while (sc->regs->mdio_ctrl & GRETH_MDIO_BUSY) {}
+ sc->regs->mdio_ctrl =
+ ((data & 0xFFFF) << 16) | (phy_addr << 11) | (reg_addr << 6) | GRETH_MDIO_WRITE;
+ while (sc->regs->mdio_ctrl & GRETH_MDIO_BUSY) {}
+ if (!(sc->regs->mdio_ctrl & GRETH_MDIO_LINKFAIL)) {
+ MIIDBG("greth%d: mii write[%d] OK to to %" PRIx32 ".%" PRIx32
+ "(0x%08" PRIx32 ",0x%08" PRIx32 ")\n",
+ sc->minor, sc->phy_write_access, phy_addr, reg_addr,
+ sc->regs->ctrl, sc->regs->mdio_ctrl);
+ } else {
+ printf("greth%d: mii write[%d] failed to to %" PRIx32 ".%" PRIx32
+ " (0x%08" PRIx32 ",0x%08" PRIx32 ")\n",
+ sc->minor, sc->phy_write_access, phy_addr, reg_addr,
+ sc->regs->ctrl, sc->regs->mdio_ctrl);
+ }
+}
+
+static void print_init_info(struct greth_softc *sc)
+{
+ printf("greth: driver attached\n");
+ if ( sc->auto_neg == -1 ){
+ printf("Auto negotiation timed out. Selecting default config\n");
+ }
+ printf("**** PHY ****\n");
+ printf("Vendor: %x Device: %x Revision: %d\n",sc->phydev.vendor, sc->phydev.device, sc->phydev.rev);
+ printf("Current Operating Mode: ");
+ if (sc->gb) {
+ printf("1000 Mbit ");
+ } else if (sc->sp) {
+ printf("100 Mbit ");
+ } else {
+ printf("10 Mbit ");
+ }
+ if (sc->fd) {
+ printf("Full Duplex\n");
+ } else {
+ printf("Half Duplex\n");
+ }
+#ifdef GRETH_AUTONEGO_PRINT_TIME
+ if ( sc->auto_neg ) {
+ printf("Autonegotiation Time: %" PRIdMAX "ms\n",
+ (intmax_t)sc->auto_neg_time.tv_sec * 1000 +
+ sc->auto_neg_time.tv_nsec / 1000000);
+ }
+#endif
+}
+
+/*
+ * Generates the hash words based on CRCs of the enabled MAC addresses that are
+ * allowed to be received. The allowed MAC addresses are maintained in a linked
+ * "multi-cast" list available in the arpcom structure.
+ *
+ * Returns the number of MAC addresses that were processed (in the list)
+ */
+static int
+greth_mac_filter_calc(struct arpcom *ac, uint32_t *msb, uint32_t *lsb)
+{
+ struct ether_multistep step;
+ struct ether_multi *enm;
+ int cnt = 0;
+ uint32_t crc, htindex, ht[2] = {0, 0};
+
+ /* Go through the Ethernet Multicast addresses one by one and add their
+ * CRC contribution to the MAC filter.
+ */
+ ETHER_FIRST_MULTI(step, ac, enm);
+ while (enm) {
+ crc = ether_crc32_be((uint8_t *)enm->enm_addrlo, 6);
+ htindex = crc & 0x3f;
+ ht[htindex >> 5] |= (1 << (htindex & 0x1F));
+ cnt++;
+ ETHER_NEXT_MULTI(step, enm);
+ }
+
+ if (cnt > 0) {
+ *msb = ht[1];
+ *lsb = ht[0];
+ }
+
+ return cnt;
+}
+
+/*
+ * Initialize the ethernet hardware
+ */
+static int greth_mac_filter_set(struct greth_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ uint32_t hash_msb, hash_lsb, ctrl;
+ SPIN_IRQFLAGS(flags);
+
+ hash_msb = 0;
+ hash_lsb = 0;
+ ctrl = 0;
+ if (ifp->if_flags & IFF_PROMISC) {
+ /* No need to enable multi-cast when promiscous mode accepts all */
+ ctrl |= GRETH_CTRL_PRO;
+ } else if(!sc->mc_available) {
+ return EINVAL; /* no hardware support for multicast filtering. */
+ } else if (ifp->if_flags & IFF_ALLMULTI) {
+ /* We should accept all multicast addresses */
+ ctrl |= GRETH_CTRL_MCE;
+ hash_msb = 0xFFFFFFFF;
+ hash_lsb = 0xFFFFFFFF;
+ } else if (greth_mac_filter_calc(&sc->arpcom, &hash_msb, &hash_lsb) > 0) {
+ /* Generate hash for MAC filtering out multicast addresses */
+ ctrl |= GRETH_CTRL_MCE;
+ } else {
+ /* Multicast list is empty .. disable multicast */
+ }
+ SPIN_LOCK_IRQ(&sc->devlock, flags);
+ sc->regs->ht_msb = hash_msb;
+ sc->regs->ht_lsb = hash_lsb;
+ sc->regs->ctrl = (sc->regs->ctrl & ~(GRETH_CTRL_PRO | GRETH_CTRL_MCE)) |
+ ctrl;
+ SPIN_UNLOCK_IRQ(&sc->devlock, flags);
+
+ return 0;
+}
+
+/*
+ * Initialize the ethernet hardware
+ */
+static void
+greth_initialize_hardware (struct greth_softc *sc)
+{
+ struct mbuf *m;
+ int i;
+ int phyaddr;
+ int phyctrl;
+ int phystatus;
+ int tmp1;
+ int tmp2;
+ struct timespec tstart, tnow;
+ greth_regs *regs;
+ unsigned int advmodes, speed;
+
+ regs = sc->regs;
+
+ /* Reset the controller. */
+ sc->rxInterrupts = 0;
+ sc->rxPackets = 0;
+
+ if (sc->greth_rst) {
+ /* Reset ON */
+ regs->ctrl = GRETH_CTRL_RST | GRETH_CTRL_DD | GRETH_CTRL_ED;
+ for (i = 0; i<100 && (regs->ctrl & GRETH_CTRL_RST); i++)
+ ;
+ speed = 0; /* probe mode below */
+ } else {
+ /* inherit EDCL mode for now */
+ speed = sc->regs->ctrl & (GRETH_CTRL_GB|GRETH_CTRL_SP|GRETH_CTRL_FULLD);
+ }
+ /* Reset OFF and RX/TX DMA OFF. SW do PHY Init */
+ regs->ctrl = GRETH_CTRL_DD | GRETH_CTRL_ED | speed;
+
+ /* Check if mac is gbit capable*/
+ sc->gbit_mac = (regs->ctrl >> 27) & 1;
+
+ /* Get the phy address which assumed to have been set
+ correctly with the reset value in hardware*/
+ if ( sc->phyaddr == -1 ) {
+ phyaddr = (regs->mdio_ctrl >> 11) & 0x1F;
+ } else {
+ phyaddr = sc->phyaddr;
+ }
+ sc->phy_read_access = 0;
+ sc->phy_write_access = 0;
+
+ /* As I understand the PHY comes back to a good default state after
+ * Power-down or Reset, so we do both just in case. Power-down bit should
+ * be cleared.
+ * Wait for old reset (if asserted by boot loader) to complete, otherwise
+ * power-down instruction might not have any effect.
+ */
+ while (read_mii(sc, phyaddr, 0) & 0x8000) {}
+ write_mii(sc, phyaddr, 0, 0x0800); /* Power-down */
+ write_mii(sc, phyaddr, 0, 0x0000); /* Power-Up */
+ write_mii(sc, phyaddr, 0, 0x8000); /* Reset */
+
+ /* We wait about 30ms */
+ rtems_task_wake_after(rtems_clock_get_ticks_per_second()/32);
+
+ /* Wait for reset to complete and get default values */
+ while ((phyctrl = read_mii(sc, phyaddr, 0)) & 0x8000) {}
+
+ /* Set up PHY advertising modes for auto-negotiation */
+ advmodes = sc->advmodes;
+ if (advmodes == 0)
+ advmodes = GRETH_ADV_ALL;
+ if (!sc->gbit_mac)
+ advmodes &= ~(GRETH_ADV_1000_FD | GRETH_ADV_1000_HD);
+
+ /* Enable/Disable GBit auto-neg advetisement so that the link partner
+ * know that we have/haven't GBit capability. The MAC may not support
+ * Gbit even though PHY does...
+ */
+ phystatus = read_mii(sc, phyaddr, 1);
+ if (phystatus & 0x0100) {
+ tmp1 = read_mii(sc, phyaddr, 9);
+ tmp1 &= ~0x300;
+ if (advmodes & GRETH_ADV_1000_FD)
+ tmp1 |= 0x200;
+ if (advmodes & GRETH_ADV_1000_HD)
+ tmp1 |= 0x100;
+ write_mii(sc, phyaddr, 9, tmp1);
+ }
+
+ /* Optionally limit the 10/100 modes as configured by user */
+ tmp1 = read_mii(sc, phyaddr, 4);
+ tmp1 &= ~0x1e0;
+ if (advmodes & GRETH_ADV_100_FD)
+ tmp1 |= 0x100;
+ if (advmodes & GRETH_ADV_100_HD)
+ tmp1 |= 0x080;
+ if (advmodes & GRETH_ADV_10_FD)
+ tmp1 |= 0x040;
+ if (advmodes & GRETH_ADV_10_HD)
+ tmp1 |= 0x020;
+ write_mii(sc, phyaddr, 4, tmp1);
+
+ /* If autonegotiation implemented we start it */
+ if (phystatus & 0x0008) {
+ write_mii(sc, phyaddr, 0, phyctrl | 0x1200);
+ phyctrl = read_mii(sc, phyaddr, 0);
+ }
+
+ /* Check if PHY is autoneg capable and then determine operating mode,
+ otherwise force it to 10 Mbit halfduplex */
+ sc->gb = 0;
+ sc->fd = 0;
+ sc->sp = 0;
+ sc->auto_neg = 0;
+ timespecclear(&sc->auto_neg_time);
+ if ((phyctrl >> 12) & 1) {
+ /*wait for auto negotiation to complete*/
+ sc->auto_neg = 1;
+ if (rtems_clock_get_uptime(&tstart) != RTEMS_SUCCESSFUL)
+ printk("rtems_clock_get_uptime failed\n");
+ while (!(((phystatus = read_mii(sc, phyaddr, 1)) >> 5) & 1)) {
+ if (rtems_clock_get_uptime(&tnow) != RTEMS_SUCCESSFUL)
+ printk("rtems_clock_get_uptime failed\n");
+ timespecsub(&tnow, &tstart, &sc->auto_neg_time);
+ if (timespeccmp(&sc->auto_neg_time, &greth_tan, >)) {
+ sc->auto_neg = -1; /* Failed */
+ tmp1 = read_mii(sc, phyaddr, 0);
+ sc->gb = ((phyctrl >> 6) & 1) && !((phyctrl >> 13) & 1);
+ sc->sp = !((phyctrl >> 6) & 1) && ((phyctrl >> 13) & 1);
+ sc->fd = (phyctrl >> 8) & 1;
+ goto auto_neg_done;
+ }
+ /* Wait about 30ms, time is PHY dependent */
+ rtems_task_wake_after(rtems_clock_get_ticks_per_second()/32);
+ }
+ sc->phydev.adv = read_mii(sc, phyaddr, 4);
+ sc->phydev.part = read_mii(sc, phyaddr, 5);
+ if ((phystatus >> 8) & 1) {
+ sc->phydev.extadv = read_mii(sc, phyaddr, 9);
+ sc->phydev.extpart = read_mii(sc, phyaddr, 10);
+ if ( (sc->phydev.extadv & GRETH_MII_EXTADV_1000HD) &&
+ (sc->phydev.extpart & GRETH_MII_EXTPRT_1000HD)) {
+ sc->gb = 1;
+ sc->fd = 0;
+ }
+ if ( (sc->phydev.extadv & GRETH_MII_EXTADV_1000FD) &&
+ (sc->phydev.extpart & GRETH_MII_EXTPRT_1000FD)) {
+ sc->gb = 1;
+ sc->fd = 1;
+ }
+ }
+ if ((sc->gb == 0) || ((sc->gb == 1) && (sc->gbit_mac == 0))) {
+ if ( (sc->phydev.adv & GRETH_MII_100TXFD) &&
+ (sc->phydev.part & GRETH_MII_100TXFD)) {
+ sc->sp = 1;
+ sc->fd = 1;
+ } else if ( (sc->phydev.adv & GRETH_MII_100TXHD) &&
+ (sc->phydev.part & GRETH_MII_100TXHD)) {
+ sc->sp = 1;
+ sc->fd = 0;
+ } else if ( (sc->phydev.adv & GRETH_MII_10FD) &&
+ (sc->phydev.part & GRETH_MII_10FD)) {
+ sc->fd = 1;
+ }
+ }
+ }
+auto_neg_done:
+ sc->phydev.vendor = 0;
+ sc->phydev.device = 0;
+ sc->phydev.rev = 0;
+ phystatus = read_mii(sc, phyaddr, 1);
+
+ /* Read out PHY info if extended registers are available */
+ if (phystatus & 1) {
+ tmp1 = read_mii(sc, phyaddr, 2);
+ tmp2 = read_mii(sc, phyaddr, 3);
+
+ sc->phydev.vendor = (tmp1 << 6) | ((tmp2 >> 10) & 0x3F);
+ sc->phydev.rev = tmp2 & 0xF;
+ sc->phydev.device = (tmp2 >> 4) & 0x3F;
+ }
+
+ /* Force to 10 mbit half duplex if the 10/100 MAC is used with a 1000 PHY */
+ if (((sc->gb) && !(sc->gbit_mac)) || !((phyctrl >> 12) & 1)) {
+ write_mii(sc, phyaddr, 0, sc->sp << 13);
+
+ /* check if marvell 88EE1111 PHY. Needs special reset handling */
+ if ((phystatus & 1) && (sc->phydev.vendor == 0x005043) &&
+ (sc->phydev.device == 0x0C))
+ write_mii(sc, phyaddr, 0, 0x8000);
+
+ sc->gb = 0;
+ sc->sp = 0;
+ sc->fd = 0;
+ }
+ while ((read_mii(sc, phyaddr, 0)) & 0x8000) {}
+
+ if (sc->greth_rst) {
+ /* Reset ON */
+ regs->ctrl = GRETH_CTRL_RST | GRETH_CTRL_DD | GRETH_CTRL_ED;
+ for (i = 0; i < 100 && (regs->ctrl & GRETH_CTRL_RST); i++)
+ ;
+ }
+ /* Reset OFF. Set mode matching PHY settings. */
+ speed = (sc->gb << 8) | (sc->sp << 7) | (sc->fd << 4);
+ regs->ctrl = GRETH_CTRL_DD | sc->edcl_dis | speed;
+
+ /* Initialize rx/tx descriptor table pointers. Due to alignment we
+ * always allocate maximum table size.
+ */
+ sc->txdesc = (greth_rxtxdesc *) almalloc(0x800, 0x400);
+ sc->rxdesc = (greth_rxtxdesc *) &sc->txdesc[128];
+ sc->tx_ptr = 0;
+ sc->tx_dptr = 0;
+ sc->tx_cnt = 0;
+ sc->rx_ptr = 0;
+
+ /* Translate the Descriptor DMA table base address into an address that
+ * the GRETH core can understand
+ */
+ drvmgr_translate_check(
+ sc->dev,
+ CPUMEM_TO_DMA,
+ (void *)sc->txdesc,
+ (void **)&sc->txdesc_remote,
+ 0x800);
+ sc->rxdesc_remote = sc->txdesc_remote + 0x400;
+ regs->txdesc = (int) sc->txdesc_remote;
+ regs->rxdesc = (int) sc->rxdesc_remote;
+
+ sc->rxmbuf = grlib_calloc(sc->rxbufs, sizeof(*sc->rxmbuf));
+ sc->txmbuf = grlib_calloc(sc->txbufs, sizeof(*sc->txmbuf));
+
+ for (i = 0; i < sc->txbufs; i++)
+ {
+ sc->txdesc[i].ctrl = 0;
+ if (!(sc->gbit_mac)) {
+ drvmgr_translate_check(
+ sc->dev,
+ CPUMEM_TO_DMA,
+ (void *)grlib_malloc(GRETH_MAXBUF_LEN),
+ (void **)&sc->txdesc[i].addr,
+ GRETH_MAXBUF_LEN);
+ }
+#ifdef GRETH_DEBUG
+ /* printf("TXBUF: %08x\n", (int) sc->txdesc[i].addr); */
+#endif
+ }
+ for (i = 0; i < sc->rxbufs; i++)
+ {
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ if (sc->gbit_mac)
+ m->m_data += 2;
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ sc->rxmbuf[i] = m;
+ drvmgr_translate_check(
+ sc->dev,
+ CPUMEM_TO_DMA,
+ (void *)mtod(m, uint32_t *),
+ (void **)&sc->rxdesc[i].addr,
+ GRETH_MAXBUF_LEN);
+ sc->rxdesc[i].ctrl = GRETH_RXD_ENABLE | GRETH_RXD_IRQ;
+#ifdef GRETH_DEBUG
+/* printf("RXBUF: %08x\n", (int) sc->rxdesc[i].addr); */
+#endif
+ }
+ sc->rxdesc[sc->rxbufs - 1].ctrl |= GRETH_RXD_WRAP;
+
+ /* set ethernet address. */
+ regs->mac_addr_msb =
+ sc->arpcom.ac_enaddr[0] << 8 | sc->arpcom.ac_enaddr[1];
+ regs->mac_addr_lsb =
+ sc->arpcom.ac_enaddr[2] << 24 | sc->arpcom.ac_enaddr[3] << 16 |
+ sc->arpcom.ac_enaddr[4] << 8 | sc->arpcom.ac_enaddr[5];
+
+ if ( sc->rxbufs < 10 ) {
+ sc->tx_int_gen = sc->tx_int_gen_cur = 1;
+ }else{
+ sc->tx_int_gen = sc->tx_int_gen_cur = sc->txbufs/2;
+ }
+ sc->next_tx_mbuf = NULL;
+
+ if ( !sc->gbit_mac )
+ sc->max_fragsize = 1;
+
+ /* clear all pending interrupts */
+ regs->status = 0xffffffff;
+
+ /* install interrupt handler */
+ drvmgr_interrupt_register(sc->dev, 0, "greth", greth_interrupt, sc);
+
+ regs->ctrl |= GRETH_CTRL_RXEN | GRETH_CTRL_RXIRQ;
+
+ print_init_info(sc);
+}
+
+#ifdef CPU_U32_FIX
+
+/*
+ * Routine to align the received packet so that the ip header
+ * is on a 32-bit boundary. Necessary for cpu's that do not
+ * allow unaligned loads and stores and when the 32-bit DMA
+ * mode is used.
+ *
+ * Transfers are done on word basis to avoid possibly slow byte
+ * and half-word writes.
+ */
+
+void ipalign(struct mbuf *m)
+{
+ unsigned int *first, *last, data;
+ unsigned int tmp = 0;
+
+ if ((((int) m->m_data) & 2) && (m->m_len)) {
+#if CPU_LITTLE_ENDIAN == TRUE
+ memmove((caddr_t)(((int) m->m_data) + 2), m->m_data, m->m_len);
+#else
+ last = (unsigned int *) ((((int) m->m_data) + m->m_len + 8) & ~3);
+ first = (unsigned int *) (((int) m->m_data) & ~3);
+ /* tmp = *first << 16; */
+ tmp = GRETH_MEM_LOAD(first);
+ tmp = tmp << 16;
+ first++;
+ do {
+ /* When snooping is not available the LDA instruction must be used
+ * to avoid the cache to return an illegal value.
+ ** Load with forced cache miss
+ * data = *first;
+ */
+ data = GRETH_MEM_LOAD(first);
+ *first = tmp | (data >> 16);
+ tmp = data << 16;
+ first++;
+ } while (first <= last);
+#endif
+ m->m_data = (caddr_t)(((int) m->m_data) + 2);
+ }
+}
+#endif
+
+static void
+greth_Daemon (void *arg)
+{
+ struct ether_header *eh;
+ struct greth_softc *dp = (struct greth_softc *) arg;
+ struct ifnet *ifp = &dp->arpcom.ac_if;
+ struct mbuf *m;
+ unsigned int len, len_status, bad;
+ rtems_event_set events;
+ SPIN_IRQFLAGS(flags);
+ int first;
+ int tmp;
+
+ for (;;)
+ {
+ rtems_bsdnet_event_receive (INTERRUPT_EVENT | GRETH_TX_WAIT_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT, &events);
+
+ if ( events & GRETH_TX_WAIT_EVENT ){
+ /* TX interrupt.
+ * We only end up here when all TX descriptors has been used,
+ * and
+ */
+ if ( dp->gbit_mac )
+ greth_process_tx_gbit(dp);
+ else
+ greth_process_tx(dp);
+
+ /* If we didn't get a RX interrupt we don't process it */
+ if ( (events & INTERRUPT_EVENT) == 0 )
+ continue;
+ }
+
+
+#ifdef GRETH_ETH_DEBUG
+ printf ("r\n");
+#endif
+ first=1;
+ /* Scan for Received packets */
+again:
+ while (!((len_status =
+ GRETH_MEM_LOAD(&dp->rxdesc[dp->rx_ptr].ctrl)) & GRETH_RXD_ENABLE))
+ {
+ bad = 0;
+ if (len_status & GRETH_RXD_TOOLONG)
+ {
+ dp->rxLengthError++;
+ bad = 1;
+ }
+ if (len_status & GRETH_RXD_DRIBBLE)
+ {
+ dp->rxNonOctet++;
+ bad = 1;
+ }
+ if (len_status & GRETH_RXD_CRCERR)
+ {
+ dp->rxBadCRC++;
+ bad = 1;
+ }
+ if (len_status & GRETH_RXD_OVERRUN)
+ {
+ dp->rxOverrun++;
+ bad = 1;
+ }
+ if (len_status & GRETH_RXD_LENERR)
+ {
+ dp->rxLengthError++;
+ bad = 1;
+ }
+ if (!bad)
+ {
+ /* pass on the packet in the receive buffer */
+ len = len_status & 0x7FF;
+ m = dp->rxmbuf[dp->rx_ptr];
+#ifdef GRETH_DEBUG
+ int i;
+ printf("RX: 0x%08x, Len: %d : ", (int) m->m_data, len);
+ for (i=0; i<len; i++)
+ printf("%x%x", (m->m_data[i] >> 4) & 0x0ff, m->m_data[i] & 0x0ff);
+ printf("\n");
+#endif
+ m->m_len = m->m_pkthdr.len =
+ len - sizeof (struct ether_header);
+
+ eh = mtod (m, struct ether_header *);
+
+ m->m_data += sizeof (struct ether_header);
+#ifdef CPU_U32_FIX
+ if(!dp->gbit_mac) {
+ /* OVERRIDE CACHED ETHERNET HEADER FOR NON-SNOOPING SYSTEMS */
+ tmp = GRETH_MEM_LOAD((uintptr_t)eh);
+ tmp = GRETH_MEM_LOAD(4+(uintptr_t)eh);
+ tmp = GRETH_MEM_LOAD(8+(uintptr_t)eh);
+ tmp = GRETH_MEM_LOAD(12+(uintptr_t)eh);
+ (void)tmp;
+
+ ipalign(m); /* Align packet on 32-bit boundary */
+ }
+#endif
+/*
+ if(!(dp->gbit_mac) && !CPU_SPARC_HAS_SNOOPING) {
+ rtems_cache_invalidate_entire_data();
+ }
+*/
+ ether_input (ifp, eh, m);
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ if (dp->gbit_mac)
+ m->m_data += 2;
+ dp->rxmbuf[dp->rx_ptr] = m;
+ m->m_pkthdr.rcvif = ifp;
+ drvmgr_translate_check(
+ dp->dev,
+ CPUMEM_TO_DMA,
+ (void *)mtod (m, uint32_t *),
+ (void **)&dp->rxdesc[dp->rx_ptr].addr,
+ GRETH_MAXBUF_LEN);
+ dp->rxPackets++;
+ }
+ if (dp->rx_ptr == dp->rxbufs - 1) {
+ dp->rxdesc[dp->rx_ptr].ctrl = GRETH_RXD_ENABLE | GRETH_RXD_IRQ | GRETH_RXD_WRAP;
+ } else {
+ dp->rxdesc[dp->rx_ptr].ctrl = GRETH_RXD_ENABLE | GRETH_RXD_IRQ;
+ }
+ SPIN_LOCK_IRQ(&dp->devlock, flags);
+ dp->regs->ctrl |= GRETH_CTRL_RXEN;
+ SPIN_UNLOCK_IRQ(&dp->devlock, flags);
+ dp->rx_ptr = (dp->rx_ptr + 1) % dp->rxbufs;
+ }
+
+ /* Always scan twice to avoid deadlock */
+ if ( first ){
+ first=0;
+ SPIN_LOCK_IRQ(&dp->devlock, flags);
+ dp->regs->ctrl |= GRETH_CTRL_RXIRQ;
+ SPIN_UNLOCK_IRQ(&dp->devlock, flags);
+ goto again;
+ }
+
+ }
+}
+
+static int
+sendpacket (struct ifnet *ifp, struct mbuf *m)
+{
+ struct greth_softc *dp = ifp->if_softc;
+ unsigned char *temp;
+ struct mbuf *n;
+ unsigned int len;
+ SPIN_IRQFLAGS(flags);
+
+ /*
+ * Is there a free descriptor available?
+ */
+ if (GRETH_MEM_LOAD(&dp->txdesc[dp->tx_ptr].ctrl) & GRETH_TXD_ENABLE){
+ /* No. */
+ return 1;
+ }
+
+ /* Remember head of chain */
+ n = m;
+
+ len = 0;
+ temp = (unsigned char *) GRETH_MEM_LOAD(&dp->txdesc[dp->tx_ptr].addr);
+ drvmgr_translate(dp->dev, CPUMEM_FROM_DMA, (void *)temp, (void **)&temp);
+#ifdef GRETH_DEBUG
+ printf("TXD: 0x%08x : BUF: 0x%08x\n", (int) m->m_data, (int) temp);
+#endif
+ for (;;)
+ {
+#ifdef GRETH_DEBUG
+ int i;
+ printf("MBUF: 0x%08x : ", (int) m->m_data);
+ for (i=0;i<m->m_len;i++)
+ printf("%x%x", (m->m_data[i] >> 4) & 0x0ff, m->m_data[i] & 0x0ff);
+ printf("\n");
+#endif
+ len += m->m_len;
+ if (len <= RBUF_SIZE)
+ memcpy ((void *) temp, (char *) m->m_data, m->m_len);
+ temp += m->m_len;
+ if ((m = m->m_next) == NULL)
+ break;
+ }
+
+ m_freem (n);
+
+ /* don't send long packets */
+
+ if (len <= GRETH_MAXBUF_LEN) {
+ if (dp->tx_ptr < dp->txbufs-1) {
+ dp->txdesc[dp->tx_ptr].ctrl = GRETH_TXD_IRQ |
+ GRETH_TXD_ENABLE | len;
+ } else {
+ dp->txdesc[dp->tx_ptr].ctrl = GRETH_TXD_IRQ |
+ GRETH_TXD_WRAP | GRETH_TXD_ENABLE | len;
+ }
+ dp->tx_ptr = (dp->tx_ptr + 1) % dp->txbufs;
+ SPIN_LOCK_IRQ(&dp->devlock, flags);
+ dp->regs->ctrl = dp->regs->ctrl | GRETH_CTRL_TXEN;
+ SPIN_UNLOCK_IRQ(&dp->devlock, flags);
+ }
+
+ return 0;
+}
+
+
+static int
+sendpacket_gbit (struct ifnet *ifp, struct mbuf *m)
+{
+ struct greth_softc *dp = ifp->if_softc;
+ unsigned int len;
+
+ unsigned int ctrl;
+ int frags;
+ struct mbuf *mtmp;
+ int int_en;
+ SPIN_IRQFLAGS(flags);
+
+ len = 0;
+#ifdef GRETH_DEBUG
+ printf("TXD: 0x%08x\n", (int) m->m_data);
+#endif
+ /* Get number of fragments too see if we have enough
+ * resources.
+ */
+ frags=1;
+ mtmp=m;
+ while(mtmp->m_next){
+ frags++;
+ mtmp = mtmp->m_next;
+ }
+
+ if ( frags > dp->max_fragsize )
+ dp->max_fragsize = frags;
+
+ if ( frags > dp->txbufs ){
+ printf("GRETH: MBUF-chain cannot be sent. Increase descriptor count.\n");
+ return -1;
+ }
+
+ if ( frags > (dp->txbufs-dp->tx_cnt) ){
+ /* Return number of fragments */
+ return frags;
+ }
+
+
+ /* Enable interrupt from descriptor every tx_int_gen
+ * descriptor. Typically every 16 descriptor. This
+ * is only to reduce the number of interrupts during
+ * heavy load.
+ */
+ dp->tx_int_gen_cur-=frags;
+ if ( dp->tx_int_gen_cur <= 0 ){
+ dp->tx_int_gen_cur = dp->tx_int_gen;
+ int_en = GRETH_TXD_IRQ;
+ }else{
+ int_en = 0;
+ }
+
+ /* At this stage we know that enough descriptors are available */
+ for (;;)
+ {
+
+#ifdef GRETH_DEBUG
+ int i;
+ printf("MBUF: 0x%08x, Len: %d : ", (int) m->m_data, m->m_len);
+ for (i=0; i<m->m_len; i++)
+ printf("%x%x", (m->m_data[i] >> 4) & 0x0ff, m->m_data[i] & 0x0ff);
+ printf("\n");
+#endif
+ len += m->m_len;
+ drvmgr_translate_check(
+ dp->dev,
+ CPUMEM_TO_DMA,
+ (void *)(uint32_t *)m->m_data,
+ (void **)&dp->txdesc[dp->tx_ptr].addr,
+ m->m_len);
+
+ /* Wrap around? */
+ if (dp->tx_ptr < dp->txbufs-1) {
+ ctrl = GRETH_TXD_ENABLE;
+ }else{
+ ctrl = GRETH_TXD_ENABLE | GRETH_TXD_WRAP;
+ }
+
+ /* Enable Descriptor */
+ if ((m->m_next) == NULL) {
+ dp->txdesc[dp->tx_ptr].ctrl = ctrl | int_en | m->m_len;
+ break;
+ }else{
+ dp->txdesc[dp->tx_ptr].ctrl = GRETH_TXD_MORE | ctrl | int_en | m->m_len;
+ }
+
+ /* Next */
+ dp->txmbuf[dp->tx_ptr] = m;
+ dp->tx_ptr = (dp->tx_ptr + 1) % dp->txbufs;
+ dp->tx_cnt++;
+ m = m->m_next;
+ }
+ dp->txmbuf[dp->tx_ptr] = m;
+ dp->tx_ptr = (dp->tx_ptr + 1) % dp->txbufs;
+ dp->tx_cnt++;
+
+ /* Tell Hardware about newly enabled descriptor */
+ SPIN_LOCK_IRQ(&dp->devlock, flags);
+ dp->regs->ctrl = dp->regs->ctrl | GRETH_CTRL_TXEN;
+ SPIN_UNLOCK_IRQ(&dp->devlock, flags);
+
+ return 0;
+}
+
+int greth_process_tx_gbit(struct greth_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ SPIN_IRQFLAGS(flags);
+ int first=1;
+
+ /*
+ * Send packets till queue is empty
+ */
+ for (;;){
+ /* Reap Sent packets */
+ while((sc->tx_cnt > 0) && !(GRETH_MEM_LOAD(&sc->txdesc[sc->tx_dptr].ctrl) & GRETH_TXD_ENABLE)) {
+ m_free(sc->txmbuf[sc->tx_dptr]);
+ sc->tx_dptr = (sc->tx_dptr + 1) % sc->txbufs;
+ sc->tx_cnt--;
+ }
+
+ if ( sc->next_tx_mbuf ){
+ /* Get packet we tried but faild to transmit last time */
+ m = sc->next_tx_mbuf;
+ sc->next_tx_mbuf = NULL; /* Mark packet taken */
+ }else{
+ /*
+ * Get the next mbuf chain to transmit from Stack.
+ */
+ IF_DEQUEUE (&ifp->if_snd, m);
+ if (!m){
+ /* Hardware has sent all schedule packets, this
+ * makes the stack enter at greth_start next time
+ * a packet is to be sent.
+ */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ break;
+ }
+ }
+
+ /* Are there free descriptors available? */
+ /* Try to send packet, if it a negative number is returned. */
+ if ( (sc->tx_cnt >= sc->txbufs) || sendpacket_gbit(ifp, m) ){
+ /* Not enough resources */
+
+ /* Since we have taken the mbuf out of the "send chain"
+ * we must remember to use that next time we come back.
+ * or else we have dropped a packet.
+ */
+ sc->next_tx_mbuf = m;
+
+ /* Not enough resources, enable interrupt for transmissions
+ * this way we will be informed when more TX-descriptors are
+ * available.
+ */
+ if ( first ){
+ first = 0;
+ SPIN_LOCK_IRQ(&sc->devlock, flags);
+ ifp->if_flags |= IFF_OACTIVE;
+ sc->regs->ctrl |= GRETH_CTRL_TXIRQ;
+ SPIN_UNLOCK_IRQ(&sc->devlock, flags);
+
+ /* We must check again to be sure that we didn't
+ * miss an interrupt (if a packet was sent just before
+ * enabling interrupts)
+ */
+ continue;
+ }
+
+ return -1;
+ }else{
+ /* Sent Ok, proceed to process more packets if available */
+ }
+ }
+ return 0;
+}
+
+int greth_process_tx(struct greth_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ SPIN_IRQFLAGS(flags);
+ int first=1;
+
+ /*
+ * Send packets till queue is empty
+ */
+ for (;;){
+ if ( sc->next_tx_mbuf ){
+ /* Get packet we tried but failed to transmit last time */
+ m = sc->next_tx_mbuf;
+ sc->next_tx_mbuf = NULL; /* Mark packet taken */
+ }else{
+ /*
+ * Get the next mbuf chain to transmit from Stack.
+ */
+ IF_DEQUEUE (&ifp->if_snd, m);
+ if (!m){
+ /* Hardware has sent all schedule packets, this
+ * makes the stack enter at greth_start next time
+ * a packet is to be sent.
+ */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ break;
+ }
+ }
+
+ /* Try to send packet, failed if it a non-zero number is returned. */
+ if ( sendpacket(ifp, m) ){
+ /* Not enough resources */
+
+ /* Since we have taken the mbuf out of the "send chain"
+ * we must remember to use that next time we come back.
+ * or else we have dropped a packet.
+ */
+ sc->next_tx_mbuf = m;
+
+ /* Not enough resources, enable interrupt for transmissions
+ * this way we will be informed when more TX-descriptors are
+ * available.
+ */
+ if ( first ){
+ first = 0;
+ SPIN_LOCK_IRQ(&sc->devlock, flags);
+ ifp->if_flags |= IFF_OACTIVE;
+ sc->regs->ctrl |= GRETH_CTRL_TXIRQ;
+ SPIN_UNLOCK_IRQ(&sc->devlock, flags);
+
+ /* We must check again to be sure that we didn't
+ * miss an interrupt (if a packet was sent just before
+ * enabling interrupts)
+ */
+ continue;
+ }
+
+ return -1;
+ }else{
+ /* Sent Ok, proceed to process more packets if available */
+ }
+ }
+ return 0;
+}
+
+static void
+greth_start (struct ifnet *ifp)
+{
+ struct greth_softc *sc = ifp->if_softc;
+
+ if ( ifp->if_flags & IFF_OACTIVE )
+ return;
+
+ if ( sc->gbit_mac ){
+ /* No use trying to handle this if we are waiting on GRETH
+ * to send the previously scheduled packets.
+ */
+
+ greth_process_tx_gbit(sc);
+ }else{
+ greth_process_tx(sc);
+ }
+
+}
+
+/*
+ * Initialize and start the device
+ */
+static void
+greth_init (void *arg)
+{
+ struct greth_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ char name[4] = {'E', 'T', 'H', '0'};
+
+ if (sc->daemonTid == 0)
+ {
+ /*
+ * Start driver tasks
+ */
+ name[3] += sc->minor;
+ sc->daemonTid = rtems_bsdnet_newproc (name, 4096,
+ greth_Daemon, sc);
+
+ /*
+ * Set up GRETH hardware
+ */
+ greth_initialize_hardware (sc);
+ }
+
+ /*
+ * Setup promiscous/multi-cast MAC address filters if user enabled it
+ */
+ greth_mac_filter_set(sc);
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+}
+
+/*
+ * Stop the device
+ */
+static void
+greth_stop (struct greth_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ SPIN_IRQFLAGS(flags);
+ unsigned int speed;
+
+ SPIN_LOCK_IRQ(&sc->devlock, flags);
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ speed = sc->regs->ctrl & (GRETH_CTRL_GB | GRETH_CTRL_SP | GRETH_CTRL_FULLD);
+
+ /* RX/TX OFF */
+ sc->regs->ctrl = GRETH_CTRL_DD | GRETH_CTRL_ED | speed;
+ /* Reset ON */
+ if (sc->greth_rst)
+ sc->regs->ctrl = GRETH_CTRL_RST | GRETH_CTRL_DD | GRETH_CTRL_ED | speed;
+ /* Reset OFF and restore link settings previously detected if any */
+ sc->regs->ctrl = GRETH_CTRL_DD | sc->edcl_dis | speed;
+ SPIN_UNLOCK_IRQ(&sc->devlock, flags);
+
+ sc->next_tx_mbuf = NULL;
+}
+
+
+/*
+ * Show interface statistics
+ */
+static void
+greth_stats (struct greth_softc *sc)
+{
+ printf (" Rx Interrupts:%-8lu", sc->rxInterrupts);
+ printf (" Rx Packets:%-8lu", sc->rxPackets);
+ printf (" Length:%-8lu", sc->rxLengthError);
+ printf (" Non-octet:%-8lu\n", sc->rxNonOctet);
+ printf (" Bad CRC:%-8lu", sc->rxBadCRC);
+ printf (" Overrun:%-8lu", sc->rxOverrun);
+ printf (" Tx Interrupts:%-8lu", sc->txInterrupts);
+ printf (" Maximal Frags:%-8d", sc->max_fragsize);
+ printf (" GBIT MAC:%-8d", sc->gbit_mac);
+}
+
+/*
+ * Driver ioctl handler
+ */
+static int
+greth_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct greth_softc *sc = ifp->if_softc;
+ int error = 0;
+ struct ifreq *ifr;
+
+ 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:
+ greth_stop (sc);
+ break;
+
+ case IFF_UP:
+ greth_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ greth_stop (sc);
+ greth_init (sc);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ greth_stats (sc);
+ break;
+
+ /*
+ * Multicast commands: Enabling/disabling filtering of MAC addresses
+ */
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ ifr = (struct ifreq *)data;
+ if (command == SIOCADDMULTI) {
+ error = ether_addmulti(ifr, &sc->arpcom);
+ } else {
+ error = ether_delmulti(ifr, &sc->arpcom);
+ }
+ if (error == ENETRESET) {
+ error = greth_mac_filter_set(sc);
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+/*
+ * Attach an GRETH driver to the system
+ */
+static int
+greth_interface_driver_attach (
+ struct rtems_bsdnet_ifconfig *config,
+ int attach
+ )
+{
+ struct greth_softc *sc;
+ struct ifnet *ifp;
+ int mtu;
+ int unitNumber;
+ char *unitName;
+
+ /* parse driver name */
+ if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0)
+ return 0;
+
+ sc = config->drv_ctrl;
+ ifp = &sc->arpcom.ac_if;
+#ifdef GRETH_DEBUG
+ printf("GRETH[%d]: %s, sc %p, dev %p on %s\n", unitNumber, config->ip_address, sc, sc->dev, sc->dev->parent->dev->name);
+#endif
+ if (config->hardware_address)
+ {
+ memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
+ ETHER_ADDR_LEN);
+ }
+ else
+ {
+ memset (sc->arpcom.ac_enaddr, 0x08, ETHER_ADDR_LEN);
+ }
+
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ sc->acceptBroadcast = !config->ignore_broadcast;
+
+ /*
+ * Set up network interface values
+ */
+ ifp->if_softc = sc;
+ ifp->if_unit = unitNumber;
+ ifp->if_name = unitName;
+ ifp->if_mtu = mtu;
+ ifp->if_init = greth_init;
+ ifp->if_ioctl = greth_ioctl;
+ ifp->if_start = greth_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+ if (sc->mc_available)
+ ifp->if_flags |= IFF_MULTICAST;
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ /*
+ * Attach the interface
+ */
+ if_attach (ifp);
+ ether_ifattach (ifp);
+
+#ifdef GRETH_DEBUG
+ printf ("GRETH : driver has been attached\n");
+#endif
+ return 1;
+}
+
+/******************* Driver manager interface ***********************/
+
+/* Driver prototypes */
+int greth_register_io(rtems_device_major_number *m);
+int greth_device_init(struct greth_softc *sc);
+int network_interface_add(struct rtems_bsdnet_ifconfig *interface);
+
+#ifdef GRETH_INFO_AVAIL
+static int greth_info(
+ struct drvmgr_dev *dev,
+ void (*print_line)(void *p, char *str),
+ void *p, int argc, char *argv[]);
+#define GRETH_INFO_FUNC greth_info
+#else
+#define GRETH_INFO_FUNC NULL
+#endif
+
+int greth_init2(struct drvmgr_dev *dev);
+int greth_init3(struct drvmgr_dev *dev);
+
+struct drvmgr_drv_ops greth_ops =
+{
+ .init =
+ {
+ NULL,
+ greth_init2,
+ greth_init3,
+ NULL
+ },
+ .remove = NULL,
+ .info = GRETH_INFO_FUNC,
+};
+
+struct amba_dev_id greth_ids[] =
+{
+ {VENDOR_GAISLER, GAISLER_ETHMAC},
+ {0, 0} /* Mark end of table */
+};
+
+struct amba_drv_info greth_drv_info =
+{
+ {
+ DRVMGR_OBJ_DRV, /* Driver */
+ NULL, /* Next driver */
+ NULL, /* Device list */
+ DRIVER_AMBAPP_GAISLER_GRETH_ID, /* Driver ID */
+ "GRETH_DRV", /* Driver Name */
+ DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */
+ &greth_ops,
+ NULL, /* Funcs */
+ 0, /* No devices yet */
+ 0,
+ },
+ &greth_ids[0]
+};
+
+void greth_register_drv (void)
+{
+ DBG("Registering GRETH driver\n");
+ drvmgr_drv_register(&greth_drv_info.general);
+}
+
+int greth_init2(struct drvmgr_dev *dev)
+{
+ struct greth_softc *priv;
+
+ DBG("GRETH[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name);
+ priv = dev->priv = grlib_calloc(1, sizeof(*priv));
+ if ( !priv )
+ return DRVMGR_NOMEM;
+ priv->dev = dev;
+
+ /* This core will not find other cores, so we wait for init3() */
+
+ return DRVMGR_OK;
+}
+
+int greth_init3(struct drvmgr_dev *dev)
+{
+ struct greth_softc *sc;
+ struct rtems_bsdnet_ifconfig *ifp;
+ rtems_status_code status;
+
+ sc = dev->priv;
+ sprintf(sc->devName, "gr_eth%d", (dev->minor_drv+1));
+
+ /* Init GRETH device */
+ if ( greth_device_init(sc) ) {
+ printk("GRETH: Failed to init device\n");
+ return DRVMGR_FAIL;
+ }
+
+ /* Initialize Spin-lock for GRSPW Device. This is to protect
+ * CTRL and DMACTRL registers from ISR.
+ */
+ SPIN_INIT(&sc->devlock, sc->devName);
+
+ /* Register GRETH device as an Network interface */
+ ifp = grlib_calloc(1, sizeof(*ifp));
+
+ ifp->name = sc->devName;
+ ifp->drv_ctrl = sc;
+ ifp->attach = greth_interface_driver_attach;
+
+ status = network_interface_add(ifp);
+ if (status != 0) {
+ return DRVMGR_FAIL;
+ }
+
+ return DRVMGR_OK;
+}
+
+int greth_device_init(struct greth_softc *sc)
+{
+ struct amba_dev_info *ambadev;
+ struct ambapp_core *pnpinfo;
+ union drvmgr_key_value *value;
+ unsigned int speed;
+
+ /* Get device information from AMBA PnP information */
+ ambadev = (struct amba_dev_info *)sc->dev->businfo;
+ if ( ambadev == NULL ) {
+ return -1;
+ }
+ pnpinfo = &ambadev->info;
+ sc->regs = (greth_regs *)pnpinfo->apb_slv->start;
+ sc->minor = sc->dev->minor_drv;
+ sc->greth_rst = 1;
+
+ /* Remember EDCL enabled/disable state before reset */
+ sc->edcl_dis = sc->regs->ctrl & GRETH_CTRL_ED;
+
+ /* Default is to inherit EDCL Disable bit from HW. User can force En/Dis */
+ value = drvmgr_dev_key_get(sc->dev, "edclDis", DRVMGR_KT_INT);
+ if ( value ) {
+ /* Force EDCL mode. Has an effect later when GRETH+PHY is initialized */
+ if (value->i > 0) {
+ sc->edcl_dis = GRETH_CTRL_ED;
+ } else {
+ /* Default to avoid soft-reset the GRETH when EDCL is forced */
+ sc->edcl_dis = 0;
+ sc->greth_rst = 0;
+ }
+ }
+
+ /* let user control soft-reset of GRETH (for debug) */
+ value = drvmgr_dev_key_get(sc->dev, "soft-reset", DRVMGR_KT_INT);
+ if ( value) {
+ sc->greth_rst = value->i ? 1 : 0;
+ }
+
+ /* clear control register and reset NIC and keep current speed modes.
+ * This should be done as quick as possible during startup, this is to
+ * stop DMA transfers after a reboot.
+ *
+ * When EDCL is forced enabled reset is skipped, disabling RX/TX DMA is
+ * is enough during debug.
+ */
+ speed = sc->regs->ctrl & (GRETH_CTRL_GB | GRETH_CTRL_SP | GRETH_CTRL_FULLD);
+ sc->regs->ctrl = GRETH_CTRL_DD | GRETH_CTRL_ED | speed;
+ if (sc->greth_rst)
+ sc->regs->ctrl = GRETH_CTRL_RST | GRETH_CTRL_DD | GRETH_CTRL_ED | speed;
+ sc->regs->ctrl = GRETH_CTRL_DD | sc->edcl_dis | speed;
+
+ /* Configure driver by overriding default config with the bus resources
+ * configured by the user
+ */
+ sc->txbufs = 32;
+ sc->rxbufs = 32;
+ sc->phyaddr = -1;
+
+ value = drvmgr_dev_key_get(sc->dev, "txDescs", DRVMGR_KT_INT);
+ if ( value && (value->i <= 128) )
+ sc->txbufs = value->i;
+
+ value = drvmgr_dev_key_get(sc->dev, "rxDescs", DRVMGR_KT_INT);
+ if ( value && (value->i <= 128) )
+ sc->rxbufs = value->i;
+
+ value = drvmgr_dev_key_get(sc->dev, "phyAdr", DRVMGR_KT_INT);
+ if ( value && (value->i < 32) )
+ sc->phyaddr = value->i;
+
+ value = drvmgr_dev_key_get(sc->dev, "advModes", DRVMGR_KT_INT);
+ if ( value )
+ sc->advmodes = value->i;
+
+ /* Check if multicast support is available */
+ sc->mc_available = sc->regs->ctrl & GRETH_CTRL_MC;
+
+ return 0;
+}
+
+#ifdef GRETH_INFO_AVAIL
+static int greth_info(
+ struct drvmgr_dev *dev,
+ void (*print_line)(void *p, char *str),
+ void *p, int argc, char *argv[])
+{
+ struct greth_softc *sc;
+ char buf[64];
+
+ if (dev->priv == NULL)
+ return -DRVMGR_EINVAL;
+ sc = dev->priv;
+
+ sprintf(buf, "IFACE NAME: %s", sc->devName);
+ print_line(p, buf);
+ sprintf(buf, "GBIT MAC: %s", sc->gbit_mac ? "YES" : "NO");
+ print_line(p, buf);
+
+ return DRVMGR_OK;
+}
+#endif
+
+#endif
diff --git a/bsps/shared/grlib/net/network_interface_add.c b/bsps/shared/grlib/net/network_interface_add.c
new file mode 100644
index 0000000..0111374
--- /dev/null
+++ b/bsps/shared/grlib/net/network_interface_add.c
@@ -0,0 +1,62 @@
+/* Network interface register help function
+ *
+ * COPYRIGHT (c) 2008.
+ * Cobham Gaisler AB.
+ *
+ * This function adds a network interface to the
+ * rtems_bsdnet_config.ifconfig linked list of interfaces.
+ * The interface configuration is taken from the user defined
+ * array interface_configs. This function is useful for PnP
+ * systems when an unknown number of interfaces are available.
+ *
+ * The license and distribution terms for this file may be
+ * found in found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <rtems/rtems_bsdnet.h>
+#include <stdio.h>
+
+#include <grlib/network_interface_add.h>
+
+extern struct rtems_bsdnet_config rtems_bsdnet_config;
+
+/* Number of interfaces taken */
+int network_interface_cnt = 0;
+
+int network_interface_add(struct rtems_bsdnet_ifconfig *interface)
+{
+ struct ethernet_config *cfg = NULL;
+ int i, last_entry = 1;
+
+ /* Init interface description */
+ interface->next = NULL;
+
+ cfg = &interface_configs[network_interface_cnt];
+ for(i=0; i<6; i++) {
+ if ( cfg->eth_adr[i] != 0 ) {
+ last_entry = 0;
+ break;
+ }
+ }
+ /* Do we have a valid configuration? */
+ if ( last_entry == 0 ) {
+ cfg = &interface_configs[network_interface_cnt];
+
+ interface->ip_address = cfg->ip_addr;
+ interface->ip_netmask = cfg->ip_netmask;
+ interface->hardware_address = cfg->eth_adr;
+
+ network_interface_cnt++;
+ } else {
+ interface->ip_address = NULL;
+ interface->ip_netmask = NULL;
+ interface->hardware_address = NULL;
+ }
+
+ /* Insert interface first into list */
+ interface->next = rtems_bsdnet_config.ifconfig;
+ rtems_bsdnet_config.ifconfig = interface;
+
+ return 0;
+}
diff --git a/bsps/shared/net/README b/bsps/shared/net/README
new file mode 100644
index 0000000..ecb996e
--- /dev/null
+++ b/bsps/shared/net/README
@@ -0,0 +1,12 @@
+This is the network interface controller portion of the libchip library.
+This directory contains the source code for reusable TCP/IP network driver
+support code. Each driver has its own configuration table and its
+chip specific attach routine must be called by a board specific
+attach routine. The board specific chip routine passes the chip
+configuration and network configuration to the resuable device driver.
+
+The reusable chip drivers do not directly access the controller.
+They access the registers on the controller via a set of
+functions which are provided by the BSP. These functions set and get
+general registers and data buffers.
+
diff --git a/bsps/shared/net/README.3com b/bsps/shared/net/README.3com
new file mode 100644
index 0000000..b67061d
--- /dev/null
+++ b/bsps/shared/net/README.3com
@@ -0,0 +1,3 @@
+#
+# README.3com
+#
diff --git a/bsps/shared/net/README.cs8900 b/bsps/shared/net/README.cs8900
new file mode 100644
index 0000000..ecd5752
--- /dev/null
+++ b/bsps/shared/net/README.cs8900
@@ -0,0 +1,26 @@
+Target Support
+==============
+
+The target is required to provide the low level support routines as
+listed in the Configuration section of this file.
+
+The file cs8900.[ch].bsp are an example BSP files for DIMMPC target.
+
+Conditionals
+============
+CS8900_DATA_BUS_SWAPPED - XXX
+
+CS8900_TRACE - XXX
+
+CS8900_VERBOSE - XXX
+
+Todo
+====
++ Build two versions -- one with swapped, one without.
+
++ Document conditionals.
+
+Configuration
+=============
+See the cs8900.h header file for the documentation.
+
diff --git a/bsps/shared/net/README.dec21140 b/bsps/shared/net/README.dec21140
new file mode 100644
index 0000000..f07bec7
--- /dev/null
+++ b/bsps/shared/net/README.dec21140
@@ -0,0 +1,116 @@
+This TULIP driver can be used on BSPs that support PCI bus.
+
+It can handle any DEC21140 and DEC21143 based Ethernet controller
+although the DEC21143 support has only been tested on Intel.
+
+It works on big or little endian target.
+
+The DEC21140 has been tested with powerpc/mcp750 BSP (OnBoard Ethernet
+controller) and i386/pc386 BSP (D-Link DFE-500TX Ethernet board).
+
+The DEC21143 has been tested only on the i386/pc386 using
+the Kingston KNE100TX with 21143PD chip.
+
+*****************************************************************
+******** ***************
+******** tests with ttcp benchmark for DEC driver ***************
+******** optimization ***************
+******** ***************
+*****************************************************************
+
+
+LINUX -> LINUX-ix86
+
+Transmitter :
+
+ttcp-t: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp -> genesis
+ttcp-t: 16777216 bytes in 1.87 real seconds = 8775.25 KB/sec +++
+ttcp-t: 2048 I/O calls, msec/call = 0.93, calls/sec = 1096.91
+ttcp-t: 0.0user 0.9sys 0:01real 51% 0i+0d 0maxrss 0+2pf 0+0csw
+
+Receiver :
+
+ttcp-r: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp
+ttcp-r: 16777216 bytes in 1.88 real seconds = 8706.53 KB/sec +++
+ttcp-r: 10802 I/O calls, msec/call = 0.18, calls/sec = 5740.23
+ttcp-r: 0.0user 0.2sys 0:01real 13% 0i+0d 0maxrss 0+2pf 0+0csw
+
+==============================================================
+==============================================================
+==============================================================
+
+LINUX -> RTEMS-ix86 with tulip driver from pc386 bsp
+
+Transmitter :
+
+ttcp-t: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp -> neil-young-100
+ttcp-t: 16777216 bytes in 1.98 real seconds = 8294.76 KB/sec +++
+ttcp-t: 2048 I/O calls, msec/call = 0.99, calls/sec = 1036.85
+ttcp-t: 0.0user 0.1sys 0:01real 9% 0i+0d 0maxrss 0+2pf 0+0csw
+
+Receiver :
+
+ttcp-r: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp
+ttcp-r: 16777216 bytes in 2.03 real seconds = 8065.14 KB/sec +++
+ttcp-r: 3088 I/O calls, msec/call = 0.67, calls/sec = 1520.09
+ttcp-r: 0.0user 2.0sys 0:02real 100% 0i+0d 0maxrss 0+0pf 0+0csw
+
+==============================================================
+==============================================================
+==============================================================
+
+RTEMS-ix86 with tulip driver from pc386 bsp -> LINUX
+
+Transmitter :
+
+ttcp-t: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp -> 194.2.81.126
+ttcp-t: 16777216 bytes in 2.76 real seconds = 5938.77 KB/sec +++
+ttcp-t: 2048 I/O calls, msec/call = 1.38, calls/sec = 742.35
+ttcp-t: 0.0user 2.5sys 0:02real 100% 0i+0d 0maxrss 0+0pf 0+0csw
+
+Receiver :
+
+ttcp-r: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp
+ttcp-r: 16777216 bytes in 2.75 real seconds = 5948.53 KB/sec +++
+ttcp-r: 11349 I/O calls, msec/call = 0.25, calls/sec = 4120.48
+ttcp-r: 0.0user 0.1sys 0:02real 6% 0i+0d 0maxrss 0+2pf 0+0csw
+
+==============================================================
+==============================================================
+==============================================================
+
+LINUX -> RTEMS-ix86 with optimized tulip driver
+
+Transmitter :
+
+ttcp-t: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp -> neil-young-100
+ttcp-t: 16777216 bytes in 1.73 real seconds = 9470.13 KB/sec +++
+ttcp-t: 2048 I/O calls, msec/call = 0.87, calls/sec = 1183.77
+ttcp-t: 0.0user 0.1sys 0:01real 6% 0i+0d 0maxrss 0+2pf 0+0csw
+
+Receiver :
+
+ttcp-r: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp
+ttcp-r: 16777216 bytes in 1.76 real seconds = 9315.33 KB/sec +++
+ttcp-r: 4558 I/O calls, msec/call = 0.40, calls/sec = 2591.51
+ttcp-r: 0.0user 1.7sys 0:01real 100% 0i+0d 0maxrss 0+0pf 0+0csw
+
+==============================================================
+==============================================================
+==============================================================
+
+RTEMS-ix86 with optimized tulip driver -> LINUX
+
+Transmitter :
+
+ttcp-t: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp -> 194.2.81.126
+ttcp-t: 16777216 bytes in 2.09 real seconds = 7847.80 KB/sec +++
+ttcp-t: 2048 I/O calls, msec/call = 1.04, calls/sec = 980.98
+ttcp-t: 0.0user 2.0sys 0:02real 100% 0i+0d 0maxrss 0+0pf 0+0csw
+
+Receiver :
+
+ttcp-r: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp
+ttcp-r: 16777216 bytes in 2.08 real seconds = 7874.23 KB/sec +++
+ttcp-r: 11404 I/O calls, msec/call = 0.19, calls/sec = 5480.82
+ttcp-r: 0.0user 0.1sys 0:02real 8% 0i+0d 0maxrss 0+2pf 0+0csw
diff --git a/bsps/shared/net/README.i82586 b/bsps/shared/net/README.i82586
new file mode 100644
index 0000000..a099036
--- /dev/null
+++ b/bsps/shared/net/README.i82586
@@ -0,0 +1 @@
+TBD
diff --git a/bsps/shared/net/README.open_eth b/bsps/shared/net/README.open_eth
new file mode 100644
index 0000000..af9d888
--- /dev/null
+++ b/bsps/shared/net/README.open_eth
@@ -0,0 +1,72 @@
+Driver for opencores ethernet MAC - README
+------------------------------------------
+
+The device name for the driver is 'open_eth1', the attach
+function for the leon bsp is rtems_leon_open_eth_driver_attach().
+
+No cache flushing is made when a frame is received. On leon,
+this means that cache snooping must be configured in the
+vhdl model and enabled by software.
+
+TX interrupts are not used and masked in the interrupt mask
+register.
+
+For now, only 10 Mbit/s half-duplex is supported.
+100 Mbit/s operations does not work reliably, the transmitter
+locks up or skips frames. Seems to depend on the TX fifo
+implementation in the opencores MAC. Send a mail to
+jiri@gaisler.com if you know how to fix this.
+
+Tested only on leon, using the GR-PCI-XC2V board @ 40 MHz.
+Output from ttcp receiving 1 Mbyte file:
+
+>>> ttcp -r -s
+ttcp-r: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp
+ttcp-r: socket
+ttcp-r: accept from 192.168.0.2
+ttcp-r: 1145339 bytes in 1.18 real seconds = 947.88 KB/sec +++
+ttcp-r: 792 I/O calls, msec/call = 1.53, calls/sec = 671.19
+ttcp-r: 0.0user 1.1sys 0:01real 100% 0i+0d 0maxrss 0+0pf 0+0csw
+************ MBUF STATISTICS ************
+mbufs:1024 clusters: 128 free: 112
+drops: 0 waits: 0 drains: 0
+ free:1007 data:17 header:0 socket:0
+ pcb:0 rtable:0 htable:0 atable:0
+ soname:0 soopts:0 ftable:0 rights:0
+ ifaddr:0 control:0 oobdata:0
+
+************ INTERFACE STATISTICS ************
+***** open_eth1 *****
+Address:192.168.0.66 Broadcast Address:192.168.0.255
+Flags: Up Broadcast Running Simplex
+Send queue limit:50 length:0 Dropped:0
+ Rx Packets:796 Rx Interrupts:796 Length:0
+ Bad CRC:0 Overrun:0 Miss:0
+ Tx Interrupts:0 Deferred:0 Missed Hearbeat:0
+ No Carrier:0 Retransmit Limit:0 Late Collision:0
+ Underrun:0 Raw output wait:0
+
+************ IP Statistics ************
+ total packets received 795
+ datagrams delivered to upper level 795
+ total ip packets generated here 401
+
+************ TCP Statistics ************
+ connections accepted 1
+ connections established 1
+ conn. closed (includes drops) 1
+ segs where we tried to get rtt 2
+ times we succeeded 2
+ delayed acks sent 4
+ total packets sent 401
+ ack-only packets sent 6
+ window update-only packets sent 394
+ control (SYN|FIN|RST) packets sent 1
+ total packets received 795
+ packets received in sequence 792
+ bytes received in sequence 1145339
+ rcvd ack packets 2
+ bytes acked by rcvd acks 2
+ times hdr predict ok for data pkts 791
+
+
diff --git a/bsps/shared/net/README.sonic b/bsps/shared/net/README.sonic
new file mode 100644
index 0000000..b2478b5
--- /dev/null
+++ b/bsps/shared/net/README.sonic
@@ -0,0 +1,135 @@
+This SONIC driver does not make any attempt to support the SONIC chip
+in any of the following modes:
+
+ + 16-bit
+ + little endian
+
+It does not attempt to handle SONIC's older than Revision C. There is
+a bug in chips before that revision that must be handled in the driver.
+
+The configuration table should be discussed here but if you look in the
+include file for the sonic, it is reasonably obvious. :)
+
+The performance impact of transforming this driver into libchip format
+was minimal.
+
+The powerpc/dmv177 BSP used this driver and the following should
+serve as an example configuration table. This BSP was obsoleted after
+the 4.6 release series so the code is included here.
+
+======================================================================
+
+/*
+ * DMV177 SONIC Configuration Information
+ *
+ * References:
+ *
+ * 1) SVME/DMV-171 Single Board Computer Documentation Package, #805905,
+ * DY 4 Systems Inc., Kanata, Ontario, September, 1996.
+ */
+
+#include <bsp.h>
+#include <rtems/rtems_bsdnet.h>
+#include <libchip/sonic.h>
+
+void dmv177_sonic_write_register(
+ void *base,
+ unsigned32 regno,
+ unsigned32 value
+)
+{
+ volatile unsigned32 *p = base;
+
+#if (SONIC_DEBUG & SONIC_DEBUG_PRINT_REGISTERS)
+ printf( "%p Write 0x%04x to %s (0x%02x)\n",
+ &p[regno], value, SONIC_Reg_name[regno], regno );
+ fflush( stdout );
+#endif
+ p[regno] = value;
+}
+
+unsigned32 dmv177_sonic_read_register(
+ void *base,
+ unsigned32 regno
+)
+{
+ volatile unsigned32 *p = base;
+ unsigned32 value;
+
+ value = p[regno];
+#if (SONIC_DEBUG & SONIC_DEBUG_PRINT_REGISTERS)
+ printf( "%p Read 0x%04x from %s (0x%02x)\n",
+ &p[regno], value, SONIC_Reg_name[regno], regno );
+ fflush( stdout );
+#endif
+ return value;
+}
+
+/*
+ * Default sizes of transmit and receive descriptor areas
+ */
+#define RDA_COUNT 20 /* 20 */
+#define TDA_COUNT 20 /* 10 */
+
+/*
+ * Default device configuration register values
+ * Conservative, generic values.
+ * DCR:
+ * No extended bus mode
+ * Unlatched bus retry
+ * Programmable outputs unused
+ * Asynchronous bus mode
+ * User definable pins unused
+ * No wait states (access time controlled by DTACK*)
+ * 32-bit DMA
+ * Empty/Fill DMA mode
+ * Maximum Transmit/Receive FIFO
+ * DC2:
+ * Extended programmable outputs unused
+ * Normal HOLD request
+ * Packet compress output unused
+ * No reject on CAM match
+ */
+#define SONIC_DCR \
+ (DCR_DW32 | DCR_WAIT0 | DCR_PO0 | DCR_PO1 | DCR_RFT24 | DCR_TFT28)
+#ifndef SONIC_DCR
+# define SONIC_DCR (DCR_DW32 | DCR_TFT28)
+#endif
+#ifndef SONIC_DC2
+# define SONIC_DC2 (0)
+#endif
+
+/*
+ * Default location of device registers
+ */
+#ifndef SONIC_BASE_ADDRESS
+# define SONIC_BASE_ADDRESS 0xF3000000
+# warning "Using default SONIC_BASE_ADDRESS."
+#endif
+
+/*
+ * Default interrupt vector
+ */
+#ifndef SONIC_VECTOR
+# define SONIC_VECTOR 1
+# warning "Using default SONIC_VECTOR."
+#endif
+
+sonic_configuration_t dmv177_sonic_configuration = {
+ SONIC_BASE_ADDRESS, /* base address */
+ SONIC_VECTOR, /* vector number */
+ SONIC_DCR, /* DCR register value */
+ SONIC_DC2, /* DC2 register value */
+ TDA_COUNT, /* number of transmit descriptors */
+ RDA_COUNT, /* number of receive descriptors */
+ dmv177_sonic_write_register,
+ dmv177_sonic_read_register
+};
+
+int rtems_dmv177_sonic_driver_attach(struct rtems_bsdnet_ifconfig *config)
+{
+ return rtems_sonic_driver_attach( config, &dmv177_sonic_configuration );
+
+}
+
+======================================================================
diff --git a/bsps/shared/net/README.tulipclone b/bsps/shared/net/README.tulipclone
new file mode 100644
index 0000000..90332bf
--- /dev/null
+++ b/bsps/shared/net/README.tulipclone
@@ -0,0 +1,101 @@
+*****************************************************************
+******** ***************
+******** ttcp benchmark tests of dec2114x driver ***************
+******** adapted from FreeBSD's if_dc.c for RTEMS ***************
+******** by: Daron Chabot (12/15/03), ***************
+******** <daron@nucleus.usask.ca> ***************
+*****************************************************************
+
+Test Equipment:
+-----------------------
+- Intel 450 MHz P3's
+- RH Linux v7.3, 2.4.7-10 kernel, D-Link DFE-530TX (via-rhine)
+- RTEMS rtems-ss-20030703, pc386 BSP, Linksys LNE100TX ( actually,
+a cleverly disguised ADMtek Centaur, aka "Comet").
+- the PCs were directly connected ( RJ-45 X-over cable ) on a class C
+subnet (private)
+
+NOTE:
+-----------------------
+- the following lines must be added to the BSP's "bsp.h" file, or
+inserted into an application header (e.g. a "network_config.h" file, or
+similar):
+
+extern int rtems_dc_driver_attach(struct rtems_bsdnet_ifconfig *, int);
+#define BSP_DEC_NETWORK_DRIVER_NAME "tl1" /* "tl" as in "tulip-clone" */
+#define BSP_DEC_NETWORK_DRIVER_ATTACH rtems_dc_driver_attach
+
+
+**************************
+Linux Tx ----> RTEMS Rx: *
+**************************
+TRANSMITTER:
+ttcp-t: buflen=8192, nbuf=65536, align=16384/0, port=5001 tcp -> rtems
+ttcp-t: 536870912 bytes in 45.72 real seconds = 11468.54 KB/sec +++
+ttcp-t: 536870912 bytes in 6.87 CPU seconds = 76315.57 KB/cpu sec
+ttcp-t: 65536 I/O calls, msec/call = 0.71, calls/sec = 1433.57
+ttcp-t: 0.1user 6.7sys 0:45real 15% 0i+0d 0maxrss 0+2pf 0+0csw
+
+
+RECEIVER:
+ttcp-r: buflen=8192, nbuf=65536, align=16384/0, port=5001 tcp
+ttcp-r: 536870912 bytes in 45.72 real seconds = 11467.37 KB/sec +++
+ttcp-r: 536870912 bytes in 45.87 CPU seconds = 11467.37 KB/cpu sec
+ttcp-r: 370837 I/O calls, msec/call = 0.13, calls/sec = 8111.05
+ttcp-r: 0.0user 45.7sys 0:45real 100% 0i+0d 0maxrss 0+0pf 0+0csw
+
+
+
+**************************
+RTEMS Tx ----> Linux Rx: *
+**************************
+TRANSMITTER:
+ttcp-t: buflen=8192, nbuf=65536, align=16384/0, port=5001 tcp ->192.168.1.1
+ttcp-t: 536870912 bytes in 46.22 real seconds = 11343.31 KB/sec +++
+ttcp-t: 536870912 bytes in 46.22 CPU seconds = 11343.31 KB/cpu sec
+ttcp-t: 65536 I/O calls, msec/call = 0.72, calls/sec = 1417.91
+ttcp-t: 0.0user 46.2sys 0:46real 100% 0i+0d 0maxrss 0+0pf 0+0csw
+
+
+
+
+RECEIVER:
+ttcp-r: buflen=8192, nbuf=65536, align=16384/0, port=5001 tcp
+ttcp-r: 536870912 bytes in 46.22 real seconds = 11343.05 KB/sec +++
+ttcp-r: 536870912 bytes in 11.60 CPU seconds = 45197.24 KB/cpu sec
+ttcp-r: 356183 I/O calls, msec/call = 0.13, calls/sec = 7706.07
+ttcp-r: 0.6user 10.9sys 0:46real 25% 0i+0d 0maxrss 0+2pf 0+0csw
+
+
+
+****************************************************************************
+****************************************************************************
+****************************************************************************
+******************* Test with 40kB buffer size *****************************
+****************************************************************************
+****************************************************************************
+****************************************************************************
+
+
+**************************
+RTEMS Tx ----> Linux Rx: *
+**************************
+TRANSMITTER:
+ttcp-t: buflen=40960, nbuf=13107, align=16384/0, port=5001 tcp -> 192.168.1.1
+ttcp-t: 536862720 bytes in 46.23 real seconds = 11340.69 KB/sec +++
+ttcp-t: 536862720 bytes in 46.23 CPU seconds = 11340.69 KB/cpu sec
+ttcp-t: 13107 I/O calls, msec/call = 3.61, calls/sec = 283.52
+ttcp-t: 0.0user 46.2sys 0:46real 100% 0i+0d 0maxrss 0+0pf 0+0csw
+
+
+RECEIVER:
+ttcp-r: buflen=40960, nbuf=13107, align=16384/0, port=5001 tcp
+ttcp-r: 536862720 bytes in 46.23 real seconds = 11339.54 KB/sec +++
+ttcp-r: 536862720 bytes in 10.70 CPU seconds = 48998.13 KB/cpu sec
+ttcp-r: 355970 I/O calls, msec/call = 0.13, calls/sec = 7699.20
+ttcp-r: 0.5user 10.1sys 0:46real 23% 0i+0d 0maxrss 0+5pf 0+0csw
+
+
+
+****************************************************************************
+****************************************************************************
diff --git a/bsps/shared/net/cs8900.c b/bsps/shared/net/cs8900.c
new file mode 100644
index 0000000..452a33a
--- /dev/null
+++ b/bsps/shared/net/cs8900.c
@@ -0,0 +1,1216 @@
+/*
+ ------------------------------------------------------------------------
+
+ Copyright Cybertec Pty Ltd, 2000
+ All rights reserved Cybertec Pty Ltd, 2000
+
+ Port to the DIMM PC copyright (c) 2004 Angelo Fraietta
+ This project has been assisted by the Commonwealth Government
+ through the Australia Council, its arts funding and advisory body.
+
+ COPYRIGHT (c) 1989-1998.
+ On-Line Applications Research Corporation (OAR).
+
+ The license and distribution terms for this file may be
+ found in the file LICENSE in this distribution or at
+ http://www.rtems.org/license/LICENSE.
+
+ ------------------------------------------------------------------------
+
+ CS8900 RTEMS driver.
+
+ See the header file for details.
+
+*/
+
+#include <machine/rtems-bsd-kernel-space.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <libchip/cs8900.h>
+
+/*
+ * We expect to be able to read a complete packet into an mbuf.
+ */
+
+#if (MCLBYTES < 1520)
+#error "CS8900 Driver must have MCLBYTES >= 1520"
+#endif
+
+/*
+ * Task event usage.
+ */
+
+#define CS8900_RX_OK_EVENT RTEMS_EVENT_1
+#define CS8900_TX_START_EVENT RTEMS_EVENT_1
+#define CS8900_TX_OK_EVENT RTEMS_EVENT_2
+#define CS8900_TX_WAIT_EVENT RTEMS_EVENT_3
+
+/*
+ * IO Packet Page inteface.
+ */
+
+static inline unsigned short
+io_pp_get_reg_16 (cs8900_device *cs, unsigned short reg)
+{
+ rtems_interrupt_level level;
+ unsigned short data;
+ rtems_interrupt_disable (level);
+ cs8900_io_set_reg (cs, CS8900_IO_PACKET_PAGE_PTR,
+ 0x3000 | CS8900_PPP_AUTO_INCREMENT | reg);
+ data = cs8900_io_get_reg (cs, CS8900_IO_PP_DATA_PORT0);
+ rtems_interrupt_enable (level);
+ return data;
+}
+
+static inline uint32_t
+io_pp_get_reg_32 (cs8900_device *cs, uint16_t reg)
+{
+ rtems_interrupt_level level;
+ uint32_t data;
+ rtems_interrupt_disable (level);
+ cs8900_io_set_reg (cs, CS8900_IO_PACKET_PAGE_PTR,
+ 0x3000 | CS8900_PPP_AUTO_INCREMENT | reg);
+ data = cs8900_io_get_reg (cs, CS8900_IO_PP_DATA_PORT0);
+ data <<= 16;
+ data |= cs8900_io_get_reg (cs, CS8900_IO_PP_DATA_PORT1);
+ rtems_interrupt_enable (level);
+ return data;
+}
+
+static inline void
+io_pp_set_reg_16 (cs8900_device *cs, unsigned short reg, unsigned short data)
+{
+ rtems_interrupt_level level;
+ rtems_interrupt_disable (level);
+ cs8900_io_set_reg (cs, CS8900_IO_PACKET_PAGE_PTR,
+ 0x3000 | CS8900_PPP_AUTO_INCREMENT | reg);
+ cs8900_io_set_reg (cs, CS8900_IO_PP_DATA_PORT0, data);
+ rtems_interrupt_enable (level);
+}
+
+static inline void
+io_pp_set_reg_32 (cs8900_device *cs, unsigned short reg, unsigned long data)
+{
+ cs8900_io_set_reg (cs, CS8900_IO_PACKET_PAGE_PTR,
+ 0x3000 | CS8900_PPP_AUTO_INCREMENT | reg);
+ cs8900_io_set_reg (cs, CS8900_IO_PP_DATA_PORT0, data >> 16);
+ cs8900_io_set_reg (cs, CS8900_IO_PP_DATA_PORT1, data);
+}
+
+static inline void
+io_pp_bit_set_reg_16 (cs8900_device *cs, unsigned short reg, unsigned short mask)
+{
+ rtems_interrupt_level level;
+ rtems_interrupt_disable (level);
+ io_pp_set_reg_16 (cs, reg, io_pp_get_reg_16 (cs, reg) | mask);
+ rtems_interrupt_enable (level);
+}
+
+static inline void
+io_pp_bit_clear_reg_16 (cs8900_device *cs, unsigned short reg, unsigned short mask)
+{
+ rtems_interrupt_level level;
+ rtems_interrupt_disable (level);
+ io_pp_set_reg_16 (cs, reg, io_pp_get_reg_16 (cs, reg) & ~mask);
+ rtems_interrupt_enable (level);
+}
+
+/*
+ * Memory Mapped Packet Page interface.
+ *
+ * If the BSP does not configure mem_base use the I/O register accesses.
+ */
+
+static inline unsigned short
+mem_pp_get_reg (cs8900_device *cs, unsigned short reg)
+{
+ if (!cs->mem_base)
+ return io_pp_get_reg_16 (cs, reg);
+ return cs8900_mem_get_reg (cs, reg);
+}
+
+static inline void
+mem_pp_set_reg (cs8900_device *cs, unsigned short reg, unsigned short data)
+{
+ if (!cs->mem_base)
+ io_pp_set_reg_16 (cs, reg, data);
+ else
+ cs8900_mem_set_reg (cs, reg, data);
+}
+
+static inline void
+mem_pp_bit_set_reg (cs8900_device *cs, unsigned short reg, unsigned short mask)
+{
+ if (!cs->mem_base)
+ io_pp_bit_set_reg_16 (cs, reg, mask);
+ else
+ {
+ rtems_interrupt_level level;
+ rtems_interrupt_disable (level);
+ mem_pp_set_reg (cs, reg, mem_pp_get_reg (cs, reg) | mask);
+ rtems_interrupt_enable (level);
+ }
+}
+
+static inline void
+mem_pp_bit_clear_reg (cs8900_device *cs, unsigned short reg, unsigned short mask)
+{
+ if (!cs->mem_base)
+ io_pp_bit_clear_reg_16 (cs, reg, mask);
+ else
+ {
+ rtems_interrupt_level level;
+ rtems_interrupt_disable (level);
+ mem_pp_set_reg (cs, reg, mem_pp_get_reg (cs, reg) & ~mask);
+ rtems_interrupt_enable (level);
+ }
+}
+
+/*
+ * Trace defines and control structures.
+ */
+
+#define CS8900_T_INT (0)
+#define CS8900_T_RX_OK (1)
+#define CS8900_T_RX_DROPPED (2)
+#define CS8900_T_NO_MBUF (3)
+#define CS8900_T_NO_CLUSTERS (4)
+#define CS8900_T_RX_BEGIN (5)
+#define CS8900_T_RX_END (6)
+
+#if CS8900_TRACE
+
+static const char *cs8900_trace_labels[] =
+{
+ "int",
+ "rx ok",
+ "rx dropped",
+ "no mbuf",
+ "no clusters",
+ "rx begin",
+ "rx end"
+};
+
+/*
+ * Assumes a micro-second timer such as the Coldfire.
+ */
+
+uint32_t rtems_read_timer ();
+
+static inline void
+cs8900_trace (cs8900_device *cs, unsigned short key, unsigned long var)
+{
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable (level);
+
+ if (cs->trace_in < CS8900_TRACE_SIZE)
+ {
+ cs->trace_key[cs->trace_in] = key;
+ cs->trace_var[cs->trace_in] = var;
+ cs->trace_time[cs->trace_in] = rtems_read_timer ();
+ cs->trace_in++;
+ }
+
+ rtems_interrupt_enable (level);
+}
+#else
+#define cs8900_trace(c, k, v)
+#endif
+
+void cs8900_get_mac_addr (cs8900_device *cs, unsigned char *mac_address)
+{
+ unsigned short ma;
+
+ /*
+ * Only ever use IO calls for this function as it can be
+ * called before memory mode has been enabled.
+ */
+
+ ma = io_pp_get_reg_16 (cs, CS8900_PP_IA);
+ mac_address[0] = ma >> 8;
+ mac_address[1] = ma;
+
+ ma = io_pp_get_reg_16 (cs, CS8900_PP_IA + 2);
+ mac_address[2] = ma >> 8;
+ mac_address[3] = ma;
+
+ ma = io_pp_get_reg_16 (cs, CS8900_PP_IA + 4);
+ mac_address[4] = ma >> 8;
+ mac_address[5] = ma;
+}
+
+/*
+ * Bring the chip online.
+ */
+
+static void
+cs8900_hardware_init (cs8900_device *cs)
+{
+ unsigned long prod_id;
+ unsigned short status;
+
+ /*
+ * Do nothing while the device is calibrating and checking the EEPROM.
+ * We must wait 20msecs.
+ */
+
+ io_pp_bit_set_reg_16 (cs, CS8900_PP_SelfCTL, CS8900_SELF_CTRL_RESET);
+
+ rtems_task_wake_after (RTEMS_MILLISECONDS_TO_TICKS (20));
+
+ status = io_pp_get_reg_16 (cs, CS8900_PP_SelfST);
+ if (status == 0) {
+ printf("Reading status register again\n");
+ status = io_pp_get_reg_16 (cs, CS8900_PP_SelfST);
+ }
+
+ if (((status & CS8900_SELF_STATUS_INITD) == 0) ||
+ ((status & CS8900_SELF_STATUS_INITD) &&
+ (status & CS8900_SELF_STATUS_EEPROM_PRESENT) &&
+ (status & CS8900_SELF_STATUS_SIBUST)))
+ {
+ printf ("CS8900: %s. Initialisation aborted.\n",
+ (status & CS8900_SELF_STATUS_INITD) ?
+ "EEPROM read/write failed to complete" :
+ "Failed to complete to reset");
+ return;
+ }
+
+ /* Set the RX queue size if not set by the BSP. */
+
+ if (cs->rx_queue_size == 0)
+ cs->rx_queue_size = 10;
+
+ /* Probe the device for its ID */
+
+ prod_id = io_pp_get_reg_32 (cs, CS8900_PP_PROD_ID);
+
+ if ((prod_id >> 16) != CS8900_ESIA_ID)
+ {
+ printf ("CS8900: Invalid EISA ID, read product code 0x%08lx\n", prod_id);
+ return;
+ }
+
+ if ((prod_id & 0x000000ff) != 0)
+ {
+ printf ("CS8900: Unsupported product id, read product code 0x%08lx\n",
+ prod_id);
+ return;
+ }
+
+ printf ("CS8900 Rev %ld, %s, %s.\n",
+ (prod_id >> 8) & 0x1f,
+ status & CS8900_SELF_STATUS_3_3_V ? "3.3V" : "5.0V",
+ status & CS8900_SELF_STATUS_EEPROM_PRESENT ?
+ "EEPROM present" : "no EEPROM");
+
+ /*
+ * Switch to memory base accesses as they are faster. No indirect access.
+ */
+
+ if (cs->mem_base)
+ {
+ io_pp_set_reg_16 (cs, CS8900_PP_MEM_BASE, cs->mem_base);
+ io_pp_set_reg_16 (cs, CS8900_PP_MEM_BASE + 2, (cs->mem_base >> 16) & 0xf);
+
+ io_pp_set_reg_16 (cs,
+ CS8900_PP_BusCTL,
+ CS8900_BUS_CTRL_RESET_RX_DMA |
+ CS8900_BUS_CTRL_USE_SA |
+ CS8900_BUS_CTRL_MEMORY_ENABLE);
+ io_pp_set_reg_16 (cs,
+ CS8900_PP_BusCTL,
+ CS8900_BUS_CTRL_USE_SA |
+ CS8900_BUS_CTRL_MEMORY_ENABLE);
+ }
+
+ /*
+ * We are now in memory mapped mode.
+ */
+
+ /*
+ * Program the Line Control register with the mode we want.
+ *
+ * No auto detect support at the moment. Only 10BaseT.
+ */
+
+ mem_pp_set_reg (cs, CS8900_PP_LineCFG, CS8900_LINE_CTRL_10BASET);
+
+ /*
+ * Ask the user for the MAC address, the program into the device.
+ */
+
+#define MACO(o) cs->arpcom.ac_enaddr[o]
+
+ mem_pp_set_reg (cs, CS8900_PP_IA,
+ (((unsigned int) MACO (1)) << 8) |
+ ((unsigned int) MACO (0)));
+ mem_pp_set_reg (cs, CS8900_PP_IA + 2,
+ (((unsigned int) MACO (3)) << 8) |
+ ((unsigned int) MACO (2)));
+ mem_pp_set_reg (cs, CS8900_PP_IA + 4,
+ (((unsigned int) MACO (5)) << 8) |
+ ((unsigned int) MACO (4)));
+
+ /*
+ * Set the Buffer configuration.
+ */
+
+ mem_pp_set_reg (cs, CS8900_PP_BufCFG,
+ CS8900_BUFFER_CONFIG_RDY_FOR_TX |
+ CS8900_BUFFER_CONFIG_TX_UNDERRUN |
+ CS8900_BUFFER_CONFIG_TX_COL_OVF |
+ CS8900_BUFFER_CONFIG_RX_MISSED_OVF);
+
+ /*
+ * Set the Receiver configuration.
+ */
+
+ mem_pp_set_reg (cs, CS8900_PP_RxCFG,
+ CS8900_RX_CONFIG_RX_OK |
+ CS8900_RX_CONFIG_CRC_ERROR |
+ CS8900_RX_CONFIG_RUNT|
+ CS8900_RX_CONFIG_EXTRA_DATA);
+
+ /*
+ * Set the Receiver control.
+ */
+
+ mem_pp_set_reg (cs, CS8900_PP_RxCTL,
+ CS8900_RX_CTRL_RX_OK |
+ CS8900_RX_CTRL_MULTICAST |
+ CS8900_RX_CTRL_INDIVIDUAL |
+ CS8900_RX_CTRL_BROADCAST);
+
+ /*
+ * Set the Transmitter configuration.
+ */
+
+ mem_pp_set_reg (cs, CS8900_PP_TxCFG,
+ CS8900_TX_CONFIG_TX_OK |
+ CS8900_TX_CONFIG_OUT_OF_WINDOW |
+ CS8900_TX_CONFIG_JABBER |
+ CS8900_TX_CONFIG_16_COLLISION);
+
+ /*
+ * Attach the interrupt handler.
+ */
+
+ cs8900_attach_interrupt (cs);
+
+ /*
+ * Program the interrupt level we require then enable interrupts.
+ *
+ * Note, this will need to change to support other levels.
+ */
+
+ mem_pp_set_reg (cs, CS8900_PP_INT, cs->irq_level & 3);
+
+ mem_pp_bit_set_reg (cs, CS8900_PP_BusCTL,
+ CS8900_BUS_CTRL_ENABLE_INT);
+}
+
+rtems_isr
+cs8900_interrupt (rtems_vector_number v, void *csp)
+{
+ cs8900_device *cs = csp;
+ unsigned short isq = 0;
+ struct mbuf *m;
+ unsigned char *p;
+
+ ++cs->eth_stats.interrupts;
+
+ while (1)
+ {
+ isq = mem_pp_get_reg (cs, CS8900_PP_ISQ);
+
+ cs8900_trace (cs, CS8900_T_INT, isq);
+
+ /*
+ * No more interrupts to service.
+ */
+
+ if (isq == 0)
+ return;
+
+ switch (isq & 0x1f)
+ {
+ case 0x04:
+
+ /*
+ * RxEvent.
+ */
+
+ ++cs->eth_stats.rx_interrupts;
+
+ if (isq & CS8900_RX_EVENT_RX_OK)
+ {
+ m = cs->rx_ready_head;
+ if (m)
+ {
+ cs->rx_ready_head = m->m_nextpkt;
+ if (cs->rx_ready_head == 0)
+ cs->rx_ready_tail = 0;
+ m->m_nextpkt = 0;
+ cs->rx_ready_len--;
+
+ p = mtod (m, unsigned char *);
+
+ m->m_pkthdr.len = cs8900_get_data_block (cs, p);
+
+ if (cs->rx_loaded_tail == 0)
+ cs->rx_loaded_head = m;
+ else
+ cs->rx_loaded_tail->m_nextpkt = m;
+ cs->rx_loaded_tail = m;
+ cs->rx_loaded_len++;
+
+ if (cs->rx_loaded_len == 1)
+ {
+ cs8900_trace (cs, CS8900_T_RX_OK, cs->rx_loaded_len);
+ rtems_bsdnet_event_send (cs->rx_task, CS8900_RX_OK_EVENT);
+ }
+ }
+ else
+ {
+ ++cs->eth_stats.rx_dropped;
+
+ cs8900_trace (cs, CS8900_T_RX_DROPPED, cs->rx_loaded_len);
+
+ if (cs->rx_loaded_len == 0)
+ rtems_bsdnet_event_send (cs->rx_task, CS8900_RX_OK_EVENT);
+ }
+ }
+ else
+ {
+ if (isq & CS8900_RX_EVENT_CRC_ERROR)
+ ++cs->eth_stats.rx_crc_errors;
+
+ if (isq & CS8900_RX_EVENT_RUNT)
+ ++cs->eth_stats.rx_runt_errors;
+
+ if (isq & CS8900_RX_EVENT_EXTRA_DATA)
+ ++cs->eth_stats.rx_oversize_errors;
+ }
+ break;
+
+ case 0x08:
+
+ /*
+ * TxEvent.
+ */
+
+ ++cs->eth_stats.tx_interrupts;
+
+ if (cs->tx_active)
+ {
+ if (isq & CS8900_TX_EVENT_TX_OK)
+ ++cs->eth_stats.tx_ok;
+
+ cs->tx_active = 0;
+
+ rtems_bsdnet_event_send (cs->tx_task, CS8900_TX_OK_EVENT);
+ }
+ break;
+
+ case 0x0c:
+
+ /*
+ * BufEvent.
+ */
+
+ if (isq & CS8900_BUFFER_EVENT_RDY_FOR_TX)
+ {
+ if (cs->tx_active)
+ {
+ ++cs->eth_stats.tx_rdy4tx;
+ rtems_bsdnet_event_send (cs->tx_task, CS8900_TX_WAIT_EVENT);
+ }
+ }
+ else if (isq & CS8900_BUFFER_EVENT_TX_UNDERRUN)
+ {
+ ++cs->eth_stats.tx_underrun_errors;
+ if (cs->tx_active)
+ rtems_bsdnet_event_send (cs->tx_task, CS8900_TX_OK_EVENT);
+ }
+ else if (isq & CS8900_BUFFER_EVENT_SW_INT)
+ {
+ ++cs->eth_stats.int_swint_res;
+ }
+ break;
+
+ case 0x10:
+
+ /*
+ * RxMiss.
+ */
+
+ cs->eth_stats.rx_missed_errors +=
+ mem_pp_get_reg (cs, CS8900_PP_RxMISS) >> 6;
+ break;
+
+ case 0x12:
+
+ /*
+ * TxCol.
+ */
+
+ cs->eth_stats.tx_collisions +=
+ mem_pp_get_reg (cs, CS8900_PP_TxCol) >> 6;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+}
+
+int
+cs8900_link_active (cs8900_device *cs)
+{
+ return ((mem_pp_get_reg (cs, CS8900_PP_LineST) & CS8900_LINE_STATUS_LINK_OK) ?
+ 1 : 0);
+}
+
+static inline void
+cs8900_rx_refill_queue (cs8900_device *cs)
+{
+ struct ifnet *ifp = &cs->arpcom.ac_if;
+ struct mbuf *m;
+ rtems_interrupt_level level;
+
+ /*
+ * Hold a single queue of mbuf's at the interface. This
+ * will lower the latency of the driver.
+ */
+
+ while (cs->rx_ready_len < cs->rx_queue_size)
+ {
+ MGETHDR (m, M_DONTWAIT, MT_DATA);
+
+ if (!m)
+ {
+ ++cs->eth_stats.rx_no_mbufs;
+ cs8900_trace (cs, CS8900_T_NO_MBUF, cs->eth_stats.rx_no_mbufs);
+ return;
+ }
+
+ MCLGET (m, M_DONTWAIT);
+
+ if (!m->m_ext.ext_buf)
+ {
+ ++cs->eth_stats.rx_no_clusters;
+ cs8900_trace (cs, CS8900_T_NO_CLUSTERS, cs->eth_stats.rx_no_clusters);
+ m_free (m);
+ return;
+ }
+ m->m_pkthdr.rcvif = ifp;
+ m->m_nextpkt = 0;
+
+ rtems_interrupt_disable (level);
+
+ if (cs->rx_ready_tail == 0)
+ cs->rx_ready_head = m;
+ else
+ cs->rx_ready_tail->m_nextpkt = m;
+ cs->rx_ready_tail = m;
+ cs->rx_ready_len++;
+
+ rtems_interrupt_enable (level);
+ }
+}
+
+static void
+cs8900_rx_task (void *arg)
+{
+ cs8900_device *cs = arg;
+ struct ifnet *ifp = &cs->arpcom.ac_if;
+ rtems_event_set events;
+ struct mbuf *m;
+ struct ether_header *eh;
+ rtems_status_code sc;
+ rtems_interrupt_level level;
+
+ /*
+ * Turn the receiver and transmitter on.
+ */
+
+ mem_pp_bit_set_reg (cs, CS8900_PP_LineCFG,
+ CS8900_LINE_CTRL_RX_ON |
+ CS8900_LINE_CTRL_TX_ON);
+
+ /*
+ * Start the software interrupt watchdog.
+ */
+
+ mem_pp_bit_set_reg (cs, CS8900_PP_BufCFG,
+ CS8900_BUFFER_CONFIG_SW_INT);
+ ++cs->eth_stats.int_swint_req;
+
+ /*
+ * Loop reading packets.
+ */
+
+ while (1)
+ {
+ cs8900_rx_refill_queue (cs);
+
+ sc = rtems_bsdnet_event_receive (CS8900_RX_OK_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_MILLISECONDS_TO_TICKS (250),
+ &events);
+
+ cs8900_rx_refill_queue (cs);
+
+ if (sc == RTEMS_TIMEOUT)
+ {
+ /*
+ * We need to check the interrupt hardware in the cs8900a
+ * has not locked up. It seems this occurs if the ISQ
+ * queue fills up.
+ * To test we generate a software interrupt and watch
+ * a counter go up. If the counter does not go for 2
+ * software interrupts requests we flush the ISQ queue.
+ */
+
+ if ((cs->eth_stats.int_swint_req - cs->eth_stats.int_swint_res) > 1)
+ {
+ printf ("cs8900: int lockup, isq flush\n");
+
+ mem_pp_bit_clear_reg (cs, CS8900_PP_BusCTL,
+ CS8900_BUS_CTRL_ENABLE_INT);
+
+ while (mem_pp_get_reg (cs, CS8900_PP_ISQ) != 0);
+
+ cs->eth_stats.int_swint_req = cs->eth_stats.int_swint_res = 0;
+ ++cs->eth_stats.int_lockup;
+
+ mem_pp_bit_set_reg (cs, CS8900_PP_BusCTL,
+ CS8900_BUS_CTRL_ENABLE_INT);
+ }
+
+ mem_pp_bit_set_reg (cs, CS8900_PP_BufCFG,
+ CS8900_BUFFER_CONFIG_SW_INT);
+ ++cs->eth_stats.int_swint_req;
+ }
+
+ cs8900_trace (cs, CS8900_T_RX_BEGIN, cs->rx_loaded_len);
+
+ while (cs->rx_loaded_len)
+ {
+ rtems_interrupt_disable (level);
+
+ m = cs->rx_loaded_head;
+ if (m)
+ {
+ cs->rx_loaded_head = m->m_nextpkt;
+ if (cs->rx_loaded_head == 0)
+ cs->rx_loaded_tail = 0;
+ m->m_nextpkt = 0;
+ cs->rx_loaded_len--;
+
+ rtems_interrupt_enable (level);
+
+ m->m_pkthdr.rcvif = ifp;
+
+ cs->eth_stats.rx_bytes += m->m_pkthdr.len;
+
+ m->m_len = m->m_pkthdr.len = m->m_pkthdr.len - sizeof (struct ether_header);
+
+ eh = mtod (m, struct ether_header *);
+ m->m_data += sizeof (struct ether_header);
+
+ ++cs->eth_stats.rx_packets;
+
+ ether_input (ifp, eh, m);
+ }
+ else
+ {
+ rtems_interrupt_enable (level);
+ }
+ }
+ cs8900_trace (cs, CS8900_T_RX_END, cs->rx_loaded_len);
+ }
+}
+
+static void
+cs8900_tx_task (void *arg)
+{
+ cs8900_device *cs = arg;
+ struct ifnet *ifp = &cs->arpcom.ac_if;
+ rtems_event_set events;
+ struct mbuf *m;
+ rtems_status_code sc;
+
+ /*
+ * Wait for the link to come up.
+ */
+
+ rtems_task_wake_after (RTEMS_MILLISECONDS_TO_TICKS (750));
+
+ /*
+ * Loop processing the tx queue.
+ */
+
+ while (1)
+ {
+ /*
+ * Fetch the mbuf list from the interface's queue.
+ */
+
+ IF_DEQUEUE (&ifp->if_snd, m);
+
+ /*
+ * If something actually is present send it.
+ */
+
+ if (!m)
+ {
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ rtems_bsdnet_event_receive (CS8900_TX_START_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events);
+ }
+ else
+ {
+ if (cs8900_link_active (cs))
+ {
+ int resending;
+
+ do
+ {
+ unsigned short buf_status;
+
+ resending = 0;
+
+ cs->tx_active = 1;
+
+ mem_pp_set_reg (cs, CS8900_PP_TxCMD,
+ CS8900_TX_CMD_STATUS_TX_START_ENTIRE |
+ CS8900_TX_CMD_STATUS_FORCE);
+ mem_pp_set_reg (cs, CS8900_PP_TxLength, m->m_pkthdr.len);
+
+ buf_status = mem_pp_get_reg (cs, CS8900_PP_BusST);
+
+ /*
+ * If the bid for memory in the device fails trash the
+ * transmit and try again next time.
+ */
+
+ if (buf_status & CS8900_BUS_STATUS_TX_BID_ERROR)
+ ++cs->eth_stats.tx_bid_errors;
+ else
+ {
+ /*
+ * If the buffer is not read enable the interrupt and then wait.
+ */
+
+ if ((buf_status & CS8900_BUS_STATUS_RDY_FOR_TX_NOW) == 0)
+ {
+ cs->eth_stats.tx_wait_for_rdy4tx++;
+ sc = rtems_bsdnet_event_receive (CS8900_TX_WAIT_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_MILLISECONDS_TO_TICKS (750),
+ &events);
+ if (sc == RTEMS_TIMEOUT)
+ {
+ /*
+ * For some reason the wait request has been dropped,
+ * so lets resend from the start.
+ */
+
+ printf ("tx resend\n");
+ ++cs->eth_stats.tx_resends;
+ resending = 1;
+ }
+ }
+
+ if (!resending)
+ {
+ cs8900_tx_load (cs, m);
+ cs->eth_stats.tx_packets++;
+ cs->eth_stats.tx_bytes += m->m_pkthdr.len;
+ }
+ }
+ }
+ while (resending);
+
+ m_freem (m);
+
+ do
+ {
+ rtems_bsdnet_event_receive (CS8900_TX_OK_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events);
+ }
+ while (cs->tx_active);
+ }
+ else
+ {
+ ++cs->eth_stats.tx_dropped;
+ m_freem (m);
+ }
+ }
+ }
+}
+
+static void
+cs8900_start (struct ifnet *ifp)
+{
+ cs8900_device *cs = ifp->if_softc;
+
+ /*
+ * Tell the transmit daemon to wake up and send a packet.
+ */
+
+ ifp->if_flags |= IFF_OACTIVE;
+
+ rtems_bsdnet_event_send (cs->tx_task, CS8900_TX_START_EVENT);
+}
+
+static void
+cs8900_stop (cs8900_device *cs)
+{
+ mem_pp_bit_clear_reg (cs, CS8900_PP_LineCFG,
+ CS8900_LINE_CTRL_RX_ON |
+ CS8900_LINE_CTRL_TX_ON);
+
+ mem_pp_bit_clear_reg (cs, CS8900_PP_BusCTL,
+ CS8900_BUS_CTRL_ENABLE_INT);
+}
+
+static const char *eth_statistics_labels[] =
+{
+ "rx packets",
+ "tx packets",
+ "rx bytes",
+ "tx bytes",
+ "rx interrupts",
+ "tx interrupts",
+ "rx dropped",
+ "rx no mbuf",
+ "rx no custers",
+ "rx oversize errors",
+ "rx crc errors",
+ "rx runt errors",
+ "rx missed errors",
+ "tx ok",
+ "tx collisions",
+ "tx bid errors",
+ "tx wait for rdy4tx",
+ "tx rdy4tx",
+ "tx underrun errors",
+ "tx dropped",
+ "tx resends",
+ "int swint req",
+ "int swint res",
+ "int lockup",
+ "interrupts"
+};
+
+static void
+cs8900_stats (cs8900_device *cs)
+{
+ int i;
+ int max_label = 0;
+ int len;
+ unsigned long *value = (unsigned long*) &cs->eth_stats.rx_packets;
+
+ cs->eth_stats.rx_missed_errors +=
+ mem_pp_get_reg (cs, CS8900_PP_RxMISS) >> 6;
+
+ cs->eth_stats.tx_collisions +=
+ mem_pp_get_reg (cs, CS8900_PP_TxCol) >> 6;
+
+ printf ("Network Driver Stats for CS8900 :\n");
+
+ for (i = 0; i < (sizeof (eth_statistics_labels) / sizeof (const char *)); i++)
+ {
+ len = strlen (eth_statistics_labels[i]);
+ if (len > max_label)
+ max_label = len;
+ }
+
+ max_label += 2;
+
+ printf ("%*s - %10u %*s - %10u\n",
+ max_label, "rx ready len", cs->rx_ready_len,
+ max_label, "rx loaded len", cs->rx_loaded_len);
+
+ for (i = 0;
+ i < (sizeof (eth_statistics_labels) / sizeof (const char *));
+ i++)
+ {
+ printf ("%*s - %10lu",
+ max_label, eth_statistics_labels[i], value[i]);
+
+ i++;
+
+ if (i < (sizeof (eth_statistics_labels) / sizeof (const char *)))
+ printf (" %*s - %10lu",
+ max_label, eth_statistics_labels[i], value[i]);
+ printf ("\n");
+ }
+
+#if CS8900_TRACE
+
+ for (i = 0; i < cs->trace_in; i++)
+ {
+ printf ("%8ld.%03ld ", cs->trace_time[i] / 1000, cs->trace_time[i] % 1000);
+
+ if (cs->trace_key[i] < sizeof (cs8900_trace_labels) / sizeof (char*))
+ printf ("%s : ", cs8900_trace_labels[cs->trace_key[i]]);
+ else
+ printf ("unknown trace key, %d : ", cs->trace_key[i]);
+
+ if (cs->trace_key[i] == CS8900_T_INT)
+ {
+ printf ("0x%04lx ", cs->trace_var[i]);
+ if (cs->trace_var[i] == 0)
+ printf ("end");
+ else
+ {
+ switch (cs->trace_var[i] & 0x1f)
+ {
+ case 0x04:
+ printf ("rx event");
+ break;
+
+ case 0x08:
+ printf ("tx event");
+ break;
+
+ case 0x0c:
+ printf ("buffer event");
+ break;
+
+ case 0x10:
+ printf ("rx missed");
+ break;
+
+ case 0x12:
+ printf ("tx collisions");
+ break;
+
+ case 0x1f:
+ printf ("tx request");
+ break;
+
+ case 0x1e:
+ printf ("tx wait 4 tx");
+ break;
+
+ case 0x1d:
+ printf ("tx already active");
+ break;
+
+ default:
+ printf ("unknown event");
+ break;
+ }
+ }
+ }
+ else
+ printf ("0x%08lx", cs->trace_var[i]);
+
+ printf ("\n");
+ }
+
+ cs->trace_in = 0;
+
+#endif
+}
+
+static void
+cs8900_init (void *arg)
+{
+ cs8900_device *cs = arg;
+ struct ifnet *ifp = &cs->arpcom.ac_if;
+
+ if (cs->rx_task == 0)
+ {
+
+ /*
+ * Set up the hardware.
+ */
+
+ cs8900_hardware_init (cs);
+
+ /*
+ * Start driver task. We have only one task.
+ */
+
+ cs->rx_task = rtems_bsdnet_newproc ("CSr0", 4096, cs8900_rx_task, cs);
+ cs->tx_task = rtems_bsdnet_newproc ("CSt0", 4096, cs8900_tx_task, cs);
+ }
+
+#ifdef todo
+ /*
+ * Set flags appropriately
+ */
+ if (ifp->if_flags & IFF_PROMISC)
+ else
+#endif
+
+ /*
+ * Tell the world that we're running.
+ */
+
+ ifp->if_flags |= IFF_RUNNING;
+
+ /*
+ * Set the Line Control to bring the receive and transmitter online.
+ */
+
+ mem_pp_bit_set_reg (cs, CS8900_PP_LineCFG,
+ CS8900_LINE_CTRL_RX_ON |
+ CS8900_LINE_CTRL_TX_ON);
+
+ mem_pp_bit_set_reg (cs, CS8900_PP_BusCTL,
+ CS8900_BUS_CTRL_ENABLE_INT);
+}
+
+static int
+cs8900_ioctl (struct ifnet *ifp, ioctl_command_t cmd, caddr_t data)
+{
+ cs8900_device *cs = ifp->if_softc;
+ int error = 0;
+
+ switch (cmd)
+ {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+
+ error = ether_ioctl (ifp, cmd, data);
+ break;
+
+ case SIOCSIFFLAGS:
+
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING))
+ {
+ case IFF_RUNNING:
+
+ cs8900_stop (cs);
+ break;
+
+ case IFF_UP:
+
+ cs8900_init (cs);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+
+ cs8900_stop (cs);
+ cs8900_init (cs);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+
+ cs8900_stats (cs);
+ break;
+
+ /* FIXME: Multicast commands must be added here. */
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+int
+cs8900_driver_attach (struct rtems_bsdnet_ifconfig *config, int attaching)
+{
+ cs8900_device *cs;
+ struct ifnet *ifp;
+ int mtu;
+ int unit;
+ char *name;
+
+ /*
+ * Parse driver name
+ */
+
+ if ((unit = rtems_bsdnet_parse_driver_name (config, &name)) < 0)
+ return 0;
+
+ cs = config->drv_ctrl;
+ cs->dev = unit;
+ ifp = &cs->arpcom.ac_if;
+
+ if (attaching)
+ {
+ if (ifp->if_softc)
+ {
+ printf ("Driver `%s' already in use.\n", config->name);
+ return 0;
+ }
+
+ /*
+ * Process options
+ */
+
+ if (config->hardware_address)
+ memcpy (cs->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
+ else
+ cs8900_get_mac_addr (cs, cs->arpcom.ac_enaddr);
+
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ cs->accept_bcast = !config->ignore_broadcast;
+
+ /*
+ * Set up network interface values.
+ */
+
+ ifp->if_softc = cs;
+ ifp->if_unit = unit;
+ ifp->if_name = name;
+ ifp->if_mtu = mtu;
+ ifp->if_init = cs8900_init;
+ ifp->if_ioctl = cs8900_ioctl;
+ ifp->if_start = cs8900_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 to the stack.
+ */
+
+ if_attach (ifp);
+ ether_ifattach (ifp);
+ }
+ else
+ {
+ if (!ifp->if_softc)
+ {
+ printf ("Driver `%s' not found.\n", config->name);
+ return 0;
+ }
+
+ cs8900_stop (cs);
+ cs8900_detach_interrupt (cs);
+ }
+
+ return 1;
+}
diff --git a/bsps/shared/net/cs8900.c.bsp b/bsps/shared/net/cs8900.c.bsp
new file mode 100644
index 0000000..7b7374a
--- /dev/null
+++ b/bsps/shared/net/cs8900.c.bsp
@@ -0,0 +1,510 @@
+/*
+ * RTEMS CS8900 Driver Setup for the DIMM-PC/i386 made by Kontron.
+ *
+ * Port to the DIMM PC copyright (c) 2004 Angelo Fraietta
+ * This project has been assisted by the Commonwealth Government
+ * through the Australia Council, its arts funding and advisory body.
+ *
+ * Port performed by Chris Johns, Cybertec Pty Ltd, Jan 2004.
+ * Based on the Cybertec CS8900 driver setup for the SFP-101.
+ *
+ */
+
+#define CS8900_VERBOSE 0
+#define HAVE_MRB_CS8900_DATA_BUS_SWAPPED 1
+
+#include <bsp.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <arpa/inet.h>
+
+#include <rtems.h>
+#include <rtems/monitor.h>
+#include <rtems/rtems_bsdnet.h>
+#include <irq.h>
+
+#include "cs8900.h"
+
+#include <net/route.h>
+
+/*
+ * Loopback interface.
+ */
+
+extern int rtems_bsdnet_loopattach (struct rtems_bsdnet_ifconfig *, int);
+
+static struct rtems_bsdnet_ifconfig loopback_config =
+{
+ "lo0", /* name */
+ rtems_bsdnet_loopattach, /* attach function */
+ NULL, /* link to next interface */
+ "127.0.0.1", /* IP address */
+ "255.0.0.0", /* IP net mask */
+};
+
+/*
+ * Network configuration
+ */
+
+struct rtems_bsdnet_config rtems_bsdnet_config =
+{
+ &loopback_config,
+ NULL,
+ 20, /* Network task priority */
+ 32 * 1024, /* Mbuf capacity */
+ 96 * 1024, /* Mbuf cluster capacity */
+};
+
+
+static void cs8900_isr ();
+static void cs8900_int_off (const rtems_irq_connect_data* unused);
+static void cs8900_int_on (const rtems_irq_connect_data* unused);
+static int cs8900_int_is_on (const rtems_irq_connect_data *irq);
+
+/**
+ * The device's data.
+ */
+static cs8900_device cs8900;
+static rtems_irq_connect_data cs8900_irq =
+{
+ 0,
+ cs8900_isr,
+ cs8900_int_on,
+ cs8900_int_off,
+ cs8900_int_is_on
+};
+
+#if CS8900_VERBOSE
+static int cs8900_io_verbose = 1;
+#endif
+
+/**
+ * Device structure for attaching to the BSD stack.
+ */
+static struct rtems_bsdnet_ifconfig cs8900_ifconfig =
+{
+ "cs0", /* name */
+ cs8900_driver_attach, /* attach funtion */
+ NULL, /* next interface */
+ NULL, /* ip address */
+ NULL, /* ip netmask */
+ NULL, /* hardware address */
+ 0, /* ignore broadcast */
+ 0, /* mtu */
+ 0, /* rbuf count */
+ 0, /* xbuf count */
+ 0, /* port */
+ 0, /* irno */
+ 0, /* bpar */
+ 0 /* drv ctrl */
+};
+
+
+/*
+ * Commands to register.
+ */
+
+rtems_monitor_command_entry_t rtems_bsdnet_commands[] =
+{
+ {
+ "ifstats",
+ "Show the interface stats.\n",
+ 0,
+ (void*) rtems_bsdnet_show_if_stats,
+ 0,
+ 0,
+ },
+ {
+ "ipstats",
+ "Show the IP stats.\n",
+ 0,
+ (void*) rtems_bsdnet_show_ip_stats,
+ 0,
+ 0,
+ },
+ {
+ "routes",
+ "Show the inet routes.\n",
+ 0,
+ (void*) rtems_bsdnet_show_inet_routes,
+ 0,
+ 0,
+ },
+ {
+ "mbufs",
+ "Show the mbuf stats.\n",
+ 0,
+ (void*) rtems_bsdnet_show_mbuf_stats,
+ 0,
+ 0,
+ },
+ {
+ "icmp",
+ "Show the ICMP stats.\n",
+ 0,
+ (void*) rtems_bsdnet_show_icmp_stats,
+ 0,
+ 0,
+ },
+ {
+ "udp",
+ "Show the UDP stats.\n",
+ 0,
+ (void*) rtems_bsdnet_show_udp_stats,
+ 0,
+ 0,
+ },
+ {
+ "tcp",
+ "Show the TCP stats.\n",
+ 0,
+ (void*) rtems_bsdnet_show_tcp_stats,
+ 0,
+ 0,
+ }
+};
+
+static void
+cs8900_isr ()
+{
+ /*
+ * Note: we could have a high priority task here to call the
+ * drivers handler. The would lower the interrupt latancy
+ * we aother wise have.
+ */
+ cs8900_interrupt (cs8900_irq.name, &cs8900);
+}
+
+static void
+cs8900_int_on (const rtems_irq_connect_data *unused)
+{
+}
+
+static void
+cs8900_int_off (const rtems_irq_connect_data *unused)
+{
+}
+
+static int
+cs8900_int_is_on (const rtems_irq_connect_data *irq)
+{
+ return BSP_irq_enabled_at_i8259s (irq->name);
+}
+
+void cs8900_io_set_reg (cs8900_device *cs, unsigned short reg, unsigned short data)
+{
+#if CS8900_VERBOSE
+ if (cs8900_io_verbose)
+ printk ("CS8900: io set reg=0x%04x, data=0x%04x\n", reg, data);
+#endif
+ outport_word (cs->io_base + reg, data);
+}
+
+unsigned short cs8900_io_get_reg (cs8900_device *cs, unsigned short reg)
+{
+ unsigned short data;
+ inport_word (cs->io_base + reg, data);
+#if CS8900_VERBOSE
+ if (cs8900_io_verbose)
+ printk ("CS8900: io get reg=0x%04x, data=0x%04x\n", reg, data);
+#endif
+ return data;
+}
+
+void cs8900_mem_set_reg (cs8900_device *cs, unsigned long reg, unsigned short data)
+{
+ printk ("CS8900: mem_set_reg register access called. Only IO supported.\n");
+ while (1);
+}
+
+unsigned short cs8900_mem_get_reg (cs8900_device *cs, unsigned long reg)
+{
+ printk ("CS8900: mem_get_reg register access called. Only IO supported.\n");
+ while (1);
+ return 0;
+}
+
+void cs8900_put_data_block (cs8900_device *cs, int len, unsigned char *data)
+{
+ unsigned short *src = (unsigned short *) ((unsigned long) data);
+
+ for (; len > 1; len -= 2)
+ outport_word (cs->io_base, *src++);
+
+ if (len)
+ outport_word (cs->io_base, *src++);
+}
+
+unsigned short cs8900_get_data_block (cs8900_device *cs, unsigned char *data)
+{
+ unsigned short *dst;
+ int cnt;
+ int len;
+
+ /*
+ * Drop the Rx status first.
+ */
+ inport_word (cs->io_base, len);
+
+ /*
+ * Now the length.
+ */
+ inport_word (cs->io_base, len);
+
+ dst = (unsigned short *) ((unsigned long) data);
+ cnt = len >> 1;
+
+ while (cnt--)
+ inport_word (cs->io_base, *dst++);
+
+ if (len & 1)
+ inport_word (cs->io_base, *dst++);
+
+ return len;
+}
+
+void
+cs8900_tx_load (cs8900_device *cs, struct mbuf *m)
+{
+ unsigned int len;
+ unsigned char *src = 0;
+ unsigned short *wsrc = 0;
+ unsigned char remainder = 0;
+ unsigned char word[2];
+
+ while (m)
+ {
+ /*
+ * We can get empty mbufs from the stack.
+ */
+
+ len = m->m_len;
+ src = mtod (m, unsigned char*);
+
+ if (len)
+ {
+ if (remainder)
+ {
+#if HAVE_MRB_CS8900_DATA_BUS_SWAPPED
+ word[1] = *src++;
+#else
+ word[0] = *src++;
+#endif
+ outport_word (cs->io_base, *((unsigned short*) (unsigned long) &word));
+ len--;
+ remainder = 0;
+ }
+
+ if (len & 1)
+ {
+ remainder = 1;
+ len--;
+ }
+
+ wsrc = (unsigned short*) src;
+
+ for (; len; len -= 2, src += 2)
+ outport_word (cs->io_base, *wsrc++);
+
+ if (remainder)
+#if HAVE_MRB_CS8900_DATA_BUS_SWAPPED
+ word[0] = *src++;
+#else
+ word[1] = *src++;
+#endif
+ }
+
+ m = m->m_next;
+ }
+
+ if (remainder)
+ {
+#if HAVE_MRB_CS8900_DATA_BUS_SWAPPED
+ word[1] = *src++;
+#else
+ word[0] = *src++;
+#endif
+ outport_word (cs->io_base, *((unsigned short*) (unsigned long) &word));
+ }
+}
+
+void cs8900_attach_interrupt (cs8900_device *cs)
+{
+ BSP_install_rtems_irq_handler (&cs8900_irq);
+}
+
+void cs8900_detach_interrupt (cs8900_device *cs)
+{
+ BSP_remove_rtems_irq_handler (&cs8900_irq);
+}
+
+void
+BSP_cs8900_attach (unsigned long io_base, unsigned long mem_base, int intrp,
+ const char* ip, const char* nm, const char* gw)
+{
+ cs8900_device *cs = &cs8900;
+ int flags;
+ struct sockaddr_in address;
+ struct sockaddr_in netmask;
+ struct sockaddr_in broadcast;
+ struct sockaddr_in gateway;
+ int cmd;
+
+ printf ("cso: io=0x%0lx mem=0 irq=%d\n", io_base, intrp);
+
+ memset (cs, 0, sizeof (cs8900));
+
+ cs->dev = 0;
+ cs->rx_queue_size = 30;
+ cs->io_base = io_base;
+
+ if (mem_base)
+ printf ("cs8900: memory mode is currently not supported.\n");
+
+ cs->mem_base = 0;
+
+ switch (intrp)
+ {
+ case 5:
+ cs->irq_level = 3;
+ break;
+ case 10:
+ cs->irq_level = 0;
+ break;
+ case 11:
+ cs->irq_level = 1;
+ break;
+ case 12:
+ cs->irq_level = 2;
+ break;
+ default:
+ printf ("cs8900: unsupported IRQ level\n");
+ return;
+ }
+
+ cs8900_irq.name = intrp;
+
+ /*
+ * Get the MAC adress from the CS8900.
+ */
+
+ cs8900_get_mac_addr (cs, cs->mac_address);
+
+ /*
+ * Setup the BSD interface configure structure.
+ */
+
+ cs8900_ifconfig.drv_ctrl = cs;
+ cs8900_ifconfig.hardware_address = cs->mac_address;
+
+ printf ("CS8900 initialisation\n");
+
+ rtems_bsdnet_attach (&cs8900_ifconfig);
+
+ /*
+ * Configure the interface using the boot configuration.
+ */
+
+ flags = IFF_UP;
+ if (rtems_bsdnet_ifconfig (cs8900_ifconfig.name,
+ SIOCSIFFLAGS,
+ &flags) < 0)
+ {
+ printf ("error: can't bring up %s: %s\n",
+ cs8900_ifconfig.name, strerror (errno));
+ return;
+ }
+
+ if (ip && strlen (ip) && nm && strlen (nm))
+ {
+ printf ("%s: addr: %s netmask: %s gateway: %s\n",
+ cs8900_ifconfig.name, ip, nm, gw ? gw : "none");
+
+ memset (&netmask, '\0', sizeof netmask);
+ netmask.sin_len = sizeof netmask;
+ netmask.sin_family = AF_INET;
+
+ if (!inet_aton (nm, &netmask.sin_addr))
+ {
+ printf ("error: cannot parse the network mask: %s\n", nm);
+ return;
+ }
+
+ memset (&address, '\0', sizeof address);
+ address.sin_len = sizeof address;
+ address.sin_family = AF_INET;
+
+ if (!inet_aton (ip, &address.sin_addr))
+ {
+ printf ("error: cannot parse the ip address: %s\n", ip);
+ return;
+ }
+
+ if (rtems_bsdnet_ifconfig (cs8900_ifconfig.name,
+ SIOCSIFNETMASK,
+ &netmask) < 0)
+ {
+ printf ("error: can't set %s netmask: %s\n",
+ cs8900_ifconfig.name, strerror (errno));
+ return;
+ }
+
+ if (rtems_bsdnet_ifconfig (cs8900_ifconfig.name,
+ SIOCSIFADDR,
+ &address) < 0)
+ {
+ printf ("error: can't set %s address: %s\n",
+ cs8900_ifconfig.name, strerror (errno));
+ return;
+ }
+
+ memset (&broadcast, '\0', sizeof broadcast);
+ broadcast.sin_len = sizeof broadcast;
+ broadcast.sin_family = AF_INET;
+ broadcast.sin_addr.s_addr =
+ (address.sin_addr.s_addr & netmask.sin_addr.s_addr) | ~netmask.sin_addr.s_addr;
+
+ if (rtems_bsdnet_ifconfig (cs8900_ifconfig.name,
+ SIOCSIFBRDADDR,
+ &broadcast) < 0)
+ {
+ printf ("error: can't set %s broadcast address: %s\n",
+ cs8900_ifconfig.name, strerror (errno));
+ return;
+ }
+
+ if (gw && strlen (gw))
+ {
+ address.sin_addr.s_addr = INADDR_ANY;
+ netmask.sin_addr.s_addr = INADDR_ANY;
+ memset (&gateway, '\0', sizeof gateway);
+ gateway.sin_len = sizeof gateway;
+ gateway.sin_family = AF_INET;
+
+ if (!inet_aton (gw, &gateway.sin_addr))
+ printf ("warning: cannot parse the gateway address: %s\n", ip);
+ else
+ {
+ if (rtems_bsdnet_rtrequest (RTM_ADD,
+ (struct sockaddr *) &address,
+ (struct sockaddr *) &gateway,
+ (struct sockaddr *) &netmask,
+ (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL) < 0)
+ printf ("error: can't set default route: %s\n", strerror (errno));
+ }
+ }
+ }
+ else
+ {
+ rtems_bsdnet_do_bootp_and_rootfs ();
+ }
+
+ for (cmd = 0;
+ cmd < sizeof (rtems_bsdnet_commands) / sizeof (rtems_monitor_command_entry_t);
+ cmd++)
+ rtems_monitor_insert_cmd (&rtems_bsdnet_commands[cmd]);
+}
diff --git a/bsps/shared/net/cs8900.h.bsp b/bsps/shared/net/cs8900.h.bsp
new file mode 100644
index 0000000..65ce0d2
--- /dev/null
+++ b/bsps/shared/net/cs8900.h.bsp
@@ -0,0 +1,38 @@
+/*
+ * RTEMS CS8900 Driver Setup for the DIMM-PC/i386 made by Kontron.
+ *
+ * Port to the DIMM PC copyright (c) 2004 Angelo Fraietta
+ * This project has been assisted by the Commonwealth Government
+ * through the Australia Council, its arts funding and advisory body.
+ *
+ * Port performed by Chris Johns, Cybertec Pty Ltd, Jan 2004.
+ * Based on the Cybertec CS8900 driver setup for the SFP-101.
+ */
+
+#if !defined (__BSP_CS8900_H__)
+#define __BSP_CS8900_H__
+
+/**
+ * BSP CS8900 Device initialisation and interface attach.
+ *
+ * @param io_base The I/O base address of the device.
+ *
+ * @param mem_base The memory base address. Currently not used.
+ *
+ * @param intrp The ISA bus IRQ. These are currently limited to
+ * 5, 10, 11, and 12 as documented in the CS8900
+ * manual.
+ *
+ * @param ip IP address in ASCII. For example 10.10.10.10. If the
+ * pointer is a NULL (0) the interface will BOOTP.
+ *
+ * @param nm Network Mask in ASCII. For example 10.10.10.255.
+ *
+ * @param gw Address of the gateway machine. For example
+ * 10.10.10.1.
+ */
+
+void BSP_cs8900_attach (unsigned long io_base, unsigned long mem_base, int intrp,
+ const char* ip, const char* nm, const char* gw);
+
+#endif
diff --git a/bsps/shared/net/dec21140.c b/bsps/shared/net/dec21140.c
new file mode 100644
index 0000000..382f3d6
--- /dev/null
+++ b/bsps/shared/net/dec21140.c
@@ -0,0 +1,1112 @@
+/*
+ * RTEMS driver for TULIP based Ethernet Controller
+ *
+ * Copyright (C) 1999 Emmanuel Raguet. raguet@crf.canon.fr
+ *
+ * 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.
+ *
+ * ------------------------------------------------------------------------
+ * [22.05.2000,StWi/CWA] added support for the DEC/Intel 21143 chip
+ *
+ * Thanks go to Andrew Klossner who provided the vital information about the
+ * Intel 21143 chip. FWIW: The 21143 additions to this driver were initially
+ * tested with a PC386 BSP using a Kingston KNE100TX with 21143PD chip.
+ *
+ * The driver will automatically detect whether there is a 21140 or 21143
+ * network card in the system and activate support accordingly. It will
+ * look for the 21140 first. If the 21140 is not found the driver will
+ * look for the 21143.
+ *
+ * 2004-11-10, Joel/Richard - 21143 support works on MVME2100.
+ * ------------------------------------------------------------------------
+ *
+ * 2003-03-13, Greg Menke, gregory.menke@gsfc.nasa.gov
+ *
+ * Added support for up to 8 units (which is an arbitrary limit now),
+ * consolidating their support into a single pair of rx/tx daemons and a
+ * single ISR for all vectors servicing the DEC units. The driver now
+ * simply uses whatever INTERRUPT_LINE the card supplies, requiring it
+ * be configured either by the boot monitor or bspstart() hackery.
+ * Tested on a MCP750 PPC based system with 2 DEC21140 boards.
+ *
+ * Also fixed a few bugs related to board configuration, start and stop.
+ *
+ */
+
+#include <machine/rtems-bsd-kernel-space.h>
+
+#include <rtems.h>
+#include <inttypes.h>
+
+/*
+ * This driver only supports architectures with the new style
+ * exception processing. The following checks try to keep this
+ * from being compiled on systems which can't support this driver.
+ */
+
+#if defined(__i386__)
+ #define DEC21140_SUPPORTED
+ #define PCI_DRAM_OFFSET 0
+#endif
+#if defined(__PPC__)
+ #define DEC21140_SUPPORTED
+#endif
+
+#include <bsp.h>
+
+#if !defined(PCI_DRAM_OFFSET)
+ #undef DEC21140_SUPPORTED
+#endif
+
+#if defined(DEC21140_SUPPORTED)
+#include <rtems/pci.h>
+
+#if defined(__PPC__)
+#include <libcpu/byteorder.h>
+#include <libcpu/io.h>
+#endif
+
+#if defined(__i386__)
+#include <libcpu/byteorder.h>
+#include <libcpu/page.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <rtems/error.h>
+#include <rtems/bspIo.h>
+#include <rtems/rtems_bsdnet.h>
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <bsp/irq.h>
+
+#ifdef malloc
+#undef malloc
+#endif
+#ifdef free
+#undef free
+#endif
+
+#define DEC_DEBUG
+
+/* note: the 21143 isn't really a DEC, it's an Intel chip */
+#define PCI_VENDOR_ID_DEC 0x1011
+#define PCI_DEVICE_ID_DEC_21140 0x0009
+#define PCI_DEVICE_ID_DEC_21143 0x0019
+
+#define DRIVER_PREFIX "dc"
+
+#define IO_MASK 0x3
+#define MEM_MASK 0xF
+
+/* command and status registers, 32-bit access, only if IO-ACCESS */
+#define ioCSR0 0x00 /* bus mode register */
+#define ioCSR1 0x08 /* transmit poll demand */
+#define ioCSR2 0x10 /* receive poll demand */
+#define ioCSR3 0x18 /* receive list base address */
+#define ioCSR4 0x20 /* transmit list base address */
+#define ioCSR5 0x28 /* status register */
+#define ioCSR6 0x30 /* operation mode register */
+#define ioCSR7 0x38 /* interrupt mask register */
+#define ioCSR8 0x40 /* missed frame counter */
+#define ioCSR9 0x48 /* Ethernet ROM register */
+#define ioCSR10 0x50 /* reserved */
+#define ioCSR11 0x58 /* full-duplex register */
+#define ioCSR12 0x60 /* SIA status register */
+#define ioCSR13 0x68
+#define ioCSR14 0x70
+#define ioCSR15 0x78 /* SIA general register */
+
+/* command and status registers, 32-bit access, only if MEMORY-ACCESS */
+#define memCSR0 0x00 /* bus mode register */
+#define memCSR1 0x02 /* transmit poll demand */
+#define memCSR2 0x04 /* receive poll demand */
+#define memCSR3 0x06 /* receive list base address */
+#define memCSR4 0x08 /* transmit list base address */
+#define memCSR5 0x0A /* status register */
+#define memCSR6 0x0C /* operation mode register */
+#define memCSR7 0x0E /* interrupt mask register */
+#define memCSR8 0x10 /* missed frame counter */
+#define memCSR9 0x12 /* Ethernet ROM register */
+#define memCSR10 0x14 /* reserved */
+#define memCSR11 0x16 /* full-duplex register */
+#define memCSR12 0x18 /* SIA status register */
+#define memCSR13 0x1A
+#define memCSR14 0x1C
+#define memCSR15 0x1E /* SIA general register */
+
+#define DEC_REGISTER_SIZE 0x100 /* to reserve virtual memory */
+
+
+
+
+#define RESET_CHIP 0x00000001
+#if defined(__PPC__)
+#define CSR0_MODE 0x0030e002 /* 01b08000 */
+#else
+#define CSR0_MODE 0x0020e002 /* 01b08000 */
+#endif
+#define ROM_ADDRESS 0x00004800
+#define CSR6_INIT 0x022cc000 /* 022c0000 020c0000 */
+#define CSR6_TX 0x00002000
+#define CSR6_TXRX 0x00002002
+#define IT_SETUP 0x000100c0 /* 000100e0 */
+#define CLEAR_IT 0xFFFFFFFF
+#define NO_IT 0x00000000
+
+/* message descriptor entry */
+struct MD {
+ /* used by hardware */
+ volatile uint32_t status;
+ volatile uint32_t counts;
+ volatile uint32_t buf1, buf2;
+ /* used by software */
+ volatile struct mbuf *m;
+ volatile struct MD *next;
+} __attribute__ ((packed));
+
+/*
+** These buffers allocated for each unit, so ensure
+**
+** rtems_bsdnet_config.mbuf_bytecount
+** rtems_bsdnet_config.mbuf_cluster_bytecount
+**
+** are adequately sized to provide enough clusters and mbufs for all the
+** units. The default bsdnet configuration is sufficient for one dec
+** unit, but will be nearing exhaustion with 2 or more. Although a
+** little expensive in memory, the following configuration should
+** eliminate all mbuf/cluster issues;
+**
+** rtems_bsdnet_config.mbuf_bytecount = 128*1024;
+** rtems_bsdnet_config.mbuf_cluster_bytecount = 256*1024;
+*/
+
+#define NRXBUFS 16 /* number of receive buffers */
+#define NTXBUFS 16 /* number of transmit buffers */
+
+/*
+ * Number of DEC boards supported by this driver
+ */
+#define NDECDRIVER 8
+
+/*
+ * Receive buffer size -- Allow for a full ethernet packet including CRC
+ */
+#define RBUF_SIZE 1536
+
+#define ET_MINLEN 60 /* minimum message length */
+
+/*
+** Events, one per unit. The event is sent to the rx task from the isr
+** or from the stack to the tx task whenever a unit needs service. The
+** rx/tx tasks identify the requesting unit(s) by their particular
+** events so only requesting units are serviced.
+*/
+
+static rtems_event_set unit_signals[NDECDRIVER]= { RTEMS_EVENT_1,
+ RTEMS_EVENT_2,
+ RTEMS_EVENT_3,
+ RTEMS_EVENT_4,
+ RTEMS_EVENT_5,
+ RTEMS_EVENT_6,
+ RTEMS_EVENT_7,
+ RTEMS_EVENT_8 };
+
+#if defined(__PPC__)
+#define phys_to_bus(address) ((unsigned int)((address)) + PCI_DRAM_OFFSET)
+#define bus_to_phys(address) ((unsigned int)((address)) - PCI_DRAM_OFFSET)
+#define CPU_CACHE_ALIGNMENT_FOR_BUFFER PPC_CACHE_ALIGNMENT
+#else
+extern void Wait_X_ms( unsigned int timeToWait );
+#define phys_to_bus(address) ((unsigned int) ((address)))
+#define bus_to_phys(address) ((unsigned int) ((address)))
+#define rtems_bsp_delay_in_bus_cycles(cycle) Wait_X_ms( cycle/100 )
+#define CPU_CACHE_ALIGNMENT_FOR_BUFFER PG_SIZE
+#endif
+
+#if (MCLBYTES < RBUF_SIZE)
+# error "Driver must have MCLBYTES > RBUF_SIZE"
+#endif
+
+/*
+ * Per-device data
+ */
+struct dec21140_softc {
+
+ struct arpcom arpcom;
+
+ rtems_irq_connect_data irqInfo;
+ rtems_event_set ioevent;
+
+ int numRxbuffers, numTxbuffers;
+
+ volatile struct MD *MDbase;
+ volatile struct MD *nextRxMD;
+ volatile unsigned char *bufferBase;
+ int acceptBroadcast;
+
+ volatile struct MD *TxMD;
+ volatile struct MD *SentTxMD;
+ int PendingTxCount;
+ int TxSuspended;
+
+ unsigned int port;
+ volatile uint32_t *base;
+
+ /*
+ * Statistics
+ */
+ unsigned long rxInterrupts;
+ unsigned long rxNotFirst;
+ unsigned long rxNotLast;
+ unsigned long rxGiant;
+ unsigned long rxNonOctet;
+ unsigned long rxRunt;
+ unsigned long rxBadCRC;
+ unsigned long rxOverrun;
+ unsigned long rxCollision;
+
+ unsigned long txInterrupts;
+ unsigned long txDeferred;
+ unsigned long txHeartbeat;
+ unsigned long txLateCollision;
+ unsigned long txRetryLimit;
+ unsigned long txUnderrun;
+ unsigned long txLostCarrier;
+ unsigned long txRawWait;
+};
+
+static struct dec21140_softc dec21140_softc[NDECDRIVER];
+static rtems_id rxDaemonTid;
+static rtems_id txDaemonTid;
+
+void dec21140_txDaemon (void *arg);
+
+/*
+ * This routine reads a word (16 bits) from the serial EEPROM.
+ */
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */
+#define EE_CS 0x01 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
+#define EE_WRITE_0 0x01
+#define EE_WRITE_1 0x05
+#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
+#define EE_ENB (0x4800 | EE_CS)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5 << 6)
+#define EE_READ_CMD (6 << 6)
+#define EE_ERASE_CMD (7 << 6)
+
+static int eeget16(volatile uint32_t *ioaddr, int location)
+{
+ int i;
+ unsigned short retval = 0;
+ int read_cmd = location | EE_READ_CMD;
+
+ st_le32(ioaddr, EE_ENB & ~EE_CS);
+ st_le32(ioaddr, EE_ENB);
+
+ /* Shift the read command bits out. */
+ for (i = 10; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ st_le32(ioaddr, EE_ENB | dataval);
+ rtems_bsp_delay_in_bus_cycles(200);
+ st_le32(ioaddr, EE_ENB | dataval | EE_SHIFT_CLK);
+ rtems_bsp_delay_in_bus_cycles(200);
+ st_le32(ioaddr, EE_ENB | dataval); /* Finish EEPROM a clock tick. */
+ rtems_bsp_delay_in_bus_cycles(200);
+ }
+ st_le32(ioaddr, EE_ENB);
+
+ for (i = 16; i > 0; i--) {
+ st_le32(ioaddr, EE_ENB | EE_SHIFT_CLK);
+ rtems_bsp_delay_in_bus_cycles(200);
+ retval = (retval << 1) | ((ld_le32(ioaddr) & EE_DATA_READ) ? 1 : 0);
+ st_le32(ioaddr, EE_ENB);
+ rtems_bsp_delay_in_bus_cycles(200);
+ }
+
+ /* Terminate the EEPROM access. */
+ st_le32(ioaddr, EE_ENB & ~EE_CS);
+ return ( ((retval<<8)&0xff00) | ((retval>>8)&0xff) );
+}
+
+static void no_op(const rtems_irq_connect_data* irq)
+{
+ return;
+}
+
+/*
+ * DEC21140 interrupt handler
+ */
+static rtems_isr
+dec21140Enet_interrupt_handler ( struct dec21140_softc *sc )
+{
+ volatile uint32_t *tbase;
+ uint32_t status;
+
+ tbase = (uint32_t*)(sc->base);
+
+ /*
+ * Read status
+ */
+ status = ld_le32(tbase+memCSR5);
+ st_le32((tbase+memCSR5), status);
+
+ /*
+ * Frame received?
+ */
+ if( status & 0x000000c0 )
+ {
+ sc->rxInterrupts++;
+ rtems_bsdnet_event_send(rxDaemonTid, sc->ioevent);
+ }
+}
+
+static rtems_isr
+dec21140Enet_interrupt_handler_entry(void)
+{
+ int i;
+
+ /*
+ ** Check all the initialized dec units for interrupt service
+ */
+
+ for(i=0; i< NDECDRIVER; i++ )
+ {
+ if( dec21140_softc[i].base )
+ dec21140Enet_interrupt_handler( &dec21140_softc[i] );
+ }
+}
+
+/*
+ * Initialize the ethernet hardware
+ */
+static void
+dec21140Enet_initialize_hardware (struct dec21140_softc *sc)
+{
+ int i,st;
+ volatile uint32_t *tbase;
+ volatile unsigned char *cp, *setup_frm, *eaddrs;
+ volatile unsigned char *buffer;
+ volatile struct MD *rmd;
+
+
+#ifdef DEC_DEBUG
+ printk("dec2114x : %02x:%02x:%02x:%02x:%02x:%02x name '%s%d', io %x, mem %x, int %d\n",
+ sc->arpcom.ac_enaddr[0], sc->arpcom.ac_enaddr[1],
+ sc->arpcom.ac_enaddr[2], sc->arpcom.ac_enaddr[3],
+ sc->arpcom.ac_enaddr[4], sc->arpcom.ac_enaddr[5],
+ sc->arpcom.ac_if.if_name, sc->arpcom.ac_if.if_unit,
+ sc->port, (unsigned) sc->base, sc->irqInfo.name );
+#endif
+
+ tbase = sc->base;
+
+ /*
+ * WARNING : First write in CSR6
+ * Then Reset the chip ( 1 in CSR0)
+ */
+ st_le32( (tbase+memCSR6), CSR6_INIT);
+ st_le32( (tbase+memCSR0), RESET_CHIP);
+ rtems_bsp_delay_in_bus_cycles(200);
+
+ st_le32( (tbase+memCSR7), NO_IT);
+
+ /*
+ * Init CSR0
+ */
+ st_le32( (tbase+memCSR0), CSR0_MODE);
+
+ /*
+ * Init RX ring
+ */
+ cp = (volatile unsigned char *)malloc(((sc->numRxbuffers+sc->numTxbuffers)*sizeof(struct MD))
+ + (sc->numTxbuffers*RBUF_SIZE)
+ + CPU_CACHE_ALIGNMENT_FOR_BUFFER);
+ sc->bufferBase = cp;
+ cp += (CPU_CACHE_ALIGNMENT_FOR_BUFFER - (int)cp) & (CPU_CACHE_ALIGNMENT_FOR_BUFFER - 1);
+#if defined(__i386__)
+#ifdef PCI_BRIDGE_DOES_NOT_ENSURE_CACHE_COHERENCY_FOR_DMA
+ if (_CPU_is_paging_enabled())
+ _CPU_change_memory_mapping_attribute
+ (NULL, cp,
+ ((sc->numRxbuffers+sc->numTxbuffers)*sizeof(struct MD))
+ + (sc->numTxbuffers*RBUF_SIZE),
+ PTE_CACHE_DISABLE | PTE_WRITABLE);
+#endif
+#endif
+ rmd = (volatile struct MD*)cp;
+ sc->MDbase = rmd;
+ sc->nextRxMD = sc->MDbase;
+
+ buffer = cp + ((sc->numRxbuffers+sc->numTxbuffers)*sizeof(struct MD));
+ st_le32( (tbase+memCSR3), (long)(phys_to_bus((long)(sc->MDbase))));
+
+ for (i=0 ; i<sc->numRxbuffers; i++)
+ {
+ struct mbuf *m;
+
+ /* allocate an mbuf for each receive descriptor */
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ rmd->m = m;
+
+ rmd->buf2 = phys_to_bus(rmd+1);
+ rmd->buf1 = phys_to_bus(mtod(m, void *));
+ rmd->status = 0x80000000;
+ rmd->counts = 0xfdc00000 | (RBUF_SIZE);
+ rmd->next = rmd+1;
+ rmd++;
+ }
+ /*
+ * mark last RX buffer.
+ */
+ sc->MDbase [sc->numRxbuffers-1].buf2 = 0;
+ sc->MDbase [sc->numRxbuffers-1].counts = 0xfec00000 | (RBUF_SIZE);
+ sc->MDbase [sc->numRxbuffers-1].next = sc->MDbase;
+
+
+
+ /*
+ * Init TX ring
+ */
+ st_le32( (tbase+memCSR4), (long)(phys_to_bus((long)(rmd))) );
+ for (i=0 ; i<sc->numTxbuffers; i++){
+ (rmd+i)->buf2 = phys_to_bus(rmd+i+1);
+ (rmd+i)->buf1 = phys_to_bus(buffer + (i*RBUF_SIZE));
+ (rmd+i)->counts = 0x01000000;
+ (rmd+i)->status = 0x0;
+ (rmd+i)->next = rmd+i+1;
+ (rmd+i)->m = 0;
+ }
+
+ /*
+ * mark last TX buffer.
+ */
+ (rmd+sc->numTxbuffers-1)->buf2 = phys_to_bus(rmd);
+ (rmd+sc->numTxbuffers-1)->next = rmd;
+
+
+ /*
+ * Build setup frame
+ */
+ setup_frm = (volatile unsigned char *)(bus_to_phys(rmd->buf1));
+ eaddrs = (unsigned char *)(sc->arpcom.ac_enaddr);
+ /* Fill the buffer with our physical address. */
+ for (i = 1; i < 16; i++) {
+ *setup_frm++ = eaddrs[0];
+ *setup_frm++ = eaddrs[1];
+ *setup_frm++ = eaddrs[0];
+ *setup_frm++ = eaddrs[1];
+ *setup_frm++ = eaddrs[2];
+ *setup_frm++ = eaddrs[3];
+ *setup_frm++ = eaddrs[2];
+ *setup_frm++ = eaddrs[3];
+ *setup_frm++ = eaddrs[4];
+ *setup_frm++ = eaddrs[5];
+ *setup_frm++ = eaddrs[4];
+ *setup_frm++ = eaddrs[5];
+ }
+
+ /* Add the broadcast address when doing perfect filtering */
+ memset((void*) setup_frm, 0xff, 12);
+ rmd->counts = 0x09000000 | 192 ;
+ rmd->status = 0x80000000;
+ st_le32( (tbase+memCSR6), CSR6_INIT | CSR6_TX);
+ st_le32( (tbase+memCSR1), 1);
+
+ while (rmd->status != 0x7fffffff);
+ rmd->counts = 0x01000000;
+
+ sc->TxMD = rmd+1;
+
+ sc->irqInfo.hdl = (rtems_irq_hdl)dec21140Enet_interrupt_handler_entry;
+ sc->irqInfo.on = no_op;
+ sc->irqInfo.off = no_op;
+ sc->irqInfo.isOn = NULL;
+
+#ifdef DEC_DEBUG
+ printk( "dec2114x: Installing IRQ %d\n", sc->irqInfo.name );
+#endif
+#ifdef BSP_SHARED_HANDLER_SUPPORT
+ st = BSP_install_rtems_shared_irq_handler( &sc->irqInfo );
+#else
+ st = BSP_install_rtems_irq_handler( &sc->irqInfo );
+#endif
+
+ if (!st)
+ rtems_panic ("dec2114x : Interrupt name %d already in use\n", sc->irqInfo.name );
+}
+
+static void
+dec21140_rxDaemon (void *arg)
+{
+ volatile struct MD *rmd;
+ struct dec21140_softc *sc;
+ struct ifnet *ifp;
+ struct ether_header *eh;
+ struct mbuf *m;
+ unsigned int i,len;
+ rtems_event_set events;
+
+ for (;;)
+ {
+
+ rtems_bsdnet_event_receive( RTEMS_ALL_EVENTS,
+ RTEMS_WAIT|RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events);
+
+ for(i=0; i< NDECDRIVER; i++ )
+ {
+ sc = &dec21140_softc[i];
+ if( sc->base )
+ {
+ if( events & sc->ioevent )
+ {
+ ifp = &sc->arpcom.ac_if;
+ rmd = sc->nextRxMD;
+
+ /*
+ ** Read off all the packets we've received on this unit
+ */
+ while((rmd->status & 0x80000000) == 0)
+ {
+ /* printk("unit %i rx\n", ifp->if_unit ); */
+
+ /* pass on the packet in the mbuf */
+ len = (rmd->status >> 16) & 0x7ff;
+ m = (struct mbuf *)(rmd->m);
+ m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header);
+ eh = mtod (m, struct ether_header *);
+ m->m_data += sizeof(struct ether_header);
+ ether_input (ifp, eh, m);
+
+ /* get a new mbuf for the 21140 */
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = ifp;
+ rmd->m = m;
+ rmd->buf1 = phys_to_bus(mtod(m, void *));
+
+ /* mark the descriptor as ready to receive */
+ rmd->status = 0x80000000;
+
+ rmd=rmd->next;
+ }
+
+ sc->nextRxMD = rmd;
+ }
+ }
+ }
+
+ }
+}
+
+static void
+sendpacket (struct ifnet *ifp, struct mbuf *m)
+{
+ struct dec21140_softc *dp = ifp->if_softc;
+ volatile struct MD *tmd;
+ volatile unsigned char *temp;
+ struct mbuf *n;
+ unsigned int len;
+ volatile uint32_t *tbase;
+
+ tbase = dp->base;
+ /*
+ * Waiting for Transmitter ready
+ */
+
+ tmd = dp->TxMD;
+ n = m;
+
+ while ((tmd->status & 0x80000000) != 0)
+ {
+ tmd=tmd->next;
+ }
+
+ len = 0;
+ temp = (volatile unsigned char *)(bus_to_phys(tmd->buf1));
+
+ for (;;)
+ {
+ len += m->m_len;
+ memcpy((void*) temp, (char *)m->m_data, m->m_len);
+ temp += m->m_len ;
+ if ((m = m->m_next) == NULL)
+ break;
+ }
+
+ if (len < ET_MINLEN) len = ET_MINLEN;
+ tmd->counts = 0xe1000000 | (len & 0x7ff);
+ tmd->status = 0x80000000;
+
+ st_le32( (tbase+memCSR1), 0x1);
+
+ m_freem(n);
+
+ dp->TxMD = tmd->next;
+}
+
+/*
+ * Driver transmit daemon
+ */
+void
+dec21140_txDaemon (void *arg)
+{
+ struct dec21140_softc *sc;
+ struct ifnet *ifp;
+ struct mbuf *m;
+ int i;
+ rtems_event_set events;
+
+ for (;;)
+ {
+ /*
+ * Wait for packets bound for any of the dec units
+ */
+ rtems_bsdnet_event_receive( RTEMS_ALL_EVENTS,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT, &events);
+
+ for(i=0; i< NDECDRIVER; i++ )
+ {
+ sc = &dec21140_softc[i];
+ if( sc->base )
+ {
+ if( events & sc->ioevent )
+ {
+ ifp = &sc->arpcom.ac_if;
+
+ /*
+ * Send packets till queue is empty
+ */
+ for(;;)
+ {
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if( !m ) break;
+ /* printk("unit %i tx\n", ifp->if_unit ); */
+ sendpacket (ifp, m);
+ }
+
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+ }
+ }
+
+ }
+}
+
+static void
+dec21140_start (struct ifnet *ifp)
+{
+ struct dec21140_softc *sc = ifp->if_softc;
+ rtems_bsdnet_event_send( txDaemonTid, sc->ioevent );
+ ifp->if_flags |= IFF_OACTIVE;
+}
+
+/*
+ * Initialize and start the device
+ */
+static void
+dec21140_init (void *arg)
+{
+ struct dec21140_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ volatile uint32_t *tbase;
+
+ /*
+ * Set up DEC21140 hardware if its not already been done
+ */
+ if( !sc->irqInfo.hdl )
+ {
+ dec21140Enet_initialize_hardware (sc);
+ }
+
+ /*
+ * Enable RX and TX
+ */
+ tbase = sc->base;
+ st_le32( (tbase+memCSR5), IT_SETUP);
+ st_le32( (tbase+memCSR7), IT_SETUP);
+ st_le32( (tbase+memCSR6), CSR6_INIT | CSR6_TXRX);
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+}
+
+/*
+ * Stop the device
+ */
+static void
+dec21140_stop (struct dec21140_softc *sc)
+{
+ volatile uint32_t *tbase;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /*
+ * Stop the transmitter
+ */
+ tbase = sc->base;
+ st_le32( (tbase+memCSR7), NO_IT);
+ st_le32( (tbase+memCSR6), CSR6_INIT);
+
+ /* free((void*)sc->bufferBase); */
+}
+
+/*
+ * Show interface statistics
+ */
+static void
+dec21140_stats (struct dec21140_softc *sc)
+{
+ printf (" Rx Interrupts:%-8lu", sc->rxInterrupts);
+ printf (" Not First:%-8lu", sc->rxNotFirst);
+ printf (" Not Last:%-8lu\n", sc->rxNotLast);
+ printf (" Giant:%-8lu", sc->rxGiant);
+ printf (" Runt:%-8lu", sc->rxRunt);
+ printf (" Non-octet:%-8lu\n", sc->rxNonOctet);
+ printf (" Bad CRC:%-8lu", sc->rxBadCRC);
+ printf (" Overrun:%-8lu", sc->rxOverrun);
+ printf (" Collision:%-8lu\n", sc->rxCollision);
+
+ printf (" Tx Interrupts:%-8lu", sc->txInterrupts);
+ printf (" Deferred:%-8lu", sc->txDeferred);
+ printf (" Missed Hearbeat:%-8lu\n", sc->txHeartbeat);
+ printf (" No Carrier:%-8lu", sc->txLostCarrier);
+ printf ("Retransmit Limit:%-8lu", sc->txRetryLimit);
+ printf (" Late Collision:%-8lu\n", sc->txLateCollision);
+ printf (" Underrun:%-8lu", sc->txUnderrun);
+ printf (" Raw output wait:%-8lu\n", sc->txRawWait);
+}
+
+/*
+ * Driver ioctl handler
+ */
+static int
+dec21140_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct dec21140_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command) {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl (ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
+ case IFF_RUNNING:
+ dec21140_stop (sc);
+ break;
+
+ case IFF_UP:
+ dec21140_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ dec21140_stop (sc);
+ dec21140_init (sc);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ dec21140_stats (sc);
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+
+/*
+int iftap(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m )
+{
+ int i;
+
+ if( ifp->if_unit == 1 ) return 0;
+
+ printf("unit %i, src ", ifp->if_unit );
+ for(i=0; i< ETHER_ADDR_LEN; i++) printf("%02x", (char) eh->ether_shost[i] );
+ printf(" dest ");
+ for(i=0; i< ETHER_ADDR_LEN; i++) printf("%02x", (char) eh->ether_dhost[i] );
+ printf("\n");
+
+ return -1;
+}
+*/
+
+/*
+ * Attach an DEC21140 driver to the system
+ */
+int
+rtems_dec21140_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
+{
+ struct dec21140_softc *sc;
+ struct ifnet *ifp;
+ char *unitName;
+ int unitNumber;
+ int mtu;
+ unsigned char cvalue;
+#if defined(__i386__)
+ uint32_t value;
+ uint8_t interrupt;
+#endif
+ int pbus, pdev, pfun;
+#if defined(__PPC__)
+ int tmp;
+ uint32_t lvalue;
+#endif
+
+ /*
+ * Get the instance number for the board we're going to configure
+ * from the user.
+ */
+ if( (unitNumber = rtems_bsdnet_parse_driver_name(config, &unitName)) == -1 )
+ {
+ return 0;
+ }
+ if( strcmp(unitName, DRIVER_PREFIX) )
+ {
+ printk("dec2114x : unit name '%s' not %s\n", unitName, DRIVER_PREFIX );
+ return 0;
+ }
+
+ /*
+ * Find the board
+ */
+ if ( pci_find_device(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21140,
+ unitNumber-1, &pbus, &pdev, &pfun) == -1 ) {
+ if ( pci_find_device(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21143,
+ unitNumber-1, &pbus, &pdev, &pfun) != -1 ) {
+
+ /* the 21143 chip must be enabled before it can be accessed */
+#if defined(__i386__)
+ pci_write_config_dword(pbus, pdev, pfun, 0x40, 0 );
+#else
+ pci_write_config_dword(pbus, pdev, pfun, 0x40, PCI_DEVICE_ID_DEC_21143);
+#endif
+
+ } else {
+ printk("dec2114x : device '%s' not found on PCI bus\n", config->name );
+ return 0;
+ }
+ }
+
+#ifdef DEC_DEBUG
+ else {
+ printk("dec21140 : found device '%s', bus 0x%02x, dev 0x%02x, func 0x%02x\n",
+ config->name, pbus, pdev, pfun);
+ }
+#endif
+
+ if ((unitNumber < 1) || (unitNumber > NDECDRIVER))
+ {
+ printk("dec2114x : unit %i is invalid, must be (1 <= n <= %d)\n",
+ unitNumber, NDECDRIVER);
+ return 0;
+ }
+
+ sc = &dec21140_softc[unitNumber - 1];
+ ifp = &sc->arpcom.ac_if;
+ if (ifp->if_softc != NULL)
+ {
+ printk("dec2114x : unit %i already in use.\n", unitNumber );
+ return 0;
+ }
+
+
+ /*
+ ** Get this unit's rx/tx event
+ */
+ sc->ioevent = unit_signals[unitNumber-1];
+
+ /*
+ ** Save the buffer counts
+ */
+ sc->numRxbuffers = (config->rbuf_count) ? config->rbuf_count : NRXBUFS;
+ sc->numTxbuffers = (config->xbuf_count) ? config->xbuf_count : NTXBUFS;
+
+
+ /*
+ * Get card address spaces & retrieve its isr vector
+ */
+#if defined(__i386__)
+
+ pci_read_config_dword(pbus, pdev, pfun, 16, &value);
+ sc->port = value & ~IO_MASK;
+
+ pci_read_config_dword(pbus, pdev, pfun, 20, &value);
+ if (_CPU_is_paging_enabled())
+ _CPU_map_phys_address((void **) &(sc->base),
+ (void *)(value & ~MEM_MASK),
+ DEC_REGISTER_SIZE ,
+ PTE_CACHE_DISABLE | PTE_WRITABLE);
+ else
+ sc->base = (uint32_t *)(value & ~MEM_MASK);
+
+ pci_read_config_byte(pbus, pdev, pfun, 60, &interrupt);
+ cvalue = interrupt;
+#endif
+#if defined(__PPC__)
+ (void)pci_read_config_dword(pbus,
+ pdev,
+ pfun,
+ PCI_BASE_ADDRESS_0,
+ &lvalue);
+
+ sc->port = lvalue & (unsigned int)(~IO_MASK);
+
+ (void)pci_read_config_dword(pbus,
+ pdev,
+ pfun,
+ PCI_BASE_ADDRESS_1,
+ &lvalue);
+
+ tmp = (unsigned int)(lvalue & (unsigned int)(~MEM_MASK))
+ + (unsigned int)PCI_MEM_BASE;
+
+ sc->base = (uint32_t*)(tmp);
+
+ pci_read_config_byte(pbus,
+ pdev,
+ pfun,
+ PCI_INTERRUPT_LINE,
+ &cvalue);
+
+#endif
+
+ /*
+ ** Prep the board
+ */
+
+ pci_write_config_word(pbus, pdev, pfun,
+ PCI_COMMAND,
+ (uint16_t) ( PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER ) );
+
+ /*
+ ** Store the interrupt name, we'll use it later when we initialize
+ ** the board.
+ */
+ memset(&sc->irqInfo,0,sizeof(rtems_irq_connect_data));
+ sc->irqInfo.name = cvalue;
+
+
+#ifdef DEC_DEBUG
+ printk("dec2114x : unit %d base address %p.\n", unitNumber, sc->base);
+#endif
+
+
+ /*
+ ** Setup ethernet address
+ */
+ if (config->hardware_address) {
+ memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
+ ETHER_ADDR_LEN);
+ }
+ else {
+ union {char c[64]; unsigned short s[32];} rombuf;
+ int i;
+
+ for (i=0; i<32; i++){
+ rombuf.s[i] = eeget16( sc->base + memCSR9, i);
+ }
+#if defined(__i386__)
+ for (i=0 ; i<(ETHER_ADDR_LEN/2); i++){
+ sc->arpcom.ac_enaddr[2*i] = rombuf.c[20+2*i+1];
+ sc->arpcom.ac_enaddr[2*i+1] = rombuf.c[20+2*i];
+ }
+#endif
+#if defined(__PPC__)
+ memcpy (sc->arpcom.ac_enaddr, rombuf.c+20, ETHER_ADDR_LEN);
+#endif
+ }
+
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ sc->acceptBroadcast = !config->ignore_broadcast;
+
+ /*
+ * Set up network interface values
+ */
+
+/* ifp->if_tap = iftap; */
+
+ ifp->if_softc = sc;
+ ifp->if_unit = unitNumber;
+ ifp->if_name = unitName;
+ ifp->if_mtu = mtu;
+ ifp->if_init = dec21140_init;
+ ifp->if_ioctl = dec21140_ioctl;
+ ifp->if_start = dec21140_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);
+
+#ifdef DEC_DEBUG
+ printk( "dec2114x : driver attached\n" );
+#endif
+
+ /*
+ * Start driver tasks if this is the first dec unit initialized
+ */
+ if (txDaemonTid == 0)
+ {
+ rxDaemonTid = rtems_bsdnet_newproc( "DCrx", 4096,
+ dec21140_rxDaemon, NULL);
+
+ txDaemonTid = rtems_bsdnet_newproc( "DCtx", 4096,
+ dec21140_txDaemon, NULL);
+#ifdef DEC_DEBUG
+ printk( "dec2114x : driver tasks created\n" );
+#endif
+ }
+
+ return 1;
+};
+
+#endif /* DEC21140_SUPPORTED */
diff --git a/bsps/shared/net/elnk.c b/bsps/shared/net/elnk.c
new file mode 100644
index 0000000..85af4b5
--- /dev/null
+++ b/bsps/shared/net/elnk.c
@@ -0,0 +1,3553 @@
+/*
+ * RTEMS driver for Etherlink based Ethernet Controllers
+ *
+ * Copyright (C) 2003, Gregory Menke, NASA/GSFC
+ *
+ * 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.
+ *
+ * elnk.c
+ *
+ *
+ */
+
+
+/*
+ * Portions of this driver are taken from the Freebsd if_xl.c driver,
+ * version "1.133 2003/03/19 01:48:14" and are covered by the license
+ * text included below that was taken verbatim from the original file.
+ * More particularly, all structures, variables, and #defines prefixed
+ * with XL_ or xl_, along with their associated comments were taken
+ * directly from the Freebsd driver and modified as required to suit the
+ * purposes of this one. Additionally, much of the device setup &
+ * manipulation logic was also copied and modified to suit. All types
+ * and functions beginning with elnk are either my own creations or were
+ * adapted from other RTEMS components, and regardless, are subject to
+ * the standard OAR licensing terms given in the comments at the top of
+ * this file.
+ *
+ * Greg Menke, 6/11/2003
+ */
+
+ /*
+ * Copyright (c) 1997, 1998, 1999
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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 <machine/rtems-bsd-kernel-space.h>
+
+#include <rtems.h>
+
+/*
+ * This driver only supports architectures with the new style
+ * exception processing. The following checks try to keep this
+ * from being compiled on systems which can't support this driver.
+ */
+
+#if defined(__i386__)
+#define ELNK_SUPPORTED
+ #define PCI_DRAM_OFFSET 0
+#endif
+
+#if defined(__PPC__)
+#define ELNK_SUPPORTED
+#endif
+
+#include <bsp.h>
+
+#if !defined(PCI_DRAM_OFFSET)
+ #undef ELNK_SUPPORTED
+#endif
+
+/* #undef ELNK_SUPPORTED */
+
+
+#if defined(ELNK_SUPPORTED)
+#include <rtems/pci.h>
+
+#if defined(__PPC__)
+#include <libcpu/byteorder.h>
+#include <libcpu/io.h>
+#endif
+
+#if defined(__i386__)
+#include <libcpu/byteorder.h>
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <rtems/error.h>
+#include <rtems/bspIo.h>
+#include <rtems/rtems_bsdnet.h>
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <net/if_media.h>
+#include <dev/mii/mii.h>
+#include <bsp/irq.h>
+
+#if defined(__i386__)
+#define IO_MASK 0x3
+#define MEM_MASK 0xF
+#endif
+
+#ifdef malloc
+#undef malloc
+#endif
+#ifdef free
+#undef free
+#endif
+
+#define ELNK_DEBUG
+
+
+#define DRIVER_PREFIX "elnk"
+
+
+
+
+
+/*
+* These buffers allocated for each unit, so ensure
+*
+* rtems_bsdnet_config.mbuf_bytecount
+* rtems_bsdnet_config.mbuf_cluster_bytecount
+*
+* are adequately sized to provide enough clusters and mbufs for all the
+* units. The default bsdnet configuration is sufficient for one unit,
+* but will be nearing exhaustion with 2 or more. Although a little
+* expensive in memory, the following configuration should eliminate all
+* mbuf/cluster issues;
+*
+* rtems_bsdnet_config.mbuf_bytecount = 128*1024;
+* rtems_bsdnet_config.mbuf_cluster_bytecount = 256*1024;
+*
+* The default size in buffers of the rx & tx rings are given below.
+* This driver honors the rtems_bsdnet_ifconfig fields 'rbuf_count' and
+* 'xbuf_count', allowing the user to specify something else.
+*/
+
+#define RX_RING_SIZE 16 /* default number of receive buffers */
+#define TX_RING_SIZE 16 /* default number of transmit buffers */
+
+
+/*
+ * Number of boards supported by this driver
+ */
+#define NUM_UNITS 8
+
+/*
+ * Receive buffer size -- Allow for a full ethernet packet including CRC
+ */
+#define XL_PACKET_SIZE 1540
+
+
+/*
+** Events, one per unit. The event is sent to the rx task from the isr
+** or from the stack to the tx task whenever a unit needs service. The
+** rx/tx tasks identify the requesting unit(s) by their particular
+** events so only requesting units are serviced.
+*/
+
+static rtems_event_set unit_signals[NUM_UNITS]= { RTEMS_EVENT_1,
+ RTEMS_EVENT_2,
+ RTEMS_EVENT_3,
+ RTEMS_EVENT_4,
+ RTEMS_EVENT_5,
+ RTEMS_EVENT_6,
+ RTEMS_EVENT_7,
+ RTEMS_EVENT_8 };
+
+
+
+
+#if defined(__PPC__)
+#define phys_to_bus(address) ((unsigned int)((address)) + PCI_DRAM_OFFSET)
+#define bus_to_phys(address) ((unsigned int)((address)) - PCI_DRAM_OFFSET)
+#define CPU_CACHE_ALIGNMENT_FOR_BUFFER PPC_CACHE_ALIGNMENT
+#else
+extern void Wait_X_ms( unsigned int timeToWait );
+#define phys_to_bus(address) ((unsigned int) ((address)))
+#define bus_to_phys(address) ((unsigned int) ((address)))
+#define rtems_bsp_delay_in_bus_cycles(cycle) Wait_X_ms( cycle/100 )
+#define CPU_CACHE_ALIGNMENT_FOR_BUFFER PG_SIZE
+#endif
+
+/* the actual duration waited in DELAY is not especially predictable,
+ * though it will be consistent on a given host. It should not be
+ * relied upon for specific timing given the vague per-bsp,
+ * per-architecture implementation of the actual delay function. It
+ * would probably be helpful to make this more accurate at some point...
+ */
+#define DELAY(n) rtems_bsp_delay_in_bus_cycles( n*20 )
+
+
+
+
+/*
+ * Register layouts.
+ */
+#define XL_COMMAND 0x0E
+#define XL_STATUS 0x0E
+
+#define XL_TX_STATUS 0x1B
+#define XL_TX_FREE 0x1C
+#define XL_DMACTL 0x20
+#define XL_DOWNLIST_PTR 0x24
+#define XL_DOWN_POLL 0x2D /* 3c90xB only */
+#define XL_TX_FREETHRESH 0x2F
+#define XL_UPLIST_PTR 0x38
+#define XL_UPLIST_STATUS 0x30
+#define XL_UP_POLL 0x3D /* 3c90xB only */
+
+#define XL_PKTSTAT_UP_STALLED 0x00002000
+#define XL_PKTSTAT_UP_ERROR 0x00004000
+#define XL_PKTSTAT_UP_CMPLT 0x00008000
+
+#define XL_DMACTL_DN_CMPLT_REQ 0x00000002
+#define XL_DMACTL_DOWN_STALLED 0x00000004
+#define XL_DMACTL_UP_CMPLT 0x00000008
+#define XL_DMACTL_DOWN_CMPLT 0x00000010
+#define XL_DMACTL_UP_RX_EARLY 0x00000020
+#define XL_DMACTL_ARM_COUNTDOWN 0x00000040
+#define XL_DMACTL_DOWN_INPROG 0x00000080
+#define XL_DMACTL_COUNTER_SPEED 0x00000100
+#define XL_DMACTL_DOWNDOWN_MODE 0x00000200
+#define XL_DMACTL_TARGET_ABORT 0x40000000
+#define XL_DMACTL_MASTER_ABORT 0x80000000
+
+/*
+ * Command codes. Some command codes require that we wait for
+ * the CMD_BUSY flag to clear. Those codes are marked as 'mustwait.'
+ */
+#define XL_CMD_RESET 0x0000 /* mustwait */
+#define XL_CMD_WINSEL 0x0800
+#define XL_CMD_COAX_START 0x1000
+#define XL_CMD_RX_DISABLE 0x1800
+#define XL_CMD_RX_ENABLE 0x2000
+#define XL_CMD_RX_RESET 0x2800 /* mustwait */
+#define XL_CMD_UP_STALL 0x3000 /* mustwait */
+#define XL_CMD_UP_UNSTALL 0x3001
+#define XL_CMD_DOWN_STALL 0x3002 /* mustwait */
+#define XL_CMD_DOWN_UNSTALL 0x3003
+#define XL_CMD_RX_DISCARD 0x4000
+#define XL_CMD_TX_ENABLE 0x4800
+#define XL_CMD_TX_DISABLE 0x5000
+#define XL_CMD_TX_RESET 0x5800 /* mustwait */
+#define XL_CMD_INTR_FAKE 0x6000
+#define XL_CMD_INTR_ACK 0x6800
+#define XL_CMD_INTR_ENB 0x7000
+#define XL_CMD_STAT_ENB 0x7800
+#define XL_CMD_RX_SET_FILT 0x8000
+#define XL_CMD_RX_SET_THRESH 0x8800
+#define XL_CMD_TX_SET_THRESH 0x9000
+#define XL_CMD_TX_SET_START 0x9800
+#define XL_CMD_DMA_UP 0xA000
+#define XL_CMD_DMA_STOP 0xA001
+#define XL_CMD_STATS_ENABLE 0xA800
+#define XL_CMD_STATS_DISABLE 0xB000
+#define XL_CMD_COAX_STOP 0xB800
+
+#define XL_CMD_SET_TX_RECLAIM 0xC000 /* 3c905B only */
+#define XL_CMD_RX_SET_HASH 0xC800 /* 3c905B only */
+
+#define XL_HASH_SET 0x0400
+#define XL_HASHFILT_SIZE 256
+
+/*
+ * status codes
+ * Note that bits 15 to 13 indicate the currently visible register window
+ * which may be anything from 0 to 7.
+ */
+#define XL_STAT_INTLATCH 0x0001 /* 0 */
+#define XL_STAT_ADFAIL 0x0002 /* 1 */
+#define XL_STAT_TX_COMPLETE 0x0004 /* 2 */
+#define XL_STAT_TX_AVAIL 0x0008 /* 3 first generation */
+#define XL_STAT_RX_COMPLETE 0x0010 /* 4 */
+#define XL_STAT_RX_EARLY 0x0020 /* 5 */
+#define XL_STAT_INTREQ 0x0040 /* 6 */
+#define XL_STAT_STATSOFLOW 0x0080 /* 7 */
+#define XL_STAT_DMADONE 0x0100 /* 8 first generation */
+#define XL_STAT_LINKSTAT 0x0100 /* 8 3c509B */
+#define XL_STAT_DOWN_COMPLETE 0x0200 /* 9 */
+#define XL_STAT_UP_COMPLETE 0x0400 /* 10 */
+#define XL_STAT_DMABUSY 0x0800 /* 11 first generation */
+#define XL_STAT_CMDBUSY 0x1000 /* 12 */
+
+/*
+ * Interrupts we normally want enabled.
+ */
+#define XL_INTRS \
+ (XL_STAT_UP_COMPLETE | XL_STAT_STATSOFLOW | XL_STAT_ADFAIL| \
+ XL_STAT_DOWN_COMPLETE | XL_STAT_TX_COMPLETE | XL_STAT_INTLATCH)
+
+
+
+/*
+ * General constants that are fun to know.
+ *
+ * 3Com PCI vendor ID
+ */
+#define TC_VENDORID 0x10B7
+
+/*
+ * 3Com chip device IDs.
+ */
+#define TC_DEVICEID_BOOMERANG_10BT 0x9000
+#define TC_DEVICEID_BOOMERANG_10BT_COMBO 0x9001
+#define TC_DEVICEID_BOOMERANG_10_100BT 0x9050
+#define TC_DEVICEID_BOOMERANG_100BT4 0x9051
+#define TC_DEVICEID_KRAKATOA_10BT 0x9004
+#define TC_DEVICEID_KRAKATOA_10BT_COMBO 0x9005
+#define TC_DEVICEID_KRAKATOA_10BT_TPC 0x9006
+#define TC_DEVICEID_CYCLONE_10FL 0x900A
+#define TC_DEVICEID_HURRICANE_10_100BT 0x9055
+#define TC_DEVICEID_CYCLONE_10_100BT4 0x9056
+#define TC_DEVICEID_CYCLONE_10_100_COMBO 0x9058
+#define TC_DEVICEID_CYCLONE_10_100FX 0x905A
+#define TC_DEVICEID_TORNADO_10_100BT 0x9200
+#define TC_DEVICEID_TORNADO_10_100BT_920B 0x9201
+#define TC_DEVICEID_HURRICANE_10_100BT_SERV 0x9800
+#define TC_DEVICEID_TORNADO_10_100BT_SERV 0x9805
+#define TC_DEVICEID_HURRICANE_SOHO100TX 0x7646
+#define TC_DEVICEID_TORNADO_HOMECONNECT 0x4500
+#define TC_DEVICEID_HURRICANE_555 0x5055
+#define TC_DEVICEID_HURRICANE_556 0x6055
+#define TC_DEVICEID_HURRICANE_556B 0x6056
+#define TC_DEVICEID_HURRICANE_575A 0x5057
+#define TC_DEVICEID_HURRICANE_575B 0x5157
+#define TC_DEVICEID_HURRICANE_575C 0x5257
+#define TC_DEVICEID_HURRICANE_656 0x6560
+#define TC_DEVICEID_HURRICANE_656B 0x6562
+#define TC_DEVICEID_TORNADO_656C 0x6564
+
+
+
+#define XL_RXSTAT_LENMASK 0x00001FFF
+#define XL_RXSTAT_UP_ERROR 0x00004000
+#define XL_RXSTAT_UP_CMPLT 0x00008000
+#define XL_RXSTAT_UP_OVERRUN 0x00010000
+#define XL_RXSTAT_RUNT 0x00020000
+#define XL_RXSTAT_ALIGN 0x00040000
+#define XL_RXSTAT_CRC 0x00080000
+#define XL_RXSTAT_OVERSIZE 0x00100000
+#define XL_RXSTAT_DRIBBLE 0x00800000
+#define XL_RXSTAT_UP_OFLOW 0x01000000
+#define XL_RXSTAT_IPCKERR 0x02000000 /* 3c905B only */
+#define XL_RXSTAT_TCPCKERR 0x04000000 /* 3c905B only */
+#define XL_RXSTAT_UDPCKERR 0x08000000 /* 3c905B only */
+#define XL_RXSTAT_BUFEN 0x10000000 /* 3c905B only */
+#define XL_RXSTAT_IPCKOK 0x20000000 /* 3c905B only */
+#define XL_RXSTAT_TCPCOK 0x40000000 /* 3c905B only */
+#define XL_RXSTAT_UDPCKOK 0x80000000 /* 3c905B only */
+
+#define XL_TXSTAT_LENMASK 0x00001FFF
+#define XL_TXSTAT_CRCDIS 0x00002000
+#define XL_TXSTAT_TX_INTR 0x00008000
+#define XL_TXSTAT_DL_COMPLETE 0x00010000
+#define XL_TXSTAT_IPCKSUM 0x02000000 /* 3c905B only */
+#define XL_TXSTAT_TCPCKSUM 0x04000000 /* 3c905B only */
+#define XL_TXSTAT_UDPCKSUM 0x08000000 /* 3c905B only */
+#define XL_TXSTAT_RND_DEFEAT 0x10000000 /* 3c905B only */
+#define XL_TXSTAT_EMPTY 0x20000000 /* 3c905B only */
+#define XL_TXSTAT_DL_INTR 0x80000000
+
+
+#define XL_FLAG_FUNCREG 0x0001
+#define XL_FLAG_PHYOK 0x0002
+#define XL_FLAG_EEPROM_OFFSET_30 0x0004
+#define XL_FLAG_WEIRDRESET 0x0008
+#define XL_FLAG_8BITROM 0x0010
+#define XL_FLAG_INVERT_LED_PWR 0x0020
+#define XL_FLAG_INVERT_MII_PWR 0x0040
+#define XL_FLAG_NO_XCVR_PWR 0x0080
+#define XL_FLAG_USE_MMIO 0x0100
+
+
+
+#define XL_EE_READ 0x0080 /* read, 5 bit address */
+#define XL_EE_WRITE 0x0040 /* write, 5 bit address */
+#define XL_EE_ERASE 0x00c0 /* erase, 5 bit address */
+#define XL_EE_EWEN 0x0030 /* erase, no data needed */
+#define XL_EE_8BIT_READ 0x0200 /* read, 8 bit address */
+#define XL_EE_BUSY 0x8000
+
+#define XL_EE_EADDR0 0x00 /* station address, first word */
+#define XL_EE_EADDR1 0x01 /* station address, next word, */
+#define XL_EE_EADDR2 0x02 /* station address, last word */
+#define XL_EE_PRODID 0x03 /* product ID code */
+#define XL_EE_MDATA_DATE 0x04 /* manufacturing data, date */
+#define XL_EE_MDATA_DIV 0x05 /* manufacturing data, division */
+#define XL_EE_MDATA_PCODE 0x06 /* manufacturing data, product code */
+#define XL_EE_MFG_ID 0x07
+#define XL_EE_PCI_PARM 0x08
+#define XL_EE_ROM_ONFO 0x09
+#define XL_EE_OEM_ADR0 0x0A
+#define XL_EE_OEM_ADR1 0x0B
+#define XL_EE_OEM_ADR2 0x0C
+#define XL_EE_SOFTINFO1 0x0D
+#define XL_EE_COMPAT 0x0E
+#define XL_EE_SOFTINFO2 0x0F
+#define XL_EE_CAPS 0x10 /* capabilities word */
+#define XL_EE_RSVD0 0x11
+#define XL_EE_ICFG_0 0x12
+#define XL_EE_ICFG_1 0x13
+#define XL_EE_RSVD1 0x14
+#define XL_EE_SOFTINFO3 0x15
+#define XL_EE_RSVD_2 0x16
+
+/*
+ * Bits in the capabilities word
+ */
+#define XL_CAPS_PNP 0x0001
+#define XL_CAPS_FULL_DUPLEX 0x0002
+#define XL_CAPS_LARGE_PKTS 0x0004
+#define XL_CAPS_SLAVE_DMA 0x0008
+#define XL_CAPS_SECOND_DMA 0x0010
+#define XL_CAPS_FULL_BM 0x0020
+#define XL_CAPS_FRAG_BM 0x0040
+#define XL_CAPS_CRC_PASSTHRU 0x0080
+#define XL_CAPS_TXDONE 0x0100
+#define XL_CAPS_NO_TXLENGTH 0x0200
+#define XL_CAPS_RX_REPEAT 0x0400
+#define XL_CAPS_SNOOPING 0x0800
+#define XL_CAPS_100MBPS 0x1000
+#define XL_CAPS_PWRMGMT 0x2000
+
+
+
+/*
+ * Window 0 registers
+ */
+#define XL_W0_EE_DATA 0x0C
+#define XL_W0_EE_CMD 0x0A
+#define XL_W0_RSRC_CFG 0x08
+#define XL_W0_ADDR_CFG 0x06
+#define XL_W0_CFG_CTRL 0x04
+
+#define XL_W0_PROD_ID 0x02
+#define XL_W0_MFG_ID 0x00
+
+/*
+ * Window 1
+ */
+
+#define XL_W1_TX_FIFO 0x10
+
+#define XL_W1_FREE_TX 0x0C
+#define XL_W1_TX_STATUS 0x0B
+#define XL_W1_TX_TIMER 0x0A
+#define XL_W1_RX_STATUS 0x08
+#define XL_W1_RX_FIFO 0x00
+
+/*
+ * RX status codes
+ */
+#define XL_RXSTATUS_OVERRUN 0x01
+#define XL_RXSTATUS_RUNT 0x02
+#define XL_RXSTATUS_ALIGN 0x04
+#define XL_RXSTATUS_CRC 0x08
+#define XL_RXSTATUS_OVERSIZE 0x10
+#define XL_RXSTATUS_DRIBBLE 0x20
+
+/*
+ * TX status codes
+ */
+#define XL_TXSTATUS_RECLAIM 0x02 /* 3c905B only */
+#define XL_TXSTATUS_OVERFLOW 0x04
+#define XL_TXSTATUS_MAXCOLS 0x08
+#define XL_TXSTATUS_UNDERRUN 0x10
+#define XL_TXSTATUS_JABBER 0x20
+#define XL_TXSTATUS_INTREQ 0x40
+#define XL_TXSTATUS_COMPLETE 0x80
+
+/*
+ * Window 2
+ */
+#define XL_W2_RESET_OPTIONS 0x0C /* 3c905B only */
+#define XL_W2_STATION_MASK_HI 0x0A
+#define XL_W2_STATION_MASK_MID 0x08
+#define XL_W2_STATION_MASK_LO 0x06
+#define XL_W2_STATION_ADDR_HI 0x04
+#define XL_W2_STATION_ADDR_MID 0x02
+#define XL_W2_STATION_ADDR_LO 0x00
+
+#define XL_RESETOPT_FEATUREMASK 0x0001|0x0002|0x004
+#define XL_RESETOPT_D3RESETDIS 0x0008
+#define XL_RESETOPT_DISADVFD 0x0010
+#define XL_RESETOPT_DISADV100 0x0020
+#define XL_RESETOPT_DISAUTONEG 0x0040
+#define XL_RESETOPT_DEBUGMODE 0x0080
+#define XL_RESETOPT_FASTAUTO 0x0100
+#define XL_RESETOPT_FASTEE 0x0200
+#define XL_RESETOPT_FORCEDCONF 0x0400
+#define XL_RESETOPT_TESTPDTPDR 0x0800
+#define XL_RESETOPT_TEST100TX 0x1000
+#define XL_RESETOPT_TEST100RX 0x2000
+
+#define XL_RESETOPT_INVERT_LED 0x0010
+#define XL_RESETOPT_INVERT_MII 0x4000
+
+/*
+ * Window 3 (fifo management)
+ */
+#define XL_W3_INTERNAL_CFG 0x00
+#define XL_W3_MAXPKTSIZE 0x04 /* 3c905B only */
+#define XL_W3_RESET_OPT 0x08
+#define XL_W3_FREE_TX 0x0C
+#define XL_W3_FREE_RX 0x0A
+#define XL_W3_MAC_CTRL 0x06
+
+#define XL_ICFG_CONNECTOR_MASK 0x00F00000
+#define XL_ICFG_CONNECTOR_BITS 20
+
+#define XL_ICFG_RAMSIZE_MASK 0x00000007
+#define XL_ICFG_RAMWIDTH 0x00000008
+#define XL_ICFG_ROMSIZE_MASK (0x00000040|0x00000080)
+#define XL_ICFG_DISABLE_BASSD 0x00000100
+#define XL_ICFG_RAMLOC 0x00000200
+#define XL_ICFG_RAMPART (0x00010000|0x00020000)
+#define XL_ICFG_XCVRSEL (0x00100000|0x00200000|0x00400000)
+#define XL_ICFG_AUTOSEL 0x01000000
+
+#define XL_XCVR_10BT 0x00
+#define XL_XCVR_AUI 0x01
+#define XL_XCVR_RSVD_0 0x02
+#define XL_XCVR_COAX 0x03
+#define XL_XCVR_100BTX 0x04
+#define XL_XCVR_100BFX 0x05
+#define XL_XCVR_MII 0x06
+#define XL_XCVR_RSVD_1 0x07
+#define XL_XCVR_AUTO 0x08 /* 3c905B only */
+
+#define XL_MACCTRL_DEFER_EXT_END 0x0001
+#define XL_MACCTRL_DEFER_0 0x0002
+#define XL_MACCTRL_DEFER_1 0x0004
+#define XL_MACCTRL_DEFER_2 0x0008
+#define XL_MACCTRL_DEFER_3 0x0010
+#define XL_MACCTRL_DUPLEX 0x0020
+#define XL_MACCTRL_ALLOW_LARGE_PACK 0x0040
+#define XL_MACCTRL_EXTEND_AFTER_COL 0x0080 (3c905B only)
+#define XL_MACCTRL_FLOW_CONTROL_ENB 0x0100 (3c905B only)
+#define XL_MACCTRL_VLT_END 0x0200 (3c905B only)
+
+/*
+ * The 'reset options' register contains power-on reset values
+ * loaded from the EEPROM. This includes the supported media
+ * types on the card. It is also known as the media options register.
+ */
+#define XL_W3_MEDIA_OPT 0x08
+
+#define XL_MEDIAOPT_BT4 0x0001 /* MII */
+#define XL_MEDIAOPT_BTX 0x0002 /* on-chip */
+#define XL_MEDIAOPT_BFX 0x0004 /* on-chip */
+#define XL_MEDIAOPT_BT 0x0008 /* on-chip */
+#define XL_MEDIAOPT_BNC 0x0010 /* on-chip */
+#define XL_MEDIAOPT_AUI 0x0020 /* on-chip */
+#define XL_MEDIAOPT_MII 0x0040 /* MII */
+#define XL_MEDIAOPT_VCO 0x0100 /* 1st gen chip only */
+
+#define XL_MEDIAOPT_10FL 0x0100 /* 3x905B only, on-chip */
+#define XL_MEDIAOPT_MASK 0x01FF
+
+/*
+ * Window 4 (diagnostics)
+ */
+#define XL_W4_UPPERBYTESOK 0x0D
+#define XL_W4_BADSSD 0x0C
+#define XL_W4_MEDIA_STATUS 0x0A
+#define XL_W4_PHY_MGMT 0x08
+#define XL_W4_NET_DIAG 0x06
+#define XL_W4_FIFO_DIAG 0x04
+#define XL_W4_VCO_DIAG 0x02
+
+#define XL_W4_CTRLR_STAT 0x08
+#define XL_W4_TX_DIAG 0x00
+
+#define XL_MII_CLK 0x01
+#define XL_MII_DATA 0x02
+#define XL_MII_DIR 0x04
+
+#define XL_MEDIA_SQE 0x0008
+#define XL_MEDIA_10TP 0x00C0
+#define XL_MEDIA_LNK 0x0080
+#define XL_MEDIA_LNKBEAT 0x0800
+
+#define XL_MEDIASTAT_CRCSTRIP 0x0004
+#define XL_MEDIASTAT_SQEENB 0x0008
+#define XL_MEDIASTAT_COLDET 0x0010
+#define XL_MEDIASTAT_CARRIER 0x0020
+#define XL_MEDIASTAT_JABGUARD 0x0040
+#define XL_MEDIASTAT_LINKBEAT 0x0080
+#define XL_MEDIASTAT_JABDETECT 0x0200
+#define XL_MEDIASTAT_POLREVERS 0x0400
+#define XL_MEDIASTAT_LINKDETECT 0x0800
+#define XL_MEDIASTAT_TXINPROG 0x1000
+#define XL_MEDIASTAT_DCENB 0x4000
+#define XL_MEDIASTAT_AUIDIS 0x8000
+
+#define XL_NETDIAG_TEST_LOWVOLT 0x0001
+#define XL_NETDIAG_ASIC_REVMASK (0x0002|0x0004|0x0008|0x0010|0x0020)
+#define XL_NETDIAG_UPPER_BYTES_ENABLE 0x0040
+#define XL_NETDIAG_STATS_ENABLED 0x0080
+#define XL_NETDIAG_TX_FATALERR 0x0100
+#define XL_NETDIAG_TRANSMITTING 0x0200
+#define XL_NETDIAG_RX_ENABLED 0x0400
+#define XL_NETDIAG_TX_ENABLED 0x0800
+#define XL_NETDIAG_FIFO_LOOPBACK 0x1000
+#define XL_NETDIAG_MAC_LOOPBACK 0x2000
+#define XL_NETDIAG_ENDEC_LOOPBACK 0x4000
+#define XL_NETDIAG_EXTERNAL_LOOP 0x8000
+
+/*
+ * Window 5
+ */
+#define XL_W5_STAT_ENB 0x0C
+#define XL_W5_INTR_ENB 0x0A
+#define XL_W5_RECLAIM_THRESH 0x09 /* 3c905B only */
+#define XL_W5_RX_FILTER 0x08
+#define XL_W5_RX_EARLYTHRESH 0x06
+#define XL_W5_TX_AVAILTHRESH 0x02
+#define XL_W5_TX_STARTTHRESH 0x00
+
+/*
+ * RX filter bits
+ */
+#define XL_RXFILTER_INDIVIDUAL 0x01
+#define XL_RXFILTER_ALLMULTI 0x02
+#define XL_RXFILTER_BROADCAST 0x04
+#define XL_RXFILTER_ALLFRAMES 0x08
+#define XL_RXFILTER_MULTIHASH 0x10 /* 3c905B only */
+
+/*
+ * Window 6 (stats)
+ */
+#define XL_W6_TX_BYTES_OK 0x0C
+#define XL_W6_RX_BYTES_OK 0x0A
+#define XL_W6_UPPER_FRAMES_OK 0x09
+#define XL_W6_DEFERRED 0x08
+#define XL_W6_RX_OK 0x07
+#define XL_W6_TX_OK 0x06
+#define XL_W6_RX_OVERRUN 0x05
+#define XL_W6_COL_LATE 0x04
+#define XL_W6_COL_SINGLE 0x03
+#define XL_W6_COL_MULTIPLE 0x02
+#define XL_W6_SQE_ERRORS 0x01
+#define XL_W6_CARRIER_LOST 0x00
+
+/*
+ * Window 7 (bus master control)
+ */
+#define XL_W7_BM_ADDR 0x00
+#define XL_W7_BM_LEN 0x06
+#define XL_W7_BM_STATUS 0x0B
+#define XL_W7_BM_TIMEr 0x0A
+
+/*
+ * bus master control registers
+ */
+#define XL_BM_PKTSTAT 0x20
+#define XL_BM_DOWNLISTPTR 0x24
+#define XL_BM_FRAGADDR 0x28
+#define XL_BM_FRAGLEN 0x2C
+#define XL_BM_TXFREETHRESH 0x2F
+#define XL_BM_UPPKTSTAT 0x30
+#define XL_BM_UPLISTPTR 0x38
+
+
+
+
+
+
+
+
+struct xl_mii_frame {
+ u_int8_t mii_stdelim;
+ u_int8_t mii_opcode;
+ u_int8_t mii_phyaddr;
+ u_int8_t mii_regaddr;
+ u_int8_t mii_turnaround;
+ u_int16_t mii_data;
+};
+
+/*
+ * MII constants
+ */
+#define XL_MII_STARTDELIM 0x01
+#define XL_MII_READOP 0x02
+#define XL_MII_WRITEOP 0x01
+#define XL_MII_TURNAROUND 0x02
+
+
+
+/*
+ * The 3C905B adapters implement a few features that we want to
+ * take advantage of, namely the multicast hash filter. With older
+ * chips, you only have the option of turning on reception of all
+ * multicast frames, which is kind of lame.
+ *
+ * We also use this to decide on a transmit strategy. For the 3c90xB
+ * cards, we can use polled descriptor mode, which reduces CPU overhead.
+ */
+#define XL_TYPE_905B 1
+#define XL_TYPE_90X 2
+
+#define XL_FLAG_FUNCREG 0x0001
+#define XL_FLAG_PHYOK 0x0002
+#define XL_FLAG_EEPROM_OFFSET_30 0x0004
+#define XL_FLAG_WEIRDRESET 0x0008
+#define XL_FLAG_8BITROM 0x0010
+#define XL_FLAG_INVERT_LED_PWR 0x0020
+#define XL_FLAG_INVERT_MII_PWR 0x0040
+#define XL_FLAG_NO_XCVR_PWR 0x0080
+#define XL_FLAG_USE_MMIO 0x0100
+
+#define XL_NO_XCVR_PWR_MAGICBITS 0x0900
+
+
+#define XL_MIN_FRAMELEN 60
+
+#define XL_LAST_FRAG 0x80000000
+
+
+
+
+
+
+
+struct xl_stats
+{
+ /* accumulated stats */
+ u_int16_t xl_carrier_lost;
+ u_int16_t xl_sqe_errs;
+ u_int16_t xl_tx_multi_collision;
+ u_int16_t xl_tx_single_collision;
+ u_int16_t xl_tx_late_collision;
+ u_int16_t xl_rx_overrun;
+ u_int16_t xl_tx_deferred;
+
+ u_int32_t xl_rx_bytes_ok;
+ u_int32_t xl_tx_bytes_ok;
+
+ u_int32_t xl_tx_frames_ok;
+ u_int32_t xl_rx_frames_ok;
+
+ u_int16_t xl_badssd;
+
+ /* non-accumulated stats */
+ u_int16_t intstatus;
+ u_int16_t rxstatus;
+ u_int8_t txstatus;
+ u_int16_t mediastatus;
+
+ u_int32_t txcomplete_ints;
+
+ u_int16_t miianr, miipar, miistatus, miicmd;
+
+ u_int32_t device_interrupts;
+ u_int32_t internalconfig;
+ u_int16_t mac_control;
+
+ u_int16_t smbstatus;
+ u_int32_t dmactl;
+ u_int16_t txfree;
+};
+
+
+
+struct xl_type
+{
+ u_int16_t xl_vid;
+ u_int16_t xl_did;
+ char *xl_name;
+};
+
+
+
+/*
+ * Various supported device vendors/types and their names.
+ */
+static struct xl_type xl_devs[] = {
+ { TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT,
+ "3Com 3c900-TPO Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT_COMBO,
+ "3Com 3c900-COMBO Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_BOOMERANG_10_100BT,
+ "3Com 3c905-TX Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_BOOMERANG_100BT4,
+ "3Com 3c905-T4 Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT,
+ "3Com 3c900B-TPO Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT_COMBO,
+ "3Com 3c900B-COMBO Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT_TPC,
+ "3Com 3c900B-TPC Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_CYCLONE_10FL,
+ "3Com 3c900B-FL Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_10_100BT,
+ "3Com 3c905B-TX Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100BT4,
+ "3Com 3c905B-T4 Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100FX,
+ "3Com 3c905B-FX/SC Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100_COMBO,
+ "3Com 3c905B-COMBO Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT,
+ "3Com 3c905C-TX Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_920B,
+ "3Com 3c920B-EMB Integrated Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_10_100BT_SERV,
+ "3Com 3c980 Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_SERV,
+ "3Com 3c980C Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_SOHO100TX,
+ "3Com 3cSOHO100-TX OfficeConnect" },
+ { TC_VENDORID, TC_DEVICEID_TORNADO_HOMECONNECT,
+ "3Com 3c450-TX HomeConnect" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_555,
+ "3Com 3c555 Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_556,
+ "3Com 3c556 Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_556B,
+ "3Com 3c556B Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_575A,
+ "3Com 3c575TX Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_575B,
+ "3Com 3c575B Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_575C,
+ "3Com 3c575C Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_656,
+ "3Com 3c656 Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_656B,
+ "3Com 3c656B Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_TORNADO_656C,
+ "3Com 3c656C Fast Etherlink XL" },
+ { 0, 0, NULL }
+};
+
+
+#define XL_TIMEOUT 1000
+
+
+
+
+
+
+/* rx message descriptor entry, ensure the struct is aligned to 8 bytes */
+struct RXMD
+{
+ /* used by hardware */
+ volatile uint32_t next;
+ volatile uint32_t status;
+ volatile uint32_t addr;
+ volatile uint32_t length;
+ /* used by software */
+ struct mbuf *mbuf; /* scratch variable used in the tx ring */
+ struct RXMD *next_md;
+} __attribute__ ((aligned (8), packed));
+
+
+
+
+
+#define NUM_FRAGS 6
+
+/*
+ * tx message descriptor entry, ensure the struct is aligned to 8 bytes
+ */
+
+struct tfrag
+{
+ volatile uint32_t addr;
+ volatile uint32_t length;
+} __attribute__ ((packed));
+
+struct TXMD
+{
+ /* used by hardware */
+ volatile uint32_t next;
+ volatile uint32_t status;
+ struct tfrag txfrags[NUM_FRAGS];
+ /* used by software */
+ struct mbuf *mbuf; /* scratch variable used in the tx ring */
+ struct TXMD *next_md, *chainptr;
+} __attribute__ ((aligned (8), packed));
+
+
+
+
+
+#define NUM_CHAIN_LENGTHS 50
+
+
+
+/*
+ * Per-device data
+ */
+struct elnk_softc
+{
+ struct arpcom arpcom;
+
+ rtems_irq_connect_data irqInfo;
+ rtems_event_set ioevent;
+ unsigned int ioaddr;
+
+ unsigned char *bufferBase, *ringBase;
+
+ struct RXMD *rx_ring, *curr_rx_md;
+ struct TXMD *tx_ring, *last_tx_md, *last_txchain_head;
+
+ rtems_id stat_timer_id;
+ uint32_t stats_update_ticks;
+
+ struct xl_stats xl_stats;
+
+ u_int8_t xl_unit; /* interface number */
+ u_int8_t xl_type;
+ int xl_flags;
+ u_int16_t xl_media;
+ u_int16_t xl_caps;
+ u_int32_t xl_xcvr;
+ u_int8_t xl_stats_no_timeout;
+ u_int16_t xl_tx_thresh;
+
+ int tx_idle;
+
+ short chain_lengths[NUM_CHAIN_LENGTHS];
+ int chlenIndex;
+
+ unsigned short vendorID, deviceID;
+ int acceptBroadcast;
+ int numTxbuffers, numRxbuffers;
+};
+
+static struct elnk_softc elnk_softc[NUM_UNITS];
+static rtems_id rxDaemonTid;
+static rtems_id txDaemonTid;
+static rtems_id chainRecoveryQueue;
+
+
+
+
+
+
+
+#if defined(__i386__)
+
+#define CSR_WRITE_4(sc, reg, val) i386_outport_long( sc->ioaddr + reg, val )
+#define CSR_WRITE_2(sc, reg, val) i386_outport_word( sc->ioaddr + reg, val )
+#define CSR_WRITE_1(sc, reg, val) i386_outport_byte( sc->ioaddr + reg, val )
+
+
+inline unsigned int CSR_READ_4( struct elnk_softc *sc, int reg)
+{
+ unsigned int myval;
+ i386_inport_long( sc->ioaddr + reg, myval );
+ return myval;
+}
+
+inline unsigned short CSR_READ_2( struct elnk_softc *sc, int reg)
+{
+ unsigned short myval;
+ i386_inport_word( sc->ioaddr + reg, myval );
+ return myval;
+}
+
+inline unsigned char CSR_READ_1( struct elnk_softc *sc, int reg)
+{
+ unsigned char myval;
+ i386_inport_byte( sc->ioaddr + reg, myval );
+ return myval;
+}
+
+#endif
+
+#if defined(__PPC__)
+
+#define CSR_WRITE_4(sc, reg, val) outl( val, sc->ioaddr + reg)
+#define CSR_WRITE_2(sc, reg, val) outw( val, sc->ioaddr + reg)
+#define CSR_WRITE_1(sc, reg, val) outb( val, sc->ioaddr + reg)
+
+#define CSR_READ_4(sc, reg) inl(sc->ioaddr + reg)
+#define CSR_READ_2(sc, reg) inw(sc->ioaddr + reg)
+#define CSR_READ_1(sc, reg) inb(sc->ioaddr + reg)
+
+#endif
+
+
+#define XL_SEL_WIN(x) CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_WINSEL | x)
+
+
+
+
+
+
+
+
+
+
+
+/*
+ * Murphy's law says that it's possible the chip can wedge and
+ * the 'command in progress' bit may never clear. Hence, we wait
+ * only a finite amount of time to avoid getting caught in an
+ * infinite loop. Normally this delay routine would be a macro,
+ * but it isn't called during normal operation so we can afford
+ * to make it a function.
+ */
+static void
+xl_wait(struct elnk_softc *sc)
+{
+ register int i;
+
+ for(i = 0; i < XL_TIMEOUT; i++)
+ {
+ if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY))
+ break;
+ }
+
+ if (i == XL_TIMEOUT)
+ printk("etherlink : unit elnk%d command never completed\n", sc->xl_unit );
+ return;
+}
+
+
+
+
+
+
+/*
+ * MII access routines are provided for adapters with external
+ * PHYs (3c905-TX, 3c905-T4, 3c905B-T4) and those with built-in
+ * autoneg logic that's faked up to look like a PHY (3c905B-TX).
+ * Note: if you don't perform the MDIO operations just right,
+ * it's possible to end up with code that works correctly with
+ * some chips/CPUs/processor speeds/bus speeds/etc but not
+ * with others.
+ */
+#define MII_SET(x) \
+ CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \
+ CSR_READ_2(sc, XL_W4_PHY_MGMT) | (x))
+
+#define MII_CLR(x) \
+ CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \
+ CSR_READ_2(sc, XL_W4_PHY_MGMT) & ~(x))
+
+/*
+ * Sync the PHYs by setting data bit and strobing the clock 32 times.
+ */
+static void
+xl_mii_sync(
+ struct elnk_softc *sc)
+{
+ register int i;
+
+ XL_SEL_WIN(4);
+ MII_SET(XL_MII_DIR|XL_MII_DATA);
+
+ for (i = 0; i < 32; i++) {
+ MII_SET(XL_MII_CLK);
+ MII_SET(XL_MII_DATA);
+ MII_CLR(XL_MII_CLK);
+ MII_SET(XL_MII_DATA);
+ }
+
+ return;
+}
+
+/*
+ * Clock a series of bits through the MII.
+ */
+static void
+xl_mii_send(
+ struct elnk_softc *sc,
+ u_int32_t bits,
+ int cnt )
+{
+ int i;
+
+ XL_SEL_WIN(4);
+ MII_CLR(XL_MII_CLK);
+
+ for (i = (0x1 << (cnt - 1)); i; i >>= 1) {
+ if (bits & i) {
+ MII_SET(XL_MII_DATA);
+ } else {
+ MII_CLR(XL_MII_DATA);
+ }
+ MII_CLR(XL_MII_CLK);
+ MII_SET(XL_MII_CLK);
+ }
+}
+
+/*
+ * Read an PHY register through the MII.
+ */
+static int
+xl_mii_readreg(
+ struct elnk_softc *sc,
+ struct xl_mii_frame *frame )
+{
+ int i, ack;
+
+ /*
+ * Set up frame for RX.
+ */
+ frame->mii_stdelim = XL_MII_STARTDELIM;
+ frame->mii_opcode = XL_MII_READOP;
+ frame->mii_turnaround = 0;
+ frame->mii_data = 0;
+
+ /*
+ * Select register window 4.
+ */
+
+ XL_SEL_WIN(4);
+
+ CSR_WRITE_2(sc, XL_W4_PHY_MGMT, 0);
+ /*
+ * Turn on data xmit.
+ */
+ MII_SET(XL_MII_DIR);
+
+ xl_mii_sync(sc);
+
+ /*
+ * Send command/address info.
+ */
+ xl_mii_send(sc, frame->mii_stdelim, 2);
+ xl_mii_send(sc, frame->mii_opcode, 2);
+ xl_mii_send(sc, frame->mii_phyaddr, 5);
+ xl_mii_send(sc, frame->mii_regaddr, 5);
+
+ /* Idle bit */
+ MII_CLR((XL_MII_CLK|XL_MII_DATA));
+ MII_SET(XL_MII_CLK);
+
+ /* Turn off xmit. */
+ MII_CLR(XL_MII_DIR);
+
+ /* Check for ack */
+ MII_CLR(XL_MII_CLK);
+ ack = CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA;
+ MII_SET(XL_MII_CLK);
+
+ /*
+ * Now try reading data bits. If the ack failed, we still
+ * need to clock through 16 cycles to keep the PHY(s) in sync.
+ */
+ if (ack) {
+ for(i = 0; i < 16; i++) {
+ MII_CLR(XL_MII_CLK);
+ MII_SET(XL_MII_CLK);
+ }
+ goto fail;
+ }
+
+ for (i = 0x8000; i; i >>= 1) {
+ MII_CLR(XL_MII_CLK);
+ if (!ack) {
+ if (CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA)
+ frame->mii_data |= i;
+ }
+ MII_SET(XL_MII_CLK);
+ }
+
+ fail:
+
+ MII_CLR(XL_MII_CLK);
+ MII_SET(XL_MII_CLK);
+
+ if (ack)
+ return(1);
+ return(0);
+}
+
+/*
+ * Write to a PHY register through the MII.
+ */
+static int
+xl_mii_writereg(
+ struct elnk_softc *sc,
+ struct xl_mii_frame *frame )
+{
+ /*
+ * Set up frame for TX.
+ */
+
+ frame->mii_stdelim = XL_MII_STARTDELIM;
+ frame->mii_opcode = XL_MII_WRITEOP;
+ frame->mii_turnaround = XL_MII_TURNAROUND;
+
+ /*
+ * Select the window 4.
+ */
+ XL_SEL_WIN(4);
+
+ /*
+ * Turn on data output.
+ */
+ MII_SET(XL_MII_DIR);
+
+ xl_mii_sync(sc);
+
+ xl_mii_send(sc, frame->mii_stdelim, 2);
+ xl_mii_send(sc, frame->mii_opcode, 2);
+ xl_mii_send(sc, frame->mii_phyaddr, 5);
+ xl_mii_send(sc, frame->mii_regaddr, 5);
+ xl_mii_send(sc, frame->mii_turnaround, 2);
+ xl_mii_send(sc, frame->mii_data, 16);
+
+ /* Idle bit. */
+ MII_SET(XL_MII_CLK);
+ MII_CLR(XL_MII_CLK);
+
+ /*
+ * Turn off xmit.
+ */
+ MII_CLR(XL_MII_DIR);
+
+ return(0);
+}
+
+static int
+xl_miibus_readreg(
+ struct elnk_softc *sc,
+ int phy,
+ int reg )
+{
+ struct xl_mii_frame frame;
+
+ /*
+ * Pretend that PHYs are only available at MII address 24.
+ * This is to guard against problems with certain 3Com ASIC
+ * revisions that incorrectly map the internal transceiver
+ * control registers at all MII addresses. This can cause
+ * the miibus code to attach the same PHY several times over.
+ */
+ if ((!(sc->xl_flags & XL_FLAG_PHYOK)) && phy != 24)
+ {
+ printk("etherlink : unit elnk%d xl_miibus_readreg returned\n", sc->xl_unit);
+ return(0);
+ }
+
+ memset((char *)&frame, 0, sizeof(frame));
+
+ frame.mii_phyaddr = phy;
+ frame.mii_regaddr = reg;
+ xl_mii_readreg(sc, &frame);
+
+ return(frame.mii_data);
+}
+
+static int
+xl_miibus_writereg(
+ struct elnk_softc *sc,
+ int phy,
+ int reg,
+ int data )
+{
+ struct xl_mii_frame frame;
+
+ if ((!(sc->xl_flags & XL_FLAG_PHYOK)) && phy != 24)
+ {
+ printk("etherlink : unit elnk%d xl_miibus_writereg returned\n", sc->xl_unit);
+ return(0);
+ }
+
+ memset((char *)&frame, 0, sizeof(frame));
+
+ frame.mii_phyaddr = phy;
+ frame.mii_regaddr = reg;
+ frame.mii_data = data;
+
+ xl_mii_writereg(sc, &frame);
+
+ return(0);
+}
+
+
+
+
+
+
+
+
+
+/*
+ * The EEPROM is slow: give it time to come ready after issuing
+ * it a command.
+ */
+static int
+xl_eeprom_wait(struct elnk_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < 100; i++) {
+ if (CSR_READ_2(sc, XL_W0_EE_CMD) & XL_EE_BUSY)
+ DELAY(162);
+ else
+ break;
+ }
+
+ if (i == 100) {
+ printk("etherlink : unit elnk%d eeprom failed to come ready\n", sc->xl_unit);
+ return(1);
+ }
+
+ return(0);
+}
+
+/*
+ * Read a sequence of words from the EEPROM. Note that ethernet address
+ * data is stored in the EEPROM in network byte order.
+ */
+static int
+xl_read_eeprom(
+ struct elnk_softc *sc,
+ caddr_t dest,
+ int off,
+ int cnt,
+ int swap)
+{
+ int err = 0, i;
+ u_int16_t word = 0, *ptr;
+#define EEPROM_5BIT_OFFSET(A) ((((A) << 2) & 0x7F00) | ((A) & 0x003F))
+#define EEPROM_8BIT_OFFSET(A) ((A) & 0x003F)
+ /* WARNING! DANGER!
+ * It's easy to accidentally overwrite the rom content!
+ * Note: the 3c575 uses 8bit EEPROM offsets.
+ */
+ XL_SEL_WIN(0);
+
+ if (xl_eeprom_wait(sc))
+ return(1);
+
+ if (sc->xl_flags & XL_FLAG_EEPROM_OFFSET_30)
+ off += 0x30;
+
+ for (i = 0; i < cnt; i++) {
+ if (sc->xl_flags & XL_FLAG_8BITROM)
+ CSR_WRITE_2(sc, XL_W0_EE_CMD,
+ XL_EE_8BIT_READ | EEPROM_8BIT_OFFSET(off + i));
+ else
+ CSR_WRITE_2(sc, XL_W0_EE_CMD,
+ XL_EE_READ | EEPROM_5BIT_OFFSET(off + i));
+ err = xl_eeprom_wait(sc);
+ if (err)
+ break;
+ word = CSR_READ_2(sc, XL_W0_EE_DATA);
+ ptr = (u_int16_t*)(dest + (i * 2));
+ if (swap)
+ *ptr = ntohs(word);
+ else
+ *ptr = word;
+ }
+
+ return(err ? 1 : 0);
+}
+
+
+
+
+static void
+xl_stats_update(
+ rtems_id timerid,
+ void *xsc)
+{
+ struct elnk_softc *sc = (struct elnk_softc *)xsc;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ u_int32_t t1;
+
+ sc->xl_stats.intstatus = CSR_READ_2(sc, XL_STATUS);
+
+ sc->xl_stats.miianr = xl_miibus_readreg(sc, 0x18, MII_ANAR );
+ sc->xl_stats.miipar = xl_miibus_readreg(sc, 0x18, MII_ANLPAR );
+ sc->xl_stats.miistatus = xl_miibus_readreg(sc, 0x18, MII_BMSR );
+ sc->xl_stats.miicmd = xl_miibus_readreg(sc, 0x18, MII_BMCR );
+
+ XL_SEL_WIN(1);
+ sc->xl_stats.rxstatus = CSR_READ_2(sc, XL_W1_RX_STATUS );
+ sc->xl_stats.txstatus = CSR_READ_1(sc, XL_W1_TX_STATUS );
+ sc->xl_stats.smbstatus = CSR_READ_2(sc, 2 );
+
+ XL_SEL_WIN(3);
+ sc->xl_stats.internalconfig = CSR_READ_4(sc, XL_W3_INTERNAL_CFG);
+ sc->xl_stats.mac_control = CSR_READ_2(sc, XL_W3_MAC_CTRL);
+ sc->xl_stats.txfree = CSR_READ_2(sc, XL_W3_FREE_TX );
+
+
+ /* Read all the stats registers. */
+ XL_SEL_WIN(6);
+
+ sc->xl_stats.xl_carrier_lost += CSR_READ_1(sc, XL_W6_CARRIER_LOST);
+ sc->xl_stats.xl_sqe_errs += CSR_READ_1(sc, XL_W6_SQE_ERRORS);
+ sc->xl_stats.xl_tx_multi_collision += CSR_READ_1(sc, XL_W6_COL_MULTIPLE);
+ sc->xl_stats.xl_tx_single_collision += CSR_READ_1(sc, XL_W6_COL_SINGLE);
+ sc->xl_stats.xl_tx_late_collision += CSR_READ_1(sc, XL_W6_COL_LATE);
+ sc->xl_stats.xl_rx_overrun += CSR_READ_1(sc, XL_W6_RX_OVERRUN);
+ sc->xl_stats.xl_tx_deferred += CSR_READ_1(sc, XL_W6_DEFERRED);
+
+ sc->xl_stats.xl_tx_frames_ok += CSR_READ_1(sc, XL_W6_TX_OK);
+ sc->xl_stats.xl_rx_frames_ok += CSR_READ_1(sc, XL_W6_RX_OK);
+
+ sc->xl_stats.xl_rx_bytes_ok += CSR_READ_2(sc, XL_W6_TX_BYTES_OK );
+ sc->xl_stats.xl_tx_bytes_ok += CSR_READ_2(sc, XL_W6_RX_BYTES_OK );
+
+ t1 = CSR_READ_1(sc, XL_W6_UPPER_FRAMES_OK);
+ sc->xl_stats.xl_rx_frames_ok += ((t1 & 0x3) << 8);
+ sc->xl_stats.xl_tx_frames_ok += (((t1 >> 4) & 0x3) << 8);
+
+
+ ifp->if_ierrors += sc->xl_stats.xl_rx_overrun;
+
+ ifp->if_collisions += sc->xl_stats.xl_tx_multi_collision +
+ sc->xl_stats.xl_tx_single_collision +
+ sc->xl_stats.xl_tx_late_collision;
+
+ /*
+ * Boomerang and cyclone chips have an extra stats counter
+ * in window 4 (BadSSD). We have to read this too in order
+ * to clear out all the stats registers and avoid a statsoflow
+ * interrupt.
+ */
+ XL_SEL_WIN(4);
+
+ t1 = CSR_READ_1(sc, XL_W4_UPPERBYTESOK);
+ sc->xl_stats.xl_rx_bytes_ok += ((t1 & 0xf) << 16);
+ sc->xl_stats.xl_tx_bytes_ok += (((t1 >> 4) & 0xf) << 16);
+
+ sc->xl_stats.xl_badssd += CSR_READ_1(sc, XL_W4_BADSSD);
+
+ sc->xl_stats.mediastatus = CSR_READ_2(sc, XL_W4_MEDIA_STATUS );
+ sc->xl_stats.dmactl = CSR_READ_4(sc, XL_DMACTL );
+
+
+ XL_SEL_WIN(7);
+
+ if (!sc->xl_stats_no_timeout)
+ rtems_timer_fire_after( sc->stat_timer_id, sc->stats_update_ticks, xl_stats_update, (void *)sc );
+ return;
+}
+
+
+
+
+
+
+
+static void
+xl_reset(struct elnk_softc *sc)
+{
+ register int i;
+
+ XL_SEL_WIN(0);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RESET |
+ ((sc->xl_flags & XL_FLAG_WEIRDRESET) ?
+ XL_RESETOPT_DISADVFD:0));
+
+ for (i = 0; i < XL_TIMEOUT; i++) {
+ DELAY(10);
+ if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY))
+ break;
+ }
+
+ if (i == XL_TIMEOUT)
+ printk("etherlink : unit elnk%d reset didn't complete\n", sc->xl_unit);
+
+ /* Reset TX and RX. */
+ /* Note: the RX reset takes an absurd amount of time
+ * on newer versions of the Tornado chips such as those
+ * on the 3c905CX and newer 3c908C cards. We wait an
+ * extra amount of time so that xl_wait() doesn't complain
+ * and annoy the users.
+ */
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
+ DELAY(100000);
+ xl_wait(sc);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
+ xl_wait(sc);
+
+ if (sc->xl_flags & XL_FLAG_INVERT_LED_PWR ||
+ sc->xl_flags & XL_FLAG_INVERT_MII_PWR)
+ {
+ XL_SEL_WIN(2);
+ CSR_WRITE_2(sc, XL_W2_RESET_OPTIONS, CSR_READ_2(sc,
+ XL_W2_RESET_OPTIONS)
+ | ((sc->xl_flags & XL_FLAG_INVERT_LED_PWR)?XL_RESETOPT_INVERT_LED:0)
+ | ((sc->xl_flags & XL_FLAG_INVERT_MII_PWR)?XL_RESETOPT_INVERT_MII:0)
+ );
+ }
+
+ /* Wait a little while for the chip to get its brains in order. */
+ DELAY(100000);
+ return;
+}
+
+
+
+
+static void
+xl_stop(struct elnk_softc *sc)
+{
+ struct ifnet *ifp;
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_timer = 0;
+
+ rtems_timer_cancel( sc->stat_timer_id );
+
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISABLE);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISCARD);
+ xl_wait(sc);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
+ DELAY(800);
+
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|XL_STAT_INTLATCH);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|0);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0);
+
+ return;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static void
+xl_setcfg(struct elnk_softc *sc)
+{
+ u_int32_t icfg;
+
+ XL_SEL_WIN(3);
+ icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG);
+
+ icfg &= ~XL_ICFG_CONNECTOR_MASK;
+
+ if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BT4)
+ icfg |= (XL_XCVR_MII << XL_ICFG_CONNECTOR_BITS);
+
+ if (sc->xl_media & XL_MEDIAOPT_BTX)
+ icfg |= (XL_XCVR_AUTO << XL_ICFG_CONNECTOR_BITS);
+
+ CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
+
+ XL_SEL_WIN(7);
+ return;
+}
+
+
+
+static void
+xl_setmode(
+ struct elnk_softc *sc,
+ int media)
+{
+ u_int32_t icfg;
+ u_int16_t mediastat;
+
+ printk("etherlink : unit elnk%d selecting ", sc->xl_unit);
+
+ XL_SEL_WIN(4);
+ mediastat = CSR_READ_2(sc, XL_W4_MEDIA_STATUS);
+ XL_SEL_WIN(3);
+ icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG);
+
+ if (sc->xl_media & XL_MEDIAOPT_BT) {
+ if (IFM_SUBTYPE(media) == IFM_10_T) {
+ printk("10baseT transceiver, ");
+ sc->xl_xcvr = XL_XCVR_10BT;
+ icfg &= ~XL_ICFG_CONNECTOR_MASK;
+ icfg |= (XL_XCVR_10BT << XL_ICFG_CONNECTOR_BITS);
+ mediastat |= XL_MEDIASTAT_LINKBEAT|
+ XL_MEDIASTAT_JABGUARD;
+ mediastat &= ~XL_MEDIASTAT_SQEENB;
+ }
+ }
+
+ if (sc->xl_media & XL_MEDIAOPT_BFX) {
+ if (IFM_SUBTYPE(media) == IFM_100_FX) {
+ printk("100baseFX port, ");
+ sc->xl_xcvr = XL_XCVR_100BFX;
+ icfg &= ~XL_ICFG_CONNECTOR_MASK;
+ icfg |= (XL_XCVR_100BFX << XL_ICFG_CONNECTOR_BITS);
+ mediastat |= XL_MEDIASTAT_LINKBEAT;
+ mediastat &= ~XL_MEDIASTAT_SQEENB;
+ }
+ }
+
+ if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) {
+ if (IFM_SUBTYPE(media) == IFM_10_5) {
+ printk("AUI port, ");
+ sc->xl_xcvr = XL_XCVR_AUI;
+ icfg &= ~XL_ICFG_CONNECTOR_MASK;
+ icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS);
+ mediastat &= ~(XL_MEDIASTAT_LINKBEAT|
+ XL_MEDIASTAT_JABGUARD);
+ mediastat |= ~XL_MEDIASTAT_SQEENB;
+ }
+ if (IFM_SUBTYPE(media) == IFM_10_FL) {
+ printk("10baseFL transceiver, ");
+ sc->xl_xcvr = XL_XCVR_AUI;
+ icfg &= ~XL_ICFG_CONNECTOR_MASK;
+ icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS);
+ mediastat &= ~(XL_MEDIASTAT_LINKBEAT|
+ XL_MEDIASTAT_JABGUARD);
+ mediastat |= ~XL_MEDIASTAT_SQEENB;
+ }
+ }
+
+ if (sc->xl_media & XL_MEDIAOPT_BNC) {
+ if (IFM_SUBTYPE(media) == IFM_10_2) {
+ printk("BNC port, ");
+ sc->xl_xcvr = XL_XCVR_COAX;
+ icfg &= ~XL_ICFG_CONNECTOR_MASK;
+ icfg |= (XL_XCVR_COAX << XL_ICFG_CONNECTOR_BITS);
+ mediastat &= ~(XL_MEDIASTAT_LINKBEAT|
+ XL_MEDIASTAT_JABGUARD|
+ XL_MEDIASTAT_SQEENB);
+ }
+ }
+
+ if ((media & IFM_GMASK) == IFM_FDX ||
+ IFM_SUBTYPE(media) == IFM_100_FX) {
+ printk("full duplex\n");
+ XL_SEL_WIN(3);
+ CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX);
+ } else {
+ printk("half duplex\n");
+ XL_SEL_WIN(3);
+ CSR_WRITE_1(sc, XL_W3_MAC_CTRL,
+ (CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX));
+ }
+
+ if (IFM_SUBTYPE(media) == IFM_10_2)
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START);
+ else
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
+
+ CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg);
+ XL_SEL_WIN(4);
+ CSR_WRITE_2(sc, XL_W4_MEDIA_STATUS, mediastat);
+ DELAY(800);
+ XL_SEL_WIN(7);
+
+ return;
+}
+
+
+
+
+
+
+
+static void
+xl_choose_xcvr(
+ struct elnk_softc *sc,
+ int verbose)
+{
+ u_int16_t devid;
+
+ /*
+ * Read the device ID from the EEPROM.
+ * This is what's loaded into the PCI device ID register, so it has
+ * to be correct otherwise we wouldn't have gotten this far.
+ */
+ xl_read_eeprom(sc, (caddr_t)&devid, XL_EE_PRODID, 1, 0);
+
+ switch(devid) {
+ case TC_DEVICEID_BOOMERANG_10BT: /* 3c900-TPO */
+ case TC_DEVICEID_KRAKATOA_10BT: /* 3c900B-TPO */
+ sc->xl_media = XL_MEDIAOPT_BT;
+ sc->xl_xcvr = XL_XCVR_10BT;
+ if (verbose)
+ printk("etherlink : unit elnk%d guessing 10BaseT "
+ "transceiver\n", sc->xl_unit);
+ break;
+ case TC_DEVICEID_BOOMERANG_10BT_COMBO: /* 3c900-COMBO */
+ case TC_DEVICEID_KRAKATOA_10BT_COMBO: /* 3c900B-COMBO */
+ sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI;
+ sc->xl_xcvr = XL_XCVR_10BT;
+ if (verbose)
+ printk("etherlink : unit elnk%d guessing COMBO "
+ "(AUI/BNC/TP)\n", sc->xl_unit);
+ break;
+ case TC_DEVICEID_KRAKATOA_10BT_TPC: /* 3c900B-TPC */
+ sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC;
+ sc->xl_xcvr = XL_XCVR_10BT;
+ if (verbose)
+ printk("etherlink : unit elnk%d guessing TPC (BNC/TP)\n", sc->xl_unit);
+ break;
+ case TC_DEVICEID_CYCLONE_10FL: /* 3c900B-FL */
+ sc->xl_media = XL_MEDIAOPT_10FL;
+ sc->xl_xcvr = XL_XCVR_AUI;
+ if (verbose)
+ printk("etherlink : unit elnk%d guessing 10baseFL\n", sc->xl_unit);
+ break;
+ case TC_DEVICEID_BOOMERANG_10_100BT: /* 3c905-TX */
+ case TC_DEVICEID_HURRICANE_555: /* 3c555 */
+ case TC_DEVICEID_HURRICANE_556: /* 3c556 */
+ case TC_DEVICEID_HURRICANE_556B: /* 3c556B */
+ case TC_DEVICEID_HURRICANE_575A: /* 3c575TX */
+ case TC_DEVICEID_HURRICANE_575B: /* 3c575B */
+ case TC_DEVICEID_HURRICANE_575C: /* 3c575C */
+ case TC_DEVICEID_HURRICANE_656: /* 3c656 */
+ case TC_DEVICEID_HURRICANE_656B: /* 3c656B */
+ case TC_DEVICEID_TORNADO_656C: /* 3c656C */
+ case TC_DEVICEID_TORNADO_10_100BT_920B: /* 3c920B-EMB */
+ sc->xl_media = XL_MEDIAOPT_MII;
+ sc->xl_xcvr = XL_XCVR_MII;
+ if (verbose)
+ printk("etherlink : unit elnk%d guessing MII\n", sc->xl_unit);
+ break;
+ case TC_DEVICEID_BOOMERANG_100BT4: /* 3c905-T4 */
+ case TC_DEVICEID_CYCLONE_10_100BT4: /* 3c905B-T4 */
+ sc->xl_media = XL_MEDIAOPT_BT4;
+ sc->xl_xcvr = XL_XCVR_MII;
+ if (verbose)
+ printk("etherlink : unit elnk%d guessing 100BaseT4/MII\n", sc->xl_unit);
+ break;
+ case TC_DEVICEID_HURRICANE_10_100BT: /* 3c905B-TX */
+ case TC_DEVICEID_HURRICANE_10_100BT_SERV:/*3c980-TX */
+ case TC_DEVICEID_TORNADO_10_100BT_SERV: /* 3c980C-TX */
+ case TC_DEVICEID_HURRICANE_SOHO100TX: /* 3cSOHO100-TX */
+ case TC_DEVICEID_TORNADO_10_100BT: /* 3c905C-TX */
+ case TC_DEVICEID_TORNADO_HOMECONNECT: /* 3c450-TX */
+ sc->xl_media = XL_MEDIAOPT_BTX;
+ sc->xl_xcvr = XL_XCVR_AUTO;
+ if (verbose)
+ printk("etherlink : unit elnk%d guessing 10/100 internal\n", sc->xl_unit);
+ break;
+ case TC_DEVICEID_CYCLONE_10_100_COMBO: /* 3c905B-COMBO */
+ sc->xl_media = XL_MEDIAOPT_BTX|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI;
+ sc->xl_xcvr = XL_XCVR_AUTO;
+ if (verbose)
+ printk("etherlink : unit elnk%d guessing 10/100 "
+ "plus BNC/AUI\n", sc->xl_unit);
+ break;
+ default:
+ printk("etherlink : unit elnk%d unknown device ID: %x -- "
+ "defaulting to 10baseT\n", sc->xl_unit, devid);
+ sc->xl_media = XL_MEDIAOPT_BT;
+ break;
+ }
+
+ return;
+}
+
+
+
+
+
+
+
+/*
+ * This routine is a kludge to work around possible hardware faults
+ * or manufacturing defects that can cause the media options register
+ * (or reset options register, as it's called for the first generation
+ * 3c90x adapters) to return an incorrect result. I have encountered
+ * one Dell Latitude laptop docking station with an integrated 3c905-TX
+ * which doesn't have any of the 'mediaopt' bits set. This screws up
+ * the attach routine pretty badly because it doesn't know what media
+ * to look for. If we find ourselves in this predicament, this routine
+ * will try to guess the media options values and warn the user of a
+ * possible manufacturing defect with his adapter/system/whatever.
+ */
+static void
+xl_mediacheck(struct elnk_softc *sc)
+{
+
+ xl_choose_xcvr(sc, 1);
+
+ /*
+ * If some of the media options bits are set, assume they are
+ * correct. If not, try to figure it out down below.
+ * XXX I should check for 10baseFL, but I don't have an adapter
+ * to test with.
+ */
+ if (sc->xl_media & (XL_MEDIAOPT_MASK & ~XL_MEDIAOPT_VCO)) {
+ /*
+ * Check the XCVR value. If it's not in the normal range
+ * of values, we need to fake it up here.
+ */
+ if (sc->xl_xcvr <= XL_XCVR_AUTO)
+ return;
+ else {
+ printk("etherlink : unit elnk%d bogus xcvr value "
+ "in EEPROM (%" PRIx32 ")\n", sc->xl_unit, sc->xl_xcvr);
+ printk("etherlink : unit elnk%d choosing new default based "
+ "on card type\n", sc->xl_unit);
+ }
+ } else {
+ if (sc->xl_type == XL_TYPE_905B &&
+ sc->xl_media & XL_MEDIAOPT_10FL)
+ return;
+ printk("etherlink : unit elnk%d WARNING: no media options bits set in "
+ "the media options register!!\n", sc->xl_unit);
+ printk("etherlink : unit elnk%d this could be a manufacturing defect in "
+ "your adapter or system\n", sc->xl_unit);
+ printk("etherlink : unit elnk%d attempting to guess media type; you "
+ "should probably consult your vendor\n", sc->xl_unit);
+ }
+
+ return;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static void no_op(const rtems_irq_connect_data* irq)
+{
+ return;
+}
+
+
+
+
+static void
+elnk_start_txchain( struct elnk_softc *sc, struct TXMD *chainhead )
+{
+ xl_wait(sc);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL);
+
+ /* save the address of the TX list */
+ sc->last_txchain_head = chainhead;
+ sc->tx_idle = 0;
+
+ xl_wait(sc);
+
+ CSR_WRITE_4(sc, XL_DOWNLIST_PTR, phys_to_bus( sc->last_txchain_head ));
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
+}
+
+
+
+
+
+/*
+ * ELNK interrupt handler
+ */
+static rtems_isr
+elnk_interrupt_handler ( struct elnk_softc *sc )
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ u_int16_t status;
+
+ while( ((status = CSR_READ_2(sc, XL_STATUS)) & XL_INTRS) && status != 0xFFFF)
+ {
+ sc->xl_stats.device_interrupts++;
+
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK | (status & XL_INTRS));
+
+#if 0
+ printk("etherlink : unit elnk%d intstatus %04x\n", sc->xl_unit, status );
+#endif
+
+ if (status & XL_STAT_UP_COMPLETE)
+ {
+#if 0
+ printk("etherlink : unit elnk%d rx\n", sc->xl_unit );
+#endif
+ /* received packets */
+ rtems_bsdnet_event_send(rxDaemonTid, sc->ioevent);
+ }
+
+ if( (status & XL_STAT_DOWN_COMPLETE) || (status & XL_STAT_TX_COMPLETE) )
+ {
+ /* all packets uploaded to the device */
+ struct TXMD *chaintailmd = NULL;
+
+
+ if( status & XL_STAT_TX_COMPLETE )
+ {
+ /* if we got a tx complete error, count it, then reset the
+ transmitter. Consider the entire chain lost.. */
+
+ ifp->if_oerrors++;
+ sc->xl_stats.txcomplete_ints++;
+
+ printk("etherlink : unit elnk%d transmit error\n", sc->xl_unit );
+
+ /* reset, re-enable fifo */
+
+ xl_wait(sc);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE);
+
+ xl_wait(sc);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET | 1 );
+
+ xl_wait(sc);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
+
+ xl_wait(sc);
+ }
+
+
+ /* send the chain head to the tx task which will recover the
+ whole chain */
+ rtems_message_queue_send( chainRecoveryQueue, &sc->last_txchain_head, sizeof(struct TXMD *));
+
+
+ /* set up the next chain */
+ if( sc->last_txchain_head->chainptr )
+ {
+ /* check the head of the chain of packets we just finished,
+ * if != 0, either this is a chain of 2 or more packets or
+ * its a single packet chain and another chain is ready to
+ * send.
+ */
+ if( (int)sc->last_txchain_head->chainptr == -1 )
+ {
+ /*
+ ** single packet was sent so no indirection to the last
+ ** entry in the chain. since chainptr is != 0, then
+ ** another chain is ready starting from the packet AFTER
+ ** the chain we just finished. - in this case the last
+ ** chain's head == its tail
+ */
+ chaintailmd = sc->last_txchain_head;
+ }
+ else
+ {
+ /*
+ ** otherwise, this is a pointer to the last packet in the
+ ** chain of 2 or more packets. If the chain's last
+ ** packet's chainptr is != 0, then another chain is ready
+ ** to send.
+ */
+ chaintailmd = sc->last_txchain_head->chainptr;
+ if( !chaintailmd->chainptr ) chaintailmd = NULL;
+ }
+ }
+
+ if( chaintailmd )
+ {
+ /* the next MD is the start of another chain */
+ elnk_start_txchain(sc, chaintailmd->next_md );
+ }
+ else
+ {
+ /* otherwise nothing to send, so go idle */
+ sc->tx_idle = -1;
+
+ /* wake up the tx daemon once so we're sure this last chain
+ will be freed */
+ rtems_bsdnet_event_send( txDaemonTid, sc->ioevent );
+#if 0
+ printk("unit elnk%d tx done\n", sc->xl_unit );
+#endif
+ }
+ }
+
+
+ if (status & XL_STAT_ADFAIL)
+ {
+ printk("etherlink : unit elnk%d Catastrophic bus failure\n", sc->xl_unit );
+ }
+ if (status & XL_STAT_STATSOFLOW)
+ {
+ sc->xl_stats_no_timeout = 1;
+ xl_stats_update(sc->stat_timer_id,sc);
+ sc->xl_stats_no_timeout = 0;
+ }
+ }
+
+
+#if 0
+ {
+ uint16_t intstatus, intenable, indenable;
+
+ intstatus = CSR_READ_2(sc, XL_STATUS );
+
+ XL_SEL_WIN(5);
+ intenable = CSR_READ_2(sc, XL_W5_INTR_ENB );
+ indenable = CSR_READ_2(sc, XL_W5_STAT_ENB );
+ XL_SEL_WIN(7);
+ printk("etherlink : unit elnk%d istat %04x, ien %04x, ind %04x\n", sc->xl_unit, intstatus, intenable, indenable );
+ }
+#endif
+}
+
+
+
+
+
+static rtems_isr
+elnk_interrupt_handler_entry(void)
+{
+ int i;
+
+ /*
+ ** Check all the initialized units for interrupt service
+ */
+
+ for(i=0; i< NUM_UNITS; i++ )
+ {
+ if( elnk_softc[i].ioaddr )
+ elnk_interrupt_handler( &elnk_softc[i] );
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+/*
+ * Initialize the ethernet hardware
+ */
+static void
+elnk_initialize_hardware (struct elnk_softc *sc)
+{
+ unsigned char *cp;
+ int i, j, rxsize, txsize, ringsize;
+
+ /*
+ * Init RX ring
+ */
+ cp = (unsigned char *)malloc( (ringsize = ((rxsize = (sc->numRxbuffers * sizeof(struct RXMD))) +
+ (txsize = (sc->numTxbuffers * sizeof(struct TXMD)))) ) +
+ + CPU_CACHE_ALIGNMENT_FOR_BUFFER);
+ sc->bufferBase = cp;
+ cp += (CPU_CACHE_ALIGNMENT_FOR_BUFFER - (int)cp) & (CPU_CACHE_ALIGNMENT_FOR_BUFFER - 1);
+#if defined(__i386__)
+#ifdef PCI_BRIDGE_DOES_NOT_ENSURE_CACHE_COHERENCY_FOR_DMA
+ if (_CPU_is_paging_enabled())
+ _CPU_change_memory_mapping_attribute
+ (NULL, cp, ringsize, PTE_CACHE_DISABLE | PTE_WRITABLE);
+#endif
+#endif
+ sc->ringBase = cp;
+
+ /* build tx and rx rings */
+
+ sc->rx_ring = (struct RXMD *)sc->ringBase;
+ sc->tx_ring = (struct TXMD *)&sc->ringBase[ rxsize ];
+
+ {
+ struct mbuf *m;
+ struct RXMD *nxtmd;
+ /*
+ * The rx ring is easy as its just an array of RXMD structs. New
+ * mbuf entries are allocated from the stack whenever the rx
+ * daemon forwards an incoming packet into it. Here, we
+ * pre-allocate the rx mbufs for the rx ring entries.
+ */
+ for(i=0 ; i<sc->numRxbuffers; i++)
+ {
+ if( ((uint32_t)&sc->rx_ring[i] & 0x7) )
+ {
+ rtems_panic ("etherlink : unit elnk%d rx ring entry %d not aligned to 8 bytes\n", sc->xl_unit, i );
+ }
+
+ /* allocate an mbuf for each receive descriptor */
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+
+ if( i == sc->numRxbuffers-1 )
+ nxtmd = &sc->rx_ring[0];
+ else
+ nxtmd = &sc->rx_ring[i+1];
+
+ sc->rx_ring[i].next_md = nxtmd;
+ sc->rx_ring[i].mbuf = m;
+
+ st_le32( &sc->rx_ring[i].status, 0);
+ st_le32( &sc->rx_ring[i].next, (uint32_t)phys_to_bus( nxtmd ));
+ st_le32( &sc->rx_ring[i].addr, (uint32_t)phys_to_bus( mtod(m, void *) ));
+ st_le32( &sc->rx_ring[i].length, XL_LAST_FRAG | XL_PACKET_SIZE );
+ }
+ sc->curr_rx_md = &sc->rx_ring[0];
+ }
+
+
+ {
+ struct TXMD *thismd, *nxtmd;
+ /*
+ * The tx ring is more complex. Each MD has an array of fragment
+ * descriptors that are loaded from each packet as they arrive
+ * from the stack. Each packet gets one ring entry, this allows
+ * the lanboard to efficiently assemble the piecemeal packets into
+ * a contiguous unit at transmit time, rather than spending
+ * cputime concatenating them first. Although the next_md fields
+ * form a ring, the DPD next is filled only when packets are added
+ * to the tx chain, thus last entry of a series of packets has the
+ * requisite dpd->next value == 0 to terminate the dma. mbuf
+ * holds the packet info so it can be freed once the packet has
+ * been sent. chainptr is used to link the head & tail of a chain
+ * of 2 or more packets. A chain is formed when the tx daemon
+ * gets 2 or more packets from the stack's queue in a service
+ * period, so higher outgoing loads are handled as efficiently as
+ * possible.
+ */
+
+ for(i=0 ; i<sc->numTxbuffers; i++)
+ {
+ if( ((uint32_t)&sc->tx_ring[i] & 0x7) )
+ {
+ rtems_panic ("etherlink : unit elnk%d tx ring entry %d not aligned to 8 bytes\n", sc->xl_unit, i );
+ }
+
+ if( i == sc->numTxbuffers-1 )
+ nxtmd = &sc->tx_ring[0];
+ else
+ nxtmd = &sc->tx_ring[i+1];
+
+ thismd = &sc->tx_ring[i];
+
+ thismd->next_md = nxtmd;
+ thismd->chainptr = NULL;
+ thismd->mbuf = NULL;
+
+ st_le32( &thismd->status, XL_TXSTAT_DL_COMPLETE );
+ st_le32( &thismd->next, 0);
+
+ for(j=0; j< NUM_FRAGS; j++)
+ {
+ st_le32( &thismd->txfrags[j].addr, 0 );
+ st_le32( &thismd->txfrags[j].length, 0 );
+ }
+ }
+ sc->last_tx_md = &sc->tx_ring[0];
+ }
+
+
+
+
+#ifdef ELNK_DEBUG
+ printk("etherlink : %02x:%02x:%02x:%02x:%02x:%02x name 'elnk%d', io %x, int %d\n",
+ sc->arpcom.ac_enaddr[0], sc->arpcom.ac_enaddr[1],
+ sc->arpcom.ac_enaddr[2], sc->arpcom.ac_enaddr[3],
+ sc->arpcom.ac_enaddr[4], sc->arpcom.ac_enaddr[5],
+ sc->xl_unit,
+ (unsigned)sc->ioaddr, sc->irqInfo.name );
+#endif
+
+
+ sc->irqInfo.hdl = (rtems_irq_hdl)elnk_interrupt_handler_entry;
+ sc->irqInfo.on = no_op;
+ sc->irqInfo.off = no_op;
+ sc->irqInfo.isOn = NULL;
+
+ if( sc->irqInfo.name != 255 )
+ {
+ int st;
+
+#ifdef BSP_SHARED_HANDLER_SUPPORT
+ st = BSP_install_rtems_shared_irq_handler( &sc->irqInfo );
+#else
+ st = BSP_install_rtems_irq_handler( &sc->irqInfo );
+#endif
+ if (!st)
+ rtems_panic ("etherlink : unit elnk%d Interrupt name %d already in use\n", sc->xl_unit, sc->irqInfo.name );
+ }
+ else
+ {
+ printk("etherlink : unit elnk%d Interrupt not specified by device\n", sc->xl_unit );
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+static void
+elnk_rxDaemon (void *arg)
+{
+ struct elnk_softc *sc;
+ struct ether_header *eh;
+ struct mbuf *m;
+ struct RXMD *rmd;
+ unsigned int i,len, rxstat;
+ rtems_event_set events;
+
+ for (;;)
+ {
+
+ rtems_bsdnet_event_receive( RTEMS_ALL_EVENTS,
+ RTEMS_WAIT|RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events);
+
+ for(;;)
+ {
+ for(i=0; i< NUM_UNITS; i++ )
+ {
+ sc = &elnk_softc[i];
+ if( sc->ioaddr )
+ {
+ if( events & sc->ioevent )
+ {
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ rmd = sc->curr_rx_md;
+
+ /*
+ ** Read off all the packets we've received on this unit
+ */
+ while( (rxstat = ld_le32(&rmd->status)) )
+ {
+ if (rxstat & XL_RXSTAT_UP_ERROR)
+ {
+ printk("unit %i up error\n", sc->xl_unit );
+ ifp->if_ierrors++;
+ }
+
+ if( (rxstat & XL_RXSTAT_UP_CMPLT) )
+ {
+
+#if 0
+ {
+ char *pkt, *delim;
+ int i;
+ pkt = mtod(rmd->mbuf, char *);
+ printk("unit %i rx pkt (%08x) ", sc->xl_unit, pkt );
+ for(delim="", i=0; i < sizeof(struct ether_header)+8; i++, delim=":")
+ printk("%s%02x", delim, (char) pkt[i] );
+ printk("\n");
+ }
+#endif
+
+ /* pass on the packet in the mbuf */
+ len = ( ld_le32(&rmd->status) & XL_RXSTAT_LENMASK);
+ m = rmd->mbuf;
+ m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header);
+ eh = mtod(m, struct ether_header *);
+ m->m_data += sizeof(struct ether_header);
+
+ ether_input(ifp, eh, m);
+
+ /* get a new mbuf */
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = ifp;
+ rmd->mbuf = m;
+ st_le32( &rmd->status, 0 );
+ st_le32( &rmd->addr, (uint32_t)phys_to_bus(mtod(m, void *)) );
+ }
+ else
+ {
+ /* some kind of packet failure */
+ printk("etherlink : unit elnk%d bad receive status -- packet dropped\n", sc->xl_unit);
+ ifp->if_ierrors++;
+ }
+ /* clear descriptor status */
+ rmd->status = 0;
+
+ rmd = rmd->next_md;
+ }
+
+ sc->curr_rx_md = rmd;
+ }
+ }
+ }
+
+ /*
+ ** If more events are pending, service them before we go back to sleep
+ */
+ if( rtems_event_system_receive( RTEMS_ALL_EVENTS,
+ RTEMS_NO_WAIT | RTEMS_EVENT_ANY,
+ 0,
+ &events ) == RTEMS_UNSATISFIED ) break;
+ }
+ }
+}
+
+
+
+
+
+
+
+
+/*
+ * Driver transmit daemon
+ */
+static void
+elnk_txDaemon (void *arg)
+{
+ struct elnk_softc *sc;
+ struct ifnet *ifp;
+ struct mbuf *m;
+ struct TXMD *lastmd, *nextmd, *firstmd;
+ int chainCount,i;
+ rtems_event_set events;
+
+ for (;;)
+ {
+ /*
+ * Wait for any unit's signal to wake us up
+ */
+ rtems_bsdnet_event_receive( RTEMS_ALL_EVENTS,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT, &events);
+
+ for(i=0; i< NUM_UNITS; i++ )
+ {
+ sc = &elnk_softc[i];
+ if( sc->ioaddr )
+ {
+ if( events & sc->ioevent )
+ {
+ ifp = &sc->arpcom.ac_if;
+
+ /*
+ * Send packets till queue is empty or tx ring is full
+ */
+
+ chainCount = 0;
+ firstmd = NULL;
+
+ lastmd = sc->last_tx_md;
+
+ for(;;)
+ {
+ /*
+ ** Check the chain recovery queue whenever the tx
+ ** daemon services the stack. Note this routine does
+ ** not assume the context of one of the lanboard units
+ ** because used tx mbufs are no longer associated with
+ ** any unit.
+ */
+ {
+ struct TXMD *chainhead, *chaintail;
+ size_t esize;
+
+ if( rtems_message_queue_receive( chainRecoveryQueue, &chainhead, &esize,
+ RTEMS_NO_WAIT, 0) == RTEMS_SUCCESSFUL )
+ {
+ /* get a pointer to the tail */
+ chaintail = chainhead->chainptr;
+
+ /* if the tail points somewhere, free the entire
+ chain */
+ if( chaintail && (int)chaintail != -1 )
+ {
+ for(;;)
+ {
+ m_freem( chainhead->mbuf );
+ st_le32( &chainhead->status, XL_TXSTAT_DL_COMPLETE );
+ chainhead->mbuf = NULL;
+
+ if( chainhead == chaintail ) break;
+ chainhead = chainhead->next_md;
+ }
+ }
+ else
+ {
+ /* a single packet chain */
+ m_freem( chainhead->mbuf );
+ st_le32( &chainhead->status, XL_TXSTAT_DL_COMPLETE );
+ chainhead->mbuf = NULL;
+ }
+ }
+ }
+
+ nextmd = lastmd->next_md;
+
+ /* stop when ring is full */
+ if( ! (ld_le32(&nextmd->status) & XL_TXSTAT_DL_COMPLETE) )
+ {
+ printk("etherlink : unit elnk%d tx ring full!\n", sc->xl_unit);
+ break;
+ }
+ /* sanity check the next packet descriptor */
+ if( nextmd->mbuf )
+ {
+ printk("etherlink : unit elnk%d tx ring corrupt!\n", sc->xl_unit);
+ break;
+ }
+
+
+
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if( !m ) break;
+
+ {
+ int i;
+
+ nextmd->mbuf = m;
+
+ for(i=0; i< NUM_FRAGS; i++)
+ {
+ st_le32( &nextmd->txfrags[i].length, ((m->m_next)?0:XL_LAST_FRAG) | ( m->m_len & XL_TXSTAT_LENMASK) );
+ st_le32( &nextmd->txfrags[i].addr, (uint32_t)phys_to_bus( m->m_data ) );
+ if ((m = m->m_next) == NULL)
+ break;
+ }
+ if( m )
+ {
+ printk("etherlink : unit elnk%d tx fragments exhausted, truncating packet!\n", sc->xl_unit);
+ st_le32( &nextmd->txfrags[NUM_FRAGS-1].length, XL_LAST_FRAG |
+ ld_le32( &nextmd->txfrags[NUM_FRAGS-1].length) );
+ }
+ }
+
+#if 0
+ {
+ char *pkt = bus_to_phys( ld_le32( &nextmd->txfrags[i].addr )), *delim;
+ int i;
+ printk("unit %d queued pkt (%08x) ", sc->xl_unit, (uint32_t)pkt );
+ for(delim="", i=0; i < sizeof(struct ether_header); i++, delim=":")
+ printk("%s%02x", delim, (char) pkt[i] );
+ printk("\n");
+ }
+#endif
+
+
+ /* this packet will be the new end of the list */
+ st_le32( &nextmd->next, 0);
+ st_le32( &nextmd->status, 0);
+
+ if( !firstmd )
+ {
+ /* keep track of the first packet we add to the chain */
+ firstmd = nextmd;
+
+ /*
+ ** use the chainbuf pointer of the last packet of
+ ** the previous chain as a flag so when a
+ ** dnComplete interrupt indicates the card is
+ ** finished downloading the chain, the isr can
+ ** immediately start the next which always begins
+ ** with the next packet in the ring. Note several
+ ** chains of packets may be assembled this way.
+ */
+ lastmd->chainptr = (struct TXMD *)-1;
+ }
+ else
+ {
+ /* hook this packet to the previous one */
+ st_le32( &lastmd->next, (uint32_t)phys_to_bus( nextmd ));
+ }
+
+ ++chainCount;
+ lastmd = nextmd;
+ }
+
+
+
+
+
+ if( firstmd )
+ {
+ /* only enter if we've queued one or more packets */
+
+ /* save the last descriptor we set up in the chain */
+ sc->last_tx_md = lastmd;
+
+ /*
+ * We've added one or more packets to a chain, flag
+ * the last packet so we get an dnComplete interrupt
+ * when the card finishes accepting the chain
+ */
+ st_le32( &lastmd->status, XL_TXSTAT_DL_INTR );
+
+ /*
+ * point the chain head's chainptr to the tail so we
+ * can jump to the next chain to send inside the isr.
+ * If we're only sending one packet, then don't bother
+ * with the link, as the chainptr value will either be
+ * 0 if theres no next chain or -1 if there is.
+ */
+ if( chainCount > 1 )
+ {
+ firstmd->chainptr = lastmd;
+
+ sc->chain_lengths[sc->chlenIndex]= (short)chainCount;
+ if( ++sc->chlenIndex == NUM_CHAIN_LENGTHS ) sc->chlenIndex = 0;
+ }
+
+ /*
+ ** clear the last packet's chainptr flag. If another
+ ** chain is added later but before this chain is
+ ** finished being sent, this flag on this packet will
+ ** be re-set to -1
+ */
+ lastmd->chainptr = NULL;
+
+#if 0
+ printk("unit %d queued %d pkts, lastpkt status %08X\n",
+ sc->xl_unit,
+ chainCount,
+ (uint32_t)ld_le32( &lastmd->status) );
+#endif
+
+ if( sc->tx_idle == 0 && CSR_READ_4(sc, XL_DOWNLIST_PTR) == 0 )
+ {
+ printk("etherlink : unit elnk%d tx forced!\n", sc->xl_unit);
+ sc->tx_idle = -1;
+ }
+
+ /*
+ ** start sending this chain of packets if tx isn't
+ ** busy, else the dnComplete interrupt will see there
+ ** is another chain waiting and begin it immediately.
+ */
+ if( sc->tx_idle )
+ {
+#if 0
+ printk("etherlink : unit elnk%d tx started %d packets\n", sc->xl_unit, chainCount );
+#endif
+ elnk_start_txchain(sc, firstmd);
+ }
+ }
+
+
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+static void
+elnk_start (struct ifnet *ifp)
+{
+ struct elnk_softc *sc = ifp->if_softc;
+#if 0
+ printk("unit %i tx signaled\n", sc->xl_unit );
+#endif
+ ifp->if_flags |= IFF_OACTIVE;
+ rtems_bsdnet_event_send( txDaemonTid, sc->ioevent );
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+ * Initialize and start the device
+ */
+static void
+elnk_init (void *arg)
+{
+ int i;
+ struct elnk_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ if( !(ifp->if_flags & IFF_RUNNING) )
+ {
+ xl_stop(sc);
+ xl_reset(sc);
+ sc->tx_idle = -1;
+
+ {
+ uint32_t cr,sr;
+
+ xl_miibus_writereg(sc, 0x18, MII_BMCR, BMCR_RESET );
+
+ while( (cr = xl_miibus_readreg(sc, 0x18, MII_BMCR )) & BMCR_RESET )
+ {
+ DELAY(100000);
+ }
+
+ xl_miibus_writereg(sc, 0x18, MII_ANAR, ANAR_10 | ANAR_TX | ANAR_10_FD | ANAR_TX_FD ); /* ANAR_T4 */
+ xl_miibus_writereg(sc, 0x18, MII_BMCR, BMCR_STARTNEG | BMCR_AUTOEN );
+
+ for (i=0; ((sr = xl_miibus_readreg(sc, 0x18, MII_BMSR)) & BMSR_ACOMP) == 0 && i < 20; i++)
+ DELAY(10000);
+ }
+
+
+ /*
+ * Set up hardware if its not already been done
+ */
+ if( !sc->irqInfo.hdl )
+ {
+ elnk_initialize_hardware(sc);
+ }
+
+ /*
+ * Enable the card
+ */
+ {
+ u_int8_t rxfilt;
+
+ /* Init our MAC address */
+ XL_SEL_WIN(2);
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ {
+ CSR_WRITE_1(sc, XL_W2_STATION_ADDR_LO + i, sc->arpcom.ac_enaddr[i]);
+ }
+
+ {
+ int media = IFM_ETHER|IFM_100_TX|IFM_FDX;
+
+ xl_mediacheck(sc);
+
+ /* Choose a default media. */
+ switch(sc->xl_xcvr) {
+ case XL_XCVR_10BT:
+ media = IFM_ETHER|IFM_10_T;
+ xl_setmode(sc, media);
+ break;
+ case XL_XCVR_AUI:
+ if (sc->xl_type == XL_TYPE_905B &&
+ sc->xl_media == XL_MEDIAOPT_10FL) {
+ media = IFM_ETHER|IFM_10_FL;
+ xl_setmode(sc, media);
+ } else {
+ media = IFM_ETHER|IFM_10_5;
+ xl_setmode(sc, media);
+ }
+ break;
+ case XL_XCVR_COAX:
+ media = IFM_ETHER|IFM_10_2;
+ xl_setmode(sc, media);
+ break;
+ case XL_XCVR_AUTO:
+ case XL_XCVR_100BTX:
+ xl_setcfg(sc);
+ break;
+ case XL_XCVR_MII:
+ printk(
+ "etherlink : unit elnk%d MII media not supported!\n",
+ sc->xl_unit);
+ break;
+ case XL_XCVR_100BFX:
+ media = IFM_ETHER|IFM_100_FX;
+ break;
+ default:
+ printk(
+ "etherlink : unit elnk%d unknown XCVR type: %" PRId32 "\n",
+ sc->xl_unit,
+ sc->xl_xcvr);
+ /*
+ * This will probably be wrong, but it prevents
+ * the ifmedia code from panicking.
+ */
+ media = IFM_ETHER|IFM_10_T;
+ break;
+ }
+
+
+ if (sc->xl_flags & XL_FLAG_NO_XCVR_PWR) {
+ XL_SEL_WIN(0);
+ CSR_WRITE_2(sc, XL_W0_MFG_ID, XL_NO_XCVR_PWR_MAGICBITS);
+ }
+ }
+
+
+
+ XL_SEL_WIN(2);
+ /* Clear the station mask. */
+ for (i = 0; i < 3; i++)
+ CSR_WRITE_2(sc, XL_W2_STATION_MASK_LO + (i * 2), 0);
+
+ /*
+ * Set the TX freethresh value.
+ * Note that this has no effect on 3c905B "cyclone"
+ * cards but is required for 3c900/3c905 "boomerang"
+ * cards in order to enable the download engine.
+ */
+ CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8);
+
+ /* Set the TX start threshold for best performance. */
+ sc->xl_tx_thresh = XL_MIN_FRAMELEN;
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_SET_START|sc->xl_tx_thresh);
+
+ /*
+ * If this is a 3c905B, also set the tx reclaim threshold.
+ * This helps cut down on the number of tx reclaim errors
+ * that could happen on a busy network. The chip multiplies
+ * the register value by 16 to obtain the actual threshold
+ * in bytes, so we divide by 16 when setting the value here.
+ * The existing threshold value can be examined by reading
+ * the register at offset 9 in window 5.
+ */
+ if (sc->xl_type == XL_TYPE_905B) {
+ CSR_WRITE_2(sc, XL_COMMAND,
+ XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4));
+ }
+
+ /* Set RX filter bits. */
+ XL_SEL_WIN(5);
+ rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER);
+
+ /* Set the individual bit to receive frames for this host only. */
+ rxfilt |= XL_RXFILTER_INDIVIDUAL;
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC) {
+ rxfilt |= XL_RXFILTER_ALLFRAMES;
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt);
+ } else {
+ rxfilt &= ~XL_RXFILTER_ALLFRAMES;
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt);
+ }
+
+ /*
+ * Set capture broadcast bit to capture broadcast frames.
+ */
+ if (ifp->if_flags & IFF_BROADCAST) {
+ rxfilt |= XL_RXFILTER_BROADCAST;
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt);
+ } else {
+ rxfilt &= ~XL_RXFILTER_BROADCAST;
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt);
+ }
+
+#if 0
+ /*
+ * Program the multicast filter, if necessary.
+ */
+ if (sc->xl_type == XL_TYPE_905B)
+ xl_setmulti_hash(sc);
+ else
+ xl_setmulti(sc);
+#endif
+ /*
+ * Load the address of the RX list. We have to
+ * stall the upload engine before we can manipulate
+ * the uplist pointer register, then unstall it when
+ * we're finished. We also have to wait for the
+ * stall command to complete before proceeding.
+ * Note that we have to do this after any RX resets
+ * have completed since the uplist register is cleared
+ * by a reset.
+ */
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL);
+ xl_wait(sc);
+ CSR_WRITE_4(sc, XL_UPLIST_PTR, phys_to_bus( sc->curr_rx_md ));
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL);
+ xl_wait(sc);
+
+
+#if 0
+ if (sc->xl_type == XL_TYPE_905B) {
+ /* Set polling interval */
+ CSR_WRITE_1(sc, XL_DOWN_POLL, 64);
+ xl_wait(sc);
+ printk("etherlink : unit elnk%d tx polling enabled\n", sc->xl_unit );
+ }
+#endif
+
+ /*
+ * If the coax transceiver is on, make sure to enable
+ * the DC-DC converter.
+ */
+ XL_SEL_WIN(3);
+ if (sc->xl_xcvr == XL_XCVR_COAX)
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START);
+ else
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
+
+ /* increase packet size to allow reception of 802.1q or ISL packets */
+ if (sc->xl_type == XL_TYPE_905B)
+ CSR_WRITE_2(sc, XL_W3_MAXPKTSIZE, XL_PACKET_SIZE);
+ /* Clear out the stats counters. */
+
+ memset( &sc->xl_stats, 0, sizeof(struct xl_stats));
+
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE);
+ sc->xl_stats_no_timeout = 1;
+ xl_stats_update(sc->stat_timer_id,sc);
+ sc->xl_stats_no_timeout = 0;
+ XL_SEL_WIN(4);
+ CSR_WRITE_2(sc, XL_W4_NET_DIAG, XL_NETDIAG_UPPER_BYTES_ENABLE);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_ENABLE);
+
+
+ /*
+ * Enable interrupts.
+ */
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|0xFF);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|XL_INTRS);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|XL_INTRS);
+
+ /* Set the RX early threshold */
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_THRESH|(XL_PACKET_SIZE >>2));
+ CSR_WRITE_4(sc, XL_DMACTL, XL_DMACTL_UP_RX_EARLY );
+
+ /* Enable receiver and transmitter. */
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
+ xl_wait(sc);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_ENABLE);
+ xl_wait(sc);
+
+ /* Select window 7 for normal operations. */
+ XL_SEL_WIN(7);
+
+ /* schedule the stats update timer */
+ rtems_timer_fire_after( sc->stat_timer_id, sc->stats_update_ticks, xl_stats_update, (void *)sc );
+ }
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+ }
+}
+
+
+
+
+
+
+
+/*
+ * Stop the device
+ */
+static void
+elnk_stop (struct elnk_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ int i;
+
+ /*
+ * Stop the transmitter
+ */
+ xl_stop(sc);
+ xl_reset(sc);
+ sc->tx_idle = -1;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /*
+ ** Clear out the rx & tx rings
+ */
+ {
+ struct TXMD *chainhead;
+ size_t esize;
+
+ while( rtems_message_queue_receive( chainRecoveryQueue, &chainhead, &esize,
+ RTEMS_NO_WAIT, 0) == RTEMS_SUCCESSFUL );
+ }
+
+ for(i=0 ; i<sc->numRxbuffers; i++)
+ {
+ st_le32( &sc->rx_ring[i].status, 0);
+ st_le32( &sc->rx_ring[i].length, XL_LAST_FRAG | XL_PACKET_SIZE );
+ }
+
+ for(i=0 ; i<sc->numTxbuffers; i++)
+ {
+ st_le32( &sc->tx_ring[i].status, XL_TXSTAT_DL_COMPLETE );
+ st_le32( &sc->tx_ring[i].next, 0);
+ if( sc->tx_ring[i].mbuf )
+ {
+ m_free( sc->tx_ring[i].mbuf );
+ sc->tx_ring[i].mbuf = NULL;
+ }
+ }
+}
+
+
+
+
+/*
+ * Show interface statistics
+ */
+static void
+elnk_stats (struct elnk_softc *sc)
+{
+ printf(" MII PHY data { anr:%04x lpar:%04x stat:%04x ctl:%04x }\n",
+ sc->xl_stats.miianr,
+ sc->xl_stats.miipar,
+ sc->xl_stats.miistatus,
+ sc->xl_stats.miicmd);
+
+ printf(" internalcfg:%08" PRIx32 " macctl:%04x dmactl:%08" PRIx32 "\n",
+ sc->xl_stats.internalconfig,
+ sc->xl_stats.mac_control,
+ sc->xl_stats.dmactl);
+
+ printf(" rxstatus:%04x txstatus:%02x smbstat:%04x\n",
+ sc->xl_stats.rxstatus,
+ sc->xl_stats.txstatus,
+ sc->xl_stats.smbstatus);
+
+ printf(" txfree:%04X intstatus:%04x mediastat:%04x\n",
+ sc->xl_stats.txfree,
+ sc->xl_stats.intstatus,
+ sc->xl_stats.mediastatus);
+
+
+ {
+ int i, totalLengths= 0, numLengths= 0;
+
+ for(i=0; i< NUM_CHAIN_LENGTHS; i++)
+ {
+ if( sc->chain_lengths[i] > -1 )
+ {
+ totalLengths += sc->chain_lengths[i];
+ ++numLengths;
+ }
+ }
+
+ printf(" interrupts:%-9" PRIu32 " txcmp_ints:%-5" PRIu32 " avg_chain_len:%-4d\n",
+ sc->xl_stats.device_interrupts,
+ sc->xl_stats.txcomplete_ints,
+ numLengths ? (totalLengths / numLengths) : -1 );
+ }
+
+ printf(" carrier_lost:%-5d sqe_errs:%-5d\n",
+ sc->xl_stats.xl_carrier_lost,
+ sc->xl_stats.xl_sqe_errs);
+
+ printf(" tx_multi_collision:%-5d tx_single_collision:%-5d\n",
+ sc->xl_stats.xl_tx_multi_collision,
+ sc->xl_stats.xl_tx_single_collision);
+
+ printf(" tx_late_collision:%-5d rx_overrun:%-5d\n",
+ sc->xl_stats.xl_tx_late_collision,
+ sc->xl_stats.xl_rx_overrun);
+
+ printf(" tx_deferred:%-5d badssd:%-5d\n",
+ sc->xl_stats.xl_tx_deferred,
+ sc->xl_stats.xl_badssd);
+
+ printf(" rx_frames_ok:%-9" PRIu32 " tx_frames_ok:%-9" PRIu32 "\n",
+ sc->xl_stats.xl_rx_frames_ok,
+ sc->xl_stats.xl_tx_frames_ok);
+
+ printf(" rx_bytes_ok:%-9" PRIu32 " tx_bytes_ok:%-9" PRIu32 "\n",
+ sc->xl_stats.xl_rx_bytes_ok,
+ sc->xl_stats.xl_tx_bytes_ok );
+}
+
+
+
+
+
+
+
+/*
+ * Driver ioctl handler
+ */
+static int
+elnk_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct elnk_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command) {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl (ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
+ case IFF_RUNNING:
+ elnk_stop (sc);
+ break;
+
+ case IFF_UP:
+ elnk_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ elnk_stop (sc);
+ elnk_init (sc);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ elnk_stats (sc);
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+
+
+
+
+
+
+
+#if 0
+static int iftap(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m )
+{
+ int i;
+ char *delim, *pkt;
+
+ printk("unit %i, src ", ifp->if_unit );
+ for(delim= "", i=0; i< ETHER_ADDR_LEN; i++, delim=":")
+ printk("%s%02x", delim, (char) eh->ether_shost[i] );
+
+ printk(" dest ");
+
+ for(delim= "", i=0; i< ETHER_ADDR_LEN; i++, delim=":")
+ printk("%s%02x", delim, (char) eh->ether_dhost[i] );
+ printk(" pkt ");
+
+ pkt = (char *)eh;
+ for(delim="", i=0; i < sizeof(struct ether_header); i++, delim=":")
+ printk("%s%02x", delim, (char) pkt[i] );
+
+ printk("\n");
+ return 0;
+}
+#endif
+
+
+
+struct el_boards
+{
+ int pbus,pdev,pfun, vid, did, tindex;
+};
+
+/* Prototype to avoid warning. This must be a global symbol. */
+int rtems_elnk_driver_attach(struct rtems_bsdnet_ifconfig *config, int attach);
+
+/*
+ * Attach an ELNK driver to the system
+ */
+int
+rtems_elnk_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
+{
+ struct elnk_softc *sc;
+ struct ifnet *ifp;
+ char *unitName;
+ int unitNumber;
+ int mtu, i;
+ unsigned char cvalue;
+ struct el_boards sysboards[NUM_UNITS];
+ int numFound = 0;
+ int pbus, pdev, pfun;
+#if defined(__i386__)
+ uint32_t value;
+ uint8_t interrupt;
+#endif
+#if defined(__PPC__)
+ uint32_t lvalue;
+#endif
+
+
+ /*
+ * Get the instance number for the board we're going to configure
+ * from the user.
+ */
+ if( (unitNumber = rtems_bsdnet_parse_driver_name( config, &unitName)) == -1 )
+ {
+ return 0;
+ }
+
+ if( strcmp(unitName, DRIVER_PREFIX) )
+ {
+ printk("etherlink : invalid unit name '%s'\n", unitName );
+ return 0;
+ }
+
+ if ((unitNumber < 1) || (unitNumber > NUM_UNITS))
+ {
+ printk("etherlink : unit %i is invalid, must be (1 <= n <= %d)\n", unitNumber, NUM_UNITS);
+ return 0;
+ }
+
+
+ {
+ int done= 0, unum;
+
+ /*
+ * Run thru the list of boards, finding all that are present in
+ * the system. Sort by slot,dev - and then use the unitNumber-1
+ * to index the list and select the device. Yucky.
+ */
+ for( i=0; !done && xl_devs[i].xl_vid; i++)
+ {
+ for(unum= 1; !done &&
+ pci_find_device( xl_devs[i].xl_vid, xl_devs[i].xl_did, unum-1,
+ &sysboards[numFound].pbus,
+ &sysboards[numFound].pdev,
+ &sysboards[numFound].pfun)==0; unum++)
+ {
+ if( numFound == NUM_UNITS )
+ {
+ printk("etherlink : Maximum of %d units found, extra devices ignored.\n", NUM_UNITS );
+ done=-1;
+ }
+ else
+ {
+ sysboards[numFound].vid = xl_devs[i].xl_vid;
+ sysboards[numFound].did = xl_devs[i].xl_did;
+ sysboards[numFound].tindex = i;
+ ++numFound;
+ }
+ }
+ }
+
+ if( ! numFound )
+ {
+ printk("etherlink : No Etherlink devices found\n");
+ return 0;
+ }
+
+ if( unitNumber-1 >= numFound )
+ {
+ printk("etherlink : device '%s' not found\n", config->name );
+ return 0;
+ }
+
+ /*
+ * Got the list of etherlink boards in the system, now sort by
+ * slot,device. bubble sorts aren't all that wonderful, but this
+ * is a short & infrequently sorted list.
+ */
+ if( numFound > 1 )
+ {
+ struct el_boards tboard;
+ int didsort;
+
+ do
+ {
+ didsort = 0;
+
+ for(i=1; i<numFound; i++)
+ {
+ if( sysboards[i-1].pbus > sysboards[i].pbus ||
+ (sysboards[i-1].pbus == sysboards[i].pbus && sysboards[i-1].pdev > sysboards[i].pdev) )
+ {
+ memcpy(&tboard, &sysboards[i-1], sizeof(struct el_boards));
+ memcpy(&sysboards[i-1], &sysboards[i], sizeof(struct el_boards));
+ memcpy(&sysboards[i], &tboard, sizeof(struct el_boards));
+ didsort++;
+ }
+ }
+ }
+ while( didsort );
+ }
+
+ /*
+ ** board list is sorted, now select the unit
+ */
+
+ pbus = sysboards[unitNumber-1].pbus;
+ pdev = sysboards[unitNumber-1].pdev;
+ pfun = sysboards[unitNumber-1].pfun;
+ }
+
+ sc = &elnk_softc[unitNumber - 1];
+ ifp = &sc->arpcom.ac_if;
+ if (ifp->if_softc != NULL)
+ {
+ printk("etherlink : unit %i already in use.\n", unitNumber );
+ return 0;
+ }
+
+ /*
+ ** Save various things
+ */
+ sc->xl_unit = unitNumber;
+ sc->xl_type = sysboards[ unitNumber-1 ].tindex;
+
+ sc->vendorID = sysboards[numFound].vid;
+ sc->deviceID = sysboards[numFound].did;
+
+ sc->numRxbuffers = (config->rbuf_count) ? config->rbuf_count : RX_RING_SIZE;
+ sc->numTxbuffers = (config->xbuf_count) ? config->xbuf_count : TX_RING_SIZE;
+
+
+ for(i=0; i< NUM_CHAIN_LENGTHS; i++) sc->chain_lengths[i]= -1;
+ sc->chlenIndex = 0;
+
+
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ sc->acceptBroadcast = !config->ignore_broadcast;
+
+
+
+#ifdef ELNK_DEBUG
+ printk("etherlink : device '%s', name 'elnk%d', pci %02x:%02x.%02x, %d rx/%d tx buffers\n",
+ xl_devs[sc->xl_type].xl_name, sc->xl_unit,
+ pbus, pdev, pfun,
+ sc->numRxbuffers, sc->numTxbuffers);
+#endif
+
+
+ /*
+ ** Create this unit's stats timer
+ */
+ if( rtems_timer_create( rtems_build_name( 'X', 'L', 't', (char)(sc->xl_unit & 255)),
+ &sc->stat_timer_id ) != RTEMS_SUCCESSFUL )
+ {
+ printk("etherlink : unit elnk%d unable to create stats timer\n", sc->xl_unit );
+ return 0;
+ }
+
+ /* update stats 1 times/second if things aren't incrementing fast
+ * enough to trigger stats interrupts
+ */
+ sc->stats_update_ticks = rtems_clock_get_ticks_per_second();
+
+ /*
+ ** Get this unit's rx/tx event
+ */
+ sc->ioevent = unit_signals[unitNumber-1];
+
+
+#if defined(__i386__)
+ pci_read_config_dword(pbus, pdev, pfun, 16, &value);
+ sc->ioaddr = value & ~IO_MASK;
+
+ pci_read_config_byte(pbus, pdev, pfun, 60, &interrupt);
+ cvalue = interrupt;
+#endif
+#if defined(__PPC__)
+ /*
+ ** Prep the board
+ */
+ pci_write_config_word(pbus, pdev, pfun,
+ PCI_COMMAND,
+ (uint16_t)( PCI_COMMAND_IO |
+ PCI_COMMAND_MASTER |
+ PCI_COMMAND_INVALIDATE |
+ PCI_COMMAND_WAIT ) );
+ /*
+ * Get the device's base address
+ */
+ pci_read_config_dword(pbus, pdev, pfun,
+ PCI_BASE_ADDRESS_0,
+ &lvalue);
+
+ sc->ioaddr = (uint32_t)lvalue & PCI_BASE_ADDRESS_IO_MASK;
+ /*
+ ** Store the interrupt name, we'll use it later when we initialize
+ ** the board.
+ */
+ pci_read_config_byte(pbus, pdev, pfun,
+ PCI_INTERRUPT_LINE,
+ &cvalue);
+#endif
+
+ memset(&sc->irqInfo,0,sizeof(rtems_irq_connect_data));
+ sc->irqInfo.name = cvalue;
+
+
+ /*
+ ** Establish basic board config, set node address from config or
+ ** board eeprom, do stuff with additional device properties
+ */
+
+ {
+ uint8_t pci_latency;
+ uint8_t new_latency = 248;
+
+ /* Check the PCI latency value. On the 3c590 series the latency timer
+ must be set to the maximum value to avoid data corruption that occurs
+ when the timer expires during a transfer. This bug exists the Vortex
+ chip only. */
+#if defined(__i386__)
+ pci_read_config_byte(pbus, pdev, pfun, 0x0d, &pci_latency);
+#endif
+#if defined(__PPC__)
+ pci_read_config_byte(pbus,pdev,pfun, PCI_LATENCY_TIMER, &pci_latency);
+#endif
+ if (pci_latency < new_latency)
+ {
+ printk("etherlink : unit elnk%d Overriding PCI latency, timer (CFLT) setting of %d, new value is %d.\n", sc->xl_unit, pci_latency, new_latency );
+#if defined(__i386__)
+ pci_write_config_byte(pbus, pdev, pfun, 0x0d, new_latency);
+#endif
+#if defined(__PPC__)
+ pci_write_config_byte(pbus,pdev,pfun, PCI_LATENCY_TIMER, new_latency);
+#endif
+ }
+ }
+
+ /* Reset the adapter. */
+ xl_reset(sc);
+
+
+ {
+ u_int16_t xcvr[2];
+ u_char eaddr[ETHER_ADDR_LEN];
+
+ sc->xl_flags = 0;
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_555)
+ sc->xl_flags |= XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_PHYOK;
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_556 ||
+ sc->deviceID == TC_DEVICEID_HURRICANE_556B)
+ sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK |
+ XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_WEIRDRESET |
+ XL_FLAG_INVERT_LED_PWR | XL_FLAG_INVERT_MII_PWR;
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_555 ||
+ sc->deviceID == TC_DEVICEID_HURRICANE_556)
+ sc->xl_flags |= XL_FLAG_8BITROM;
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_556B)
+ sc->xl_flags |= XL_FLAG_NO_XCVR_PWR;
+
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_575A ||
+ sc->deviceID == TC_DEVICEID_HURRICANE_575B ||
+ sc->deviceID == TC_DEVICEID_HURRICANE_575C ||
+ sc->deviceID == TC_DEVICEID_HURRICANE_656B ||
+ sc->deviceID == TC_DEVICEID_TORNADO_656C)
+ sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK |
+ XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_8BITROM;
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_656)
+ sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK;
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_575B)
+ sc->xl_flags |= XL_FLAG_INVERT_LED_PWR;
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_575C)
+ sc->xl_flags |= XL_FLAG_INVERT_MII_PWR;
+ if (sc->deviceID == TC_DEVICEID_TORNADO_656C)
+ sc->xl_flags |= XL_FLAG_INVERT_MII_PWR;
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_656 ||
+ sc->deviceID == TC_DEVICEID_HURRICANE_656B)
+ sc->xl_flags |= XL_FLAG_INVERT_MII_PWR |
+ XL_FLAG_INVERT_LED_PWR;
+ if (sc->deviceID == TC_DEVICEID_TORNADO_10_100BT_920B)
+ sc->xl_flags |= XL_FLAG_PHYOK;
+
+
+ if (config->hardware_address)
+ {
+ memcpy(sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
+ }
+ else
+ {
+ if (xl_read_eeprom(sc, (caddr_t)&eaddr, XL_EE_OEM_ADR0, 3, 1))
+ {
+ printk("etherlink : unit elnk%d Failed to read station address\n", sc->xl_unit );
+ return 0;
+ }
+ memcpy((char *)&sc->arpcom.ac_enaddr, eaddr, ETHER_ADDR_LEN);
+ }
+
+ /*
+ * Figure out the card type. 3c905B adapters have the
+ * 'supportsNoTxLength' bit set in the capabilities
+ * word in the EEPROM.
+ */
+ xl_read_eeprom(sc, (caddr_t)&sc->xl_caps, XL_EE_CAPS, 1, 0);
+ if (sc->xl_caps & XL_CAPS_NO_TXLENGTH)
+ sc->xl_type = XL_TYPE_905B;
+ else
+ sc->xl_type = XL_TYPE_90X;
+
+
+ /*
+ * Now we have to see what sort of media we have.
+ * This includes probing for an MII interace and a
+ * possible PHY.
+ */
+ XL_SEL_WIN(3);
+ sc->xl_media = CSR_READ_2(sc, XL_W3_MEDIA_OPT);
+
+ xl_read_eeprom(sc, (char *)&xcvr, XL_EE_ICFG_0, 2, 0);
+ sc->xl_xcvr = xcvr[0] | xcvr[1] << 16;
+ sc->xl_xcvr &= XL_ICFG_CONNECTOR_MASK;
+ sc->xl_xcvr >>= XL_ICFG_CONNECTOR_BITS;
+
+#if 0
+ printk("etherlink : unit elnk%d EEPROM set xcvr to 0x%x\n", sc->xl_unit, sc->xl_xcvr);
+#endif
+
+ {
+ char msg[255];
+ int i;
+
+ struct _availmedia
+ {
+ int bit;
+ char *name;
+ } _am[]= {{ XL_MEDIAOPT_BT4, "100BaseT4" },
+ { XL_MEDIAOPT_BTX, "100BaseTX" },
+ { XL_MEDIAOPT_BFX, "100BaseFX" },
+ { XL_MEDIAOPT_BT, "10BaseT" },
+ { XL_MEDIAOPT_BNC, "10Base2" },
+ { XL_MEDIAOPT_AUI, "10mbps AUI"},
+ { XL_MEDIAOPT_MII, "MII"},
+ { 0, NULL }};
+
+ msg[0]= 0;
+ for( i=0; _am[i].bit; i++)
+ {
+ if( sc->xl_media & _am[i].bit )
+ sprintf( &msg[strlen(msg)], ",%s", _am[i].name );
+ }
+ if( !strlen(msg) ) strcpy( &msg[1], "<no media bits>");
+
+ printk("etherlink : unit elnk%d available media : %s\n", sc->xl_unit, &msg[1]);
+ }
+
+ XL_SEL_WIN(7);
+ }
+
+
+
+ /*
+ * Set up network interface
+ */
+ ifp->if_softc = sc;
+ ifp->if_name = unitName;
+ ifp->if_unit = sc->xl_unit;
+ ifp->if_mtu = mtu;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+ ifp->if_init = elnk_init;
+ ifp->if_start = elnk_start;
+ ifp->if_ioctl = elnk_ioctl;
+ ifp->if_output = ether_output;
+
+#if 0
+ ifp->if_tap = iftap;
+#endif
+
+ /*
+ * Attach the interface
+ */
+ if_attach (ifp);
+ ether_ifattach (ifp);
+
+#ifdef ELNK_DEBUG
+ printk( "etherlink : unit elnk%d driver attached\n", sc->xl_unit );
+#endif
+
+ /*
+ * Start driver tasks if this is the first unit initialized
+ */
+ if (txDaemonTid == 0)
+ {
+ if( rtems_message_queue_create( rtems_build_name('X','L','c','r'),
+ sc->numTxbuffers+1,
+ sizeof(struct TXMD *),
+ RTEMS_FIFO | RTEMS_LOCAL,
+ &chainRecoveryQueue ) != RTEMS_SUCCESSFUL )
+ {
+ rtems_panic( "etherlink : Unable to create TX buffer recovery queue\n" );
+ }
+
+
+ rxDaemonTid = rtems_bsdnet_newproc( "XLrx", 4096,
+ elnk_rxDaemon, NULL);
+
+ txDaemonTid = rtems_bsdnet_newproc( "XLtx", 4096,
+ elnk_txDaemon, NULL);
+#ifdef ELNK_DEBUG
+ printk( "etherlink : driver tasks created\n" );
+#endif
+ }
+
+ return 1;
+};
+
+#endif /* ELNK_SUPPORTED */
+
+/* eof */
diff --git a/bsps/shared/net/greth2.c b/bsps/shared/net/greth2.c
new file mode 100644
index 0000000..13bf7b8
--- /dev/null
+++ b/bsps/shared/net/greth2.c
@@ -0,0 +1,1200 @@
+/*
+ * Gaisler Research ethernet MAC driver
+ * adapted from Opencores driver by Marko Isomaki
+ *
+ * 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.
+ *
+ * 2007-09-07, Ported GBIT support from 4.6.5
+ */
+
+#include <machine/rtems-bsd-kernel-space.h>
+
+#include <rtems.h>
+#include <bsp.h>
+
+#ifdef GRETH_SUPPORTED
+
+#include <inttypes.h>
+#include <errno.h>
+#include <rtems/bspIo.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+#include <libchip/greth.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>
+
+#ifdef malloc
+#undef malloc
+#endif
+#ifdef free
+#undef free
+#endif
+
+/* #define GRETH_DEBUG */
+
+#ifdef CPU_U32_FIX
+extern void ipalign(struct mbuf *m);
+#endif
+
+/* Used when reading from memory written by GRETH DMA unit */
+#ifndef GRETH_MEM_LOAD
+#define GRETH_MEM_LOAD(addr) (*(volatile unsigned int *)(addr))
+#endif
+
+/*
+ * Number of OCs supported by this driver
+ */
+#define NOCDRIVER 1
+
+/*
+ * Receive buffer size -- Allow for a full ethernet packet including CRC
+ */
+#define RBUF_SIZE 1518
+
+#define ET_MINLEN 64 /* minimum message length */
+
+/*
+ * RTEMS event used by interrupt handler to signal driver tasks.
+ * This must not be any of the events used by the network task synchronization.
+ */
+#define INTERRUPT_EVENT RTEMS_EVENT_1
+
+/*
+ * RTEMS event used to start transmit daemon.
+ * This must not be the same as INTERRUPT_EVENT.
+ */
+#define START_TRANSMIT_EVENT RTEMS_EVENT_2
+
+ /* event to send when tx buffers become available */
+#define GRETH_TX_WAIT_EVENT RTEMS_EVENT_3
+
+#if (MCLBYTES < RBUF_SIZE)
+# error "Driver must have MCLBYTES > RBUF_SIZE"
+#endif
+
+/* 4s Autonegotiation Timeout */
+#ifndef GRETH_AUTONEGO_TIMEOUT_MS
+#define GRETH_AUTONEGO_TIMEOUT_MS 4000
+#endif
+const struct timespec greth_tan = {
+ GRETH_AUTONEGO_TIMEOUT_MS/1000,
+ (GRETH_AUTONEGO_TIMEOUT_MS % 1000) *1000000
+};
+
+/* For optimizing the autonegotiation time */
+#define GRETH_AUTONEGO_PRINT_TIME
+
+/* Ethernet buffer descriptor */
+
+typedef struct _greth_rxtxdesc {
+ volatile uint32_t ctrl; /* Length and status */
+ uint32_t *addr; /* Buffer pointer */
+} greth_rxtxdesc;
+
+
+/*
+ * Per-device data
+ */
+struct greth_softc
+{
+
+ struct arpcom arpcom;
+
+ greth_regs *regs;
+
+ int acceptBroadcast;
+ rtems_id daemonTid;
+
+ unsigned int tx_ptr;
+ unsigned int tx_dptr;
+ unsigned int tx_cnt;
+ unsigned int rx_ptr;
+ unsigned int txbufs;
+ unsigned int rxbufs;
+ greth_rxtxdesc *txdesc;
+ greth_rxtxdesc *rxdesc;
+ struct mbuf **rxmbuf;
+ struct mbuf **txmbuf;
+ rtems_vector_number vector;
+
+ /* TX descriptor interrupt generation */
+ int tx_int_gen;
+ int tx_int_gen_cur;
+ struct mbuf *next_tx_mbuf;
+ int max_fragsize;
+
+ /*Status*/
+ struct phy_device_info phydev;
+ int fd;
+ int sp;
+ int gb;
+ int gbit_mac;
+ int auto_neg;
+ struct timespec auto_neg_time;
+
+ /*
+ * Statistics
+ */
+ unsigned long rxInterrupts;
+
+ unsigned long rxPackets;
+ unsigned long rxLengthError;
+ unsigned long rxNonOctet;
+ unsigned long rxBadCRC;
+ unsigned long rxOverrun;
+
+ unsigned long txInterrupts;
+
+ unsigned long txDeferred;
+ unsigned long txHeartbeat;
+ unsigned long txLateCollision;
+ unsigned long txRetryLimit;
+ unsigned long txUnderrun;
+
+};
+
+static struct greth_softc greth;
+
+int greth_process_tx_gbit(struct greth_softc *sc);
+int greth_process_tx(struct greth_softc *sc);
+
+static char *almalloc(int sz)
+{
+ char *tmp;
+ tmp = calloc(1,2*sz);
+ tmp = (char *) (((uintptr_t)tmp+sz) & ~(sz -1));
+ return(tmp);
+}
+
+/* GRETH interrupt handler */
+
+static void greth_interrupt_handler (void *arg)
+{
+ uint32_t status;
+ uint32_t ctrl;
+ rtems_event_set events = 0;
+ struct greth_softc *greth = arg;
+
+ /* read and clear interrupt cause */
+ status = greth->regs->status;
+ greth->regs->status = status;
+ ctrl = greth->regs->ctrl;
+
+ /* Frame received? */
+ if ((ctrl & GRETH_CTRL_RXIRQ) && (status & (GRETH_STATUS_RXERR | GRETH_STATUS_RXIRQ)))
+ {
+ greth->rxInterrupts++;
+ /* Stop RX-Error and RX-Packet interrupts */
+ ctrl &= ~GRETH_CTRL_RXIRQ;
+ events |= INTERRUPT_EVENT;
+ }
+
+ if ( (ctrl & GRETH_CTRL_TXIRQ) && (status & (GRETH_STATUS_TXERR | GRETH_STATUS_TXIRQ)) )
+ {
+ greth->txInterrupts++;
+ ctrl &= ~GRETH_CTRL_TXIRQ;
+ events |= GRETH_TX_WAIT_EVENT;
+ }
+
+ /* Clear interrupt sources */
+ greth->regs->ctrl = ctrl;
+
+ /* Send the event(s) */
+ if ( events )
+ rtems_bsdnet_event_send (greth->daemonTid, events);
+}
+
+static uint32_t read_mii(uint32_t phy_addr, uint32_t reg_addr)
+{
+ while (greth.regs->mdio_ctrl & GRETH_MDIO_BUSY) {}
+ greth.regs->mdio_ctrl = (phy_addr << 11) | (reg_addr << 6) | GRETH_MDIO_READ;
+ while (greth.regs->mdio_ctrl & GRETH_MDIO_BUSY) {}
+ if (!(greth.regs->mdio_ctrl & GRETH_MDIO_LINKFAIL))
+ return((greth.regs->mdio_ctrl >> 16) & 0xFFFF);
+ else {
+ printf("greth: failed to read mii\n");
+ return (0);
+ }
+}
+
+static void write_mii(uint32_t phy_addr, uint32_t reg_addr, uint32_t data)
+{
+ while (greth.regs->mdio_ctrl & GRETH_MDIO_BUSY) {}
+ greth.regs->mdio_ctrl =
+ ((data & 0xFFFF) << 16) | (phy_addr << 11) | (reg_addr << 6) | GRETH_MDIO_WRITE;
+ while (greth.regs->mdio_ctrl & GRETH_MDIO_BUSY) {}
+}
+
+static void print_init_info(struct greth_softc *sc)
+{
+ printf("greth: driver attached\n");
+ if ( sc->auto_neg == -1 ){
+ printf("Auto negotiation timed out. Selecting default config\n");
+ }
+ printf("**** PHY ****\n");
+ printf("Vendor: %x Device: %x Revision: %d\n",sc->phydev.vendor, sc->phydev.device, sc->phydev.rev);
+ printf("Current Operating Mode: ");
+ if (sc->gb) {
+ printf("1000 Mbit ");
+ } else if (sc->sp) {
+ printf("100 Mbit ");
+ } else {
+ printf("10 Mbit ");
+ }
+ if (sc->fd) {
+ printf("Full Duplex\n");
+ } else {
+ printf("Half Duplex\n");
+ }
+#ifdef GRETH_AUTONEGO_PRINT_TIME
+ if ( sc->auto_neg ) {
+ printf("Autonegotiation Time: %lldms\n", sc->auto_neg_time.tv_sec*1000 +
+ sc->auto_neg_time.tv_nsec/1000000);
+ }
+#endif
+}
+
+
+/*
+ * Initialize the ethernet hardware
+ */
+static void
+greth_initialize_hardware (struct greth_softc *sc)
+{
+ struct mbuf *m;
+ int i;
+ int phyaddr;
+ int phyctrl;
+ int phystatus;
+ int tmp1;
+ int tmp2;
+ struct timespec tstart, tnow;
+
+ greth_regs *regs;
+
+ regs = sc->regs;
+
+ /* Reset the controller. */
+ greth.rxInterrupts = 0;
+ greth.rxPackets = 0;
+
+ regs->ctrl = 0;
+ regs->ctrl = GRETH_CTRL_RST; /* Reset ON */
+ regs->ctrl = 0; /* Reset OFF */
+
+ /* Check if mac is gbit capable*/
+ sc->gbit_mac = (regs->ctrl >> 27) & 1;
+
+ /* Get the phy address which assumed to have been set
+ correctly with the reset value in hardware*/
+ phyaddr = (regs->mdio_ctrl >> 11) & 0x1F;
+
+ /* get phy control register default values */
+ while ((phyctrl = read_mii(phyaddr, 0)) & 0x8000) {}
+
+ /* reset PHY and wait for completion */
+ write_mii(phyaddr, 0, 0x8000 | phyctrl);
+
+ while ((read_mii(phyaddr, 0)) & 0x8000) {}
+ phystatus = read_mii(phyaddr, 1);
+
+ /* Disable Gbit auto-neg advertisement if MAC does not support it */
+
+ if ((!sc->gbit_mac) && (phystatus & 0x100)) write_mii(phyaddr, 9, 0);
+
+ /* Restart auto-negotiation if available */
+ if (phystatus & 0x08) {
+ write_mii(phyaddr, 0, phyctrl | 0x1200);
+ phyctrl = read_mii(phyaddr, 0);
+ }
+
+ /* Check if PHY is autoneg capable and then determine operating mode,
+ otherwise force it to 10 Mbit halfduplex */
+ sc->gb = 0;
+ sc->fd = 0;
+ sc->sp = 0;
+ sc->auto_neg = 0;
+ timespecclear(&sc->auto_neg_time);
+ if ((phyctrl >> 12) & 1) {
+ /*wait for auto negotiation to complete*/
+ sc->auto_neg = 1;
+ if (rtems_clock_get_uptime(&tstart) != RTEMS_SUCCESSFUL)
+ printk("rtems_clock_get_uptime failed\n");
+ while (!(((phystatus = read_mii(phyaddr, 1)) >> 5) & 1)) {
+ if (rtems_clock_get_uptime(&tnow) != RTEMS_SUCCESSFUL)
+ printk("rtems_clock_get_uptime failed\n");
+ timespecsub(&tnow, &tstart, &sc->auto_neg_time);
+ if (timespeccmp(&sc->auto_neg_time, &greth_tan, >)) {
+ sc->auto_neg = -1; /* Failed */
+ tmp1 = read_mii(phyaddr, 0);
+ sc->gb = ((phyctrl >> 6) & 1) && !((phyctrl >> 13) & 1);
+ sc->sp = !((phyctrl >> 6) & 1) && ((phyctrl >> 13) & 1);
+ sc->fd = (phyctrl >> 8) & 1;
+ goto auto_neg_done;
+ }
+ /* Wait about 30ms, time is PHY dependent */
+ rtems_task_wake_after(rtems_clock_get_ticks_per_second()/32);
+ }
+ sc->phydev.adv = read_mii(phyaddr, 4);
+ sc->phydev.part = read_mii(phyaddr, 5);
+ if ((phystatus >> 8) & 1) {
+ sc->phydev.extadv = read_mii(phyaddr, 9);
+ sc->phydev.extpart = read_mii(phyaddr, 10);
+ if ( (sc->phydev.extadv & GRETH_MII_EXTADV_1000FD) &&
+ (sc->phydev.extpart & GRETH_MII_EXTPRT_1000FD)) {
+ sc->gb = 1;
+ sc->fd = 1;
+ }
+ else if ( (sc->phydev.extadv & GRETH_MII_EXTADV_1000HD) &&
+ (sc->phydev.extpart & GRETH_MII_EXTPRT_1000HD)) {
+ sc->gb = 1;
+ sc->fd = 0;
+ }
+ }
+ if ((sc->gb == 0) || ((sc->gb == 1) && (sc->gbit_mac == 0))) {
+ if ( (sc->phydev.adv & GRETH_MII_100TXFD) &&
+ (sc->phydev.part & GRETH_MII_100TXFD)) {
+ sc->sp = 1;
+ sc->fd = 1;
+ }
+ else if ( (sc->phydev.adv & GRETH_MII_100TXHD) &&
+ (sc->phydev.part & GRETH_MII_100TXHD)) {
+ sc->sp = 1;
+ sc->fd = 0;
+ }
+ else if ( (sc->phydev.adv & GRETH_MII_10FD) &&
+ (sc->phydev.part & GRETH_MII_10FD)) {
+ sc->fd = 1;
+ }
+ }
+ }
+auto_neg_done:
+ sc->phydev.vendor = 0;
+ sc->phydev.device = 0;
+ sc->phydev.rev = 0;
+ phystatus = read_mii(phyaddr, 1);
+
+ /*Read out PHY info if extended registers are available */
+ if (phystatus & 1) {
+ tmp1 = read_mii(phyaddr, 2);
+ tmp2 = read_mii(phyaddr, 3);
+
+ sc->phydev.vendor = (tmp1 << 6) | ((tmp2 >> 10) & 0x3F);
+ sc->phydev.rev = tmp2 & 0xF;
+ sc->phydev.device = (tmp2 >> 4) & 0x3F;
+ }
+
+ /* Force to 10 mbit half duplex if the 10/100 MAC is used with a 1000 PHY*/
+ /*check if marvell 88EE1111 PHY. Needs special reset handling */
+ if ((phystatus & 1) && (sc->phydev.vendor == 0x005043) && (sc->phydev.device == 0x0C)) {
+ if (((sc->gb) && !(sc->gbit_mac)) || !((phyctrl >> 12) & 1)) {
+ write_mii(phyaddr, 0, sc->sp << 13);
+ write_mii(phyaddr, 0, 0x8000);
+ sc->gb = 0;
+ sc->sp = 0;
+ sc->fd = 0;
+ }
+ } else {
+ if (((sc->gb) && !(sc->gbit_mac)) || !((phyctrl >> 12) & 1)) {
+ write_mii(phyaddr, 0, sc->sp << 13);
+ sc->gb = 0;
+ sc->sp = 0;
+ sc->fd = 0;
+ }
+ }
+ while ((read_mii(phyaddr, 0)) & 0x8000) {}
+
+ regs->ctrl = 0;
+ regs->ctrl = GRETH_CTRL_RST; /* Reset ON */
+ regs->ctrl = 0;
+
+ /* Initialize rx/tx descriptor pointers */
+ sc->txdesc = (greth_rxtxdesc *) almalloc(1024);
+ sc->rxdesc = (greth_rxtxdesc *) almalloc(1024);
+ sc->tx_ptr = 0;
+ sc->tx_dptr = 0;
+ sc->tx_cnt = 0;
+ sc->rx_ptr = 0;
+ regs->txdesc = (uintptr_t) sc->txdesc;
+ regs->rxdesc = (uintptr_t) sc->rxdesc;
+
+ sc->rxmbuf = calloc(sc->rxbufs, sizeof(*sc->rxmbuf));
+ sc->txmbuf = calloc(sc->txbufs, sizeof(*sc->txmbuf));
+
+ for (i = 0; i < sc->txbufs; i++)
+ {
+ sc->txdesc[i].ctrl = 0;
+ if (!(sc->gbit_mac)) {
+ sc->txdesc[i].addr = malloc(GRETH_MAXBUF_LEN);
+ }
+#ifdef GRETH_DEBUG
+ /* printf("TXBUF: %08x\n", (int) sc->txdesc[i].addr); */
+#endif
+ }
+ for (i = 0; i < sc->rxbufs; i++)
+ {
+
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ if (sc->gbit_mac)
+ m->m_data += 2;
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ sc->rxmbuf[i] = m;
+ sc->rxdesc[i].addr = (uint32_t *) mtod(m, uint32_t *);
+ sc->rxdesc[i].ctrl = GRETH_RXD_ENABLE | GRETH_RXD_IRQ;
+#ifdef GRETH_DEBUG
+/* printf("RXBUF: %08x\n", (int) sc->rxdesc[i].addr); */
+#endif
+ }
+ sc->rxdesc[sc->rxbufs - 1].ctrl |= GRETH_RXD_WRAP;
+
+ /* set ethernet address. */
+ regs->mac_addr_msb =
+ sc->arpcom.ac_enaddr[0] << 8 | sc->arpcom.ac_enaddr[1];
+
+ uint32_t mac_addr_lsb;
+ mac_addr_lsb = sc->arpcom.ac_enaddr[2];
+ mac_addr_lsb <<= 8;
+ mac_addr_lsb |= sc->arpcom.ac_enaddr[3];
+ mac_addr_lsb <<= 8;
+ mac_addr_lsb |= sc->arpcom.ac_enaddr[4];
+ mac_addr_lsb <<= 8;
+ mac_addr_lsb |= sc->arpcom.ac_enaddr[5];
+ regs->mac_addr_lsb = mac_addr_lsb;
+
+ if ( sc->rxbufs < 10 ) {
+ sc->tx_int_gen = sc->tx_int_gen_cur = 1;
+ }else{
+ sc->tx_int_gen = sc->tx_int_gen_cur = sc->txbufs/2;
+ }
+ sc->next_tx_mbuf = NULL;
+
+ if ( !sc->gbit_mac )
+ sc->max_fragsize = 1;
+
+ /* clear all pending interrupts */
+ regs->status = 0xffffffff;
+
+ /* install interrupt handler */
+ rtems_interrupt_handler_install(sc->vector, "greth", RTEMS_INTERRUPT_SHARED,
+ greth_interrupt_handler, sc);
+
+ regs->ctrl |= GRETH_CTRL_RXEN | (sc->fd << 4) | GRETH_CTRL_RXIRQ | (sc->sp << 7) | (sc->gb << 8);
+
+ print_init_info(sc);
+}
+
+#ifdef CPU_U32_FIX
+
+/*
+ * Routine to align the received packet so that the ip header
+ * is on a 32-bit boundary. Necessary for cpu's that do not
+ * allow unaligned loads and stores and when the 32-bit DMA
+ * mode is used.
+ *
+ * Transfers are done on word basis to avoid possibly slow byte
+ * and half-word writes.
+ */
+
+void ipalign(struct mbuf *m)
+{
+ unsigned int *first, *last, data;
+ unsigned int tmp;
+
+ if ((((int) m->m_data) & 2) && (m->m_len)) {
+#if CPU_LITTLE_ENDIAN == TRUE
+ memmove((caddr_t)(((int) m->m_data) + 2), m->m_data, m->m_len);
+#else
+ last = (unsigned int *) ((((int) m->m_data) + m->m_len + 8) & ~3);
+ first = (unsigned int *) (((int) m->m_data) & ~3);
+ tmp = GRETH_MEM_LOAD(first);
+ tmp = tmp << 16;
+ first++;
+ do {
+ /* When snooping is not available the LDA instruction must be used
+ * to avoid the cache to return an illegal value.
+ * Load with forced cache miss
+ */
+ data = GRETH_MEM_LOAD(first);
+ *first = tmp | (data >> 16);
+ tmp = data << 16;
+ first++;
+ } while (first <= last);
+#endif
+ m->m_data = (caddr_t)(((int) m->m_data) + 2);
+ }
+}
+#endif
+
+static void
+greth_Daemon (void *arg)
+{
+ struct ether_header *eh;
+ struct greth_softc *dp = (struct greth_softc *) &greth;
+ struct ifnet *ifp = &dp->arpcom.ac_if;
+ struct mbuf *m;
+ unsigned int len, len_status, bad;
+ rtems_event_set events;
+ rtems_interrupt_level level;
+ int first;
+#ifdef CPU_U32_FIX
+ unsigned int tmp;
+#endif
+
+ for (;;)
+ {
+ rtems_bsdnet_event_receive (INTERRUPT_EVENT | GRETH_TX_WAIT_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT, &events);
+
+ if ( events & GRETH_TX_WAIT_EVENT ){
+ /* TX interrupt.
+ * We only end up here when all TX descriptors has been used,
+ * and
+ */
+ if ( dp->gbit_mac )
+ greth_process_tx_gbit(dp);
+ else
+ greth_process_tx(dp);
+
+ /* If we didn't get a RX interrupt we don't process it */
+ if ( (events & INTERRUPT_EVENT) == 0 )
+ continue;
+ }
+
+#ifdef GRETH_ETH_DEBUG
+ printf ("r\n");
+#endif
+ first=1;
+ /* Scan for Received packets */
+again:
+ while (!((len_status =
+ GRETH_MEM_LOAD(&dp->rxdesc[dp->rx_ptr].ctrl)) & GRETH_RXD_ENABLE))
+ {
+ bad = 0;
+ if (len_status & GRETH_RXD_TOOLONG)
+ {
+ dp->rxLengthError++;
+ bad = 1;
+ }
+ if (len_status & GRETH_RXD_DRIBBLE)
+ {
+ dp->rxNonOctet++;
+ bad = 1;
+ }
+ if (len_status & GRETH_RXD_CRCERR)
+ {
+ dp->rxBadCRC++;
+ bad = 1;
+ }
+ if (len_status & GRETH_RXD_OVERRUN)
+ {
+ dp->rxOverrun++;
+ bad = 1;
+ }
+ if (len_status & GRETH_RXD_LENERR)
+ {
+ dp->rxLengthError++;
+ bad = 1;
+ }
+ if (!bad)
+ {
+ /* pass on the packet in the receive buffer */
+ len = len_status & 0x7FF;
+ m = dp->rxmbuf[dp->rx_ptr];
+#ifdef GRETH_DEBUG
+ int i;
+ printf("RX: 0x%08x, Len: %d : ", (int) m->m_data, len);
+ for (i=0; i<len; i++)
+ printf("%x%x", (m->m_data[i] >> 4) & 0x0ff, m->m_data[i] & 0x0ff);
+ printf("\n");
+#endif
+ m->m_len = m->m_pkthdr.len =
+ len - sizeof (struct ether_header);
+
+ eh = mtod (m, struct ether_header *);
+
+ m->m_data += sizeof (struct ether_header);
+#ifdef CPU_U32_FIX
+ if(!dp->gbit_mac) {
+ /* OVERRIDE CACHED ETHERNET HEADER FOR NON-SNOOPING SYSTEMS */
+ tmp = GRETH_MEM_LOAD((uintptr_t)eh);
+ tmp = GRETH_MEM_LOAD(4+(uintptr_t)eh);
+ tmp = GRETH_MEM_LOAD(8+(uintptr_t)eh);
+ tmp = GRETH_MEM_LOAD(12+(uintptr_t)eh);
+ (void)tmp;
+ ipalign(m); /* Align packet on 32-bit boundary */
+ }
+#endif
+
+ ether_input (ifp, eh, m);
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ if (dp->gbit_mac)
+ m->m_data += 2;
+ dp->rxmbuf[dp->rx_ptr] = m;
+ m->m_pkthdr.rcvif = ifp;
+ dp->rxdesc[dp->rx_ptr].addr =
+ (uint32_t *) mtod (m, uint32_t *);
+ dp->rxPackets++;
+ }
+ if (dp->rx_ptr == dp->rxbufs - 1) {
+ dp->rxdesc[dp->rx_ptr].ctrl = GRETH_RXD_ENABLE | GRETH_RXD_IRQ | GRETH_RXD_WRAP;
+ } else {
+ dp->rxdesc[dp->rx_ptr].ctrl = GRETH_RXD_ENABLE | GRETH_RXD_IRQ;
+ }
+ rtems_interrupt_disable(level);
+ dp->regs->ctrl |= GRETH_CTRL_RXEN;
+ rtems_interrupt_enable(level);
+ dp->rx_ptr = (dp->rx_ptr + 1) % dp->rxbufs;
+ }
+
+ /* Always scan twice to avoid deadlock */
+ if ( first ){
+ first=0;
+ rtems_interrupt_disable(level);
+ dp->regs->ctrl |= GRETH_CTRL_RXIRQ;
+ rtems_interrupt_enable(level);
+ goto again;
+ }
+
+ }
+
+}
+
+static int inside = 0;
+static int
+sendpacket (struct ifnet *ifp, struct mbuf *m)
+{
+ struct greth_softc *dp = ifp->if_softc;
+ unsigned char *temp;
+ struct mbuf *n;
+ unsigned int len;
+ rtems_interrupt_level level;
+
+ /*printf("Send packet entered\n");*/
+ if (inside) printf ("error: sendpacket re-entered!!\n");
+ inside = 1;
+
+ /*
+ * Is there a free descriptor available?
+ */
+ if (GRETH_MEM_LOAD(&dp->txdesc[dp->tx_ptr].ctrl) & GRETH_TXD_ENABLE){
+ /* No. */
+ inside = 0;
+ return 1;
+ }
+
+ /* Remember head of chain */
+ n = m;
+
+ len = 0;
+ temp = (unsigned char *) GRETH_MEM_LOAD(&dp->txdesc[dp->tx_ptr].addr);
+#ifdef GRETH_DEBUG
+ printf("TXD: 0x%08x : BUF: 0x%08x\n", (int) m->m_data, (int) temp);
+#endif
+ for (;;)
+ {
+#ifdef GRETH_DEBUG
+ int i;
+ printf("MBUF: 0x%08x : ", (int) m->m_data);
+ for (i=0;i<m->m_len;i++)
+ printf("%x%x", (m->m_data[i] >> 4) & 0x0ff, m->m_data[i] & 0x0ff);
+ printf("\n");
+#endif
+ len += m->m_len;
+ if (len <= RBUF_SIZE)
+ memcpy ((void *) temp, (char *) m->m_data, m->m_len);
+ temp += m->m_len;
+ if ((m = m->m_next) == NULL)
+ break;
+ }
+
+ m_freem (n);
+
+ /* don't send long packets */
+
+ if (len <= GRETH_MAXBUF_LEN) {
+ if (dp->tx_ptr < dp->txbufs-1) {
+ dp->txdesc[dp->tx_ptr].ctrl = GRETH_TXD_ENABLE | len;
+ } else {
+ dp->txdesc[dp->tx_ptr].ctrl =
+ GRETH_TXD_WRAP | GRETH_TXD_ENABLE | len;
+ }
+ dp->tx_ptr = (dp->tx_ptr + 1) % dp->txbufs;
+ rtems_interrupt_disable(level);
+ dp->regs->ctrl = dp->regs->ctrl | GRETH_CTRL_TXEN;
+ rtems_interrupt_enable(level);
+
+ }
+ inside = 0;
+
+ return 0;
+}
+
+
+static int
+sendpacket_gbit (struct ifnet *ifp, struct mbuf *m)
+{
+ struct greth_softc *dp = ifp->if_softc;
+ unsigned int len;
+
+ unsigned int ctrl;
+ int frags;
+ struct mbuf *mtmp;
+ int int_en;
+ rtems_interrupt_level level;
+
+ if (inside) printf ("error: sendpacket re-entered!!\n");
+ inside = 1;
+
+ len = 0;
+#ifdef GRETH_DEBUG
+ printf("TXD: 0x%08x\n", (int) m->m_data);
+#endif
+ /* Get number of fragments too see if we have enough
+ * resources.
+ */
+ frags=1;
+ mtmp=m;
+ while(mtmp->m_next){
+ frags++;
+ mtmp = mtmp->m_next;
+ }
+
+ if ( frags > dp->max_fragsize )
+ dp->max_fragsize = frags;
+
+ if ( frags > dp->txbufs ){
+ inside = 0;
+ printf("GRETH: MBUF-chain cannot be sent. Increase descriptor count.\n");
+ return -1;
+ }
+
+ if ( frags > (dp->txbufs-dp->tx_cnt) ){
+ inside = 0;
+ /* Return number of fragments */
+ return frags;
+ }
+
+
+ /* Enable interrupt from descriptor every tx_int_gen
+ * descriptor. Typically every 16 descriptor. This
+ * is only to reduce the number of interrupts during
+ * heavy load.
+ */
+ dp->tx_int_gen_cur-=frags;
+ if ( dp->tx_int_gen_cur <= 0 ){
+ dp->tx_int_gen_cur = dp->tx_int_gen;
+ int_en = GRETH_TXD_IRQ;
+ }else{
+ int_en = 0;
+ }
+
+ /* At this stage we know that enough descriptors are available */
+ for (;;)
+ {
+
+#ifdef GRETH_DEBUG
+ int i;
+ printf("MBUF: 0x%08x, Len: %d : ", (int) m->m_data, m->m_len);
+ for (i=0; i<m->m_len; i++)
+ printf("%x%x", (m->m_data[i] >> 4) & 0x0ff, m->m_data[i] & 0x0ff);
+ printf("\n");
+#endif
+ len += m->m_len;
+ dp->txdesc[dp->tx_ptr].addr = (uint32_t *)m->m_data;
+
+ /* Wrap around? */
+ if (dp->tx_ptr < dp->txbufs-1) {
+ ctrl = GRETH_TXD_ENABLE;
+ }else{
+ ctrl = GRETH_TXD_ENABLE | GRETH_TXD_WRAP;
+ }
+
+ /* Enable Descriptor */
+ if ((m->m_next) == NULL) {
+ dp->txdesc[dp->tx_ptr].ctrl = ctrl | int_en | m->m_len;
+ break;
+ }else{
+ dp->txdesc[dp->tx_ptr].ctrl = GRETH_TXD_MORE | ctrl | int_en | m->m_len;
+ }
+
+ /* Next */
+ dp->txmbuf[dp->tx_ptr] = m;
+ dp->tx_ptr = (dp->tx_ptr + 1) % dp->txbufs;
+ dp->tx_cnt++;
+ m = m->m_next;
+ }
+ dp->txmbuf[dp->tx_ptr] = m;
+ dp->tx_ptr = (dp->tx_ptr + 1) % dp->txbufs;
+ dp->tx_cnt++;
+
+ /* Tell Hardware about newly enabled descriptor */
+ rtems_interrupt_disable(level);
+ dp->regs->ctrl = dp->regs->ctrl | GRETH_CTRL_TXEN;
+ rtems_interrupt_enable(level);
+
+ inside = 0;
+
+ return 0;
+}
+
+int greth_process_tx_gbit(struct greth_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ rtems_interrupt_level level;
+ int first=1;
+
+ /*
+ * Send packets till queue is empty
+ */
+ for (;;){
+ /* Reap Sent packets */
+ while((sc->tx_cnt > 0) && !(GRETH_MEM_LOAD(&sc->txdesc[sc->tx_dptr].ctrl) & GRETH_TXD_ENABLE)) {
+ m_free(sc->txmbuf[sc->tx_dptr]);
+ sc->tx_dptr = (sc->tx_dptr + 1) % sc->txbufs;
+ sc->tx_cnt--;
+ }
+
+ if ( sc->next_tx_mbuf ){
+ /* Get packet we tried but faild to transmit last time */
+ m = sc->next_tx_mbuf;
+ sc->next_tx_mbuf = NULL; /* Mark packet taken */
+ }else{
+ /*
+ * Get the next mbuf chain to transmit from Stack.
+ */
+ IF_DEQUEUE (&ifp->if_snd, m);
+ if (!m){
+ /* Hardware has sent all schedule packets, this
+ * makes the stack enter at greth_start next time
+ * a packet is to be sent.
+ */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ break;
+ }
+ }
+
+ /* Are there free descriptors available? */
+ /* Try to send packet, if it a negative number is returned. */
+ if ( (sc->tx_cnt >= sc->txbufs) || sendpacket_gbit(ifp, m) ){
+ /* Not enough resources */
+
+ /* Since we have taken the mbuf out of the "send chain"
+ * we must remember to use that next time we come back.
+ * or else we have dropped a packet.
+ */
+ sc->next_tx_mbuf = m;
+
+ /* Not enough resources, enable interrupt for transmissions
+ * this way we will be informed when more TX-descriptors are
+ * available.
+ */
+ if ( first ){
+ first = 0;
+ rtems_interrupt_disable(level);
+ ifp->if_flags |= IFF_OACTIVE;
+ sc->regs->ctrl |= GRETH_CTRL_TXIRQ;
+ rtems_interrupt_enable(level);
+
+ /* We must check again to be sure that we didn't
+ * miss an interrupt (if a packet was sent just before
+ * enabling interrupts)
+ */
+ continue;
+ }
+
+ return -1;
+ }else{
+ /* Sent Ok, proceed to process more packets if available */
+ }
+ }
+ return 0;
+}
+
+int greth_process_tx(struct greth_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ rtems_interrupt_level level;
+ int first=1;
+
+ /*
+ * Send packets till queue is empty
+ */
+ for (;;){
+ if ( sc->next_tx_mbuf ){
+ /* Get packet we tried but failed to transmit last time */
+ m = sc->next_tx_mbuf;
+ sc->next_tx_mbuf = NULL; /* Mark packet taken */
+ }else{
+ /*
+ * Get the next mbuf chain to transmit from Stack.
+ */
+ IF_DEQUEUE (&ifp->if_snd, m);
+ if (!m){
+ /* Hardware has sent all schedule packets, this
+ * makes the stack enter at greth_start next time
+ * a packet is to be sent.
+ */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ break;
+ }
+ }
+
+ /* Try to send packet, failed if it a non-zero number is returned. */
+ if ( sendpacket(ifp, m) ){
+ /* Not enough resources */
+
+ /* Since we have taken the mbuf out of the "send chain"
+ * we must remember to use that next time we come back.
+ * or else we have dropped a packet.
+ */
+ sc->next_tx_mbuf = m;
+
+ /* Not enough resources, enable interrupt for transmissions
+ * this way we will be informed when more TX-descriptors are
+ * available.
+ */
+ if ( first ){
+ first = 0;
+ rtems_interrupt_disable(level);
+ ifp->if_flags |= IFF_OACTIVE;
+ sc->regs->ctrl |= GRETH_CTRL_TXIRQ;
+ rtems_interrupt_enable(level);
+
+ /* We must check again to be sure that we didn't
+ * miss an interrupt (if a packet was sent just before
+ * enabling interrupts)
+ */
+ continue;
+ }
+
+ return -1;
+ }else{
+ /* Sent Ok, proceed to process more packets if available */
+ }
+ }
+ return 0;
+}
+
+static void
+greth_start (struct ifnet *ifp)
+{
+ struct greth_softc *sc = ifp->if_softc;
+
+ if ( ifp->if_flags & IFF_OACTIVE )
+ return;
+
+ if ( sc->gbit_mac ){
+ /* No use trying to handle this if we are waiting on GRETH
+ * to send the previously scheduled packets.
+ */
+
+ greth_process_tx_gbit(sc);
+ }else{
+ greth_process_tx(sc);
+ }
+}
+
+/*
+ * Initialize and start the device
+ */
+static void
+greth_init (void *arg)
+{
+ struct greth_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ if (sc->daemonTid == 0) {
+
+ /*
+ * Start driver tasks
+ */
+ sc->daemonTid = rtems_bsdnet_newproc ("DCrxtx", 4096,
+ greth_Daemon, sc);
+
+ /*
+ * Set up GRETH hardware
+ */
+ greth_initialize_hardware (sc);
+
+ }
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+}
+
+/*
+ * Stop the device
+ */
+static void
+greth_stop (struct greth_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ sc->regs->ctrl = 0; /* RX/TX OFF */
+ sc->regs->ctrl = GRETH_CTRL_RST; /* Reset ON */
+ sc->regs->ctrl = 0; /* Reset OFF */
+
+ sc->next_tx_mbuf = NULL;
+}
+
+
+/*
+ * Show interface statistics
+ */
+static void
+greth_stats (struct greth_softc *sc)
+{
+ printf (" Rx Interrupts:%-8lu", sc->rxInterrupts);
+ printf (" Rx Packets:%-8lu", sc->rxPackets);
+ printf (" Length:%-8lu", sc->rxLengthError);
+ printf (" Non-octet:%-8lu\n", sc->rxNonOctet);
+ printf (" Bad CRC:%-8lu", sc->rxBadCRC);
+ printf (" Overrun:%-8lu", sc->rxOverrun);
+ printf (" Tx Interrupts:%-8lu", sc->txInterrupts);
+ printf (" Maximal Frags:%-8d", sc->max_fragsize);
+ printf (" GBIT MAC:%-8d", sc->gbit_mac);
+}
+
+/*
+ * Driver ioctl handler
+ */
+static int
+greth_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct greth_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command)
+ {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl (ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING))
+ {
+ case IFF_RUNNING:
+ greth_stop (sc);
+ break;
+
+ case IFF_UP:
+ greth_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ greth_stop (sc);
+ greth_init (sc);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ greth_stats (sc);
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+/*
+ * Attach an GRETH driver to the system
+ */
+int
+rtems_greth_driver_attach (struct rtems_bsdnet_ifconfig *config,
+ greth_configuration_t *chip)
+{
+ struct greth_softc *sc;
+ struct ifnet *ifp;
+ int mtu;
+ int unitNumber;
+ char *unitName;
+
+ /* parse driver name */
+ if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0)
+ return 0;
+
+ sc = &greth;
+ ifp = &sc->arpcom.ac_if;
+ memset (sc, 0, sizeof (*sc));
+
+ if (config->hardware_address)
+ {
+ memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
+ ETHER_ADDR_LEN);
+ }
+ else
+ {
+ memset (sc->arpcom.ac_enaddr, 0x08, ETHER_ADDR_LEN);
+ }
+
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ sc->acceptBroadcast = !config->ignore_broadcast;
+ sc->regs = chip->base_address;
+ sc->vector = chip->vector;
+ sc->txbufs = chip->txd_count;
+ sc->rxbufs = chip->rxd_count;
+
+ /*
+ * Set up network interface values
+ */
+ ifp->if_softc = sc;
+ ifp->if_unit = unitNumber;
+ ifp->if_name = unitName;
+ ifp->if_mtu = mtu;
+ ifp->if_init = greth_init;
+ ifp->if_ioctl = greth_ioctl;
+ ifp->if_start = greth_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);
+
+#ifdef GRETH_DEBUG
+ printf ("GRETH : driver has been attached\n");
+#endif
+ return 1;
+};
+
+#endif
diff --git a/bsps/shared/net/i82586.c b/bsps/shared/net/i82586.c
new file mode 100644
index 0000000..8933ddf
--- /dev/null
+++ b/bsps/shared/net/i82586.c
@@ -0,0 +1,2198 @@
+/* $NetBSD: i82586.c,v 1.38 2001/07/07 05:35:39 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Paul Kranenburg and Charles M. Hannum.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*-
+ * Copyright (c) 1997 Paul Kranenburg.
+ * Copyright (c) 1992, 1993, University of Vermont and State
+ * Agricultural College.
+ * Copyright (c) 1992, 1993, Garrett A. Wollman.
+ *
+ * Portions:
+ * Copyright (c) 1994, 1995, Rafal K. Boni
+ * Copyright (c) 1990, 1991, William F. Jolitz
+ * Copyright (c) 1990, The Regents of the University of California
+ *
+ * RTEMS:
+ * Copyright (c) 2001, Chris Johns, Cybertec Pty Ltd,
+ * http://www.cybertec.com.au/.
+ *
+ * All rights reserved.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of Vermont
+ * and State Agricultural College and Garrett A. Wollman, by William F.
+ * Jolitz, and by the University of California, Berkeley, Lawrence
+ * Berkeley Laboratory, and its contributors.
+ * 4. Neither the names of the Universities nor the names of the authors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 UNIVERSITY OR AUTHORS 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.
+ */
+
+/*
+ * Intel 82586 Ethernet chip
+ * Register, bit, and structure definitions.
+ *
+ * Original StarLAN driver written by Garrett Wollman with reference to the
+ * Clarkson Packet Driver code for this chip written by Russ Nelson and others.
+ *
+ * BPF support code taken from hpdev/if_le.c, supplied with tcpdump.
+ *
+ * 3C507 support is loosely based on code donated to NetBSD by Rafal Boni.
+ *
+ * Majorly cleaned up and 3C507 code merged by Charles Hannum.
+ *
+ * Converted to SUN ie driver by Charles D. Cranor,
+ * October 1994, January 1995.
+ * This sun version based on i386 version 1.30.
+ */
+
+/*
+ * The i82586 is a very painful chip, found in sun3's, sun-4/100's
+ * sun-4/200's, and VME based suns. The byte order is all wrong for a
+ * SUN, making life difficult. Programming this chip is mostly the same,
+ * but certain details differ from system to system. This driver is
+ * written so that different "ie" interfaces can be controled by the same
+ * driver.
+ */
+
+/*
+Mode of operation:
+
+ We run the 82586 in a standard Ethernet mode. We keep NFRAMES
+ received frame descriptors around for the receiver to use, and
+ NRXBUF associated receive buffer descriptors, both in a circular
+ list. Whenever a frame is received, we rotate both lists as
+ necessary. (The 586 treats both lists as a simple queue.) We also
+ keep a transmit command around so that packets can be sent off
+ quickly.
+
+ We configure the adapter in AL-LOC = 1 mode, which means that the
+ Ethernet/802.3 MAC header is placed at the beginning of the receive
+ buffer rather than being split off into various fields in the RFD.
+ This also means that we must include this header in the transmit
+ buffer as well.
+
+ By convention, all transmit commands, and only transmit commands,
+ shall have the I (IE_CMD_INTR) bit set in the command. This way,
+ when an interrupt arrives at i82586_intr(), it is immediately possible
+ to tell what precisely caused it. ANY OTHER command-sending
+ routines should run at splnet(), and should post an acknowledgement
+ to every interrupt they generate.
+
+ To save the expense of shipping a command to 82586 every time we
+ want to send a frame, we use a linked list of commands consisting
+ of alternate XMIT and NOP commands. The links of these elements
+ are manipulated (in iexmit()) such that the NOP command loops back
+ to itself whenever the following XMIT command is not yet ready to
+ go. Whenever an XMIT is ready, the preceding NOP link is pointed
+ at it, while its own link field points to the following NOP command.
+ Thus, a single transmit command sets off an interlocked traversal
+ of the xmit command chain, with the host processor in control of
+ the synchronization.
+*/
+
+#include <machine/rtems-bsd-kernel-space.h>
+
+#include <rtems.h>
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include "i82586reg.h"
+#include <libchip/i82586var.h>
+
+#if defined(ALIGNBYTES) && defined(ALIGN)
+/* FIXME: Redefine because some versions of
+ * RTEMS newlib and the BSDs ship a broken ALIGN */
+#undef ALIGN
+#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) & ~ALIGNBYTES)
+#else
+#define ALIGN(p) (p)
+#endif
+
+/*
+ * A global way to change all async cmd requests at once. For RTEMS and running
+ * as tasks I wanted to see if the tx race condition is effected by this.
+ */
+#define ASYNC_OPTION (1)
+
+void i82586_reset (struct ie_softc *, int);
+void i82586_watchdog (struct ifnet *);
+void i82586_init (void *);
+int i82586_ioctl (struct ifnet *, ioctl_command_t cmd, caddr_t data);
+void i82586_start (struct ifnet *);
+
+void i82586_stop (struct ifnet *, int);
+int i82586_rint (struct ie_softc *, int);
+int i82586_tint (struct ie_softc *, int);
+
+int i82586_mediachange (struct ifnet *);
+void i82586_mediastatus (struct ifnet *, struct ifmediareq *);
+
+static void i82586_tx_task(void *arg);
+static void i82586_start_tx(struct ie_softc *sc);
+
+static int ie_readframe (struct ie_softc *, int);
+static struct mbuf *ieget (struct ie_softc *, int, int);
+static int i82586_get_rbd_list (struct ie_softc *, u_int16_t*,
+ u_int16_t*, int *);
+static void i82586_release_rbd_list (struct ie_softc *,
+ u_int16_t, u_int16_t);
+static int i82586_drop_frames (struct ie_softc *);
+static int i82586_chk_rx_ring (struct ie_softc *);
+
+static __inline__ void ie_ack (struct ie_softc *, u_int);
+static __inline__ void iexmit (struct ie_softc *);
+static void i82586_start_transceiver (struct ie_softc *);
+
+static void i82586_count_errors (struct ie_softc *);
+static void i82586_rx_errors (struct ie_softc *, int, int);
+static void i82586_setup_bufs (struct ie_softc *);
+static void setup_simple_command (struct ie_softc *, int, int);
+static int ie_cfg_setup (struct ie_softc *, int, int, int);
+static int ie_ia_setup (struct ie_softc *, int);
+static void ie_run_tdr (struct ie_softc *, int);
+static int ie_mc_setup (struct ie_softc *, int);
+static void ie_mc_reset (struct ie_softc *);
+static int i82586_start_cmd (struct ie_softc *, int, int, int, int);
+static int i82586_cmd_wait (struct ie_softc *);
+
+#if I82586_DEBUG
+static void print_softie(struct ie_softc *sc);
+static void print_rbd (struct ie_softc *, int);
+#endif
+
+#define min(l,r) ((l) < (r) ? (l) : (r))
+#define max(l,r) ((l) > (r) ? (l) : (r))
+
+#define delay(p) rtems_task_wake_after (RTEMS_MICROSECONDS_TO_TICKS (p))
+
+#define i82586_WAKE_EVENT RTEMS_EVENT_1
+#define i82586_TX_EVENT RTEMS_EVENT_2
+
+static char *
+bitmask_snprintf(unsigned long value, const char *format, char *buf, int blen)
+{
+ char *b = buf;
+ int bit = 31;
+
+ while (bit-- > *format)
+ value <<= 1;
+
+ format++;
+
+ while (*format)
+ {
+ if (value & 0x80000000)
+ while (isalnum((unsigned char)*format))
+ *b++ = *format;
+ else
+ *b++ = '0';
+
+ *b++ = ',';
+
+ while (bit-- > *format)
+ value <<= 1;
+
+ format++;
+ }
+
+ *b = '\0';
+ return buf;
+}
+
+/*
+ * Front-ends call this function to attach to the MI driver.
+ *
+ * The front-end has responsibility for managing the ICP and ISCP
+ * structures. Both of these are opaque to us. Also, the front-end
+ * chooses a location for the SCB which is expected to be addressable
+ * (through `sc->scb') as an offset against the shared-memory bus handle.
+ *
+ * The following MD interface function must be setup by the front-end
+ * before calling here:
+ *
+ * hwreset - board dependent reset
+ * hwinit - board dependent initialization
+ * chan_attn - channel attention
+ * intrhook - board dependent interrupt processing
+ * memcopyin - shared memory copy: board to KVA
+ * memcopyout - shared memory copy: KVA to board
+ * ie_bus_read16 - read a sixteen-bit i82586 pointer
+ * ie_bus_write16 - write a sixteen-bit i82586 pointer
+ * ie_bus_write24 - write a twenty-four-bit i82586 pointer
+ *
+ */
+int
+i82586_attach(struct rtems_bsdnet_ifconfig *config, int attaching)
+{
+ struct ie_softc *sc;
+ struct ifnet *ifp;
+ char *name;
+ int unit;
+ int mtu;
+
+ /*
+ * Parse driver name
+ */
+
+ if ((unit = rtems_bsdnet_parse_driver_name (config, &name)) < 0)
+ return 0;
+
+ sc = config->drv_ctrl;
+ ifp = &sc->arpcom.ac_if;
+
+#if I82586_DEBUG
+ sc->sc_debug = 0; /*IED_TINT | IED_XMIT; */
+#endif
+
+ if (attaching)
+ {
+ if (ifp->if_softc)
+ {
+ printf ("Driver `%s' already in use.\n", config->name);
+ return 0;
+ }
+
+ /*
+ * Process options
+ */
+
+ memcpy (sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
+
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ ifp->if_softc = sc;
+ ifp->if_unit = unit;
+ ifp->if_name = name;
+ ifp->if_mtu = mtu;
+ ifp->if_init = i82586_init;
+ ifp->if_ioctl = i82586_ioctl;
+ ifp->if_start = i82586_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ /* Attach the interface. */
+ if_attach(ifp);
+ ether_ifattach(ifp);
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ * Interrupt Acknowledge. Mask in native byte-order.
+ */
+static __inline__ void
+ie_ack(struct ie_softc *sc, u_int mask)
+{
+ u_int status;
+
+ IE_BUS_BARRIER(sc, 0, 0, BUS_SPACE_BARRIER_READ);
+ status = sc->ie_bus_read16(sc, IE_SCB_STATUS(sc->scb));
+ i82586_start_cmd(sc, status & mask, 0, 0, 0);
+ if (sc->intrhook)
+ sc->intrhook(sc, INTR_ACK);
+}
+
+
+/*
+ * Read data off the interface, and turn it into an mbuf chain.
+ *
+ * This code is DRAMATICALLY different from the previous version; this
+ * version tries to allocate the entire mbuf chain up front, given the
+ * length of the data available. This enables us to allocate mbuf
+ * clusters in many situations where before we would have had a long
+ * chain of partially-full mbufs. This should help to speed up the
+ * operation considerably. (Provided that it works, of course.)
+ */
+static __inline struct mbuf *
+ieget(struct ie_softc *sc, int head, int totlen)
+{
+ struct mbuf *m, *m0, *newm;
+ int len, resid;
+ int thisrboff, thismboff;
+ struct ether_header eh;
+
+ /*
+ * Snarf the Ethernet header.
+ */
+ (sc->memcopyin)(sc, &eh, IE_RBUF_ADDR(sc, head),
+ sizeof(struct ether_header));
+
+ resid = totlen;
+
+ MGETHDR(m0, M_DONTWAIT, MT_DATA);
+ if (m0 == 0)
+ return (0);
+ m0->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ m0->m_pkthdr.len = totlen;
+ len = MHLEN;
+ m = m0;
+
+ /*
+ * This loop goes through and allocates mbufs for all the data we will
+ * be copying in. It does not actually do the copying yet.
+ */
+ while (totlen > 0) {
+ if (totlen >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0)
+ goto bad;
+ len = MCLBYTES;
+ }
+
+ if (m == m0) {
+ caddr_t newdata = (caddr_t)
+ ALIGN(m->m_data + sizeof(struct ether_header)) -
+ sizeof(struct ether_header);
+ len -= newdata - m->m_data;
+ m->m_data = newdata;
+ }
+
+ m->m_len = len = min(totlen, len);
+
+ totlen -= len;
+ if (totlen > 0) {
+ MGET(newm, M_DONTWAIT, MT_DATA);
+ if (newm == 0)
+ goto bad;
+ len = MLEN;
+ m = m->m_next = newm;
+ }
+ }
+
+ m = m0;
+ thismboff = 0;
+
+ /*
+ * Copy the Ethernet header into the mbuf chain.
+ */
+ memcpy(mtod(m, caddr_t), &eh, sizeof(struct ether_header));
+ thismboff = sizeof(struct ether_header);
+ thisrboff = sizeof(struct ether_header);
+ resid -= sizeof(struct ether_header);
+
+ /*
+ * Now we take the mbuf chain (hopefully only one mbuf most of the
+ * time) and stuff the data into it. There are no possible failures
+ * at or after this point.
+ */
+ while (resid > 0) {
+ int thisrblen = IE_RBUF_SIZE - thisrboff,
+ thismblen = m->m_len - thismboff;
+ len = min(thisrblen, thismblen);
+
+ (sc->memcopyin)(sc, mtod(m, caddr_t) + thismboff,
+ IE_RBUF_ADDR(sc,head) + thisrboff,
+ (u_int)len);
+ resid -= len;
+
+ if (len == thismblen) {
+ m = m->m_next;
+ thismboff = 0;
+ } else
+ thismboff += len;
+
+ if (len == thisrblen) {
+ if (++head == sc->nrxbuf)
+ head = 0;
+ thisrboff = 0;
+ } else
+ thisrboff += len;
+ }
+
+ /*
+ * Unless something changed strangely while we were doing the copy,
+ * we have now copied everything in from the shared memory.
+ * This means that we are done.
+ */
+ return (m0);
+
+ bad:
+ m_freem(m0);
+ return (0);
+}
+
+/*
+ * Setup all necessary artifacts for an XMIT command, and then pass the XMIT
+ * command to the chip to be executed.
+ */
+static __inline__ void
+iexmit(struct ie_softc *sc)
+{
+ int off;
+ int cur, prev;
+
+ cur = sc->xctail;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_TX_EMIT, cur);
+#endif
+
+#if I82586_DEBUG
+ if (sc->sc_debug & IED_XMIT)
+ printf("%s: xmit buffer %d\n", sc->arpcom.ac_if.if_name, cur);
+#endif
+
+ /*
+ * Setup the transmit command.
+ */
+ sc->ie_bus_write16(sc, IE_CMD_XMIT_DESC(sc->xmit_cmds, cur),
+ IE_XBD_ADDR(sc->xbds, cur));
+
+ sc->ie_bus_write16(sc, IE_CMD_XMIT_STATUS(sc->xmit_cmds, cur), 0);
+
+ if (sc->do_xmitnopchain) {
+ /*
+ * Gate this XMIT command to the following NOP
+ */
+ sc->ie_bus_write16(sc, IE_CMD_XMIT_LINK(sc->xmit_cmds, cur),
+ IE_CMD_NOP_ADDR(sc->nop_cmds, cur));
+ sc->ie_bus_write16(sc, IE_CMD_XMIT_CMD(sc->xmit_cmds, cur),
+ IE_CMD_XMIT | IE_CMD_INTR);
+
+ /*
+ * Loopback at following NOP
+ */
+ sc->ie_bus_write16(sc, IE_CMD_NOP_STATUS(sc->nop_cmds, cur), 0);
+ sc->ie_bus_write16(sc, IE_CMD_NOP_LINK(sc->nop_cmds, cur),
+ IE_CMD_NOP_ADDR(sc->nop_cmds, cur));
+
+ /*
+ * Gate preceding NOP to this XMIT command
+ */
+ prev = (cur + NTXBUF - 1) % NTXBUF;
+ sc->ie_bus_write16(sc, IE_CMD_NOP_STATUS(sc->nop_cmds, prev), 0);
+ sc->ie_bus_write16(sc, IE_CMD_NOP_LINK(sc->nop_cmds, prev),
+ IE_CMD_XMIT_ADDR(sc->xmit_cmds, cur));
+
+ off = IE_SCB_STATUS(sc->scb);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+ if ((sc->ie_bus_read16(sc, off) & IE_CUS_ACTIVE) == 0) {
+ printf("iexmit: CU not active\n");
+ i82586_start_transceiver(sc);
+ }
+ } else {
+ sc->ie_bus_write16(sc, IE_CMD_XMIT_LINK(sc->xmit_cmds,cur),
+ 0xffff);
+
+ sc->ie_bus_write16(sc, IE_CMD_XMIT_CMD(sc->xmit_cmds, cur),
+ IE_CMD_XMIT | IE_CMD_INTR | IE_CMD_LAST);
+
+ off = IE_SCB_CMDLST(sc->scb);
+ sc->ie_bus_write16(sc, off, IE_CMD_XMIT_ADDR(sc->xmit_cmds, cur));
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+
+ if (i82586_start_cmd(sc, IE_CUC_START, 0, 0, ASYNC_OPTION))
+ printf("%s: iexmit: start xmit command timed out\n",
+ sc->arpcom.ac_if.if_name);
+ }
+
+ sc->arpcom.ac_if.if_timer = 5;
+}
+
+
+/*
+ * Device timeout/watchdog routine.
+ * Entered if the device neglects to generate an interrupt after a
+ * transmit has been started on it.
+ */
+void
+i82586_watchdog(struct ifnet *ifp)
+{
+ struct ie_softc *sc = ifp->if_softc;
+ printf("%s: device timeout\n", ifp->if_name);
+ ++ifp->if_oerrors;
+ i82586_reset(sc, 1);
+}
+
+static int
+i82586_cmd_wait(struct ie_softc *sc)
+{
+ /* spin on i82586 command acknowledge; wait at most 0.9 (!) seconds */
+ int i, off;
+ u_int16_t cmd;
+
+ for (i = 0; i < 900000; i++) {
+ /* Read the command word */
+ off = IE_SCB_CMD(sc->scb);
+
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+ if ((cmd = sc->ie_bus_read16(sc, off)) == 0)
+ return (0);
+ delay(1);
+ }
+
+ off = IE_SCB_STATUS(sc->scb);
+ printf("i82586_cmd_wait: timo(%ssync): scb status: 0x%x, cmd: 0x%x\n",
+ sc->async_cmd_inprogress?"a":"",
+ sc->ie_bus_read16(sc, off), cmd);
+
+ return (1); /* Timeout */
+}
+
+/*
+ * Send a command to the controller and wait for it to either complete
+ * or be accepted, depending on the command. If the command pointer
+ * is null, then pretend that the command is not an action command.
+ * If the command pointer is not null, and the command is an action
+ * command, wait for one of the MASK bits to turn on in the command's
+ * status field.
+ * If ASYNC is set, we just call the chip's attention and return.
+ * We may have to wait for the command's acceptance later though.
+ */
+static int
+i82586_start_cmd(struct ie_softc *sc, int cmd, int iecmdbuf, int mask, int async)
+{
+ int i;
+ int off;
+
+ if (sc->async_cmd_inprogress != 0) {
+ /*
+ * If previous command was issued asynchronously, wait
+ * for it now.
+ */
+ if (i82586_cmd_wait(sc) != 0)
+ return (1);
+ sc->async_cmd_inprogress = 0;
+ }
+
+ off = IE_SCB_CMD(sc->scb);
+ sc->ie_bus_write16(sc, off, cmd);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_WRITE);
+ (sc->chan_attn)(sc, CARD_RESET);
+
+ if (async != 0) {
+ sc->async_cmd_inprogress = 1;
+ return (0);
+ }
+
+ if (IE_ACTION_COMMAND(cmd) && iecmdbuf) {
+ int status;
+ /*
+ * Now spin-lock waiting for status. This is not a very nice
+ * thing to do, and can kill performance pretty well...
+ * According to the packet driver, the minimum timeout
+ * should be .369 seconds.
+ */
+ for (i = 0; i < 369000; i++) {
+ /* Read the command status */
+ off = IE_CMD_COMMON_STATUS(iecmdbuf);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+ status = sc->ie_bus_read16(sc, off);
+ if (status & mask)
+ return (0);
+ delay(1);
+ }
+
+ } else {
+ /*
+ * Otherwise, just wait for the command to be accepted.
+ */
+ return (i82586_cmd_wait(sc));
+ }
+
+ /* Timeout */
+ return (1);
+}
+
+
+/*
+ * Transfer accumulated chip error counters to IF.
+ */
+static __inline void
+i82586_count_errors(struct ie_softc *sc)
+{
+ int scb = sc->scb;
+
+ sc->arpcom.ac_if.if_ierrors +=
+ sc->ie_bus_read16(sc, IE_SCB_ERRCRC(scb)) +
+ sc->ie_bus_read16(sc, IE_SCB_ERRALN(scb)) +
+ sc->ie_bus_read16(sc, IE_SCB_ERRRES(scb)) +
+ sc->ie_bus_read16(sc, IE_SCB_ERROVR(scb));
+
+ /* Clear error counters */
+ sc->ie_bus_write16(sc, IE_SCB_ERRCRC(scb), 0);
+ sc->ie_bus_write16(sc, IE_SCB_ERRALN(scb), 0);
+ sc->ie_bus_write16(sc, IE_SCB_ERRRES(scb), 0);
+ sc->ie_bus_write16(sc, IE_SCB_ERROVR(scb), 0);
+}
+
+
+static void
+i82586_rx_errors(struct ie_softc *sc, int fn, int status)
+{
+ char bits[128];
+
+ printf("%s: rx error (frame# %d): %s\n", sc->arpcom.ac_if.if_name, fn,
+ bitmask_snprintf(status, IE_FD_STATUSBITS, bits, sizeof(bits)));
+}
+
+/*
+ * i82586 interrupt entry point.
+ */
+rtems_isr
+i82586_intr(rtems_vector_number vec, void *arg)
+{
+ struct ie_softc *sc = arg;
+
+#if I82586_DEBUG
+ static unsigned long icnt = 0;
+ I82586_TRACE(sc, I82586_INTS_REQ, icnt++);
+#endif
+
+ /*
+ * Implementation dependent interrupt handling. It must at least
+ * disabled interrupts from the i82586. It is hoped this can
+ * happen somewhere outside the i82586.
+ */
+ if (sc->intrhook)
+ (sc->intrhook)(sc, INTR_ENTER);
+
+ /*
+ * Wake the task to handle the interrupt. It will
+ * enabled the interrupts when it has finished.
+ */
+ rtems_bsdnet_event_send (sc->intr_task, i82586_WAKE_EVENT);
+}
+
+/*
+ * i82586 interrupt task. The task is actually an extension of the interrupt
+ * with a context switch in the middle. The RTEMS TCP/IP stack requires a task
+ * be used to talk to the stack as a network semaphore is claimed. This
+ * cannot happen during an interrupt.
+ */
+static void
+i82586_intr_task(void *arg)
+{
+ struct ie_softc *sc = arg;
+ rtems_event_set events;
+ u_int status;
+ int off;
+ int reset;
+
+ /*
+ * Not sure this is a good idea but as a out path exists and
+ * roads lead to it, it seems ok.
+ */
+ for (;;) {
+ rtems_bsdnet_event_receive (i82586_WAKE_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ 0, &events);
+
+ off = IE_SCB_STATUS(sc->scb);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+ status = sc->ie_bus_read16(sc, off) & IE_ST_WHENCE;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_INTS_IN, status);
+#endif
+
+ reset = 0;
+
+ while ((status & IE_ST_WHENCE) != 0) {
+#if I82586_DEBUG
+ if (sc->sc_debug)
+ printf ("%s: -------\n%s: scbstatus=0x%x\n",
+ sc->arpcom.ac_if.if_name, sc->arpcom.ac_if.if_name, status);
+#endif
+
+#if 1
+ /* Ack interrupts FIRST in case we receive more during the ISR. */
+ ie_ack(sc, status & IE_ST_WHENCE);
+#endif
+
+ i82586_start_cmd(sc, status & IE_ST_WHENCE, 0, 0, ASYNC_OPTION);
+
+ if (status & (IE_ST_FR | IE_ST_RNR))
+ if (i82586_rint(sc, status) != 0) {
+ reset = 1;
+ break;
+ }
+
+ if (status & IE_ST_CX)
+ if (i82586_tint(sc, status) != 0) {
+ reset = 1;
+ break;
+ }
+
+#if I82586_DEBUG
+ if ((status & IE_ST_CNA) && (sc->sc_debug & IED_CNA))
+ printf("%s: cna; status=0x%x\n", sc->arpcom.ac_if.if_name, status);
+#endif
+
+#if 0
+ if (sc->intrhook)
+ (sc->intrhook)(sc, INTR_LOOP);
+#endif
+
+#if 1
+ /*
+ * Interrupt ACK was posted asynchronously; wait for
+ * completion here before reading SCB status again.
+ *
+ * If ACK fails, try to reset the chip, in hopes that
+ * it helps.
+ */
+ if (i82586_cmd_wait(sc) != 0) {
+ reset = 1;
+ break;
+ }
+#endif
+
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+ status = sc->ie_bus_read16(sc, off);
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_INTS_LOOPS, status);
+#endif
+ }
+
+ if (reset) {
+#if I82586_DEBUG
+ printf("%s: intr reset; status=0x%x\n", sc->arpcom.ac_if.if_name, status);
+#endif
+ i82586_cmd_wait(sc);
+ i82586_reset(sc, 1);
+ }
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_INTS_OUT, status);
+#endif
+
+ if (sc->intrhook)
+ (sc->intrhook)(sc, INTR_EXIT);
+ }
+}
+
+/*
+ * Process a received-frame interrupt.
+ */
+int
+i82586_rint(struct ie_softc *sc, int scbstatus)
+{
+ static int timesthru = 1024;
+ int i, status, off;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_RX_INT, scbstatus);
+
+ if (sc->sc_debug & IED_RINT)
+ printf("%s: rint: status 0x%x\n",
+ sc->arpcom.ac_if.if_name, scbstatus);
+#endif
+
+ for (;;) {
+ int drop = 0;
+
+ i = sc->rfhead;
+ off = IE_RFRAME_STATUS(sc->rframes, i);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+ status = sc->ie_bus_read16(sc, off);
+
+#if I82586_DEBUG
+ if (sc->sc_debug & IED_RINT)
+ printf("%s: rint: frame(%d) status 0x%x\n",
+ sc->arpcom.ac_if.if_name, i, status);
+#endif
+ if ((status & IE_FD_COMPLETE) == 0) {
+ if ((status & IE_FD_OK) != 0) {
+ printf("%s: rint: weird: ",
+ sc->arpcom.ac_if.if_name);
+ i82586_rx_errors(sc, i, status);
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_RX_ERR, status);
+#endif
+ break;
+ }
+ if (--timesthru == 0) {
+ /* Account the accumulated errors */
+ i82586_count_errors(sc);
+ timesthru = 1024;
+ }
+ break;
+ } else if ((status & IE_FD_OK) == 0) {
+ /*
+ * If the chip is configured to automatically
+ * discard bad frames, the only reason we can
+ * get here is an "out-of-resource" condition.
+ */
+ i82586_rx_errors(sc, i, status);
+ drop = 1;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_RX_DROP, status);
+#endif
+ }
+
+#if I82586_DEBUG
+ if ((status & IE_FD_BUSY) != 0)
+ printf("%s: rint: frame(%d) busy; status=0x%x\n",
+ sc->arpcom.ac_if.if_name, i, status);
+#endif
+
+ /*
+ * Advance the RFD list, since we're done with
+ * this descriptor.
+ */
+
+ /* Clear frame status */
+ sc->ie_bus_write16(sc, off, 0);
+
+ /* Put fence at this frame (the head) */
+ off = IE_RFRAME_LAST(sc->rframes, i);
+ sc->ie_bus_write16(sc, off, IE_FD_EOL|IE_FD_SUSP);
+
+ /* and clear RBD field */
+ off = IE_RFRAME_BUFDESC(sc->rframes, i);
+ sc->ie_bus_write16(sc, off, 0xffff);
+
+ /* Remove fence from current tail */
+ off = IE_RFRAME_LAST(sc->rframes, sc->rftail);
+ sc->ie_bus_write16(sc, off, 0);
+
+ if (++sc->rftail == sc->nframes)
+ sc->rftail = 0;
+ if (++sc->rfhead == sc->nframes)
+ sc->rfhead = 0;
+
+ /* Pull the frame off the board */
+ if (drop) {
+ i82586_drop_frames(sc);
+ if ((status & IE_FD_RNR) != 0)
+ sc->rnr_expect = 1;
+ sc->arpcom.ac_if.if_ierrors++;
+ } else if (ie_readframe(sc, i) != 0)
+ return (1);
+ }
+
+ if ((scbstatus & IE_ST_RNR) != 0) {
+
+ /*
+ * Receiver went "Not Ready". We try to figure out
+ * whether this was an expected event based on past
+ * frame status values.
+ */
+
+ if ((scbstatus & IE_RUS_SUSPEND) != 0) {
+ /*
+ * We use the "suspend on last frame" flag.
+ * Send a RU RESUME command in response, since
+ * we should have dealt with all completed frames
+ * by now.
+ */
+ printf("RINT: SUSPENDED; scbstatus=0x%x\n",
+ scbstatus);
+ if (i82586_start_cmd(sc, IE_RUC_RESUME, 0, 0, 0) == 0)
+ return (0);
+ printf("%s: RU RESUME command timed out\n",
+ sc->arpcom.ac_if.if_name);
+ return (1); /* Ask for a reset */
+ }
+
+ if (sc->rnr_expect != 0) {
+ /*
+ * The RNR condition was announced in the previously
+ * completed frame. Assume the receive ring is Ok,
+ * so restart the receiver without further delay.
+ */
+ i82586_start_transceiver(sc);
+ sc->rnr_expect = 0;
+ return (0);
+
+ } else if ((scbstatus & IE_RUS_NOSPACE) != 0) {
+ /*
+ * We saw no previous IF_FD_RNR flag.
+ * We check our ring invariants and, if ok,
+ * just restart the receiver at the current
+ * point in the ring.
+ */
+ if (i82586_chk_rx_ring(sc) != 0)
+ return (1);
+
+ i82586_start_transceiver(sc);
+ sc->arpcom.ac_if.if_ierrors++;
+ return (0);
+ } else
+ printf("%s: receiver not ready; scbstatus=0x%x\n",
+ sc->arpcom.ac_if.if_name, scbstatus);
+
+ sc->arpcom.ac_if.if_ierrors++;
+ return (1); /* Ask for a reset */
+ }
+
+ return (0);
+}
+
+/*
+ * Process a command-complete interrupt. These are only generated by the
+ * transmission of frames. This routine is deceptively simple, since most
+ * of the real work is done by i82586_start().
+ */
+int
+i82586_tint(struct ie_softc *sc, int scbstatus)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ int status;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_TX_INT, sc->xmit_busy);
+#endif
+
+#if I82586_DEBUG
+ if (sc->xmit_busy <= 0) {
+ printf("i82586_tint: (%d), WEIRD: xmit_busy=%d, xctail=%d, xchead=%d\n",
+ sc->trace_flow_in / 2, sc->xmit_busy, sc->xctail, sc->xchead);
+ I82586_TRACE(sc, I82586_TX_BAD, sc->xctail);
+ return (0);
+ }
+#endif
+
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ status = sc->ie_bus_read16(sc, IE_CMD_XMIT_STATUS(sc->xmit_cmds,
+ sc->xctail));
+
+#if I82586_DEBUG
+ if (sc->sc_debug & IED_TINT)
+ printf("%s: tint: SCB status 0x%x; xmit status 0x%x\n",
+ sc->arpcom.ac_if.if_name, scbstatus, status);
+#endif
+
+ if ((status & IE_STAT_COMPL) == 0 || (status & IE_STAT_BUSY)) {
+ printf("i82586_tint: (%d) command still busy; status=0x%x; tail=%d\n",
+ sc->trace_flow_in / 2, status, sc->xctail);
+ printf("iestatus = 0x%x\n", scbstatus);
+/* sc->sc_debug = IED_ALL; */
+ }
+
+ if (status & IE_STAT_OK) {
+ ifp->if_opackets++;
+ ifp->if_collisions += (status & IE_XS_MAXCOLL);
+ } else {
+ ifp->if_oerrors++;
+ /*
+ * Check SQE and DEFERRED?
+ * What if more than one bit is set?
+ */
+ if (status & IE_STAT_ABORT)
+ printf("%s: send aborted\n", sc->arpcom.ac_if.if_name);
+ else if (status & IE_XS_NOCARRIER)
+ printf("%s: no carrier\n", sc->arpcom.ac_if.if_name);
+ else if (status & IE_XS_LOSTCTS)
+ printf("%s: lost CTS\n", sc->arpcom.ac_if.if_name);
+ else if (status & IE_XS_UNDERRUN)
+ printf("%s: DMA underrun\n", sc->arpcom.ac_if.if_name);
+ else if (status & IE_XS_EXCMAX) {
+ printf("%s: too many collisions\n",
+ sc->arpcom.ac_if.if_name);
+ sc->arpcom.ac_if.if_collisions += 16;
+ }
+ }
+
+ /*
+ * If multicast addresses were added or deleted while transmitting,
+ * ie_mc_reset() set the want_mcsetup flag indicating that we
+ * should do it.
+ */
+ if (sc->want_mcsetup) {
+ ie_mc_setup(sc, IE_XBUF_ADDR(sc, sc->xctail));
+ sc->want_mcsetup = 0;
+ }
+
+ /* Done with the buffer. */
+ sc->xmit_busy--;
+ sc->xctail = (sc->xctail + 1) % NTXBUF;
+
+ /* Start the next packet, if any, transmitting. */
+ if (sc->xmit_busy > 0)
+ iexmit(sc);
+
+ i82586_start_tx(sc);
+ return (0);
+}
+
+/*
+ * Get a range of receive buffer descriptors that represent one packet.
+ */
+static int
+i82586_get_rbd_list(struct ie_softc *sc, u_int16_t *start, u_int16_t *end, int *pktlen)
+{
+ int off, rbbase = sc->rbds;
+ int rbindex, count = 0;
+ int plen = 0;
+ int rbdstatus;
+
+ *start = rbindex = sc->rbhead;
+
+ do {
+ off = IE_RBD_STATUS(rbbase, rbindex);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+ rbdstatus = sc->ie_bus_read16(sc, off);
+ if ((rbdstatus & IE_RBD_USED) == 0) {
+ /*
+ * This means we are somehow out of sync. So, we
+ * reset the adapter.
+ */
+#if I82586_DEBUG
+ print_rbd(sc, rbindex);
+#endif
+ printf("%s: receive descriptors out of sync at %d\n",
+ sc->arpcom.ac_if.if_name, rbindex);
+ return (0);
+ }
+ plen += (rbdstatus & IE_RBD_CNTMASK);
+
+ if (++rbindex == sc->nrxbuf)
+ rbindex = 0;
+
+ ++count;
+ } while ((rbdstatus & IE_RBD_LAST) == 0);
+ *end = rbindex;
+ *pktlen = plen;
+ return (count);
+}
+
+
+/*
+ * Release a range of receive buffer descriptors after we've copied the packet.
+ */
+static void
+i82586_release_rbd_list(struct ie_softc *sc, u_int16_t start, u_int16_t end)
+{
+ int off, rbbase = sc->rbds;
+ int rbindex = start;
+
+ do {
+ /* Clear buffer status */
+ off = IE_RBD_STATUS(rbbase, rbindex);
+ sc->ie_bus_write16(sc, off, 0);
+ if (++rbindex == sc->nrxbuf)
+ rbindex = 0;
+ } while (rbindex != end);
+
+ /* Mark EOL at new tail */
+ rbindex = ((rbindex == 0) ? sc->nrxbuf : rbindex) - 1;
+ off = IE_RBD_BUFLEN(rbbase, rbindex);
+ sc->ie_bus_write16(sc, off, IE_RBUF_SIZE|IE_RBD_EOL);
+
+ /* Remove EOL from current tail */
+ off = IE_RBD_BUFLEN(rbbase, sc->rbtail);
+ sc->ie_bus_write16(sc, off, IE_RBUF_SIZE);
+
+ /* New head & tail pointer */
+/* hmm, why have both? head is always (tail + 1) % NRXBUF */
+ sc->rbhead = end;
+ sc->rbtail = rbindex;
+}
+
+/*
+ * Drop the packet at the head of the RX buffer ring.
+ * Called if the frame descriptor reports an error on this packet.
+ * Returns 1 if the buffer descriptor ring appears to be corrupt;
+ * and 0 otherwise.
+ */
+static int
+i82586_drop_frames(struct ie_softc *sc)
+{
+ u_int16_t bstart, bend;
+ int pktlen;
+
+ if (i82586_get_rbd_list(sc, &bstart, &bend, &pktlen) == 0)
+ return (1);
+ i82586_release_rbd_list(sc, bstart, bend);
+ return (0);
+}
+
+/*
+ * Check the RX frame & buffer descriptor lists for our invariants,
+ * i.e.: EOL bit set iff. it is pointed at by the r*tail pointer.
+ *
+ * Called when the receive unit has stopped unexpectedly.
+ * Returns 1 if an inconsistency is detected; 0 otherwise.
+ *
+ * The Receive Unit is expected to be NOT RUNNING.
+ */
+static int
+i82586_chk_rx_ring(struct ie_softc *sc)
+{
+ int n, off, val;
+
+ for (n = 0; n < sc->nrxbuf; n++) {
+ off = IE_RBD_BUFLEN(sc->rbds, n);
+ val = sc->ie_bus_read16(sc, off);
+ if ((n == sc->rbtail) ^ ((val & IE_RBD_EOL) != 0)) {
+ /* `rbtail' and EOL flag out of sync */
+ printf("%s: rx buffer descriptors out of sync at %d\n",
+ sc->arpcom.ac_if.if_name, n);
+ return (1);
+ }
+
+ /* Take the opportunity to clear the status fields here ? */
+ }
+
+ for (n = 0; n < sc->nframes; n++) {
+ off = IE_RFRAME_LAST(sc->rframes, n);
+ val = sc->ie_bus_read16(sc, off);
+ if ((n == sc->rftail) ^ ((val & (IE_FD_EOL|IE_FD_SUSP)) != 0)) {
+ /* `rftail' and EOL flag out of sync */
+ printf("%s: rx frame list out of sync at %d\n",
+ sc->arpcom.ac_if.if_name, n);
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * Read frame NUM from unit UNIT (pre-cached as IE).
+ *
+ * This routine reads the RFD at NUM, and copies in the buffers from the list
+ * of RBD, then rotates the RBD list so that the receiver doesn't start
+ * complaining. Trailers are DROPPED---there's no point in wasting time
+ * on confusing code to deal with them. Hopefully, this machine will
+ * never ARP for trailers anyway.
+ */
+static int
+ie_readframe(struct ie_softc *sc, int num) /* frame number to read */
+{
+ struct ether_header *eh;
+ struct mbuf *m;
+ u_int16_t bstart, bend;
+ int pktlen;
+
+ if (i82586_get_rbd_list(sc, &bstart, &bend, &pktlen) == 0) {
+ sc->arpcom.ac_if.if_ierrors++;
+ return (1);
+ }
+
+ m = ieget(sc, bstart, pktlen);
+ i82586_release_rbd_list(sc, bstart, bend);
+
+ if (m == 0) {
+ sc->arpcom.ac_if.if_ierrors++;
+ return (0);
+ }
+
+ /*
+ * Remove the mac header. This is different from the NetBSD
+ * stack.
+ */
+ eh = mtod(m, struct ether_header *);
+ m->m_data += sizeof (struct ether_header);
+ m->m_len -= sizeof (struct ether_header);
+ m->m_pkthdr.len -= sizeof (struct ether_header);
+
+#if I82586_DEBUG
+ if (sc->sc_debug & IED_READFRAME) {
+
+ printf("%s: frame from ether %s type 0x%x len %d\n",
+ sc->arpcom.ac_if.if_name,
+ ether_sprintf(eh->ether_shost),
+ (u_int)ntohs(eh->ether_type),
+ pktlen);
+ }
+#endif
+
+#if NBPFILTER > 0
+ /* Check for a BPF filter; if so, hand it up. */
+ if (sc->arpcom.ac_if.if_bpf != 0)
+ /* Pass it up. */
+ bpf_mtap(sc->arpcom.ac_if.if_bpf, m);
+#endif /* NBPFILTER > 0 */
+
+ /*
+ * Finally pass this packet up to higher layers.
+ */
+ ether_input (&sc->arpcom.ac_if, eh, m);
+ sc->arpcom.ac_if.if_ipackets++;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_RX_OK, sc->arpcom.ac_if.if_ipackets);
+#endif
+
+ return (0);
+}
+
+/*
+ * Start transmission on an interface.
+ */
+void
+i82586_start(struct ifnet *ifp)
+{
+ struct ie_softc *sc = ifp->if_softc;
+
+ if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+ return;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_TX_REQ, sc->xmit_busy);
+#endif
+
+ rtems_bsdnet_event_send (sc->tx_task, i82586_TX_EVENT);
+}
+
+static void
+i82586_tx_task(void *arg)
+{
+ struct ie_softc *sc = arg;
+ rtems_event_set events;
+
+ for (;;) {
+ rtems_bsdnet_event_receive (i82586_TX_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ 0, &events);
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_TX_EVT, sc->xmit_busy);
+
+ if (sc->sc_debug)
+ printf ("%s: =======\n", sc->arpcom.ac_if.if_name);
+#endif
+
+ i82586_start_tx(sc);
+ }
+}
+
+static void
+i82586_start_tx(struct ie_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m0, *m;
+ int buffer, head, xbase;
+ u_short len;
+ int s;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_START_TX, sc->xmit_busy);
+#endif
+
+ if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+ {
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_TX_ACTIVE, ifp->if_snd.ifq_len);
+#endif
+ return;
+ }
+
+ for (;;) {
+ if (sc->xmit_busy == NTXBUF) {
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+
+ head = sc->xchead;
+ xbase = sc->xbds;
+
+ IF_DEQUEUE(&ifp->if_snd, m0);
+ if (m0 == 0)
+ break;
+
+ /* We need to use m->m_pkthdr.len, so require the header */
+ if ((m0->m_flags & M_PKTHDR) == 0)
+ panic("i82586_start: no header mbuf");
+
+#if NBPFILTER > 0
+ /* Tap off here if there is a BPF listener. */
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m0);
+#endif
+
+#if I82586_DEBUG
+ if (sc->sc_debug & IED_ENQ)
+ printf("%s: fill buffer %d\n", sc->arpcom.ac_if.if_name,
+ sc->xchead);
+#endif
+
+ if (m0->m_pkthdr.len > IE_TBUF_SIZE)
+ printf("%s: tbuf overflow\n", sc->arpcom.ac_if.if_name);
+
+ buffer = IE_XBUF_ADDR(sc, head);
+ for (m = m0; m != 0; m = m->m_next) {
+ (sc->memcopyout)(sc, mtod(m,caddr_t), buffer, m->m_len);
+ buffer += m->m_len;
+ }
+
+ len = max(m0->m_pkthdr.len, ETHER_MIN_LEN);
+ m_freem(m0);
+
+ /*
+ * Setup the transmit buffer descriptor here, while we
+ * know the packet's length.
+ */
+ sc->ie_bus_write16(sc, IE_XBD_FLAGS(xbase, head),
+ len | IE_TBD_EOL);
+ sc->ie_bus_write16(sc, IE_XBD_NEXT(xbase, head), 0xffff);
+ sc->ie_bus_write24(sc, IE_XBD_BUF(xbase, head),
+ IE_XBUF_ADDR(sc, head));
+
+ if (++head == NTXBUF)
+ head = 0;
+ sc->xchead = head;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_TX_START, sc->xmit_busy);
+#endif
+
+ s = splnet();
+ /* Start the first packet transmitting. */
+ if (sc->xmit_busy == 0)
+ iexmit(sc);
+
+ sc->xmit_busy++;
+
+ splx(s);
+ }
+}
+
+/*
+ * Probe IE's ram setup [ Move all this into MD front-end!? ]
+ * Use only if SCP and ISCP represent offsets into shared ram space.
+ */
+int
+i82586_proberam(struct ie_softc *sc)
+{
+ int result, off;
+
+ /* Put in 16-bit mode */
+ off = IE_SCP_BUS_USE(sc->scp);
+ sc->ie_bus_write16(sc, off, 0);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_WRITE);
+
+ /* Set the ISCP `busy' bit */
+ off = IE_ISCP_BUSY(sc->iscp);
+ sc->ie_bus_write16(sc, off, 1);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_WRITE);
+
+ if (sc->hwreset)
+ (sc->hwreset)(sc, CHIP_PROBE);
+
+ (sc->chan_attn) (sc, CHIP_PROBE);
+
+ delay(100); /* wait a while... */
+
+ /* Read back the ISCP `busy' bit; it should be clear by now */
+ off = IE_ISCP_BUSY(sc->iscp);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+ result = sc->ie_bus_read16(sc, off) == 0;
+
+ /* Acknowledge any interrupts we may have caused. */
+ ie_ack(sc, IE_ST_WHENCE);
+
+ return (result);
+}
+
+void
+i82586_reset(struct ie_softc *sc, int hard)
+{
+ int s = splnet();
+
+ if (hard)
+ printf("%s: reset\n", sc->arpcom.ac_if.if_name);
+
+ /* Clear OACTIVE in case we're called from watchdog (frozen xmit). */
+ sc->arpcom.ac_if.if_timer = 0;
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+
+ /*
+ * Stop i82586 dead in its tracks.
+ */
+ if (i82586_start_cmd(sc, IE_RUC_ABORT | IE_CUC_ABORT, 0, 0, 0))
+ printf("%s: abort commands timed out\n", sc->arpcom.ac_if.if_name);
+
+ /*
+ * This can really slow down the i82586_reset() on some cards, but it's
+ * necessary to unwedge other ones (eg, the Sun VME ones) from certain
+ * lockups.
+ */
+ if (hard && sc->hwreset)
+ (sc->hwreset)(sc, CARD_RESET);
+
+ delay(100);
+ ie_ack(sc, IE_ST_WHENCE);
+
+ if ((sc->arpcom.ac_if.if_flags & IFF_UP) != 0) {
+ i82586_init(&sc->arpcom.ac_if);
+ }
+
+ splx(s);
+}
+
+static void
+setup_simple_command(struct ie_softc *sc, int cmd, int cmdbuf)
+{
+ /* Setup a simple command */
+ sc->ie_bus_write16(sc, IE_CMD_COMMON_STATUS(cmdbuf), 0);
+ sc->ie_bus_write16(sc, IE_CMD_COMMON_CMD(cmdbuf), cmd | IE_CMD_LAST);
+ sc->ie_bus_write16(sc, IE_CMD_COMMON_LINK(cmdbuf), 0xffff);
+
+ /* Assign the command buffer to the SCB command list */
+ sc->ie_bus_write16(sc, IE_SCB_CMDLST(sc->scb), cmdbuf);
+}
+
+/*
+ * Run the time-domain reflectometer.
+ */
+static void
+ie_run_tdr(struct ie_softc *sc, int cmd)
+{
+ uint32_t result;
+
+ setup_simple_command(sc, IE_CMD_TDR, cmd);
+ sc->ie_bus_write16(sc, IE_CMD_TDR_TIME(cmd), 0);
+
+ if (i82586_start_cmd(sc, IE_CUC_START, cmd, IE_STAT_COMPL, 0) ||
+ (sc->ie_bus_read16(sc, IE_CMD_COMMON_STATUS(cmd)) & IE_STAT_OK) == 0)
+ result = 0x10000; /* XXX */
+ else
+ result = sc->ie_bus_read16(sc, IE_CMD_TDR_TIME(cmd));
+
+ /* Squash any pending interrupts */
+ ie_ack(sc, IE_ST_WHENCE);
+
+ if (result & IE_TDR_SUCCESS)
+ return;
+
+ if (result & 0x10000)
+ printf("%s: TDR command failed\n", sc->arpcom.ac_if.if_name);
+ else if (result & IE_TDR_XCVR)
+ printf("%s: transceiver problem\n", sc->arpcom.ac_if.if_name);
+ else if (result & IE_TDR_OPEN)
+ printf("%s: TDR detected incorrect termination %" PRId32 " clocks away\n",
+ sc->arpcom.ac_if.if_name, result & IE_TDR_TIME);
+ else if (result & IE_TDR_SHORT)
+ printf("%s: TDR detected a short circuit %" PRId32 " clocks away\n",
+ sc->arpcom.ac_if.if_name, result & IE_TDR_TIME);
+ else
+ printf("%s: TDR returned unknown status 0x%" PRIx32 "\n",
+ sc->arpcom.ac_if.if_name, result);
+}
+
+
+/*
+ * i82586_setup_bufs: set up the buffers
+ *
+ * We have a block of KVA at sc->buf_area which is of size sc->buf_area_sz.
+ * this is to be used for the buffers. The chip indexs its control data
+ * structures with 16 bit offsets, and it indexes actual buffers with
+ * 24 bit addresses. So we should allocate control buffers first so that
+ * we don't overflow the 16 bit offset field. The number of transmit
+ * buffers is fixed at compile time.
+ *
+ */
+static void
+i82586_setup_bufs(struct ie_softc *sc)
+{
+ int ptr = sc->buf_area; /* memory pool */
+ int n, r;
+
+ /*
+ * step 0: zero memory and figure out how many recv buffers and
+ * frames we can have.
+ */
+ ptr = (ptr + 3) & ~3; /* set alignment and stick with it */
+
+
+ /*
+ * step 1: lay out data structures in the shared-memory area
+ */
+
+ /* The no-op commands; used if "nop-chaining" is in effect */
+ sc->nop_cmds = ptr;
+ ptr += NTXBUF * IE_CMD_NOP_SZ;
+
+ /* The transmit commands */
+ sc->xmit_cmds = ptr;
+ ptr += NTXBUF * IE_CMD_XMIT_SZ;
+
+ /* The transmit buffers descriptors */
+ sc->xbds = ptr;
+ ptr += NTXBUF * IE_XBD_SZ;
+
+ /* The transmit buffers */
+ sc->xbufs = ptr;
+ ptr += NTXBUF * IE_TBUF_SIZE;
+
+ ptr = (ptr + 3) & ~3; /* re-align.. just in case */
+
+ /* Compute free space for RECV stuff */
+ n = sc->buf_area_sz - (ptr - sc->buf_area);
+
+ /* Compute size of one RECV frame */
+ r = IE_RFRAME_SZ + ((IE_RBD_SZ + IE_RBUF_SIZE) * B_PER_F);
+
+ sc->nframes = n / r;
+
+ if (sc->nframes <= 0)
+ panic("ie: bogus buffer calc\n");
+
+ sc->nrxbuf = sc->nframes * B_PER_F;
+
+ /* The receice frame descriptors */
+ sc->rframes = ptr;
+ ptr += sc->nframes * IE_RFRAME_SZ;
+
+ /* The receive buffer descriptors */
+ sc->rbds = ptr;
+ ptr += sc->nrxbuf * IE_RBD_SZ;
+
+ /* The receive buffers */
+ sc->rbufs = ptr;
+ ptr += sc->nrxbuf * IE_RBUF_SIZE;
+
+#if I82586_DEBUG
+ printf("%s: %d frames %d bufs\n", sc->arpcom.ac_if.if_name, sc->nframes,
+ sc->nrxbuf);
+#endif
+
+ /*
+ * step 2: link together the recv frames and set EOL on last one
+ */
+ for (n = 0; n < sc->nframes; n++) {
+ int m = (n == sc->nframes - 1) ? 0 : n + 1;
+
+ /* Clear status */
+ sc->ie_bus_write16(sc, IE_RFRAME_STATUS(sc->rframes,n), 0);
+
+ /* RBD link = NULL */
+ sc->ie_bus_write16(sc, IE_RFRAME_BUFDESC(sc->rframes,n),
+ 0xffff);
+
+ /* Make a circular list */
+ sc->ie_bus_write16(sc, IE_RFRAME_NEXT(sc->rframes,n),
+ IE_RFRAME_ADDR(sc->rframes,m));
+
+ /* Mark last as EOL */
+ sc->ie_bus_write16(sc, IE_RFRAME_LAST(sc->rframes,n),
+ ((m==0)? (IE_FD_EOL|IE_FD_SUSP) : 0));
+ }
+
+ /*
+ * step 3: link the RBDs and set EOL on last one
+ */
+ for (n = 0; n < sc->nrxbuf; n++) {
+ int m = (n == sc->nrxbuf - 1) ? 0 : n + 1;
+
+ /* Clear status */
+ sc->ie_bus_write16(sc, IE_RBD_STATUS(sc->rbds,n), 0);
+
+ /* Make a circular list */
+ sc->ie_bus_write16(sc, IE_RBD_NEXT(sc->rbds,n),
+ IE_RBD_ADDR(sc->rbds,m));
+
+ /* Link to data buffers */
+ sc->ie_bus_write24(sc, IE_RBD_BUFADDR(sc->rbds, n),
+ IE_RBUF_ADDR(sc, n));
+ sc->ie_bus_write16(sc, IE_RBD_BUFLEN(sc->rbds,n),
+ IE_RBUF_SIZE | ((m==0)?IE_RBD_EOL:0));
+ }
+
+ /*
+ * step 4: all xmit no-op commands loopback onto themselves
+ */
+ for (n = 0; n < NTXBUF; n++) {
+ sc->ie_bus_write16(sc, IE_CMD_NOP_STATUS(sc->nop_cmds, n), 0);
+
+ sc->ie_bus_write16(sc, IE_CMD_NOP_CMD(sc->nop_cmds, n),
+ IE_CMD_NOP);
+
+ sc->ie_bus_write16(sc, IE_CMD_NOP_LINK(sc->nop_cmds, n),
+ IE_CMD_NOP_ADDR(sc->nop_cmds, n));
+ }
+
+
+ /*
+ * step 6: set the head and tail pointers on receive to keep track of
+ * the order in which RFDs and RBDs are used.
+ */
+
+ /* Pointers to last packet sent and next available transmit buffer. */
+ sc->xchead = sc->xctail = 0;
+
+ /* Clear transmit-busy flag and set number of free transmit buffers. */
+ sc->xmit_busy = 0;
+
+ /*
+ * Pointers to first and last receive frame.
+ * The RFD pointed to by rftail is the only one that has EOL set.
+ */
+ sc->rfhead = 0;
+ sc->rftail = sc->nframes - 1;
+
+ /*
+ * Pointers to first and last receive descriptor buffer.
+ * The RBD pointed to by rbtail is the only one that has EOL set.
+ */
+ sc->rbhead = 0;
+ sc->rbtail = sc->nrxbuf - 1;
+
+/* link in recv frames * and buffer into the scb. */
+#if I82586_DEBUG
+ printf("%s: reserved %d bytes\n",
+ sc->arpcom.ac_if.if_name, ptr - sc->buf_area);
+#endif
+}
+
+static int
+ie_cfg_setup(struct ie_softc *sc, int cmd, int promiscuous, int manchester)
+{
+ int cmdresult, status;
+ u_int8_t buf[IE_CMD_CFG_SZ]; /* XXX malloc? */
+
+ *IE_CMD_CFG_CNT(buf) = 0x0c;
+ *IE_CMD_CFG_FIFO(buf) = 8;
+ *IE_CMD_CFG_SAVEBAD(buf) = 0x40;
+ *IE_CMD_CFG_ADDRLEN(buf) = 0x2e;
+ *IE_CMD_CFG_PRIORITY(buf) = 0;
+ *IE_CMD_CFG_IFS(buf) = 0x60;
+ *IE_CMD_CFG_SLOT_LOW(buf) = 0;
+ *IE_CMD_CFG_SLOT_HIGH(buf) = 0xf2;
+ *IE_CMD_CFG_PROMISC(buf) = (!!promiscuous) | (manchester << 2);
+ *IE_CMD_CFG_CRSCDT(buf) = 0;
+ *IE_CMD_CFG_MINLEN(buf) = 64;
+ *IE_CMD_CFG_JUNK(buf) = 0xff;
+ sc->memcopyout(sc, buf, cmd, IE_CMD_CFG_SZ);
+ setup_simple_command(sc, IE_CMD_CONFIG, cmd);
+ IE_BUS_BARRIER(sc, cmd, IE_CMD_CFG_SZ, BUS_SPACE_BARRIER_WRITE);
+
+ cmdresult = i82586_start_cmd(sc, IE_CUC_START, cmd, IE_STAT_COMPL, 0);
+ status = sc->ie_bus_read16(sc, IE_CMD_COMMON_STATUS(cmd));
+ if (cmdresult != 0) {
+ printf("%s: configure command timed out; status %x\n",
+ sc->arpcom.ac_if.if_name, status);
+ return (0);
+ }
+ if ((status & IE_STAT_OK) == 0) {
+ printf("%s: configure command failed; status %x\n",
+ sc->arpcom.ac_if.if_name, status);
+ return (0);
+ }
+
+ /* Squash any pending interrupts */
+ ie_ack(sc, IE_ST_WHENCE);
+ return (1);
+}
+
+static int
+ie_ia_setup(struct ie_softc *sc, int cmdbuf)
+{
+ int cmdresult, status;
+
+ setup_simple_command(sc, IE_CMD_IASETUP, cmdbuf);
+
+ (sc->memcopyout)(sc, sc->arpcom.ac_enaddr,
+ IE_CMD_IAS_EADDR(cmdbuf), ETHER_ADDR_LEN);
+
+ cmdresult = i82586_start_cmd(sc, IE_CUC_START, cmdbuf, IE_STAT_COMPL, 0);
+ status = sc->ie_bus_read16(sc, IE_CMD_COMMON_STATUS(cmdbuf));
+ if (cmdresult != 0) {
+ printf("%s: individual address command timed out; status %x\n",
+ sc->arpcom.ac_if.if_name, status);
+ return (0);
+ }
+ if ((status & IE_STAT_OK) == 0) {
+ printf("%s: individual address command failed; status %x\n",
+ sc->arpcom.ac_if.if_name, status);
+ return (0);
+ }
+
+ /* Squash any pending interrupts */
+ ie_ack(sc, IE_ST_WHENCE);
+ return (1);
+}
+
+/*
+ * Run the multicast setup command.
+ * Called at splnet().
+ */
+static int
+ie_mc_setup(struct ie_softc *sc, int cmdbuf)
+{
+ int cmdresult, status;
+
+ if (sc->mcast_count == 0)
+ return (1);
+
+ setup_simple_command(sc, IE_CMD_MCAST, cmdbuf);
+
+ (sc->memcopyout)(sc, (caddr_t)sc->mcast_addrs,
+ IE_CMD_MCAST_MADDR(cmdbuf),
+ sc->mcast_count * ETHER_ADDR_LEN);
+
+ sc->ie_bus_write16(sc, IE_CMD_MCAST_BYTES(cmdbuf),
+ sc->mcast_count * ETHER_ADDR_LEN);
+
+ /* Start the command */
+ cmdresult = i82586_start_cmd(sc, IE_CUC_START, cmdbuf, IE_STAT_COMPL, 0);
+ status = sc->ie_bus_read16(sc, IE_CMD_COMMON_STATUS(cmdbuf));
+ if (cmdresult != 0) {
+ printf("%s: multicast setup command timed out; status %x\n",
+ sc->arpcom.ac_if.if_name, status);
+ return (0);
+ }
+ if ((status & IE_STAT_OK) == 0) {
+ printf("%s: multicast setup command failed; status %x\n",
+ sc->arpcom.ac_if.if_name, status);
+ return (0);
+ }
+
+ /* Squash any pending interrupts */
+ ie_ack(sc, IE_ST_WHENCE);
+ return (1);
+}
+
+/*
+ * This routine takes the environment generated by check_ie_present() and adds
+ * to it all the other structures we need to operate the adapter. This
+ * includes executing the CONFIGURE, IA-SETUP, and MC-SETUP commands, starting
+ * the receiver unit, and clearing interrupts.
+ *
+ * THIS ROUTINE MUST BE CALLED AT splnet() OR HIGHER.
+ */
+void
+i82586_init(void *arg)
+{
+ struct ie_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ int cmd;
+
+ sc->async_cmd_inprogress = 0;
+ sc->xmit_busy = 0;
+
+#if I82586_DEBUG
+ memset(sc->trace_flow, 0, sizeof(sc->trace_flow));
+ sc->trace_flow_wrap = 0;
+#endif
+ sc->trace_flow_in = 0;
+
+ cmd = sc->buf_area;
+
+#if I82586_DEBUG
+ printf ("%s: sc_debug at 0x%08x\n", sc->arpcom.ac_if.if_name, (unsigned int) &sc->sc_debug);
+#endif
+
+ /*
+ * Send the configure command first.
+ */
+ if (ie_cfg_setup(sc, cmd, sc->promisc, 0) == 0)
+ return;
+
+ /*
+ * Send the Individual Address Setup command.
+ */
+ if (ie_ia_setup(sc, cmd) == 0)
+ return;
+
+ /*
+ * Run the time-domain reflectometer.
+ */
+ ie_run_tdr(sc, cmd);
+
+ /*
+ * Set the multi-cast filter, if any
+ */
+ if (ie_mc_setup(sc, cmd) == 0)
+ return;
+
+ /*
+ * If no tasks exist, create them. Need to add something to allow
+ * different names for the different devices.
+ */
+ if (sc->intr_task == 0)
+ sc->intr_task = rtems_bsdnet_newproc ("IEi0", 2048, i82586_intr_task, sc);
+ if (sc->tx_task == 0)
+ sc->tx_task = rtems_bsdnet_newproc ("IEt0", 2048, i82586_tx_task, sc);
+
+
+ /*
+ * Acknowledge any interrupts we have generated thus far.
+ */
+ ie_ack(sc, IE_ST_WHENCE);
+
+ /*
+ * Set up the transmit and recv buffers.
+ */
+ i82586_setup_bufs(sc);
+
+ if (sc->hwinit)
+ (sc->hwinit)(sc);
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ if (NTXBUF < 2)
+ sc->do_xmitnopchain = 0;
+
+ i82586_start_transceiver(sc);
+}
+
+/*
+ * Start the RU and possibly the CU unit
+ */
+static void
+i82586_start_transceiver(struct ie_softc *sc)
+{
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_RX_START, 0);
+#endif
+
+ /*
+ * Start RU at current position in frame & RBD lists.
+ */
+ sc->ie_bus_write16(sc, IE_RFRAME_BUFDESC(sc->rframes,sc->rfhead),
+ IE_RBD_ADDR(sc->rbds, sc->rbhead));
+
+ sc->ie_bus_write16(sc, IE_SCB_RCVLST(sc->scb),
+ IE_RFRAME_ADDR(sc->rframes,sc->rfhead));
+
+ if (sc->do_xmitnopchain) {
+ /* Stop transmit command chain */
+ if (i82586_start_cmd(sc, IE_CUC_SUSPEND|IE_RUC_SUSPEND, 0, 0, 0))
+ printf("%s: CU/RU stop command timed out\n",
+ sc->arpcom.ac_if.if_name);
+
+ /* Start the receiver & transmitter chain */
+ /* sc->scb->ie_command_list =
+ IEADDR(sc->nop_cmds[(sc->xctail+NTXBUF-1) % NTXBUF]);*/
+ sc->ie_bus_write16(sc, IE_SCB_CMDLST(sc->scb),
+ IE_CMD_NOP_ADDR(
+ sc->nop_cmds,
+ (sc->xctail + NTXBUF - 1) % NTXBUF));
+
+ if (i82586_start_cmd(sc, IE_CUC_START|IE_RUC_START, 0, 0, 0))
+ printf("%s: CU/RU command timed out\n",
+ sc->arpcom.ac_if.if_name);
+ } else {
+ if (i82586_start_cmd(sc, IE_RUC_START, 0, 0, 0))
+ printf("%s: RU command timed out\n",
+ sc->arpcom.ac_if.if_name);
+ }
+}
+
+void
+i82586_stop(struct ifnet *ifp, int disable)
+{
+ struct ie_softc *sc = ifp->if_softc;
+
+ if (i82586_start_cmd(sc, IE_RUC_SUSPEND | IE_CUC_SUSPEND, 0, 0, 0))
+ printf("%s: iestop: disable commands timed out\n",
+ sc->arpcom.ac_if.if_name);
+}
+
+int
+i82586_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data)
+{
+ struct ie_softc *sc = ifp->if_softc;
+/* struct ifreq *ifr = (struct ifreq *)data; */
+ int s;
+ int error = 0;
+
+ s = splnet();
+ switch(cmd) {
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ break;
+ case SIO_RTEMS_SHOW_STATS:
+#if I82586_DEBUG
+ print_softie(sc);
+#endif
+ break;
+ default:
+ error = ether_ioctl(ifp, cmd, data);
+ if (error == ENETRESET) {
+ /*
+ * Multicast list has changed; set the hardware filter
+ * accordingly.
+ */
+ ie_mc_reset(sc);
+ error = 0;
+ }
+ break;
+ }
+#if I82586_DEBUG
+ if (cmd == SIOCSIFFLAGS)
+ sc->sc_debug = (ifp->if_flags & IFF_DEBUG) ? IED_ALL : 0;
+#endif
+ splx(s);
+ return (error);
+}
+
+static void
+ie_mc_reset(struct ie_softc *sc)
+{
+ struct ether_multi *enm;
+ struct ether_multistep step;
+ int size;
+
+ /*
+ * Step through the list of addresses.
+ */
+ again:
+ size = 0;
+ sc->mcast_count = 0;
+ ETHER_FIRST_MULTI(step, &sc->arpcom, enm);
+ while (enm) {
+ size += 6;
+ if (sc->mcast_count >= IE_MAXMCAST ||
+ memcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) {
+ sc->arpcom.ac_if.if_flags |= IFF_ALLMULTI;
+ i82586_ioctl(&sc->arpcom.ac_if,
+ SIOCSIFFLAGS, (void *)0);
+ return;
+ }
+ ETHER_NEXT_MULTI(step, enm);
+ }
+
+ if (size > sc->mcast_addrs_size) {
+ /* Need to allocate more space */
+ if (sc->mcast_addrs_size)
+ free(sc->mcast_addrs, M_IPMADDR);
+ sc->mcast_addrs = (char *)
+ malloc(size, M_IPMADDR, M_WAITOK);
+ sc->mcast_addrs_size = size;
+ }
+
+ /*
+ * We've got the space; now copy the addresses
+ */
+ ETHER_FIRST_MULTI(step, &sc->arpcom, enm);
+ while (enm) {
+ if (sc->mcast_count >= IE_MAXMCAST)
+ goto again; /* Just in case */
+
+ memcpy(&sc->mcast_addrs[sc->mcast_count], enm->enm_addrlo, 6);
+ sc->mcast_count++;
+ ETHER_NEXT_MULTI(step, enm);
+ }
+ sc->want_mcsetup = 1;
+}
+
+/*
+ * Media change callback.
+ */
+int
+i82586_mediachange(struct ifnet *ifp)
+{
+ struct ie_softc *sc = ifp->if_softc;
+
+ if (sc->sc_mediachange)
+ return ((*sc->sc_mediachange)(sc));
+ return (0);
+}
+
+/*
+ * Media status callback.
+ */
+void
+i82586_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct ie_softc *sc = ifp->if_softc;
+
+ if (sc->sc_mediastatus)
+ (*sc->sc_mediastatus)(sc, ifmr);
+}
+
+#if I82586_DEBUG
+static void
+print_softie(struct ie_softc *sc)
+{
+ static char *trace_labels[] = {
+ "INTS_REQ",
+ "INTS_IN",
+ "INTS_LOOPS",
+ "INTS_OUT",
+ "RX_INT",
+ "RX_DROP",
+ "RX_ERR",
+ "RX_OK",
+ "RX_START",
+ "START_TX",
+ "TX_START",
+ "TX_INT",
+ "TX_REQ",
+ "TX_EVT",
+ "TX_EMIT",
+ "TX_BAD",
+ "TX_ACTIVE",
+ "TRACE_CNT"
+ };
+
+ int i;
+
+ printf("i82586 %s:\n", sc->arpcom.ac_if.if_name);
+
+ printf(" iobase=%p\n", sc->sc_iobase);
+
+ printf(" scp=0x%08x\t\tiscp=0x%08x\t\tscb=0x%08x\n",
+ sc->scp, sc->iscp, sc->scb);
+ printf(" buf_area=0x%08x\tbuf_area_sz=0x%08x\n",
+ sc->buf_area, sc->buf_area_sz);
+ printf(" rframes=0x%08x\trbds=0x%08x\t\trbufs=0x%08x\n",
+ sc->rframes, sc->rbds, sc->rbufs);
+ printf(" nop_cmds=0x%08x\txmit_cmds=0x%08x\n",
+ sc->nop_cmds, sc->xmit_cmds);
+ printf(" xbds=0x%08x\txbufs=0x%08x\n\n",
+ sc->xbds, sc->xbufs);
+ printf(" rfhead=%d\trftail=%d\n",
+ sc->rfhead, sc->rftail);
+ printf(" rbhead=%d\trbtail=%d\n",
+ sc->rbhead, sc->rbtail);
+ printf(" nframes=%d\tnrxbuf=%d\trnr_expect=%d\n",
+ sc->nframes, sc->nrxbuf, sc->rnr_expect);
+ printf(" xchead=%d\txctail=%d\n",
+ sc->xchead, sc->xctail);
+ printf(" xmit_busy=%d\txmit_req=%d\tdo_xmitnopchain=%d\n",
+ sc->xmit_busy, sc->xmit_req, sc->do_xmitnopchain);
+ printf(" promisc=%d\tasync_cmd_inprogress=%d\n\n",
+ sc->promisc, sc->async_cmd_inprogress);
+
+ {
+ int cnt;
+ int in;
+ int lfdone = 0;
+ char *tabs;
+
+ if (!sc->trace_flow_wrap) {
+ cnt = sc->trace_flow_in;
+ in = 0;
+ }
+ else {
+ cnt = I82586_TRACE_FLOW;
+ in = sc->trace_flow_in;
+ }
+
+ sc->trace_flow_in = sc->trace_flow_wrap = 0;
+
+ cnt /= 2;
+
+ for (i = 0; i < cnt; i++) {
+ if (!lfdone) {
+ switch (sc->trace_flow[in]) {
+ case I82586_INTS_REQ:
+ case I82586_INTS_IN:
+ printf("\n");
+ }
+ }
+
+ lfdone = 0;
+
+ if (strlen(trace_labels[sc->trace_flow[in]]) < 8)
+ tabs = "\t\t";
+ else
+ tabs = "\t";
+
+ printf(" %d\t%s%s0x%08x (%d)\n",
+ i, trace_labels[sc->trace_flow[in]], tabs,
+ sc->trace_flow[in + 1], sc->trace_flow[in + 1]);
+
+ switch (sc->trace_flow[in]) {
+ case I82586_INTS_REQ:
+ case I82586_INTS_OUT:
+ lfdone = 1;
+ printf("\n");
+ }
+
+ in += 2;
+
+ if (in >= I82586_TRACE_FLOW)
+ in = 0;
+ }
+ }
+}
+
+static void
+print_rbd(struct ie_softc *sc, int n)
+{
+ printf("RBD at %08x:\n status %04x, next %04x, buffer %lx\n"
+ "length/EOL %04x\n", IE_RBD_ADDR(sc->rbds,n),
+ sc->ie_bus_read16(sc, IE_RBD_STATUS(sc->rbds,n)),
+ sc->ie_bus_read16(sc, IE_RBD_NEXT(sc->rbds,n)),
+ (u_long)0,/*bus_space_read_4(sc->bt, sc->bh, IE_RBD_BUFADDR(sc->rbds,n)),-* XXX */
+ sc->ie_bus_read16(sc, IE_RBD_BUFLEN(sc->rbds,n)));
+}
+
+#endif
diff --git a/bsps/shared/net/i82586reg.h b/bsps/shared/net/i82586reg.h
new file mode 100644
index 0000000..e093aff
--- /dev/null
+++ b/bsps/shared/net/i82586reg.h
@@ -0,0 +1,448 @@
+/* $NetBSD: i82586reg.h,v 1.7 1998/02/28 01:07:45 pk Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Paul Kranenburg.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*-
+ * Copyright (c) 1992, University of Vermont and State Agricultural College.
+ * Copyright (c) 1992, Garrett A. Wollman.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * Vermont and State Agricultural College and Garrett A. Wollman.
+ * 4. Neither the name of the University nor the name of the author
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 UNIVERSITY OR AUTHOR 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.
+ */
+
+/*
+ * Intel 82586 Ethernet chip
+ * Register, bit, and structure definitions.
+ *
+ * Written by GAW with reference to the Clarkson Packet Driver code for this
+ * chip written by Russ Nelson and others.
+ */
+
+/*
+ * NOTE, the structure definitions in here are for reference only.
+ * We use integer offsets exclusively to access the i82586 data structures.
+ */
+
+
+#if 0
+/*
+ * This is the master configuration block.
+ * It tells the hardware where all the rest of the stuff is.
+ */
+struct __ie_sys_conf_ptr {
+ u_int16_t mbz; /* must be zero */
+ u_int8_t ie_bus_use; /* true if 8-bit only */
+ u_int8_t mbz2[5]; /* must be zero */
+ u_int32_t ie_iscp_ptr; /* 24-bit physaddr of ISCP */
+};
+#endif
+#define IE_SCP_SZ 12
+#define IE_SCP_BUS_USE(base) ((base) + 2)
+#define IE_SCP_ISCP(base) ((base) + 8)
+
+/*
+ * Note that this is wired in hardware; the SCP is always located here, no
+ * matter what.
+ */
+#define IE_SCP_ADDR 0xfffff4
+
+#if 0
+/*
+ * The tells the hardware where all the rest of the stuff is, too.
+ * FIXME: some of these should be re-commented after we figure out their
+ * REAL function.
+ */
+struct __ie_int_sys_conf_ptr {
+ u_int8_t ie_busy; // zeroed after init
+ u_int8_t mbz;
+ u_int16_t ie_scb_offset; // 16-bit physaddr of next struct
+ caddr_t ie_base; // 24-bit physaddr for all 16-bit vars
+};
+#endif
+#define IE_ISCP_SZ 8
+#define IE_ISCP_BUSY(base) ((base) + 0)
+#define IE_ISCP_SCB(base) ((base) + 2)
+#define IE_ISCP_BASE(base) ((base) + 4)
+
+#if 0
+/*
+ * This FINALLY tells the hardware what to do and where to put it.
+ */
+struct __ie_sys_ctl_block {
+ u_int16_t ie_status; // status word
+ u_int16_t ie_command; // command word
+ u_int16_t ie_command_list; // 16-pointer to command block list
+ u_int16_t ie_recv_list; // 16-pointer to receive frame list
+ u_int16_t ie_err_crc; // CRC errors
+ u_int16_t ie_err_align; // Alignment errors
+ u_int16_t ie_err_resource; // Resource errors
+ u_int16_t ie_err_overrun; // Overrun errors
+};
+#endif
+#define IE_SCB_SZ 16
+#define IE_SCB_STATUS(base) ((base) + 0)
+#define IE_SCB_CMD(base) ((base) + 2)
+#define IE_SCB_CMDLST(base) ((base) + 4)
+#define IE_SCB_RCVLST(base) ((base) + 6)
+#define IE_SCB_ERRCRC(base) ((base) + 8)
+#define IE_SCB_ERRALN(base) ((base) + 10)
+#define IE_SCB_ERRRES(base) ((base) + 12)
+#define IE_SCB_ERROVR(base) ((base) + 14)
+
+/* Command values */
+#define IE_RUC_MASK 0x0070 /* mask for RU command */
+#define IE_RUC_NOP 0 /* for completeness */
+#define IE_RUC_START 0x0010 /* start receive unit command */
+#define IE_RUC_RESUME 0x0020 /* resume a suspended receiver command */
+#define IE_RUC_SUSPEND 0x0030 /* suspend receiver command */
+#define IE_RUC_ABORT 0x0040 /* abort current receive operation */
+
+#define IE_CUC_MASK 0x0700 /* mask for CU command */
+#define IE_CUC_NOP 0 /* included for completeness */
+#define IE_CUC_START 0x0100 /* do-command command */
+#define IE_CUC_RESUME 0x0200 /* resume a suspended cmd list */
+#define IE_CUC_SUSPEND 0x0300 /* suspend current command */
+#define IE_CUC_ABORT 0x0400 /* abort current command */
+
+#define IE_ACK_COMMAND 0xf000 /* mask for ACK command */
+#define IE_ACK_CX 0x8000 /* ack IE_ST_CX */
+#define IE_ACK_FR 0x4000 /* ack IE_ST_FR */
+#define IE_ACK_CNA 0x2000 /* ack IE_ST_CNA */
+#define IE_ACK_RNR 0x1000 /* ack IE_ST_RNR */
+
+#define IE_ACTION_COMMAND(x) (((x) & IE_CUC_MASK) == IE_CUC_START)
+ /* is this command an action command? */
+
+/* Status values */
+#define IE_ST_WHENCE 0xf000 /* mask for cause of interrupt */
+#define IE_ST_CX 0x8000 /* command with I bit completed */
+#define IE_ST_FR 0x4000 /* frame received */
+#define IE_ST_CNA 0x2000 /* all commands completed */
+#define IE_ST_RNR 0x1000 /* receive not ready */
+
+#define IE_CUS_MASK 0x0700 /* mask for command unit status */
+#define IE_CUS_ACTIVE 0x0200 /* command unit is active */
+#define IE_CUS_SUSPEND 0x0100 /* command unit is suspended */
+
+#define IE_RUS_MASK 0x0070 /* mask for receiver unit status */
+#define IE_RUS_SUSPEND 0x0010 /* receiver is suspended */
+#define IE_RUS_NOSPACE 0x0020 /* receiver has no resources */
+#define IE_RUS_READY 0x0040 /* receiver is ready */
+
+#if 0
+/*
+ * This is filled in partially by the chip, partially by us.
+ */
+struct __ie_recv_frame_desc {
+ u_int16_t ie_fd_status; // status for this frame
+ u_int16_t ie_fd_last; // end of frame list flag
+ u_int16_t ie_fd_next; // 16-pointer to next RFD
+ u_int16_t ie_fd_buf_desc; // 16-pointer to list of buffer descs
+ struct __ie_en_addr dest; // destination ether
+ struct __ie_en_addr src; // source ether
+ u_int16_t ie_length; // 802 length/Ether type
+ u_short mbz; // must be zero
+};
+#endif
+#define IE_RFRAME_SZ 24
+#define IE_RFRAME_ADDR(base,i) ((base) + (i) * IE_RFRAME_SZ)
+#define IE_RFRAME_STATUS(b,i) (IE_RFRAME_ADDR(b,i) + 0)
+#define IE_RFRAME_LAST(b,i) (IE_RFRAME_ADDR(b,i) + 2)
+#define IE_RFRAME_NEXT(b,i) (IE_RFRAME_ADDR(b,i) + 4)
+#define IE_RFRAME_BUFDESC(b,i) (IE_RFRAME_ADDR(b,i) + 6)
+#define IE_RFRAME_EDST(b,i) (IE_RFRAME_ADDR(b,i) + 8)
+#define IE_RFRAME_ESRC(b,i) (IE_RFRAME_ADDR(b,i) + 14)
+#define IE_RFRAME_ELEN(b,i) (IE_RFRAME_ADDR(b,i) + 20)
+
+/* "last" bits */
+#define IE_FD_EOL 0x8000 /* last rfd in list */
+#define IE_FD_SUSP 0x4000 /* suspend RU after receipt */
+
+/* status field bits */
+#define IE_FD_COMPLETE 0x8000 /* frame is complete */
+#define IE_FD_BUSY 0x4000 /* frame is busy */
+#define IE_FD_OK 0x2000 /* frame is ok */
+#define IE_FD_CRC 0x0800 /* CRC error */
+#define IE_FD_ALGN 0x0400 /* Alignment error */
+#define IE_FD_RNR 0x0200 /* receiver out of resources here */
+#define IE_FD_OVR 0x0100 /* DMA overrun */
+#define IE_FD_SHORT 0x0080 /* Short frame */
+#define IE_FD_NOEOF 0x0040 /* no EOF (?) */
+#define IE_FD_ERRMASK /* all error bits */ \
+ (IE_FD_CRC|IE_FD_ALGN|IE_FD_RNR|IE_FD_OVR|IE_FD_SHORT|IE_FD_NOEOF)
+#define IE_FD_STATUSBITS \
+ "\20\20COMPLT\17BUSY\16OK\14CRC\13ALGN\12RNR\11OVR\10SHORT\7NOEOF"
+
+#if 0
+/*
+ * linked list of buffers...
+ */
+struct __ie_recv_buf_desc {
+ u_int16_t ie_rbd_status; // status for this buffer
+ u_int16_t ie_rbd_next; // 16-pointer to next RBD
+ caddr_t ie_rbd_buffer; // 24-pointer to buffer for this RBD
+ u_int16_t ie_rbd_length; // length of the buffer
+ u_int16_t mbz; // must be zero
+};
+#endif
+#define IE_RBD_SZ 12
+#define IE_RBD_ADDR(base,i) ((base) + (i) * IE_RBD_SZ)
+#define IE_RBD_STATUS(b,i) (IE_RBD_ADDR(b,i) + 0)
+#define IE_RBD_NEXT(b,i) (IE_RBD_ADDR(b,i) + 2)
+#define IE_RBD_BUFADDR(b,i) (IE_RBD_ADDR(b,i) + 4)
+#define IE_RBD_BUFLEN(b,i) (IE_RBD_ADDR(b,i) + 8)
+
+/* RBD status fields */
+#define IE_RBD_LAST 0x8000 /* last buffer */
+#define IE_RBD_USED 0x4000 /* this buffer has data */
+#define IE_RBD_CNTMASK 0x3fff /* byte count of buffer data */
+
+/* RDB `End Of List' flag; encoded in `buffer length' field */
+#define IE_RBD_EOL 0x8000 /* last buffer */
+
+
+#if 0
+/*
+ * All commands share this in common.
+ */
+struct __ie_cmd_common {
+ u_int16_t ie_cmd_status; // status of this command
+ u_int16_t ie_cmd_cmd; // command word
+ u_int16_t ie_cmd_link; // link to next command
+};
+#endif
+#define IE_CMD_COMMON_SZ 6
+#define IE_CMD_COMMON_STATUS(base) ((base) + 0)
+#define IE_CMD_COMMON_CMD(base) ((base) + 2)
+#define IE_CMD_COMMON_LINK(base) ((base) + 4)
+
+#define IE_STAT_COMPL 0x8000 /* command is completed */
+#define IE_STAT_BUSY 0x4000 /* command is running now */
+#define IE_STAT_OK 0x2000 /* command completed successfully */
+#define IE_STAT_ABORT 0x1000 /* command was aborted */
+
+#define IE_CMD_NOP 0x0000 /* NOP */
+#define IE_CMD_IASETUP 0x0001 /* initial address setup */
+#define IE_CMD_CONFIG 0x0002 /* configure command */
+#define IE_CMD_MCAST 0x0003 /* multicast setup command */
+#define IE_CMD_XMIT 0x0004 /* transmit command */
+#define IE_CMD_TDR 0x0005 /* time-domain reflectometer command */
+#define IE_CMD_DUMP 0x0006 /* dump command */
+#define IE_CMD_DIAGNOSE 0x0007 /* diagnostics command */
+
+#define IE_CMD_LAST 0x8000 /* this is the last command in the list */
+#define IE_CMD_SUSPEND 0x4000 /* suspend CU after this command */
+#define IE_CMD_INTR 0x2000 /* post an interrupt after completion */
+
+/*
+ * No-op commands; just like COMMON but "indexable"
+ */
+#define IE_CMD_NOP_SZ IE_CMD_COMMON_SZ
+#define IE_CMD_NOP_ADDR(base,i) ((base) + (i) * IE_CMD_NOP_SZ)
+#define IE_CMD_NOP_STATUS(b,i) (IE_CMD_NOP_ADDR(b,i) + 0)
+#define IE_CMD_NOP_CMD(b,i) (IE_CMD_NOP_ADDR(b,i) + 2)
+#define IE_CMD_NOP_LINK(b,i) (IE_CMD_NOP_ADDR(b,i) + 4)
+
+
+#if 0
+/*
+ * This is the command to transmit a frame.
+ */
+struct __ie_xmit_cmd {
+ struct __ie_cmd_common com; // common part
+#define __ie_xmit_status com.ie_cmd_status
+
+ u_int16_t ie_xmit_desc; // pointer to buffer descriptor
+ struct __ie_en_addr ie_xmit_addr; // destination address
+ u_int16_t ie_xmit_length; // 802.3 length/Ether type field
+};
+#endif
+#define IE_CMD_XMIT_SZ (IE_CMD_COMMON_SZ + 10)
+#define IE_CMD_XMIT_ADDR(base,i) ((base) + (i) * IE_CMD_XMIT_SZ)
+#define IE_CMD_XMIT_STATUS(b,i) \
+ (IE_CMD_XMIT_ADDR(b,i) + 0) /* == CMD_COMMON_STATUS */
+#define IE_CMD_XMIT_CMD(b,i) \
+ (IE_CMD_XMIT_ADDR(b,i) + 2) /* == CMD_COMMON_CMD */
+#define IE_CMD_XMIT_LINK(b,i) \
+ (IE_CMD_XMIT_ADDR(b,i) + 4) /* == CMD_COMMON_LINK */
+#define IE_CMD_XMIT_DESC(b,i) \
+ (IE_CMD_XMIT_ADDR(b,i) + IE_CMD_COMMON_SZ + 0)
+#define IE_CMD_XMIT_EADDR(b,i) \
+ (IE_CMD_XMIT_ADDR(b,i) + IE_CMD_COMMON_SZ + 2)
+#define IE_CMD_XMIT_LEN(b,i) \
+ (IE_CMD_XMIT_ADDR(b,i) + IE_CMD_COMMON_SZ + 8)
+
+#define IE_XS_MAXCOLL 0x000f /* number of collisions during transmit */
+#define IE_XS_EXCMAX 0x0020 /* exceeded maximum number of collisions */
+#define IE_XS_SQE 0x0040 /* SQE positive */
+#define IE_XS_DEFERRED 0x0080 /* transmission deferred */
+#define IE_XS_UNDERRUN 0x0100 /* DMA underrun */
+#define IE_XS_LOSTCTS 0x0200 /* Lost CTS */
+#define IE_XS_NOCARRIER 0x0400 /* No Carrier */
+#define IE_XS_LATECOLL 0x0800 /* Late collision */
+
+#if 0
+/*
+ * This is a buffer descriptor for a frame to be transmitted.
+ */
+struct __ie_xmit_buf {
+ u_int16_t ie_xmit_flags; // see below
+ u_int16_t ie_xmit_next; // 16-pointer to next desc
+ caddr_t ie_xmit_buf; // 24-pointer to the actual buffer
+};
+#endif
+#define IE_XBD_SZ 8
+#define IE_XBD_ADDR(base,i) ((base) + (i) * IE_XBD_SZ)
+#define IE_XBD_FLAGS(b,i) (IE_XBD_ADDR(b,i) + 0)
+#define IE_XBD_NEXT(b,i) (IE_XBD_ADDR(b,i) + 2)
+#define IE_XBD_BUF(b,i) (IE_XBD_ADDR(b,i) + 4)
+
+#define IE_TBD_EOL 0x8000 /* this TBD is the last one */
+#define IE_TBD_CNTMASK 0x3fff /* The rest of the `flags' word
+ is actually the length. */
+
+
+#if 0
+/*
+ * Multicast setup command.
+ */
+struct __ie_mcast_cmd {
+ struct __ie_cmd_common com; // common part
+#define ie_mcast_status com.ie_cmd_status
+
+ // size (in bytes) of multicast addresses
+ u_short ie_mcast_bytes;
+ struct __ie_en_addr ie_mcast_addrs[IE_MAXMCAST + 1];// space for them
+};
+#endif
+#define IE_CMD_MCAST_SZ (IE_CMD_COMMON_SZ + 2 /* + XXX */)
+#define IE_CMD_MCAST_BYTES(base) ((base) + IE_CMD_COMMON_SZ + 0)
+#define IE_CMD_MCAST_MADDR(base) ((base) + IE_CMD_COMMON_SZ + 2)
+
+#if 0
+/*
+ * Time Domain Reflectometer command.
+ */
+struct __ie_tdr_cmd {
+ struct __ie_cmd_common com; // common part
+#define ie_tdr_status com.ie_cmd_status
+ u_short ie_tdr_time; // error bits and time
+};
+#endif
+#define IE_CMD_TDR_SZ (IE_CMD_COMMON_SZ + 2)
+#define IE_CMD_TDR_TIME(base) ((base) + IE_CMD_COMMON_SZ + 0)
+
+#define IE_TDR_SUCCESS 0x8000 /* TDR succeeded without error */
+#define IE_TDR_XCVR 0x4000 /* detected a transceiver problem */
+#define IE_TDR_OPEN 0x2000 /* detected an incorrect termination ("open") */
+#define IE_TDR_SHORT 0x1000 /* TDR detected a short circuit */
+#define IE_TDR_TIME 0x07ff /* mask for reflection time */
+
+#if 0
+/*
+ * Initial Address Setup command
+ */
+struct __ie_iasetup_cmd {
+ struct __ie_cmd_common com;
+#define ie_iasetup_status com.ie_cmd_status
+ struct __ie_en_addr ie_address;
+};
+#endif
+#define IE_CMD_IAS_SZ (IE_CMD_COMMON_SZ + 6)
+#define IE_CMD_IAS_EADDR(base) ((base) + IE_CMD_COMMON_SZ + 0)
+
+#if 0
+/*
+ * Configuration command
+ */
+struct __ie_config_cmd {
+ struct __ie_cmd_common com; // common part
+#define ie_config_status com.ie_cmd_status
+
+ u_int8_t ie_config_count; // byte count (0x0c)
+ u_int8_t ie_fifo; // fifo (8)
+ u_int8_t ie_save_bad; // save bad frames (0x40)
+ u_int8_t ie_addr_len; // address length (0x2e) (AL-LOC == 1)
+ u_int8_t ie_priority; // priority and backoff (0x0)
+ u_int8_t ie_ifs; // inter-frame spacing (0x60)
+ u_int8_t ie_slot_low; // slot time, LSB (0x0)
+ u_int8_t ie_slot_high; // slot time, MSN, and retries (0xf2)
+ u_int8_t ie_promisc; // 1 if promiscuous, else 0
+ u_int8_t ie_crs_cdt; // CSMA/CD parameters (0x0)
+ u_int8_t ie_min_len; // min frame length (0x40)
+ u_int8_t ie_junk; // stuff for 82596 (0xff)
+};
+#endif
+#define IE_CMD_CFG_SZ (IE_CMD_COMMON_SZ + 12)
+#define IE_CMD_CFG_CNT(base) ((base) + IE_CMD_COMMON_SZ + 0)
+#define IE_CMD_CFG_FIFO(base) ((base) + IE_CMD_COMMON_SZ + 1)
+#define IE_CMD_CFG_SAVEBAD(base) ((base) + IE_CMD_COMMON_SZ + 2)
+#define IE_CMD_CFG_ADDRLEN(base) ((base) + IE_CMD_COMMON_SZ + 3)
+#define IE_CMD_CFG_PRIORITY(base) ((base) + IE_CMD_COMMON_SZ + 4)
+#define IE_CMD_CFG_IFS(base) ((base) + IE_CMD_COMMON_SZ + 5)
+#define IE_CMD_CFG_SLOT_LOW(base) ((base) + IE_CMD_COMMON_SZ + 6)
+#define IE_CMD_CFG_SLOT_HIGH(base) ((base) + IE_CMD_COMMON_SZ + 7)
+#define IE_CMD_CFG_PROMISC(base) ((base) + IE_CMD_COMMON_SZ + 8)
+#define IE_CMD_CFG_CRSCDT(base) ((base) + IE_CMD_COMMON_SZ + 9)
+#define IE_CMD_CFG_MINLEN(base) ((base) + IE_CMD_COMMON_SZ + 10)
+#define IE_CMD_CFG_JUNK(base) ((base) + IE_CMD_COMMON_SZ + 11)
diff --git a/bsps/shared/net/if_dc.c b/bsps/shared/net/if_dc.c
new file mode 100644
index 0000000..0308d68
--- /dev/null
+++ b/bsps/shared/net/if_dc.c
@@ -0,0 +1,3844 @@
+/*
+ * Ported from FreeBSD --> RTEMS, december 03.
+ * Daron Chabot <daron@nucleus.usask.ca>
+ * -- only tested with i386 bsp.
+ * -- supports *one* card (until the PCI & IRQ APIs get sorted out ;-))
+ *
+ *
+ * Copyright (c) 1997, 1998, 1999
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ *
+ * $FreeBSD: src/sys/pci/if_dc.c,v 1.9.2.41 2003/03/05 18:42:33 njl Exp $
+ */
+
+/*
+ * DEC "tulip" clone ethernet driver. Supports the DEC/Intel 21143
+ * series chips and several workalikes including the following:
+ *
+ * Macronix 98713/98715/98725/98727/98732 PMAC (www.macronix.com)
+ * Macronix/Lite-On 82c115 PNIC II (www.macronix.com)
+ * Lite-On 82c168/82c169 PNIC (www.litecom.com)
+ * ASIX Electronics AX88140A (www.asix.com.tw)
+ * ASIX Electronics AX88141 (www.asix.com.tw)
+ * ADMtek AL981 (www.admtek.com.tw)
+ * ADMtek AN985 (www.admtek.com.tw)
+ * Davicom DM9100, DM9102, DM9102A (www.davicom8.com)
+ * Accton EN1217 (www.accton.com)
+ * Conexant LANfinity (www.conexant.com)
+ *
+ * Datasheets for the 21143 are available at developer.intel.com.
+ * Datasheets for the clone parts can be found at their respective sites.
+ * (Except for the PNIC; see www.freebsd.org/~wpaul/PNIC/pnic.ps.gz.)
+ * The PNIC II is essentially a Macronix 98715A chip; the only difference
+ * worth noting is that its multicast hash table is only 128 bits wide
+ * instead of 512.
+ *
+ * Written by Bill Paul <wpaul@ee.columbia.edu>
+ * Electrical Engineering Department
+ * Columbia University, New York City
+ */
+
+/*
+ * The Intel 21143 is the successor to the DEC 21140. It is basically
+ * the same as the 21140 but with a few new features. The 21143 supports
+ * three kinds of media attachments:
+ *
+ * o MII port, for 10Mbps and 100Mbps support and NWAY
+ * autonegotiation provided by an external PHY.
+ * o SYM port, for symbol mode 100Mbps support.
+ * o 10baseT port.
+ * o AUI/BNC port.
+ *
+ * The 100Mbps SYM port and 10baseT port can be used together in
+ * combination with the internal NWAY support to create a 10/100
+ * autosensing configuration.
+ *
+ * Note that not all tulip workalikes are handled in this driver: we only
+ * deal with those which are relatively well behaved. The Winbond is
+ * handled separately due to its different register offsets and the
+ * special handling needed for its various bugs. The PNIC is handled
+ * here, but I'm not thrilled about it.
+ *
+ * All of the workalike chips use some form of MII transceiver support
+ * with the exception of the Macronix chips, which also have a SYM port.
+ * The ASIX AX88140A is also documented to have a SYM port, but all
+ * the cards I've seen use an MII transceiver, probably because the
+ * AX88140A doesn't support internal NWAY.
+ */
+
+/*
+ * This driver only supports architectures with the new style
+ * exception processing. The following checks try to keep this
+ * from being compiled on systems which can't support this driver.
+ */
+
+#include <machine/rtems-bsd-kernel-space.h>
+
+#if defined(DRIVER_SUPPORTED)
+ #undef DRIVER_SUPPORTED
+#endif
+
+#if defined(__i386__)
+ #define DRIVER_SUPPORTED
+#endif
+
+#if defined(__PPC__)
+ #define DRIVER_SUPPORTED
+#endif
+
+#include <bsp.h>
+
+#if !defined(PCI_DRAM_OFFSET)
+ #undef DRIVER_SUPPORTED
+#endif
+
+#if defined(DRIVER_SUPPORTED) /* this covers the file "globally"... */
+#include <rtems/pci.h>
+
+#include <rtems/error.h>
+#include <errno.h>
+#include <rtems/rtems_bsdnet.h>
+
+#include <net/if_types.h>
+
+#include <sys/param.h>
+#include <sys/sockio.h>
+#include <sys/socket.h>
+#include <sys/mbuf.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <bsp.h>
+
+/* moved to cpukit/include/rtems in CVS current ! */
+/*#include "if_media.h" */
+/*#include "pci.h" */
+#include <net/if_media.h>
+#include <rtems/pci.h>
+/*
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+*/
+
+#include <vm/vm.h> /* for vtophys */
+
+
+#define vtophys(p) (uintptr_t)(p)
+
+/*
+#include <net/if_arp.h>
+#include <net/if_vlan_var.h>
+#include <net/bpf.h>
+*/
+
+#if 0
+#include <vm/pmap.h> /* for vtophys */
+#include <machine/clock.h> /* for DELAY */
+#include <machine/bus_pio.h>
+#include <machine/bus_memio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#endif
+#include <dev/mii/mii.h>
+#if 0
+#include <dev/mii/miivar.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+#endif
+
+/* NOTE: use mem space mapping (for now ...)
+#define DC_USEIOSPACE
+*/
+
+#ifdef __alpha__
+#define SRM_MEDIA
+#endif
+
+#include <bsp/irq.h>
+
+
+#include <libchip/if_dcreg.h>
+
+
+#define DRIVER_PREFIX "tl"
+#define NDRIVER 1
+#define IRQ_EVENT RTEMS_EVENT_13 /* Ha ... */
+static struct dc_softc dc_softc_devs[NDRIVER];
+
+#define UNUSED
+
+#if 0
+/* "controller miibus0" required. See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD: src/sys/pci/if_dc.c,v 1.9.2.41 2003/03/05 18:42:33 njl Exp $";
+#endif
+
+#endif
+
+
+/*
+ * Various supported device vendors/types and their names.
+ * NOTE:
+ * -----
+ * Only the "ADMtek AN985" has been tested under RTEMS !!!
+ */
+static struct dc_type dc_devs[] = {
+ { DC_VENDORID_DEC, DC_DEVICEID_21143,
+ "Intel 21143 10/100BaseTX", 0 },
+ { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9009,
+ "Davicom DM9009 10/100BaseTX", 0 },
+ { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9100,
+ "Davicom DM9100 10/100BaseTX", 0 },
+ { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9102,
+ "Davicom DM9102 10/100BaseTX", 0 },
+ { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9102,
+ "Davicom DM9102A 10/100BaseTX", 0 },
+ { DC_VENDORID_ADMTEK, DC_DEVICEID_AL981,
+ "ADMtek AL981 10/100BaseTX", 0 },
+ { DC_VENDORID_ADMTEK, DC_DEVICEID_AN985,
+ "ADMtek AN985 10/100BaseTX", 0 },
+ { DC_VENDORID_ASIX, DC_DEVICEID_AX88140A,
+ "ASIX AX88140A 10/100BaseTX", 0 },
+ { DC_VENDORID_ASIX, DC_DEVICEID_AX88140A,
+ "ASIX AX88141 10/100BaseTX", 0 },
+ { DC_VENDORID_MX, DC_DEVICEID_98713,
+ "Macronix 98713 10/100BaseTX", 0 },
+ { DC_VENDORID_MX, DC_DEVICEID_98713,
+ "Macronix 98713A 10/100BaseTX", 0 },
+ { DC_VENDORID_CP, DC_DEVICEID_98713_CP,
+ "Compex RL100-TX 10/100BaseTX", 0 },
+ { DC_VENDORID_CP, DC_DEVICEID_98713_CP,
+ "Compex RL100-TX 10/100BaseTX", 0 },
+ { DC_VENDORID_MX, DC_DEVICEID_987x5,
+ "Macronix 98715/98715A 10/100BaseTX", 0 },
+ { DC_VENDORID_MX, DC_DEVICEID_987x5,
+ "Macronix 98715AEC-C 10/100BaseTX", 0 },
+ { DC_VENDORID_MX, DC_DEVICEID_987x5,
+ "Macronix 98725 10/100BaseTX", 0 },
+ { DC_VENDORID_MX, DC_DEVICEID_98727,
+ "Macronix 98727/98732 10/100BaseTX", 0 },
+ { DC_VENDORID_LO, DC_DEVICEID_82C115,
+ "LC82C115 PNIC II 10/100BaseTX", 0 },
+ { DC_VENDORID_LO, DC_DEVICEID_82C168,
+ "82c168 PNIC 10/100BaseTX", 0 },
+ { DC_VENDORID_LO, DC_DEVICEID_82C168,
+ "82c169 PNIC 10/100BaseTX", 0 },
+ { DC_VENDORID_ACCTON, DC_DEVICEID_EN1217,
+ "Accton EN1217 10/100BaseTX", 0 },
+ { DC_VENDORID_ACCTON, DC_DEVICEID_EN2242,
+ "Accton EN2242 MiniPCI 10/100BaseTX", 0 },
+ { DC_VENDORID_CONEXANT, DC_DEVICEID_RS7112,
+ "Conexant LANfinity MiniPCI 10/100BaseTX", 0 },
+ { 0, 0, NULL, 0 }
+};
+
+#if 0
+static int dc_probe __P((device_t));
+static int dc_attach __P((device_t));
+static int dc_detach __P((device_t));
+static int dc_suspend __P((device_t));
+static int dc_resume __P((device_t));
+static void dc_shutdown __P((device_t));
+static void dc_acpi __P((device_t));
+#endif
+
+static struct dc_type *dc_devtype(int);
+static int dc_newbuf(struct dc_softc *, int, struct mbuf *);
+static int dc_encap(struct dc_softc *, struct mbuf *,
+ u_int32_t *);
+static int dc_coal(struct dc_softc *, struct mbuf **);
+static void dc_pnic_rx_bug_war(struct dc_softc *, int);
+static int dc_rx_resync(struct dc_softc *);
+static void dc_rxeof(struct dc_softc *);
+static void dc_txeof(struct dc_softc *);
+/*static void dc_tick((void *));*/
+static void dc_tx_underrun(struct dc_softc *);
+static void dc_intr(void *);
+static void dc_daemon(void *);
+static void dc_start(struct ifnet *);
+static int dc_ioctl(struct ifnet *, ioctl_command_t, caddr_t);
+static void dc_init(void *);
+static void dc_stop(struct dc_softc *);
+static void dc_watchdog(struct ifnet *);
+#if 0
+static int dc_ifmedia_upd __P((struct ifnet *));
+static void dc_ifmedia_sts __P((struct ifnet *, struct ifmediareq *));
+#endif
+
+static void dc_delay(struct dc_softc *);
+static void dc_eeprom_idle(struct dc_softc *);
+static void dc_eeprom_putbyte(struct dc_softc *, int);
+static void dc_eeprom_getword(struct dc_softc *, int, u_int16_t *);
+static void dc_eeprom_getword_pnic(struct dc_softc *, int, u_int16_t *);
+static void dc_eeprom_width(struct dc_softc *);
+static void dc_read_eeprom(struct dc_softc *, caddr_t, int,int, int);
+
+#if 0
+static void dc_mii_writebit __P((struct dc_softc *, int));
+static int dc_mii_readbit __P((struct dc_softc *));
+static void dc_mii_sync __P((struct dc_softc *));
+static void dc_mii_send __P((struct dc_softc *, u_int32_t, int));
+static int dc_mii_readreg __P((struct dc_softc *, struct dc_mii_frame *));
+static int dc_mii_writereg __P((struct dc_softc *, struct dc_mii_frame *));
+static int dc_miibus_readreg __P((device_t, int, int));
+static int dc_miibus_writereg __P((device_t, int, int, int));
+static void dc_miibus_statchg __P((device_t));
+static void dc_miibus_mediainit __P((device_t));
+#endif
+
+static void dc_setcfg(struct dc_softc *, int);
+static u_int32_t dc_crc_le(struct dc_softc *, caddr_t);
+#ifndef UNUSED
+static u_int32_t dc_crc_be(caddr_t);
+#endif
+static void dc_setfilt_21143(struct dc_softc *);
+static void dc_setfilt_asix(struct dc_softc *);
+static void dc_setfilt_admtek(struct dc_softc *);
+static void dc_setfilt(struct dc_softc *);
+static void dc_reset(struct dc_softc *);
+static int dc_list_rx_init(struct dc_softc *);
+static int dc_list_tx_init(struct dc_softc *);
+static void dc_read_srom(struct dc_softc *, int);
+static void dc_parse_21143_srom(struct dc_softc *);
+static void dc_apply_fixup(struct dc_softc *, int);
+
+#if 0
+static void dc_decode_leaf_sia __P((struct dc_softc *,
+ struct dc_eblock_sia *));
+static void dc_decode_leaf_mii __P((struct dc_softc *,
+ struct dc_eblock_mii *));
+static void dc_decode_leaf_sym __P((struct dc_softc *,
+ struct dc_eblock_sym *));
+#endif
+
+
+#ifdef DC_USEIOSPACE
+#define DC_RES SYS_RES_IOPORT
+#define DC_RID DC_PCI_CFBIO
+#else
+#define DC_RES SYS_RES_MEMORY
+#define DC_RID DC_PCI_CFBMA
+#endif
+
+#if 0
+static device_method_t dc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, dc_probe),
+ DEVMETHOD(device_attach, dc_attach),
+ DEVMETHOD(device_detach, dc_detach),
+ DEVMETHOD(device_suspend, dc_suspend),
+ DEVMETHOD(device_resume, dc_resume),
+ DEVMETHOD(device_shutdown, dc_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, dc_miibus_readreg),
+ DEVMETHOD(miibus_writereg, dc_miibus_writereg),
+ DEVMETHOD(miibus_statchg, dc_miibus_statchg),
+ DEVMETHOD(miibus_mediainit, dc_miibus_mediainit),
+
+ { 0, 0 }
+};
+
+static driver_t dc_driver = {
+ "dc",
+ dc_methods,
+ sizeof(struct dc_softc)
+};
+
+static devclass_t dc_devclass;
+#endif
+
+
+#ifdef __i386__
+static int dc_quick=1;
+#if 0
+SYSCTL_INT(_hw, OID_AUTO, dc_quick, CTLFLAG_RW,
+ &dc_quick,0,"do not mdevget in dc driver");
+#endif
+#endif
+
+#if 0
+DRIVER_MODULE(if_dc, pci, dc_driver, dc_devclass, 0, 0);
+DRIVER_MODULE(miibus, dc, miibus_driver, miibus_devclass, 0, 0);
+#endif
+
+
+#define DC_SETBIT(sc, reg, x) \
+ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | (x))
+
+#define DC_CLRBIT(sc, reg, x) \
+ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~(x))
+
+#define SIO_SET(x) DC_SETBIT(sc, DC_SIO, (x))
+#define SIO_CLR(x) DC_CLRBIT(sc, DC_SIO, (x))
+
+
+/* XXX Fixme: rtems_bsp_delay( ) for the pc386 BSP (at least)
+ * needs work... see pc386/include/bsp.h.
+ * I have "a" solution, utilizing the 2nd i8254 timer,
+ * if anyone is interested (first timer is used for clock_tick ISR)...
+ */
+#ifdef __i386__
+extern void Wait_X_ms( unsigned int );
+#define DELAY(n) Wait_X_ms( (unsigned int)((n)/100) )
+#else
+#define DELAY(n) rtems_bsp_delay(n)
+#endif
+
+
+static void dc_delay(sc)
+ struct dc_softc *sc;
+{
+ int idx;
+
+ for (idx = (300 / 33) + 1; idx > 0; idx--)
+ CSR_READ_4(sc, DC_BUSCTL);
+}
+
+static void dc_eeprom_width(sc)
+ struct dc_softc *sc;
+{
+ int i;
+
+ /* Force EEPROM to idle state. */
+ dc_eeprom_idle(sc);
+
+ /* Enter EEPROM access mode. */
+ CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ);
+ dc_delay(sc);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS);
+ dc_delay(sc);
+
+ for (i = 3; i--;) {
+ if (6 & (1 << i))
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_DATAIN);
+ else
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_DATAIN);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ }
+
+ for (i = 1; i <= 12; i++) {
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ if (!(CSR_READ_4(sc, DC_SIO) & DC_SIO_EE_DATAOUT)) {
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ break;
+ }
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ }
+
+ /* Turn off EEPROM access mode. */
+ dc_eeprom_idle(sc);
+
+ if (i < 4 || i > 12)
+ sc->dc_romwidth = 6;
+ else
+ sc->dc_romwidth = i;
+
+ /* Enter EEPROM access mode. */
+ CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ);
+ dc_delay(sc);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS);
+ dc_delay(sc);
+
+ /* Turn off EEPROM access mode. */
+ dc_eeprom_idle(sc);
+}
+
+static void dc_eeprom_idle(sc)
+ struct dc_softc *sc;
+{
+ register int i;
+
+ CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ);
+ dc_delay(sc);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS);
+ dc_delay(sc);
+
+ for (i = 0; i < 25; i++) {
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ }
+
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CS);
+ dc_delay(sc);
+ CSR_WRITE_4(sc, DC_SIO, 0x00000000);
+
+ return;
+}
+
+/*
+ * Send a read command and address to the EEPROM, check for ACK.
+ */
+static void dc_eeprom_putbyte(sc, addr)
+ struct dc_softc *sc;
+ int addr;
+{
+ register int d, i;
+
+ d = DC_EECMD_READ >> 6;
+ for (i = 3; i--; ) {
+ if (d & (1 << i))
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_DATAIN);
+ else
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_DATAIN);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ }
+
+ /*
+ * Feed in each bit and strobe the clock.
+ */
+ for (i = sc->dc_romwidth; i--;) {
+ if (addr & (1 << i)) {
+ SIO_SET(DC_SIO_EE_DATAIN);
+ } else {
+ SIO_CLR(DC_SIO_EE_DATAIN);
+ }
+ dc_delay(sc);
+ SIO_SET(DC_SIO_EE_CLK);
+ dc_delay(sc);
+ SIO_CLR(DC_SIO_EE_CLK);
+ dc_delay(sc);
+ }
+
+ return;
+}
+
+/*
+ * Read a word of data stored in the EEPROM at address 'addr.'
+ * The PNIC 82c168/82c169 has its own non-standard way to read
+ * the EEPROM.
+ */
+static void dc_eeprom_getword_pnic(sc, addr, dest)
+ struct dc_softc *sc;
+ int addr;
+ u_int16_t *dest;
+{
+ register int i;
+ u_int32_t r;
+
+ CSR_WRITE_4(sc, DC_PN_SIOCTL, DC_PN_EEOPCODE_READ|addr);
+
+ for (i = 0; i < DC_TIMEOUT; i++) {
+ DELAY(1);
+ r = CSR_READ_4(sc, DC_SIO);
+ if (!(r & DC_PN_SIOCTL_BUSY)) {
+ *dest = (u_int16_t)(r & 0xFFFF);
+ return;
+ }
+ }
+
+ return;
+}
+
+/*
+ * Read a word of data stored in the EEPROM at address 'addr.'
+ */
+static void dc_eeprom_getword(sc, addr, dest)
+ struct dc_softc *sc;
+ int addr;
+ u_int16_t *dest;
+{
+ register int i;
+ u_int16_t word = 0;
+
+ /* Force EEPROM to idle state. */
+ dc_eeprom_idle(sc);
+
+ /* Enter EEPROM access mode. */
+ CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ);
+ dc_delay(sc);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS);
+ dc_delay(sc);
+
+ /*
+ * Send address of word we want to read.
+ */
+ dc_eeprom_putbyte(sc, addr);
+
+ /*
+ * Start reading bits from EEPROM.
+ */
+ for (i = 0x8000; i; i >>= 1) {
+ SIO_SET(DC_SIO_EE_CLK);
+ dc_delay(sc);
+ if (CSR_READ_4(sc, DC_SIO) & DC_SIO_EE_DATAOUT)
+ word |= i;
+ dc_delay(sc);
+ SIO_CLR(DC_SIO_EE_CLK);
+ dc_delay(sc);
+ }
+
+ /* Turn off EEPROM access mode. */
+ dc_eeprom_idle(sc);
+
+ *dest = word;
+
+ return;
+}
+
+/*
+ * Read a sequence of words from the EEPROM.
+ */
+static void dc_read_eeprom(sc, dest, off, cnt, swap)
+ struct dc_softc *sc;
+ caddr_t dest;
+ int off;
+ int cnt;
+ int swap;
+{
+ int i;
+ u_int16_t word = 0, *ptr;
+
+ for (i = 0; i < cnt; i++) {
+ if (DC_IS_PNIC(sc))
+ dc_eeprom_getword_pnic(sc, off + i, &word);
+ else
+ dc_eeprom_getword(sc, off + i, &word);
+ ptr = (u_int16_t *)(dest + (i * 2));
+ if (swap)
+ *ptr = ntohs(word);
+ else
+ *ptr = word;
+ }
+
+ return;
+}
+
+
+#if 0
+/*
+ * The following two routines are taken from the Macronix 98713
+ * Application Notes pp.19-21.
+ */
+/*
+ * Write a bit to the MII bus.
+ */
+static void dc_mii_writebit(sc, bit)
+ struct dc_softc *sc;
+ int bit;
+{
+ if (bit)
+ CSR_WRITE_4(sc, DC_SIO,
+ DC_SIO_ROMCTL_WRITE|DC_SIO_MII_DATAOUT);
+ else
+ CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_WRITE);
+
+ DC_SETBIT(sc, DC_SIO, DC_SIO_MII_CLK);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_MII_CLK);
+
+ return;
+}
+
+/*
+ * Read a bit from the MII bus.
+ */
+static int dc_mii_readbit(sc)
+ struct dc_softc *sc;
+{
+ CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_READ|DC_SIO_MII_DIR);
+ CSR_READ_4(sc, DC_SIO);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_MII_CLK);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_MII_CLK);
+ if (CSR_READ_4(sc, DC_SIO) & DC_SIO_MII_DATAIN)
+ return(1);
+
+ return(0);
+}
+
+/*
+ * Sync the PHYs by setting data bit and strobing the clock 32 times.
+ */
+static void dc_mii_sync(sc)
+ struct dc_softc *sc;
+{
+ register int i;
+
+ CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_WRITE);
+
+ for (i = 0; i < 32; i++)
+ dc_mii_writebit(sc, 1);
+
+ return;
+}
+
+/*
+ * Clock a series of bits through the MII.
+ */
+static void dc_mii_send(sc, bits, cnt)
+ struct dc_softc *sc;
+ u_int32_t bits;
+ int cnt;
+{
+ int i;
+
+ for (i = (0x1 << (cnt - 1)); i; i >>= 1)
+ dc_mii_writebit(sc, bits & i);
+}
+
+/*
+ * Read an PHY register through the MII.
+ */
+static int dc_mii_readreg(sc, frame)
+ struct dc_softc *sc;
+ struct dc_mii_frame *frame;
+
+{
+ int i, ack, s;
+
+
+ /*
+ * Set up frame for RX.
+ */
+ frame->mii_stdelim = DC_MII_STARTDELIM;
+ frame->mii_opcode = DC_MII_READOP;
+ frame->mii_turnaround = 0;
+ frame->mii_data = 0;
+
+ /*
+ * Sync the PHYs.
+ */
+ dc_mii_sync(sc);
+
+ /*
+ * Send command/address info.
+ */
+ dc_mii_send(sc, frame->mii_stdelim, 2);
+ dc_mii_send(sc, frame->mii_opcode, 2);
+ dc_mii_send(sc, frame->mii_phyaddr, 5);
+ dc_mii_send(sc, frame->mii_regaddr, 5);
+
+#ifdef notdef
+ /* Idle bit */
+ dc_mii_writebit(sc, 1);
+ dc_mii_writebit(sc, 0);
+#endif
+
+ /* Check for ack */
+ ack = dc_mii_readbit(sc);
+
+ /*
+ * Now try reading data bits. If the ack failed, we still
+ * need to clock through 16 cycles to keep the PHY(s) in sync.
+ */
+ if (ack) {
+ for(i = 0; i < 16; i++) {
+ dc_mii_readbit(sc);
+ }
+ goto fail;
+ }
+
+ for (i = 0x8000; i; i >>= 1) {
+ if (!ack) {
+ if (dc_mii_readbit(sc))
+ frame->mii_data |= i;
+ }
+ }
+
+fail:
+
+ dc_mii_writebit(sc, 0);
+ dc_mii_writebit(sc, 0);
+
+
+ if (ack)
+ return(1);
+ return(0);
+}
+
+/*
+ * Write to a PHY register through the MII.
+ */
+static int dc_mii_writereg(sc, frame)
+ struct dc_softc *sc;
+ struct dc_mii_frame *frame;
+
+{
+ int s;
+
+ /*
+ * Set up frame for TX.
+ */
+
+ frame->mii_stdelim = DC_MII_STARTDELIM;
+ frame->mii_opcode = DC_MII_WRITEOP;
+ frame->mii_turnaround = DC_MII_TURNAROUND;
+
+ /*
+ * Sync the PHYs.
+ */
+ dc_mii_sync(sc);
+
+ dc_mii_send(sc, frame->mii_stdelim, 2);
+ dc_mii_send(sc, frame->mii_opcode, 2);
+ dc_mii_send(sc, frame->mii_phyaddr, 5);
+ dc_mii_send(sc, frame->mii_regaddr, 5);
+ dc_mii_send(sc, frame->mii_turnaround, 2);
+ dc_mii_send(sc, frame->mii_data, 16);
+
+ /* Idle bit. */
+ dc_mii_writebit(sc, 0);
+ dc_mii_writebit(sc, 0);
+
+
+ return(0);
+}
+
+static int dc_miibus_readreg(dev, phy, reg)
+ device_t dev;
+ int phy, reg;
+{
+ struct dc_mii_frame frame;
+ struct dc_softc *sc;
+ int i, rval, phy_reg = 0;
+
+ sc = device_get_softc(dev);
+ bzero((char *)&frame, sizeof(frame));
+
+ /*
+ * Note: both the AL981 and AN985 have internal PHYs,
+ * however the AL981 provides direct access to the PHY
+ * registers while the AN985 uses a serial MII interface.
+ * The AN985's MII interface is also buggy in that you
+ * can read from any MII address (0 to 31), but only address 1
+ * behaves normally. To deal with both cases, we pretend
+ * that the PHY is at MII address 1.
+ */
+ if (DC_IS_ADMTEK(sc) && phy != DC_ADMTEK_PHYADDR)
+ return(0);
+
+ /*
+ * Note: the ukphy probes of the RS7112 report a PHY at
+ * MII address 0 (possibly HomePNA?) and 1 (ethernet)
+ * so we only respond to correct one.
+ */
+ if (DC_IS_CONEXANT(sc) && phy != DC_CONEXANT_PHYADDR)
+ return(0);
+
+ if (sc->dc_pmode != DC_PMODE_MII) {
+ if (phy == (MII_NPHY - 1)) {
+ switch(reg) {
+ case MII_BMSR:
+ /*
+ * Fake something to make the probe
+ * code think there's a PHY here.
+ */
+ return(BMSR_MEDIAMASK);
+ break;
+ case MII_PHYIDR1:
+ if (DC_IS_PNIC(sc))
+ return(DC_VENDORID_LO);
+ return(DC_VENDORID_DEC);
+ break;
+ case MII_PHYIDR2:
+ if (DC_IS_PNIC(sc))
+ return(DC_DEVICEID_82C168);
+ return(DC_DEVICEID_21143);
+ break;
+ default:
+ return(0);
+ break;
+ }
+ } else
+ return(0);
+ }
+
+ if (DC_IS_PNIC(sc)) {
+ CSR_WRITE_4(sc, DC_PN_MII, DC_PN_MIIOPCODE_READ |
+ (phy << 23) | (reg << 18));
+ for (i = 0; i < DC_TIMEOUT; i++) {
+ DELAY(1);
+ rval = CSR_READ_4(sc, DC_PN_MII);
+ if (!(rval & DC_PN_MII_BUSY)) {
+ rval &= 0xFFFF;
+ return(rval == 0xFFFF ? 0 : rval);
+ }
+ }
+ return(0);
+ }
+
+ if (DC_IS_COMET(sc)) {
+ switch(reg) {
+ case MII_BMCR:
+ phy_reg = DC_AL_BMCR;
+ break;
+ case MII_BMSR:
+ phy_reg = DC_AL_BMSR;
+ break;
+ case MII_PHYIDR1:
+ phy_reg = DC_AL_VENID;
+ break;
+ case MII_PHYIDR2:
+ phy_reg = DC_AL_DEVID;
+ break;
+ case MII_ANAR:
+ phy_reg = DC_AL_ANAR;
+ break;
+ case MII_ANLPAR:
+ phy_reg = DC_AL_LPAR;
+ break;
+ case MII_ANER:
+ phy_reg = DC_AL_ANER;
+ break;
+ default:
+ printk("dc%d: phy_read: bad phy register %x\n",
+ sc->dc_unit, reg);
+ return(0);
+ break;
+ }
+
+ rval = CSR_READ_4(sc, phy_reg) & 0x0000FFFF;
+
+ if (rval == 0xFFFF)
+ return(0);
+ return(rval);
+ }
+
+ frame.mii_phyaddr = phy;
+ frame.mii_regaddr = reg;
+ if (sc->dc_type == DC_TYPE_98713) {
+ phy_reg = CSR_READ_4(sc, DC_NETCFG);
+ CSR_WRITE_4(sc, DC_NETCFG, phy_reg & ~DC_NETCFG_PORTSEL);
+ }
+ dc_mii_readreg(sc, &frame);
+ if (sc->dc_type == DC_TYPE_98713)
+ CSR_WRITE_4(sc, DC_NETCFG, phy_reg);
+
+ return(frame.mii_data);
+}
+
+static int dc_miibus_writereg(dev, phy, reg, data)
+ device_t dev;
+ int phy, reg, data;
+{
+ struct dc_softc *sc;
+ struct dc_mii_frame frame;
+ int i, phy_reg = 0;
+
+ sc = device_get_softc(dev);
+ bzero((char *)&frame, sizeof(frame));
+
+ if (DC_IS_ADMTEK(sc) && phy != DC_ADMTEK_PHYADDR)
+ return(0);
+
+ if (DC_IS_CONEXANT(sc) && phy != DC_CONEXANT_PHYADDR)
+ return(0);
+
+ if (DC_IS_PNIC(sc)) {
+ CSR_WRITE_4(sc, DC_PN_MII, DC_PN_MIIOPCODE_WRITE |
+ (phy << 23) | (reg << 10) | data);
+ for (i = 0; i < DC_TIMEOUT; i++) {
+ if (!(CSR_READ_4(sc, DC_PN_MII) & DC_PN_MII_BUSY))
+ break;
+ }
+ return(0);
+ }
+
+ if (DC_IS_COMET(sc)) {
+ switch(reg) {
+ case MII_BMCR:
+ phy_reg = DC_AL_BMCR;
+ break;
+ case MII_BMSR:
+ phy_reg = DC_AL_BMSR;
+ break;
+ case MII_PHYIDR1:
+ phy_reg = DC_AL_VENID;
+ break;
+ case MII_PHYIDR2:
+ phy_reg = DC_AL_DEVID;
+ break;
+ case MII_ANAR:
+ phy_reg = DC_AL_ANAR;
+ break;
+ case MII_ANLPAR:
+ phy_reg = DC_AL_LPAR;
+ break;
+ case MII_ANER:
+ phy_reg = DC_AL_ANER;
+ break;
+ default:
+ printk("dc%d: phy_write: bad phy register %x\n",
+ sc->dc_unit, reg);
+ return(0);
+ break;
+ }
+
+ CSR_WRITE_4(sc, phy_reg, data);
+ return(0);
+ }
+
+ frame.mii_phyaddr = phy;
+ frame.mii_regaddr = reg;
+ frame.mii_data = data;
+
+ if (sc->dc_type == DC_TYPE_98713) {
+ phy_reg = CSR_READ_4(sc, DC_NETCFG);
+ CSR_WRITE_4(sc, DC_NETCFG, phy_reg & ~DC_NETCFG_PORTSEL);
+ }
+ dc_mii_writereg(sc, &frame);
+ if (sc->dc_type == DC_TYPE_98713)
+ CSR_WRITE_4(sc, DC_NETCFG, phy_reg);
+
+ return(0);
+}
+
+static void dc_miibus_statchg(dev)
+ device_t dev;
+{
+ struct dc_softc *sc;
+ struct mii_data *mii;
+ struct ifmedia *ifm;
+
+ sc = device_get_softc(dev);
+ if (DC_IS_ADMTEK(sc))
+ return;
+
+ mii = device_get_softc(sc->dc_miibus);
+ ifm = &mii->mii_media;
+ if (DC_IS_DAVICOM(sc) &&
+ IFM_SUBTYPE(ifm->ifm_media) == IFM_homePNA) {
+ dc_setcfg(sc, ifm->ifm_media);
+ sc->dc_if_media = ifm->ifm_media;
+ } else {
+ dc_setcfg(sc, mii->mii_media_active);
+ sc->dc_if_media = mii->mii_media_active;
+ }
+
+ return;
+}
+
+/*
+ * Special support for DM9102A cards with HomePNA PHYs. Note:
+ * with the Davicom DM9102A/DM9801 eval board that I have, it seems
+ * to be impossible to talk to the management interface of the DM9801
+ * PHY (its MDIO pin is not connected to anything). Consequently,
+ * the driver has to just 'know' about the additional mode and deal
+ * with it itself. *sigh*
+ */
+static void dc_miibus_mediainit(dev)
+ device_t dev;
+{
+ struct dc_softc *sc;
+ struct mii_data *mii;
+ struct ifmedia *ifm;
+ int rev;
+
+ rev = pci_read_config(dev, DC_PCI_CFRV, 4) & 0xFF;
+
+ sc = device_get_softc(dev);
+ mii = device_get_softc(sc->dc_miibus);
+ ifm = &mii->mii_media;
+
+ if (DC_IS_DAVICOM(sc) && rev >= DC_REVISION_DM9102A)
+ ifmedia_add(ifm, IFM_ETHER|IFM_homePNA, 0, NULL);
+
+ return;
+}
+#endif
+
+#define DC_POLY 0xEDB88320
+#define DC_BITS_512 9
+#define DC_BITS_128 7
+#define DC_BITS_64 6
+
+static u_int32_t dc_crc_le(sc, addr)
+ struct dc_softc *sc;
+ caddr_t addr;
+{
+ u_int32_t idx, bit, data, crc;
+
+ /* Compute CRC for the address value. */
+ crc = 0xFFFFFFFF; /* initial value */
+
+ for (idx = 0; idx < 6; idx++) {
+ for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1)
+ crc = (crc >> 1) ^ (((crc ^ data) & 1) ? DC_POLY : 0);
+ }
+
+ /*
+ * The hash table on the PNIC II and the MX98715AEC-C/D/E
+ * chips is only 128 bits wide.
+ */
+ if (sc->dc_flags & DC_128BIT_HASH)
+ return (crc & ((1 << DC_BITS_128) - 1));
+
+ /* The hash table on the MX98715BEC is only 64 bits wide. */
+ if (sc->dc_flags & DC_64BIT_HASH)
+ return (crc & ((1 << DC_BITS_64) - 1));
+
+ return (crc & ((1 << DC_BITS_512) - 1));
+}
+
+#ifndef UNUSED
+/*
+ * Calculate CRC of a multicast group address, return the lower 6 bits.
+ */
+static u_int32_t dc_crc_be(addr)
+ caddr_t addr;
+{
+ u_int32_t crc, carry;
+ int i, j;
+ u_int8_t c;
+
+ /* Compute CRC for the address value. */
+ crc = 0xFFFFFFFF; /* initial value */
+
+ for (i = 0; i < 6; i++) {
+ c = *(addr + i);
+ for (j = 0; j < 8; j++) {
+ carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01);
+ crc <<= 1;
+ c >>= 1;
+ if (carry)
+ crc = (crc ^ 0x04c11db6) | carry;
+ }
+ }
+
+ /* return the filter bit position */
+ return((crc >> 26) & 0x0000003F);
+}
+#endif
+
+/*
+ * 21143-style RX filter setup routine. Filter programming is done by
+ * downloading a special setup frame into the TX engine. 21143, Macronix,
+ * PNIC, PNIC II and Davicom chips are programmed this way.
+ *
+ * We always program the chip using 'hash perfect' mode, i.e. one perfect
+ * address (our node address) and a 512-bit hash filter for multicast
+ * frames. We also sneak the broadcast address into the hash filter since
+ * we need that too.
+ */
+void dc_setfilt_21143(sc)
+ struct dc_softc *sc;
+{
+ struct dc_desc *sframe;
+ u_int32_t h, *sp;
+ /*struct ifmultiaddr *ifma;*/
+ struct ifnet *ifp;
+ int i;
+ u_int16_t *ac_enaddr;
+
+ ifp = &sc->arpcom.ac_if;
+
+ i = sc->dc_cdata.dc_tx_prod;
+ DC_INC(sc->dc_cdata.dc_tx_prod, DC_TX_LIST_CNT);
+ sc->dc_cdata.dc_tx_cnt++;
+ sframe = &sc->dc_ldata->dc_tx_list[i];
+ sp = (u_int32_t *)&sc->dc_cdata.dc_sbuf;
+ bzero((char *)sp, DC_SFRAME_LEN);
+
+ sframe->dc_data = vtophys(&sc->dc_cdata.dc_sbuf);
+ sframe->dc_ctl = DC_SFRAME_LEN | DC_TXCTL_SETUP | DC_TXCTL_TLINK |
+ DC_FILTER_HASHPERF | DC_TXCTL_FINT;
+
+ sc->dc_cdata.dc_tx_chain[i] = (struct mbuf *)&sc->dc_cdata.dc_sbuf;
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
+
+ if (ifp->if_flags & IFF_ALLMULTI)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
+#if 0
+ for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL;
+ ifma = ifma->ifma_link.le_next) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = dc_crc_le(sc,
+ LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ sp[h >> 4] |= 1 << (h & 0xF);
+ }
+#endif
+
+ if (ifp->if_flags & IFF_BROADCAST) {
+ h = dc_crc_le(sc, (caddr_t)&etherbroadcastaddr);
+ sp[h >> 4] |= 1 << (h & 0xF);
+ }
+
+ /* Set our MAC address */
+ ac_enaddr = (u_int16_t *)sc->arpcom.ac_enaddr;
+ sp[39] = ac_enaddr[0];
+ sp[40] = ac_enaddr[1];
+ sp[41] = ac_enaddr[2];
+
+ sframe->dc_status = DC_TXSTAT_OWN;
+ CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF);
+
+ /*
+ * The PNIC takes an exceedingly long time to process its
+ * setup frame; wait 10ms after posting the setup frame
+ * before proceeding, just so it has time to swallow its
+ * medicine.
+ */
+ DELAY(10000);
+
+ ifp->if_timer = 5;
+
+ return;
+}
+
+void dc_setfilt_admtek(sc)
+ struct dc_softc *sc;
+{
+ struct ifnet *ifp;
+#if 0
+ int h = 0;
+ u_int32_t hashes[2] = { 0, 0 };
+ struct ifmultiaddr *ifma;
+#endif
+ u_int32_t *ac_enaddr;
+
+ ifp = &sc->arpcom.ac_if;
+
+ /* Init our MAC address */
+ ac_enaddr = (u_int32_t *)&sc->arpcom.ac_enaddr[0];
+ CSR_WRITE_4(sc, DC_AL_PAR0, *ac_enaddr);
+ ac_enaddr = (u_int32_t *)&sc->arpcom.ac_enaddr[4];
+ CSR_WRITE_4(sc, DC_AL_PAR1, *ac_enaddr);
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
+
+ if (ifp->if_flags & IFF_ALLMULTI)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
+
+ /* first, zot all the existing hash bits */
+ CSR_WRITE_4(sc, DC_AL_MAR0, 0);
+ CSR_WRITE_4(sc, DC_AL_MAR1, 0);
+
+#if 0
+ /*
+ * If we're already in promisc or allmulti mode, we
+ * don't have to bother programming the multicast filter.
+ */
+ if (ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI))
+ return;
+
+ /* now program new ones */
+ for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL;
+ ifma = ifma->ifma_link.le_next) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = dc_crc_be(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ if (h < 32)
+ hashes[0] |= (1 << h);
+ else
+ hashes[1] |= (1 << (h - 32));
+ }
+
+ CSR_WRITE_4(sc, DC_AL_MAR0, hashes[0]);
+ CSR_WRITE_4(sc, DC_AL_MAR1, hashes[1]);
+#endif
+ return;
+}
+
+void dc_setfilt_asix(sc)
+ struct dc_softc *sc;
+{
+ struct ifnet *ifp;
+#if 0
+ int h = 0;
+ u_int32_t hashes[2] = { 0, 0 };
+ struct ifmultiaddr *ifma;
+#endif
+ u_int32_t *ac_enaddr;
+
+ ifp = &sc->arpcom.ac_if;
+
+ /* Init our MAC address */
+ CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_PAR0);
+ ac_enaddr = (u_int32_t *)&sc->arpcom.ac_enaddr[0];
+ CSR_WRITE_4(sc, DC_AX_FILTDATA, *ac_enaddr);
+
+ CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_PAR1);
+
+ ac_enaddr = (u_int32_t *)&sc->arpcom.ac_enaddr[4];
+ CSR_WRITE_4(sc, DC_AX_FILTDATA, *ac_enaddr);
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
+
+ if (ifp->if_flags & IFF_ALLMULTI)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
+
+ /*
+ * The ASIX chip has a special bit to enable reception
+ * of broadcast frames.
+ */
+ if (ifp->if_flags & IFF_BROADCAST)
+ DC_SETBIT(sc, DC_NETCFG, DC_AX_NETCFG_RX_BROAD);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_AX_NETCFG_RX_BROAD);
+
+ /* first, zot all the existing hash bits */
+ CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR0);
+ CSR_WRITE_4(sc, DC_AX_FILTDATA, 0);
+ CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR1);
+ CSR_WRITE_4(sc, DC_AX_FILTDATA, 0);
+
+#if 0
+ /*
+ * If we're already in promisc or allmulti mode, we
+ * don't have to bother programming the multicast filter.
+ */
+ if (ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI))
+ return;
+
+ /* now program new ones */
+ for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL;
+ ifma = ifma->ifma_link.le_next) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = dc_crc_be(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ if (h < 32)
+ hashes[0] |= (1 << h);
+ else
+ hashes[1] |= (1 << (h - 32));
+ }
+
+ CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR0);
+ CSR_WRITE_4(sc, DC_AX_FILTDATA, hashes[0]);
+ CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR1);
+ CSR_WRITE_4(sc, DC_AX_FILTDATA, hashes[1]);
+#endif
+ return;
+}
+
+static void dc_setfilt(sc)
+ struct dc_softc *sc;
+{
+ if (DC_IS_INTEL(sc) || DC_IS_MACRONIX(sc) || DC_IS_PNIC(sc) ||
+ DC_IS_PNICII(sc) || DC_IS_DAVICOM(sc) || DC_IS_CONEXANT(sc))
+ dc_setfilt_21143(sc);
+
+ if (DC_IS_ASIX(sc))
+ dc_setfilt_asix(sc);
+
+ if (DC_IS_ADMTEK(sc))
+ dc_setfilt_admtek(sc);
+
+ return;
+}
+
+/*
+ * In order to fiddle with the
+ * 'full-duplex' and '100Mbps' bits in the netconfig register, we
+ * first have to put the transmit and/or receive logic in the idle state.
+ */
+static void dc_setcfg(sc, media)
+ struct dc_softc *sc;
+ int media;
+{
+ int i, restart = 0;
+ u_int32_t isr;
+
+ if (IFM_SUBTYPE(media) == IFM_NONE)
+ return;
+
+ if (CSR_READ_4(sc, DC_NETCFG) & (DC_NETCFG_TX_ON|DC_NETCFG_RX_ON)) {
+ restart = 1;
+ DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_TX_ON|DC_NETCFG_RX_ON));
+
+ for (i = 0; i < DC_TIMEOUT; i++) {
+ isr = CSR_READ_4(sc, DC_ISR);
+ if (isr & DC_ISR_TX_IDLE ||
+ (isr & DC_ISR_RX_STATE) == DC_RXSTATE_STOPPED)
+ break;
+ DELAY(10);
+ }
+
+ if (i == DC_TIMEOUT)
+ printk("dc%d: failed to force tx and "
+ "rx to idle state\n", sc->dc_unit);
+ }
+
+ if (IFM_SUBTYPE(media) == IFM_100_TX) {
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_SPEEDSEL);
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_HEARTBEAT);
+ if (sc->dc_pmode == DC_PMODE_MII) {
+ int watchdogreg;
+
+ if (DC_IS_INTEL(sc)) {
+ /* there's a write enable bit here that reads as 1 */
+ watchdogreg = CSR_READ_4(sc, DC_WATCHDOG);
+ watchdogreg &= ~DC_WDOG_CTLWREN;
+ watchdogreg |= DC_WDOG_JABBERDIS;
+ CSR_WRITE_4(sc, DC_WATCHDOG, watchdogreg);
+ } else {
+ DC_SETBIT(sc, DC_WATCHDOG, DC_WDOG_JABBERDIS);
+ }
+ DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_PCS|
+ DC_NETCFG_PORTSEL|DC_NETCFG_SCRAMBLER));
+ if (sc->dc_type == DC_TYPE_98713)
+ DC_SETBIT(sc, DC_NETCFG, (DC_NETCFG_PCS|
+ DC_NETCFG_SCRAMBLER));
+ if (!DC_IS_DAVICOM(sc))
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
+ DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF);
+ if (DC_IS_INTEL(sc))
+ dc_apply_fixup(sc, IFM_AUTO);
+ } else {
+ if (DC_IS_PNIC(sc)) {
+ DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_SPEEDSEL);
+ DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_100TX_LOOP);
+ DC_SETBIT(sc, DC_PN_NWAY, DC_PN_NWAY_SPEEDSEL);
+ }
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PCS);
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_SCRAMBLER);
+ if (DC_IS_INTEL(sc))
+ dc_apply_fixup(sc,
+ (media & IFM_GMASK) == IFM_FDX ?
+ IFM_100_TX|IFM_FDX : IFM_100_TX);
+ }
+ }
+
+ if (IFM_SUBTYPE(media) == IFM_10_T) {
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_SPEEDSEL);
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_HEARTBEAT);
+ if (sc->dc_pmode == DC_PMODE_MII) {
+ int watchdogreg;
+
+ /* there's a write enable bit here that reads as 1 */
+ if (DC_IS_INTEL(sc)) {
+ watchdogreg = CSR_READ_4(sc, DC_WATCHDOG);
+ watchdogreg &= ~DC_WDOG_CTLWREN;
+ watchdogreg |= DC_WDOG_JABBERDIS;
+ CSR_WRITE_4(sc, DC_WATCHDOG, watchdogreg);
+ } else {
+ DC_SETBIT(sc, DC_WATCHDOG, DC_WDOG_JABBERDIS);
+ }
+ DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_PCS|
+ DC_NETCFG_PORTSEL|DC_NETCFG_SCRAMBLER));
+ if (sc->dc_type == DC_TYPE_98713)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PCS);
+ if (!DC_IS_DAVICOM(sc))
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
+ DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF);
+ if (DC_IS_INTEL(sc))
+ dc_apply_fixup(sc, IFM_AUTO);
+ } else {
+ if (DC_IS_PNIC(sc)) {
+ DC_PN_GPIO_CLRBIT(sc, DC_PN_GPIO_SPEEDSEL);
+ DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_100TX_LOOP);
+ DC_CLRBIT(sc, DC_PN_NWAY, DC_PN_NWAY_SPEEDSEL);
+ }
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PCS);
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_SCRAMBLER);
+ if (DC_IS_INTEL(sc)) {
+ DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET);
+ DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF);
+ if ((media & IFM_GMASK) == IFM_FDX)
+ DC_SETBIT(sc, DC_10BTCTRL, 0x7F3D);
+ else
+ DC_SETBIT(sc, DC_10BTCTRL, 0x7F3F);
+ DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
+ DC_CLRBIT(sc, DC_10BTCTRL,
+ DC_TCTL_AUTONEGENBL);
+ dc_apply_fixup(sc,
+ (media & IFM_GMASK) == IFM_FDX ?
+ IFM_10_T|IFM_FDX : IFM_10_T);
+ DELAY(20000);
+ }
+ }
+ }
+
+#if 0
+ /*
+ * If this is a Davicom DM9102A card with a DM9801 HomePNA
+ * PHY and we want HomePNA mode, set the portsel bit to turn
+ * on the external MII port.
+ */
+ if (DC_IS_DAVICOM(sc)) {
+ if (IFM_SUBTYPE(media) == IFM_homePNA) {
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
+ sc->dc_link = 1;
+ } else {
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
+ }
+ }
+#endif
+
+ if ((media & IFM_GMASK) == IFM_FDX) {
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX);
+ if (sc->dc_pmode == DC_PMODE_SYM && DC_IS_PNIC(sc))
+ DC_SETBIT(sc, DC_PN_NWAY, DC_PN_NWAY_DUPLEX);
+ } else {
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX);
+ if (sc->dc_pmode == DC_PMODE_SYM && DC_IS_PNIC(sc))
+ DC_CLRBIT(sc, DC_PN_NWAY, DC_PN_NWAY_DUPLEX);
+ }
+
+ if (restart)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON|DC_NETCFG_RX_ON);
+
+ return;
+}
+
+static void dc_reset(sc)
+ struct dc_softc *sc;
+{
+ register int i;
+
+ DC_SETBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET);
+
+ for (i = 0; i < DC_TIMEOUT; i++) {
+ DELAY(10);
+ if (!(CSR_READ_4(sc, DC_BUSCTL) & DC_BUSCTL_RESET))
+ break;
+ }
+
+ if (DC_IS_ASIX(sc) || DC_IS_ADMTEK(sc) || DC_IS_CONEXANT(sc)) {
+ DELAY(10000);
+ DC_CLRBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET);
+ i = 0;
+ }
+
+ if (i == DC_TIMEOUT)
+ printk("dc%d: reset never completed!\n", sc->dc_unit);
+
+ /* Wait a little while for the chip to get its brains in order. */
+ DELAY(1000);
+
+ CSR_WRITE_4(sc, DC_IMR, 0x00000000);
+ CSR_WRITE_4(sc, DC_BUSCTL, 0x00000000);
+ CSR_WRITE_4(sc, DC_NETCFG, 0x00000000);
+
+ /*
+ * Bring the SIA out of reset. In some cases, it looks
+ * like failing to unreset the SIA soon enough gets it
+ * into a state where it will never come out of reset
+ * until we reset the whole chip again.
+ */
+ if (DC_IS_INTEL(sc)) {
+ DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
+ CSR_WRITE_4(sc, DC_10BTCTRL, 0);
+ CSR_WRITE_4(sc, DC_WATCHDOG, 0);
+ }
+
+ return;
+}
+
+static
+struct dc_type *dc_devtype( int unitnum )
+{
+ struct dc_type *t;
+ uint32_t rev;
+ int rc;
+
+
+ t = dc_devs;
+
+ while(t->dc_name != NULL) {
+ rc = pci_find_device(t->dc_vid, t->dc_did, \
+ (unitnum - 1), &t->dc_bus, &t->dc_dev, &t->dc_fun);
+ if (rc == PCIB_ERR_SUCCESS) {
+ /* Check the PCI revision */
+ /*pcib_conf_read32(t->dc_devsig, DC_PCI_CFRV, &rev); */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFRV, &rev);
+ rev &= 0xFF;
+
+ if (t->dc_did == DC_DEVICEID_98713 &&
+ rev >= DC_REVISION_98713A)
+ t++;
+ if (t->dc_did == DC_DEVICEID_98713_CP &&
+ rev >= DC_REVISION_98713A)
+ t++;
+ if (t->dc_did == DC_DEVICEID_987x5 &&
+ rev >= DC_REVISION_98715AEC_C)
+ t++;
+ if (t->dc_did == DC_DEVICEID_987x5 &&
+ rev >= DC_REVISION_98725)
+ t++;
+ if (t->dc_did == DC_DEVICEID_AX88140A &&
+ rev >= DC_REVISION_88141)
+ t++;
+ if (t->dc_did == DC_DEVICEID_82C168 &&
+ rev >= DC_REVISION_82C169)
+ t++;
+ if (t->dc_did == DC_DEVICEID_DM9102 &&
+ rev >= DC_REVISION_DM9102A)
+ t++;
+ return(t);
+ }
+ t++;
+ }
+
+ return(NULL);
+}
+
+#if 0
+/*
+ * Probe for a 21143 or clone chip. Check the PCI vendor and device
+ * IDs against our list and return a device name if we find a match.
+ * We do a little bit of extra work to identify the exact type of
+ * chip. The MX98713 and MX98713A have the same PCI vendor/device ID,
+ * but different revision IDs. The same is true for 98715/98715A
+ * chips and the 98725, as well as the ASIX and ADMtek chips. In some
+ * cases, the exact chip revision affects driver behavior.
+ */
+static int dc_probe(dev)
+ device_t dev;
+{
+ struct dc_type *t;
+
+ t = dc_devtype(dev);
+
+ if (t != NULL) {
+ device_set_desc(dev, t->dc_name);
+ return(0);
+ }
+
+ return(ENXIO);
+}
+
+
+static void dc_acpi(dev)
+ device_t dev;
+{
+ u_int32_t r, cptr;
+ int unit;
+
+ unit = device_get_unit(dev);
+
+ /* Find the location of the capabilities block */
+ cptr = pci_read_config(dev, DC_PCI_CCAP, 4) & 0xFF;
+
+ r = pci_read_config(dev, cptr, 4) & 0xFF;
+ if (r == 0x01) {
+
+ r = pci_read_config(dev, cptr + 4, 4);
+ if (r & DC_PSTATE_D3) {
+ u_int32_t iobase, membase, irq;
+
+ /* Save important PCI config data. */
+ iobase = pci_read_config(dev, DC_PCI_CFBIO, 4);
+ membase = pci_read_config(dev, DC_PCI_CFBMA, 4);
+ irq = pci_read_config(dev, DC_PCI_CFIT, 4);
+
+ /* Reset the power state. */
+ printk("dc%d: chip is in D%d power mode "
+ "-- setting to D0\n", unit, r & DC_PSTATE_D3);
+ r &= 0xFFFFFFFC;
+ pci_write_config(dev, cptr + 4, r, 4);
+
+ /* Restore PCI config data. */
+ pci_write_config(dev, DC_PCI_CFBIO, iobase, 4);
+ pci_write_config(dev, DC_PCI_CFBMA, membase, 4);
+ pci_write_config(dev, DC_PCI_CFIT, irq, 4);
+ }
+ }
+ return;
+}
+#endif
+
+
+static void dc_apply_fixup(sc, media)
+ struct dc_softc *sc;
+ int media;
+{
+ struct dc_mediainfo *m;
+ u_int8_t *p;
+ int i;
+ u_int32_t reg;
+
+ m = sc->dc_mi;
+
+ while (m != NULL) {
+ if (m->dc_media == media)
+ break;
+ m = m->dc_next;
+ }
+
+ if (m == NULL)
+ return;
+
+ for (i = 0, p = m->dc_reset_ptr; i < m->dc_reset_len; i++, p += 2) {
+ reg = (p[0] | (p[1] << 8)) << 16;
+ CSR_WRITE_4(sc, DC_WATCHDOG, reg);
+ }
+
+ for (i = 0, p = m->dc_gp_ptr; i < m->dc_gp_len; i++, p += 2) {
+ reg = (p[0] | (p[1] << 8)) << 16;
+ CSR_WRITE_4(sc, DC_WATCHDOG, reg);
+ }
+
+ return;
+}
+
+#if 0
+static void dc_decode_leaf_sia(sc, l)
+ struct dc_softc *sc;
+ struct dc_eblock_sia *l;
+{
+ struct dc_mediainfo *m;
+
+ m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT);
+ bzero(m, sizeof(struct dc_mediainfo));
+ if (l->dc_sia_code == DC_SIA_CODE_10BT)
+ m->dc_media = IFM_10_T;
+
+ if (l->dc_sia_code == DC_SIA_CODE_10BT_FDX)
+ m->dc_media = IFM_10_T|IFM_FDX;
+
+ if (l->dc_sia_code == DC_SIA_CODE_10B2)
+ m->dc_media = IFM_10_2;
+
+ if (l->dc_sia_code == DC_SIA_CODE_10B5)
+ m->dc_media = IFM_10_5;
+
+ m->dc_gp_len = 2;
+ m->dc_gp_ptr = (u_int8_t *)&l->dc_sia_gpio_ctl;
+
+ m->dc_next = sc->dc_mi;
+ sc->dc_mi = m;
+
+ sc->dc_pmode = DC_PMODE_SIA;
+
+ return;
+}
+
+static void dc_decode_leaf_sym(sc, l)
+ struct dc_softc *sc;
+ struct dc_eblock_sym *l;
+{
+ struct dc_mediainfo *m;
+
+ m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT);
+ bzero(m, sizeof(struct dc_mediainfo));
+ if (l->dc_sym_code == DC_SYM_CODE_100BT)
+ m->dc_media = IFM_100_TX;
+
+ if (l->dc_sym_code == DC_SYM_CODE_100BT_FDX)
+ m->dc_media = IFM_100_TX|IFM_FDX;
+
+ m->dc_gp_len = 2;
+ m->dc_gp_ptr = (u_int8_t *)&l->dc_sym_gpio_ctl;
+
+ m->dc_next = sc->dc_mi;
+ sc->dc_mi = m;
+
+ sc->dc_pmode = DC_PMODE_SYM;
+
+ return;
+}
+
+static void dc_decode_leaf_mii(sc, l)
+ struct dc_softc *sc;
+ struct dc_eblock_mii *l;
+{
+ u_int8_t *p;
+ struct dc_mediainfo *m;
+
+ m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT);
+ bzero(m, sizeof(struct dc_mediainfo));
+ /* We abuse IFM_AUTO to represent MII. */
+ m->dc_media = IFM_AUTO;
+ m->dc_gp_len = l->dc_gpr_len;
+
+ p = (u_int8_t *)l;
+ p += sizeof(struct dc_eblock_mii);
+ m->dc_gp_ptr = p;
+ p += 2 * l->dc_gpr_len;
+ m->dc_reset_len = *p;
+ p++;
+ m->dc_reset_ptr = p;
+
+ m->dc_next = sc->dc_mi;
+ sc->dc_mi = m;
+
+ return;
+}
+#endif
+
+static void dc_read_srom(sc, bits)
+ struct dc_softc *sc;
+ int bits;
+{
+ int size;
+
+ size = 2 << bits;
+ sc->dc_srom = malloc(size, M_DEVBUF, M_NOWAIT);
+ dc_read_eeprom(sc, (caddr_t)sc->dc_srom, 0, (size / 2), 0);
+}
+
+static void dc_parse_21143_srom(sc)
+ struct dc_softc *sc;
+{
+ struct dc_leaf_hdr *lhdr;
+ struct dc_eblock_hdr *hdr;
+ int i, loff;
+ char *ptr;
+
+ loff = sc->dc_srom[27];
+ lhdr = (struct dc_leaf_hdr *)&(sc->dc_srom[loff]);
+
+ ptr = (char *)lhdr;
+ ptr += sizeof(struct dc_leaf_hdr) - 1;
+ for (i = 0; i < lhdr->dc_mcnt; i++) {
+ hdr = (struct dc_eblock_hdr *)ptr;
+ switch(hdr->dc_type) {
+#if 0
+ case DC_EBLOCK_MII:
+ dc_decode_leaf_mii(sc, (struct dc_eblock_mii *)hdr);
+ break;
+ case DC_EBLOCK_SIA:
+ dc_decode_leaf_sia(sc, (struct dc_eblock_sia *)hdr);
+ break;
+ case DC_EBLOCK_SYM:
+ dc_decode_leaf_sym(sc, (struct dc_eblock_sym *)hdr);
+ break;
+#endif
+ default:
+ /* Don't care. Yet. */
+ break;
+ }
+ ptr += (hdr->dc_len & 0x7F);
+ ptr++;
+ }
+
+ return;
+}
+
+
+static void
+nop(const rtems_irq_connect_data* unused)
+{
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+int
+rtems_dc_driver_attach(struct rtems_bsdnet_ifconfig *config, int attaching)
+{
+ int rc;
+ u_char eaddr[ETHER_ADDR_LEN];
+
+ char *unitName;
+ int unitNumber;
+
+ uint32_t command;
+ struct dc_softc *sc;
+ struct ifnet *ifp;
+ struct dc_type *t;
+ uint32_t revision;
+ int mac_offset;
+ uint32_t value;
+
+ /*
+ * Get the instance number for the board we're going to configure
+ * from the user.
+ */
+ unitNumber = rtems_bsdnet_parse_driver_name(config, &unitName);
+ if( unitNumber < 0) {
+ return 0;
+ }
+ if( strcmp(unitName, DRIVER_PREFIX) ) {
+ printk("dec2114x : unit name '%s' not %s\n", \
+ unitName, DRIVER_PREFIX );
+ return 0;
+ }
+
+ sc = &dc_softc_devs[unitNumber - 1];
+ ifp = &sc->arpcom.ac_if;
+
+ if(ifp->if_softc != NULL) {
+ printk("dec2114x[%d]: unit number already in use.\n", \
+ unitNumber);
+ return (0);
+ }
+ memset(sc, 0, sizeof(struct dc_softc));
+
+ /*unit = device_get_unit(dev);*/
+ sc->dc_unit = unitNumber;
+ sc->dc_name = unitName;
+
+ /*
+ * Handle power management nonsense.
+ *
+ dc_acpi(dev);
+ */
+
+ /* Scan for dec2114x cards in pci config space */
+ if( (sc->dc_info = dc_devtype(unitNumber)) == NULL) {
+ printk("Can't find any dec2114x NICs in PCI space.\n");
+ return 0;
+ }
+ t = sc->dc_info;
+
+
+ /*
+ * Map control/status registers.
+ */
+ /*sig = sc->dc_info->dc_devsig; */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ PCI_COMMAND, &command);
+ /*pcib_conf_read32(sig, PCI_COMMAND, &command); */
+ command |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+ pci_write_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ PCI_COMMAND, command);
+ /*pcib_conf_write32(sig, PCI_COMMAND, command); */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ PCI_COMMAND, &command);
+ /*pcib_conf_read32(sig, PCI_COMMAND, &command); */
+
+#ifdef DC_USEIOSPACE
+ if (!(command & PCI_COMMAND_IO)) {
+ printk("dc%d: failed to enable I/O ports!\n", sc->dc_unit);
+ goto fail;
+ }
+#else
+ if (!(command & PCI_COMMAND_MEMORY)) {
+ printk("dc%d: failed to enable memory mapping!\n", sc->dc_unit);
+ goto fail;
+ }
+#endif
+
+#if 0
+ rid = DC_RID;
+ sc->dc_res = bus_alloc_resource(dev, DC_RES, &rid,
+ 0, ~0, 1, RF_ACTIVE);
+
+ if (sc->dc_res == NULL) {
+ printk("dc%d: couldn't map ports/memory\n", unit);
+ goto fail;
+ }
+ sc->dc_btag = rman_get_bustag(sc->dc_res);
+ sc->dc_bhandle = rman_get_bushandle(sc->dc_res);
+#endif
+
+ /* sc->membase is the address of the card's CSRs !!! */
+ /*pcib_conf_read32(sig, DC_PCI_CFBMA, &value); */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFBMA, &value);
+ sc->membase = value;
+
+ /* Allocate interrupt */
+ memset(&sc->irqInfo, 0, sizeof(rtems_irq_connect_data));
+ /*pcib_conf_read32(sig, DC_PCI_CFIT, &value); */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFIT, &value);
+
+ sc->irqInfo.name = value & 0xFF;
+ sc->irqInfo.hdl = (rtems_irq_hdl)dc_intr;
+ sc->irqInfo.handle = (void *)sc; /* new parameter */
+ sc->irqInfo.on = nop;
+ sc->irqInfo.off = nop;
+ sc->irqInfo.isOn = NULL;
+
+#ifdef BSP_SHARED_HANDLER_SUPPORT
+ rc = BSP_install_rtems_shared_irq_handler( &sc->irqInfo );
+#else
+ rc = BSP_install_rtems_irq_handler( &sc->irqInfo );
+#endif
+ if(!rc) {
+ rtems_panic("Can't install dec2114x irq handler.\n");
+ }
+
+
+#if 0
+ rid = 0;
+ sc->dc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+
+ if (sc->dc_irq == NULL) {
+ printk("dc%d: couldn't map interrupt\n", unit);
+ bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res);
+ goto fail;
+ }
+
+ error = bus_setup_intr(dev, sc->dc_irq, INTR_TYPE_NET,
+ dc_intr, sc, &sc->dc_intrhand);
+
+ if (error) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq);
+ bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res);
+ printk("dc%d: couldn't set up irq\n", unit);
+ goto fail;
+ }
+#endif
+
+
+ /* Need this info to decide on a chip type.
+ sc->dc_info = dc_devtype(dev);
+ */
+ /*pcib_conf_read32(sig, DC_PCI_CFRV, &revision); */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFRV, &revision);
+ revision &= 0x000000FF;
+
+ /* Get the eeprom width, but PNIC has diff eeprom */
+ if (sc->dc_info->dc_did != DC_DEVICEID_82C168)
+ dc_eeprom_width(sc);
+
+ switch(sc->dc_info->dc_did) {
+ case DC_DEVICEID_21143:
+ sc->dc_type = DC_TYPE_21143;
+ sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR;
+ sc->dc_flags |= DC_REDUCED_MII_POLL;
+ /* Save EEPROM contents so we can parse them later. */
+ dc_read_srom(sc, sc->dc_romwidth);
+ break;
+ case DC_DEVICEID_DM9009:
+ case DC_DEVICEID_DM9100:
+ case DC_DEVICEID_DM9102:
+ sc->dc_type = DC_TYPE_DM9102;
+ sc->dc_flags |= DC_TX_COALESCE|DC_TX_INTR_ALWAYS;
+ sc->dc_flags |= DC_REDUCED_MII_POLL|DC_TX_STORENFWD;
+ sc->dc_pmode = DC_PMODE_MII;
+ /* Increase the latency timer value. */
+ /*pcib_conf_read32(sig, DC_PCI_CFLT, &command); */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFLT, &command);
+ command &= 0xFFFF00FF;
+ command |= 0x00008000;
+ /*pcib_conf_write32(sig, DC_PCI_CFLT, command); */
+ pci_write_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFLT, command);
+ break;
+ case DC_DEVICEID_AL981:
+ sc->dc_type = DC_TYPE_AL981;
+ sc->dc_flags |= DC_TX_USE_TX_INTR;
+ sc->dc_flags |= DC_TX_ADMTEK_WAR;
+ sc->dc_pmode = DC_PMODE_MII;
+ dc_read_srom(sc, sc->dc_romwidth);
+ break;
+ case DC_DEVICEID_AN985:
+ case DC_DEVICEID_EN2242:
+ sc->dc_type = DC_TYPE_AN985;
+ sc->dc_flags |= DC_TX_USE_TX_INTR;
+ sc->dc_flags |= DC_TX_ADMTEK_WAR;
+ sc->dc_pmode = DC_PMODE_MII;
+ dc_read_srom(sc, sc->dc_romwidth);
+ break;
+ case DC_DEVICEID_98713:
+ case DC_DEVICEID_98713_CP:
+ if (revision < DC_REVISION_98713A) {
+ sc->dc_type = DC_TYPE_98713;
+ }
+ if (revision >= DC_REVISION_98713A) {
+ sc->dc_type = DC_TYPE_98713A;
+ sc->dc_flags |= DC_21143_NWAY;
+ }
+ sc->dc_flags |= DC_REDUCED_MII_POLL;
+ sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR;
+ break;
+ case DC_DEVICEID_987x5:
+ case DC_DEVICEID_EN1217:
+ /*
+ * Macronix MX98715AEC-C/D/E parts have only a
+ * 128-bit hash table. We need to deal with these
+ * in the same manner as the PNIC II so that we
+ * get the right number of bits out of the
+ * CRC routine.
+ */
+ if (revision >= DC_REVISION_98715AEC_C &&
+ revision < DC_REVISION_98725)
+ sc->dc_flags |= DC_128BIT_HASH;
+ sc->dc_type = DC_TYPE_987x5;
+ sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR;
+ sc->dc_flags |= DC_REDUCED_MII_POLL|DC_21143_NWAY;
+ break;
+ case DC_DEVICEID_98727:
+ sc->dc_type = DC_TYPE_987x5;
+ sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR;
+ sc->dc_flags |= DC_REDUCED_MII_POLL|DC_21143_NWAY;
+ break;
+ case DC_DEVICEID_82C115:
+ sc->dc_type = DC_TYPE_PNICII;
+ sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR|DC_128BIT_HASH;
+ sc->dc_flags |= DC_REDUCED_MII_POLL|DC_21143_NWAY;
+ break;
+ case DC_DEVICEID_82C168:
+ sc->dc_type = DC_TYPE_PNIC;
+ sc->dc_flags |= DC_TX_STORENFWD|DC_TX_INTR_ALWAYS;
+ sc->dc_flags |= DC_PNIC_RX_BUG_WAR;
+ sc->dc_pnic_rx_buf = malloc(DC_RXLEN * 5, M_DEVBUF, M_NOWAIT);
+ if (revision < DC_REVISION_82C169)
+ sc->dc_pmode = DC_PMODE_SYM;
+ break;
+ case DC_DEVICEID_AX88140A:
+ sc->dc_type = DC_TYPE_ASIX;
+ sc->dc_flags |= DC_TX_USE_TX_INTR|DC_TX_INTR_FIRSTFRAG;
+ sc->dc_flags |= DC_REDUCED_MII_POLL;
+ sc->dc_pmode = DC_PMODE_MII;
+ break;
+ case DC_DEVICEID_RS7112:
+ sc->dc_type = DC_TYPE_CONEXANT;
+ sc->dc_flags |= DC_TX_INTR_ALWAYS;
+ sc->dc_flags |= DC_REDUCED_MII_POLL;
+ sc->dc_pmode = DC_PMODE_MII;
+ dc_read_srom(sc, sc->dc_romwidth);
+ break;
+ default:
+ printk("dc%d: unknown device: %x\n", sc->dc_unit,
+ sc->dc_info->dc_did);
+ break;
+ }
+
+ /* Save the cache line size. */
+ if (DC_IS_DAVICOM(sc)) {
+ sc->dc_cachesize = 0;
+ }
+ else {
+ /*pcib_conf_read32(sig, DC_PCI_CFLT, &value); */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFLT, &value);
+ sc->dc_cachesize = (u_int8_t)(value & 0xFF);
+ }
+
+ /* Reset the adapter. */
+ dc_reset(sc);
+
+ /* Take 21143 out of snooze mode */
+ if (DC_IS_INTEL(sc)) {
+ /*pcib_conf_read32(sig, DC_PCI_CFDD, &command); */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFDD, &command);
+ command &= ~(DC_CFDD_SNOOZE_MODE|DC_CFDD_SLEEP_MODE);
+ /*pcib_conf_write32(sig, DC_PCI_CFDD, command); */
+ pci_write_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFDD, command);
+ }
+
+
+ /*
+ * Try to learn something about the supported media.
+ * We know that ASIX and ADMtek and Davicom devices
+ * will *always* be using MII media, so that's a no-brainer.
+ * The tricky ones are the Macronix/PNIC II and the
+ * Intel 21143.
+ */
+ if (DC_IS_INTEL(sc))
+ dc_parse_21143_srom(sc);
+ else if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) {
+ if (sc->dc_type == DC_TYPE_98713)
+ sc->dc_pmode = DC_PMODE_MII;
+ else
+ sc->dc_pmode = DC_PMODE_SYM;
+ } else if (!sc->dc_pmode)
+ sc->dc_pmode = DC_PMODE_MII;
+
+ /*
+ * Get station address from the EEPROM.
+ */
+ switch(sc->dc_type) {
+ case DC_TYPE_98713:
+ case DC_TYPE_98713A:
+ case DC_TYPE_987x5:
+ case DC_TYPE_PNICII:
+ dc_read_eeprom(sc, (caddr_t)&mac_offset,
+ (DC_EE_NODEADDR_OFFSET / 2), 1, 0);
+ dc_read_eeprom(sc, (caddr_t)&eaddr, (mac_offset / 2), 3, 0);
+ break;
+ case DC_TYPE_PNIC:
+ dc_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 1);
+ break;
+ case DC_TYPE_DM9102:
+ case DC_TYPE_21143:
+ case DC_TYPE_ASIX:
+ dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0);
+ break;
+ case DC_TYPE_AL981:
+ case DC_TYPE_AN985:
+ bcopy(&sc->dc_srom[DC_AL_EE_NODEADDR], (caddr_t)&eaddr,
+ ETHER_ADDR_LEN);
+ dc_read_eeprom(sc, (caddr_t)&eaddr, DC_AL_EE_NODEADDR, 3, 0);
+ break;
+ case DC_TYPE_CONEXANT:
+ bcopy(sc->dc_srom + DC_CONEXANT_EE_NODEADDR, &eaddr, 6);
+ break;
+ default:
+ dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0);
+ break;
+ }
+
+ /*
+ * A 21143 or clone chip was detected. Inform the world.
+ */
+ bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
+ printk("dc%d: MAC address -- %02x:%02x:%02x:%02x:%02x:%02x\n", \
+ sc->dc_unit,sc->arpcom.ac_enaddr[0], \
+ sc->arpcom.ac_enaddr[1], sc->arpcom.ac_enaddr[2], \
+ sc->arpcom.ac_enaddr[3], sc->arpcom.ac_enaddr[4], \
+ sc->arpcom.ac_enaddr[5]);
+
+
+ sc->dc_ldata = malloc(sizeof(struct dc_list_data), M_DEVBUF, M_NOWAIT);
+
+ if (sc->dc_ldata == NULL) {
+ printk("dc%d: no memory for list buffers!\n", sc->dc_unit);
+ if (sc->dc_pnic_rx_buf != NULL)
+ free(sc->dc_pnic_rx_buf, M_DEVBUF);
+#if 0
+ bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand);
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq);
+ bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res);
+#endif
+ goto fail;
+ }
+
+ bzero(sc->dc_ldata, sizeof(struct dc_list_data));
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_softc = sc;
+ ifp->if_unit = unitNumber; /*sc->dc_unit;*/
+ ifp->if_name = unitName; /*sc->dc_name;*/
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; /* | IFF_MULTICAST;*/
+ ifp->if_ioctl = dc_ioctl;
+ ifp->if_output = ether_output;
+ ifp->if_start = dc_start;
+ ifp->if_watchdog = dc_watchdog;
+ ifp->if_init = dc_init;
+ ifp->if_baudrate = 100000000;
+ ifp->if_snd.ifq_maxlen = DC_TX_LIST_CNT - 1;
+
+#if 0
+ /*
+ * Do MII setup. If this is a 21143, check for a PHY on the
+ * MII bus after applying any necessary fixups to twiddle the
+ * GPIO bits. If we don't end up finding a PHY, restore the
+ * old selection (SIA only or SIA/SYM) and attach the dcphy
+ * driver instead.
+ */
+ if (DC_IS_INTEL(sc)) {
+ dc_apply_fixup(sc, IFM_AUTO);
+ tmp = sc->dc_pmode;
+ sc->dc_pmode = DC_PMODE_MII;
+ }
+
+ error = mii_phy_probe(dev, &sc->dc_miibus,
+ dc_ifmedia_upd, dc_ifmedia_sts);
+
+ if (error && DC_IS_INTEL(sc)) {
+ sc->dc_pmode = tmp;
+ if (sc->dc_pmode != DC_PMODE_SIA)
+ sc->dc_pmode = DC_PMODE_SYM;
+ sc->dc_flags |= DC_21143_NWAY;
+ mii_phy_probe(dev, &sc->dc_miibus,
+ dc_ifmedia_upd, dc_ifmedia_sts);
+ /*
+ * For non-MII cards, we need to have the 21143
+ * drive the LEDs. Except there are some systems
+ * like the NEC VersaPro NoteBook PC which have no
+ * LEDs, and twiddling these bits has adverse effects
+ * on them. (I.e. you suddenly can't get a link.)
+ */
+ if (pci_read_config(dev, DC_PCI_CSID, 4) != 0x80281033)
+ sc->dc_flags |= DC_TULIP_LEDS;
+ error = 0;
+ }
+
+ if (error) {
+ printk("dc%d: MII without any PHY!\n", sc->dc_unit);
+ contigfree(sc->dc_ldata, sizeof(struct dc_list_data),
+ M_DEVBUF);
+ if (sc->dc_pnic_rx_buf != NULL)
+ free(sc->dc_pnic_rx_buf, M_DEVBUF);
+ bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand);
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq);
+ bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res);
+ error = ENXIO;
+ goto fail;
+ }
+#endif
+
+ /*
+ * Call MI attach routine.
+ */
+ if_attach(ifp);
+ ether_ifattach(ifp);
+ /*callout_handle_init(&sc->dc_stat_ch);*/
+
+ if (DC_IS_ADMTEK(sc)) {
+ /*
+ * Set automatic TX underrun recovery for the ADMtek chips
+ */
+ DC_SETBIT(sc, DC_AL_CR, DC_AL_CR_ATUR);
+ }
+
+ if(sc->daemontid == 0) {
+ sc->daemontid = rtems_bsdnet_newproc("decD",4096, \
+ dc_daemon,(void *)sc);
+ printk("dec[%d]: daemon process started\n", sc->dc_unit);
+ }
+
+ /*
+ * Tell the upper layer(s) we support long frames.
+ *
+ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
+ */
+
+#ifdef SRM_MEDIA /* only defined if __alpha__ is defined... */
+ sc->dc_srm_media = 0;
+
+ /* Remember the SRM console media setting */
+ if (DC_IS_INTEL(sc)) {
+ command = pci_read_config(dev, DC_PCI_CFDD, 4);
+ command &= ~(DC_CFDD_SNOOZE_MODE|DC_CFDD_SLEEP_MODE);
+ switch ((command >> 8) & 0xff) {
+ case 3:
+ sc->dc_srm_media = IFM_10_T;
+ break;
+ case 4:
+ sc->dc_srm_media = IFM_10_T | IFM_FDX;
+ break;
+ case 5:
+ sc->dc_srm_media = IFM_100_TX;
+ break;
+ case 6:
+ sc->dc_srm_media = IFM_100_TX | IFM_FDX;
+ break;
+ }
+ if (sc->dc_srm_media)
+ sc->dc_srm_media |= IFM_ACTIVE | IFM_ETHER;
+ }
+#endif
+
+
+fail:
+
+ return (1); /*(error);*/
+}
+
+#if 0
+static int dc_detach(dev)
+ device_t dev;
+{
+ struct dc_softc *sc;
+ struct ifnet *ifp;
+ int s;
+ struct dc_mediainfo *m;
+
+
+ sc = device_get_softc(dev);
+ ifp = &sc->arpcom.ac_if;
+
+ dc_stop(sc);
+ ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
+
+ bus_generic_detach(dev);
+ device_delete_child(dev, sc->dc_miibus);
+
+ bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand);
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq);
+ bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res);
+
+ contigfree(sc->dc_ldata, sizeof(struct dc_list_data), M_DEVBUF);
+ if (sc->dc_pnic_rx_buf != NULL)
+ free(sc->dc_pnic_rx_buf, M_DEVBUF);
+
+ while(sc->dc_mi != NULL) {
+ m = sc->dc_mi->dc_next;
+ free(sc->dc_mi, M_DEVBUF);
+ sc->dc_mi = m;
+ }
+ free(sc->dc_srom, M_DEVBUF);
+
+
+ return(0);
+}
+#endif
+
+
+/*
+ * Initialize the transmit descriptors.
+ */
+static int dc_list_tx_init(sc)
+ struct dc_softc *sc;
+{
+ struct dc_chain_data *cd;
+ struct dc_list_data *ld;
+ int i;
+
+ cd = &sc->dc_cdata;
+ ld = sc->dc_ldata;
+ for (i = 0; i < DC_TX_LIST_CNT; i++) {
+ if (i == (DC_TX_LIST_CNT - 1)) {
+ ld->dc_tx_list[i].dc_next =
+ vtophys(&ld->dc_tx_list[0]);
+ } else {
+ ld->dc_tx_list[i].dc_next =
+ vtophys(&ld->dc_tx_list[i + 1]);
+ }
+ cd->dc_tx_chain[i] = NULL;
+ ld->dc_tx_list[i].dc_data = 0;
+ ld->dc_tx_list[i].dc_ctl = 0;
+ }
+
+ cd->dc_tx_prod = cd->dc_tx_cons = cd->dc_tx_cnt = 0;
+
+ return(0);
+}
+
+
+/*
+ * Initialize the RX descriptors and allocate mbufs for them. Note that
+ * we arrange the descriptors in a closed ring, so that the last descriptor
+ * points back to the first.
+ */
+static int dc_list_rx_init(sc)
+ struct dc_softc *sc;
+{
+ struct dc_chain_data *cd;
+ struct dc_list_data *ld;
+ int i;
+
+ cd = &sc->dc_cdata;
+ ld = sc->dc_ldata;
+
+ for (i = 0; i < DC_RX_LIST_CNT; i++) {
+ if (dc_newbuf(sc, i, NULL) == ENOBUFS)
+ return(ENOBUFS);
+ if (i == (DC_RX_LIST_CNT - 1)) {
+ ld->dc_rx_list[i].dc_next =
+ vtophys(&ld->dc_rx_list[0]);
+ } else {
+ ld->dc_rx_list[i].dc_next =
+ vtophys(&ld->dc_rx_list[i + 1]);
+ }
+ }
+
+ cd->dc_rx_prod = 0;
+
+ return(0);
+}
+
+/*
+ * Initialize an RX descriptor and attach an MBUF cluster.
+ */
+static int dc_newbuf(sc, i, m)
+ struct dc_softc *sc;
+ int i;
+ struct mbuf *m;
+{
+ struct mbuf *m_new = NULL;
+ struct dc_desc *c;
+
+ c = &sc->dc_ldata->dc_rx_list[i];
+
+ if (m == NULL) {
+ MGETHDR(m_new, M_DONTWAIT, MT_DATA);
+ if (m_new == NULL)
+ return(ENOBUFS);
+
+ MCLGET(m_new, M_DONTWAIT);
+ if (!(m_new->m_flags & M_EXT)) {
+ m_freem(m_new);
+ return(ENOBUFS);
+ }
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ } else {
+ m_new = m;
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ m_new->m_data = m_new->m_ext.ext_buf;
+ }
+
+ m_adj(m_new, sizeof(u_int64_t));
+
+ /*
+ * If this is a PNIC chip, zero the buffer. This is part
+ * of the workaround for the receive bug in the 82c168 and
+ * 82c169 chips.
+ */
+ if (sc->dc_flags & DC_PNIC_RX_BUG_WAR)
+ bzero((char *)mtod(m_new, char *), m_new->m_len);
+
+ sc->dc_cdata.dc_rx_chain[i] = m_new;
+ c->dc_data = vtophys(mtod(m_new, caddr_t));
+ c->dc_ctl = DC_RXCTL_RLINK | DC_RXLEN;
+ c->dc_status = DC_RXSTAT_OWN;
+
+ return(0);
+}
+
+/*
+ * Grrrrr.
+ * The PNIC chip has a terrible bug in it that manifests itself during
+ * periods of heavy activity. The exact mode of failure if difficult to
+ * pinpoint: sometimes it only happens in promiscuous mode, sometimes it
+ * will happen on slow machines. The bug is that sometimes instead of
+ * uploading one complete frame during reception, it uploads what looks
+ * like the entire contents of its FIFO memory. The frame we want is at
+ * the end of the whole mess, but we never know exactly how much data has
+ * been uploaded, so salvaging the frame is hard.
+ *
+ * There is only one way to do it reliably, and it's disgusting.
+ * Here's what we know:
+ *
+ * - We know there will always be somewhere between one and three extra
+ * descriptors uploaded.
+ *
+ * - We know the desired received frame will always be at the end of the
+ * total data upload.
+ *
+ * - We know the size of the desired received frame because it will be
+ * provided in the length field of the status word in the last descriptor.
+ *
+ * Here's what we do:
+ *
+ * - When we allocate buffers for the receive ring, we bzero() them.
+ * This means that we know that the buffer contents should be all
+ * zeros, except for data uploaded by the chip.
+ *
+ * - We also force the PNIC chip to upload frames that include the
+ * ethernet CRC at the end.
+ *
+ * - We gather all of the bogus frame data into a single buffer.
+ *
+ * - We then position a pointer at the end of this buffer and scan
+ * backwards until we encounter the first non-zero byte of data.
+ * This is the end of the received frame. We know we will encounter
+ * some data at the end of the frame because the CRC will always be
+ * there, so even if the sender transmits a packet of all zeros,
+ * we won't be fooled.
+ *
+ * - We know the size of the actual received frame, so we subtract
+ * that value from the current pointer location. This brings us
+ * to the start of the actual received packet.
+ *
+ * - We copy this into an mbuf and pass it on, along with the actual
+ * frame length.
+ *
+ * The performance hit is tremendous, but it beats dropping frames all
+ * the time.
+ */
+
+#define DC_WHOLEFRAME (DC_RXSTAT_FIRSTFRAG|DC_RXSTAT_LASTFRAG)
+static void dc_pnic_rx_bug_war(sc, idx)
+ struct dc_softc *sc;
+ int idx;
+{
+ struct dc_desc *cur_rx;
+ struct dc_desc *c = NULL;
+ struct mbuf *m = NULL;
+ unsigned char *ptr;
+ int i, total_len;
+ u_int32_t rxstat = 0;
+
+ i = sc->dc_pnic_rx_bug_save;
+ cur_rx = &sc->dc_ldata->dc_rx_list[idx];
+ ptr = sc->dc_pnic_rx_buf;
+ bzero(ptr, sizeof(DC_RXLEN * 5));
+
+ /* Copy all the bytes from the bogus buffers. */
+ while (1) {
+ c = &sc->dc_ldata->dc_rx_list[i];
+ rxstat = c->dc_status;
+ m = sc->dc_cdata.dc_rx_chain[i];
+ bcopy(mtod(m, char *), ptr, DC_RXLEN);
+ ptr += DC_RXLEN;
+ /* If this is the last buffer, break out. */
+ if (i == idx || rxstat & DC_RXSTAT_LASTFRAG)
+ break;
+ dc_newbuf(sc, i, m);
+ DC_INC(i, DC_RX_LIST_CNT);
+ }
+
+ /* Find the length of the actual receive frame. */
+ total_len = DC_RXBYTES(rxstat);
+
+ /* Scan backwards until we hit a non-zero byte. */
+ while(*ptr == 0x00)
+ ptr--;
+#if 0
+ /* Round off. */
+ if ((uintptr_t)(ptr) & 0x3)
+ ptr -= 1;
+#endif
+
+ /* Now find the start of the frame. */
+ ptr -= total_len;
+ if (ptr < sc->dc_pnic_rx_buf)
+ ptr = sc->dc_pnic_rx_buf;
+
+ /*
+ * Now copy the salvaged frame to the last mbuf and fake up
+ * the status word to make it look like a successful
+ * frame reception.
+ */
+ dc_newbuf(sc, i, m);
+ bcopy(ptr, mtod(m, char *), total_len);
+ cur_rx->dc_status = rxstat | DC_RXSTAT_FIRSTFRAG;
+
+ return;
+}
+
+/*
+ * This routine searches the RX ring for dirty descriptors in the
+ * event that the rxeof routine falls out of sync with the chip's
+ * current descriptor pointer. This may happen sometimes as a result
+ * of a "no RX buffer available" condition that happens when the chip
+ * consumes all of the RX buffers before the driver has a chance to
+ * process the RX ring. This routine may need to be called more than
+ * once to bring the driver back in sync with the chip, however we
+ * should still be getting RX DONE interrupts to drive the search
+ * for new packets in the RX ring, so we should catch up eventually.
+ */
+static int dc_rx_resync(sc)
+ struct dc_softc *sc;
+{
+ int i, pos;
+ struct dc_desc *cur_rx;
+
+ pos = sc->dc_cdata.dc_rx_prod;
+
+ for (i = 0; i < DC_RX_LIST_CNT; i++) {
+ cur_rx = &sc->dc_ldata->dc_rx_list[pos];
+ if (!(cur_rx->dc_status & DC_RXSTAT_OWN))
+ break;
+ DC_INC(pos, DC_RX_LIST_CNT);
+ }
+
+ /* If the ring really is empty, then just return. */
+ if (i == DC_RX_LIST_CNT)
+ return(0);
+
+ /* We've fallen behing the chip: catch it. */
+ sc->dc_cdata.dc_rx_prod = pos;
+
+ return(EAGAIN);
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+static void dc_rxeof(sc)
+ struct dc_softc *sc;
+{
+ struct ether_header *eh;
+ struct mbuf *m;
+ struct ifnet *ifp;
+ struct dc_desc *cur_rx;
+ int i, total_len = 0;
+ u_int32_t rxstat;
+
+ ifp = &sc->arpcom.ac_if;
+ i = sc->dc_cdata.dc_rx_prod;
+
+ while(!(sc->dc_ldata->dc_rx_list[i].dc_status & DC_RXSTAT_OWN)) {
+
+#ifdef DEVICE_POLLING
+ if (ifp->if_ipending & IFF_POLLING) {
+ if (sc->rxcycles <= 0)
+ break;
+ sc->rxcycles--;
+ }
+#endif /* DEVICE_POLLING */
+ cur_rx = &sc->dc_ldata->dc_rx_list[i];
+ rxstat = cur_rx->dc_status;
+ m = sc->dc_cdata.dc_rx_chain[i];
+ total_len = DC_RXBYTES(rxstat);
+
+ if (sc->dc_flags & DC_PNIC_RX_BUG_WAR) {
+ if ((rxstat & DC_WHOLEFRAME) != DC_WHOLEFRAME) {
+ if (rxstat & DC_RXSTAT_FIRSTFRAG)
+ sc->dc_pnic_rx_bug_save = i;
+ if ((rxstat & DC_RXSTAT_LASTFRAG) == 0) {
+ DC_INC(i, DC_RX_LIST_CNT);
+ continue;
+ }
+ dc_pnic_rx_bug_war(sc, i);
+ rxstat = cur_rx->dc_status;
+ total_len = DC_RXBYTES(rxstat);
+ }
+ }
+
+ sc->dc_cdata.dc_rx_chain[i] = NULL;
+
+ /*
+ * If an error occurs, update stats, clear the
+ * status word and leave the mbuf cluster in place:
+ * it should simply get re-used next time this descriptor
+ * comes up in the ring. However, don't report long
+ * frames as errors since they could be vlans
+ */
+ if ((rxstat & DC_RXSTAT_RXERR)){
+ if (!(rxstat & DC_RXSTAT_GIANT) ||
+ (rxstat & (DC_RXSTAT_CRCERR | DC_RXSTAT_DRIBBLE |
+ DC_RXSTAT_MIIERE | DC_RXSTAT_COLLSEEN |
+ DC_RXSTAT_RUNT | DC_RXSTAT_DE))) {
+ ifp->if_ierrors++;
+ if (rxstat & DC_RXSTAT_COLLSEEN)
+ ifp->if_collisions++;
+ dc_newbuf(sc, i, m);
+ if (rxstat & DC_RXSTAT_CRCERR) {
+ DC_INC(i, DC_RX_LIST_CNT);
+ continue;
+ } else {
+ dc_init(sc);
+ return;
+ }
+ }
+ }
+
+ /* No errors; receive the packet. */
+ total_len -= ETHER_CRC_LEN;
+
+#ifdef __i386__
+ /*
+ * On the x86 we do not have alignment problems, so try to
+ * allocate a new buffer for the receive ring, and pass up
+ * the one where the packet is already, saving the expensive
+ * copy done in m_devget().
+ * If we are on an architecture with alignment problems, or
+ * if the allocation fails, then use m_devget and leave the
+ * existing buffer in the receive ring.
+ */
+ if (dc_quick && dc_newbuf(sc, i, NULL) == 0) {
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = total_len;
+ DC_INC(i, DC_RX_LIST_CNT);
+ } else
+#endif
+ {
+ struct mbuf *m0;
+
+ m0 = m_devget(mtod(m, char *) - ETHER_ALIGN,
+ total_len + ETHER_ALIGN, 0, ifp, NULL);
+ dc_newbuf(sc, i, m);
+ DC_INC(i, DC_RX_LIST_CNT);
+ if (m0 == NULL) {
+ ifp->if_ierrors++;
+ continue;
+ }
+ m_adj(m0, ETHER_ALIGN);
+ m = m0;
+ }
+
+ ifp->if_ipackets++;
+ eh = mtod(m, struct ether_header *);
+
+ /* Remove header from mbuf and pass it on. */
+ m_adj(m, sizeof(struct ether_header));
+ ether_input(ifp, eh, m);
+ }
+
+ sc->dc_cdata.dc_rx_prod = i;
+}
+
+/*
+ * A frame was downloaded to the chip. It's safe for us to clean up
+ * the list buffers.
+ */
+
+static void
+dc_txeof(sc)
+ struct dc_softc *sc;
+{
+ struct dc_desc *cur_tx = NULL;
+ struct ifnet *ifp;
+ int idx;
+
+ ifp = &sc->arpcom.ac_if;
+
+ /*
+ * Go through our tx list and free mbufs for those
+ * frames that have been transmitted.
+ */
+ idx = sc->dc_cdata.dc_tx_cons;
+ while(idx != sc->dc_cdata.dc_tx_prod) {
+ u_int32_t txstat;
+
+ cur_tx = &sc->dc_ldata->dc_tx_list[idx];
+ txstat = cur_tx->dc_status;
+
+ if (txstat & DC_TXSTAT_OWN)
+ break;
+
+ if (!(cur_tx->dc_ctl & DC_TXCTL_LASTFRAG) ||
+ cur_tx->dc_ctl & DC_TXCTL_SETUP) {
+ if (cur_tx->dc_ctl & DC_TXCTL_SETUP) {
+ /*
+ * Yes, the PNIC is so brain damaged
+ * that it will sometimes generate a TX
+ * underrun error while DMAing the RX
+ * filter setup frame. If we detect this,
+ * we have to send the setup frame again,
+ * or else the filter won't be programmed
+ * correctly.
+ */
+ if (DC_IS_PNIC(sc)) {
+ if (txstat & DC_TXSTAT_ERRSUM)
+ dc_setfilt(sc);
+ }
+ sc->dc_cdata.dc_tx_chain[idx] = NULL;
+ }
+ sc->dc_cdata.dc_tx_cnt--;
+ DC_INC(idx, DC_TX_LIST_CNT);
+ continue;
+ }
+
+ if (DC_IS_CONEXANT(sc)) {
+ /*
+ * For some reason Conexant chips like
+ * setting the CARRLOST flag even when
+ * the carrier is there. In CURRENT we
+ * have the same problem for Xircom
+ * cards !
+ */
+ if (/*sc->dc_type == DC_TYPE_21143 &&*/
+ sc->dc_pmode == DC_PMODE_MII &&
+ ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM|
+ DC_TXSTAT_NOCARRIER)))
+ txstat &= ~DC_TXSTAT_ERRSUM;
+ } else {
+ if (/*sc->dc_type == DC_TYPE_21143 &&*/
+ sc->dc_pmode == DC_PMODE_MII &&
+ ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM|
+ DC_TXSTAT_NOCARRIER|DC_TXSTAT_CARRLOST)))
+ txstat &= ~DC_TXSTAT_ERRSUM;
+ }
+
+ if (txstat & DC_TXSTAT_ERRSUM) {
+ ifp->if_oerrors++;
+ if (txstat & DC_TXSTAT_EXCESSCOLL)
+ ifp->if_collisions++;
+ if (txstat & DC_TXSTAT_LATECOLL)
+ ifp->if_collisions++;
+ if (!(txstat & DC_TXSTAT_UNDERRUN)) {
+ dc_init(sc);
+ return;
+ }
+ }
+
+ ifp->if_collisions += (txstat & DC_TXSTAT_COLLCNT) >> 3;
+
+ ifp->if_opackets++;
+ if (sc->dc_cdata.dc_tx_chain[idx] != NULL) {
+ m_freem(sc->dc_cdata.dc_tx_chain[idx]);
+ sc->dc_cdata.dc_tx_chain[idx] = NULL;
+ }
+
+ sc->dc_cdata.dc_tx_cnt--;
+ DC_INC(idx, DC_TX_LIST_CNT);
+ }
+
+ if (idx != sc->dc_cdata.dc_tx_cons) {
+ /* some buffers have been freed */
+ sc->dc_cdata.dc_tx_cons = idx;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+ ifp->if_timer = (sc->dc_cdata.dc_tx_cnt == 0) ? 0 : 5;
+
+ return;
+}
+
+
+#if 0
+static void dc_tick(xsc)
+ void *xsc;
+{
+ struct dc_softc *sc;
+ /*struct mii_data *mii;*/
+ struct ifnet *ifp;
+ int s;
+ u_int32_t r;
+
+
+ sc = xsc;
+ ifp = &sc->arpcom.ac_if;
+ mii = device_get_softc(sc->dc_miibus);
+
+ if (sc->dc_flags & DC_REDUCED_MII_POLL) {
+ if (sc->dc_flags & DC_21143_NWAY) {
+ r = CSR_READ_4(sc, DC_10BTSTAT);
+ if (IFM_SUBTYPE(mii->mii_media_active) ==
+ IFM_100_TX && (r & DC_TSTAT_LS100)) {
+ sc->dc_link = 0;
+ mii_mediachg(mii);
+ }
+ if (IFM_SUBTYPE(mii->mii_media_active) ==
+ IFM_10_T && (r & DC_TSTAT_LS10)) {
+ sc->dc_link = 0;
+ mii_mediachg(mii);
+ }
+ if (sc->dc_link == 0)
+ mii_tick(mii);
+ } else {
+ r = CSR_READ_4(sc, DC_ISR);
+ if ((r & DC_ISR_RX_STATE) == DC_RXSTATE_WAIT &&
+ sc->dc_cdata.dc_tx_cnt == 0)
+ mii_tick(mii);
+ if (!(mii->mii_media_status & IFM_ACTIVE))
+ sc->dc_link = 0;
+ }
+ } else
+ mii_tick(mii);
+
+ /*
+ * When the init routine completes, we expect to be able to send
+ * packets right away, and in fact the network code will send a
+ * gratuitous ARP the moment the init routine marks the interface
+ * as running. However, even though the MAC may have been initialized,
+ * there may be a delay of a few seconds before the PHY completes
+ * autonegotiation and the link is brought up. Any transmissions
+ * made during that delay will be lost. Dealing with this is tricky:
+ * we can't just pause in the init routine while waiting for the
+ * PHY to come ready since that would bring the whole system to
+ * a screeching halt for several seconds.
+ *
+ * What we do here is prevent the TX start routine from sending
+ * any packets until a link has been established. After the
+ * interface has been initialized, the tick routine will poll
+ * the state of the PHY until the IFM_ACTIVE flag is set. Until
+ * that time, packets will stay in the send queue, and once the
+ * link comes up, they will be flushed out to the wire.
+ */
+ if (!sc->dc_link) {
+ mii_pollstat(mii);
+ if (mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->dc_link++;
+ if (ifp->if_snd.ifq_head != NULL)
+ dc_start(ifp);
+ }
+ }
+
+ if (sc->dc_flags & DC_21143_NWAY && !sc->dc_link)
+ sc->dc_stat_ch = timeout(dc_tick, sc, hz/10);
+ else
+ sc->dc_stat_ch = timeout(dc_tick, sc, hz);
+
+ return;
+}
+#endif
+
+/*
+ * A transmit underrun has occurred. Back off the transmit threshold,
+ * or switch to store and forward mode if we have to.
+ */
+static void dc_tx_underrun(sc)
+ struct dc_softc *sc;
+{
+ u_int32_t isr;
+ int i;
+
+ if (DC_IS_DAVICOM(sc))
+ dc_init(sc);
+
+ if (DC_IS_INTEL(sc)) {
+ /*
+ * The real 21143 requires that the transmitter be idle
+ * in order to change the transmit threshold or store
+ * and forward state.
+ */
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON);
+
+ for (i = 0; i < DC_TIMEOUT; i++) {
+ isr = CSR_READ_4(sc, DC_ISR);
+ if (isr & DC_ISR_TX_IDLE)
+ break;
+ DELAY(10);
+ }
+ if (i == DC_TIMEOUT) {
+ printk("dc%d: failed to force tx to idle state\n",
+ sc->dc_unit);
+ dc_init(sc);
+ }
+ }
+
+ printk("dc%d: TX underrun -- ", sc->dc_unit);
+ sc->dc_txthresh += DC_TXTHRESH_INC;
+ if (sc->dc_txthresh > DC_TXTHRESH_MAX) {
+ printk("using store and forward mode\n");
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD);
+ } else {
+ printk("increasing TX threshold\n");
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_THRESH);
+ DC_SETBIT(sc, DC_NETCFG, sc->dc_txthresh);
+ }
+
+ if (DC_IS_INTEL(sc))
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON);
+
+ return;
+}
+
+#ifdef DEVICE_POLLING
+static poll_handler_t dc_poll;
+
+static void
+dc_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+ struct dc_softc *sc = ifp->if_softc;
+
+ if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */
+ /* Re-enable interrupts. */
+ CSR_WRITE_4(sc, DC_IMR, DC_INTRS);
+ return;
+ }
+ sc->rxcycles = count;
+ dc_rxeof(sc);
+ dc_txeof(sc);
+ if (ifp->if_snd.ifq_head != NULL && !(ifp->if_flags & IFF_OACTIVE))
+ dc_start(ifp);
+
+ if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */
+ u_int32_t status;
+
+ status = CSR_READ_4(sc, DC_ISR);
+ status &= (DC_ISR_RX_WATDOGTIMEO|DC_ISR_RX_NOBUF|
+ DC_ISR_TX_NOBUF|DC_ISR_TX_IDLE|DC_ISR_TX_UNDERRUN|
+ DC_ISR_BUS_ERR);
+ if (!status)
+ return ;
+ /* ack what we have */
+ CSR_WRITE_4(sc, DC_ISR, status);
+
+ if (status & (DC_ISR_RX_WATDOGTIMEO|DC_ISR_RX_NOBUF) ) {
+ u_int32_t r = CSR_READ_4(sc, DC_FRAMESDISCARDED);
+ ifp->if_ierrors += (r & 0xffff) + ((r >> 17) & 0x7ff);
+
+ if (dc_rx_resync(sc))
+ dc_rxeof(sc);
+ }
+ /* restart transmit unit if necessary */
+ if (status & DC_ISR_TX_IDLE && sc->dc_cdata.dc_tx_cnt)
+ CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF);
+
+ if (status & DC_ISR_TX_UNDERRUN)
+ dc_tx_underrun(sc);
+
+ if (status & DC_ISR_BUS_ERR) {
+ printk("dc_poll: dc%d bus error\n", sc->dc_unit);
+ dc_reset(sc);
+ dc_init(sc);
+ }
+ }
+}
+#endif /* DEVICE_POLLING */
+
+static void
+dc_intr(void* arg)
+{
+ /* Need to make this work for multiple devices ... eventually */
+ struct dc_softc *sc = (struct dc_softc *)arg;
+
+
+ /* Disable interrupts. */
+ CSR_WRITE_4(sc, DC_IMR, 0x00000000);
+
+ rtems_bsdnet_event_send(sc->daemontid, IRQ_EVENT);
+#if 0
+ if (sc->suspended) {
+ return;
+ }
+
+ ifp = &sc->arpcom.ac_if;
+
+#ifdef DEVICE_POLLING
+ if (ifp->if_ipending & IFF_POLLING)
+ return;
+ if (ether_poll_register(dc_poll, ifp)) { /* ok, disable interrupts */
+ CSR_WRITE_4(sc, DC_IMR, 0x00000000);
+ return;
+ }
+#endif /* DEVICE_POLLING */
+ if ( (CSR_READ_4(sc, DC_ISR) & DC_INTRS) == 0)
+ return ;
+
+ /* Suppress unwanted interrupts */
+ if (!(ifp->if_flags & IFF_UP)) {
+ if (CSR_READ_4(sc, DC_ISR) & DC_INTRS)
+ dc_stop(sc);
+ return;
+ }
+#endif
+}
+
+
+static void
+dc_daemon(void * arg)
+{
+ struct dc_softc *sc = (struct dc_softc *)arg;
+ struct ifnet *ifp;
+ u_int32_t status;
+ rtems_event_set events;
+
+
+ for(;;) {
+ rtems_bsdnet_event_receive(RTEMS_ALL_EVENTS, \
+ RTEMS_WAIT | RTEMS_EVENT_ANY, \
+ RTEMS_NO_TIMEOUT,
+ &events);
+
+
+ ifp = &sc->arpcom.ac_if;
+
+ while((status = CSR_READ_4(sc, DC_ISR)) & DC_INTRS) {
+
+ CSR_WRITE_4(sc, DC_ISR, status);
+
+ if (status & DC_ISR_RX_OK) {
+ int curpkts;
+ curpkts = ifp->if_ipackets;
+ dc_rxeof(sc);
+ if (curpkts == ifp->if_ipackets) {
+ while(dc_rx_resync(sc))
+ dc_rxeof(sc);
+ }
+ }
+
+ if (status & (DC_ISR_TX_OK|DC_ISR_TX_NOBUF))
+ dc_txeof(sc);
+
+ if (status & DC_ISR_TX_IDLE) {
+ dc_txeof(sc);
+ if (sc->dc_cdata.dc_tx_cnt) {
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON);
+ CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF);
+ }
+ }
+
+ if (status & DC_ISR_TX_UNDERRUN)
+ dc_tx_underrun(sc);
+
+ if ((status & DC_ISR_RX_WATDOGTIMEO)
+ || (status & DC_ISR_RX_NOBUF)) {
+ int curpkts;
+ curpkts = ifp->if_ipackets;
+ dc_rxeof(sc);
+ if (curpkts == ifp->if_ipackets) {
+ while(dc_rx_resync(sc))
+ dc_rxeof(sc);
+ }
+ }
+
+ if (status & DC_ISR_BUS_ERR) {
+ dc_reset(sc);
+ dc_init(sc);
+ }
+ }
+
+ /* Make atomic !!! */
+ /* Re-enable interrupts. */
+ CSR_WRITE_4(sc, DC_IMR, DC_INTRS);
+
+ if (ifp->if_snd.ifq_head != NULL)
+ dc_start(ifp);
+ }
+
+}
+
+
+/*
+ * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data
+ * pointers to the fragment pointers.
+ */
+static int dc_encap(sc, m_head, txidx)
+ struct dc_softc *sc;
+ struct mbuf *m_head;
+ u_int32_t *txidx;
+{
+ struct dc_desc *f = NULL;
+ struct mbuf *m;
+ int frag, cur, cnt = 0;
+
+ /*
+ * Start packing the mbufs in this chain into
+ * the fragment pointers. Stop when we run out
+ * of fragments or hit the end of the mbuf chain.
+ */
+ m = m_head;
+ cur = frag = *txidx;
+
+ for (m = m_head; m != NULL; m = m->m_next) {
+ if (m->m_len != 0) {
+ if (sc->dc_flags & DC_TX_ADMTEK_WAR) {
+ if (*txidx != sc->dc_cdata.dc_tx_prod &&
+ frag == (DC_TX_LIST_CNT - 1))
+ return(ENOBUFS);
+ }
+ if ((DC_TX_LIST_CNT -
+ (sc->dc_cdata.dc_tx_cnt + cnt)) < 5)
+ return(ENOBUFS);
+
+ f = &sc->dc_ldata->dc_tx_list[frag];
+ f->dc_ctl = DC_TXCTL_TLINK | m->m_len;
+ if (cnt == 0) {
+ f->dc_status = 0;
+ f->dc_ctl |= DC_TXCTL_FIRSTFRAG;
+ } else
+ f->dc_status = DC_TXSTAT_OWN;
+ f->dc_data = vtophys(mtod(m, vm_offset_t));
+ cur = frag;
+ DC_INC(frag, DC_TX_LIST_CNT);
+ cnt++;
+ }
+ }
+
+ if (m != NULL)
+ return(ENOBUFS);
+
+ sc->dc_cdata.dc_tx_cnt += cnt;
+ sc->dc_cdata.dc_tx_chain[cur] = m_head;
+ sc->dc_ldata->dc_tx_list[cur].dc_ctl |= DC_TXCTL_LASTFRAG;
+ if (sc->dc_flags & DC_TX_INTR_FIRSTFRAG)
+ sc->dc_ldata->dc_tx_list[*txidx].dc_ctl |= DC_TXCTL_FINT;
+ if (sc->dc_flags & DC_TX_INTR_ALWAYS)
+ sc->dc_ldata->dc_tx_list[cur].dc_ctl |= DC_TXCTL_FINT;
+ if (sc->dc_flags & DC_TX_USE_TX_INTR && sc->dc_cdata.dc_tx_cnt > 64)
+ sc->dc_ldata->dc_tx_list[cur].dc_ctl |= DC_TXCTL_FINT;
+ sc->dc_ldata->dc_tx_list[*txidx].dc_status = DC_TXSTAT_OWN;
+ *txidx = frag;
+
+ return(0);
+}
+
+/*
+ * Coalesce an mbuf chain into a single mbuf cluster buffer.
+ * Needed for some really badly behaved chips that just can't
+ * do scatter/gather correctly.
+ */
+static int dc_coal(sc, m_head)
+ struct dc_softc *sc;
+ struct mbuf **m_head;
+{
+ struct mbuf *m_new, *m;
+
+ m = *m_head;
+ MGETHDR(m_new, M_DONTWAIT, MT_DATA);
+ if (m_new == NULL)
+ return(ENOBUFS);
+ if (m->m_pkthdr.len > MHLEN) {
+ MCLGET(m_new, M_DONTWAIT);
+ if (!(m_new->m_flags & M_EXT)) {
+ m_freem(m_new);
+ return(ENOBUFS);
+ }
+ }
+ m_copydata(m, 0, m->m_pkthdr.len, mtod(m_new, caddr_t));
+ m_new->m_pkthdr.len = m_new->m_len = m->m_pkthdr.len;
+ m_freem(m);
+ *m_head = m_new;
+
+ return(0);
+}
+
+/*
+ * Main transmit routine. To avoid having to do mbuf copies, we put pointers
+ * to the mbuf data regions directly in the transmit lists. We also save a
+ * copy of the pointers since the transmit list fragment pointers are
+ * physical addresses.
+ */
+
+static void dc_start(ifp)
+ struct ifnet *ifp;
+{
+ struct dc_softc *sc;
+ struct mbuf *m_head = NULL;
+ u_int32_t idx;
+
+ sc = ifp->if_softc;
+#if 0
+ if (!sc->dc_link && ifp->if_snd.ifq_len < 10)
+ return;
+#endif
+ if (ifp->if_flags & IFF_OACTIVE)
+ return;
+
+ idx = sc->dc_cdata.dc_tx_prod;
+
+ while(sc->dc_cdata.dc_tx_chain[idx] == NULL) {
+ IF_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL)
+ break;
+
+ if (sc->dc_flags & DC_TX_COALESCE &&
+ m_head->m_next != NULL) {
+ /* only coalesce if have >1 mbufs */
+ if (dc_coal(sc, &m_head)) {
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+ }
+
+ if (dc_encap(sc, m_head, &idx)) {
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+#if 0
+ /*
+ * If there's a BPF listener, bounce a copy of this frame
+ * to him.
+ */
+ if (ifp->if_bpf)
+ bpf_mtap(ifp, m_head);
+#endif
+ if (sc->dc_flags & DC_TX_ONE) {
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+ }
+
+ /* Transmit */
+ sc->dc_cdata.dc_tx_prod = idx;
+ if (!(sc->dc_flags & DC_TX_POLL))
+ CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF);
+
+ /*
+ * Set a timeout in case the chip goes out to lunch.
+ */
+ ifp->if_timer = 5;
+
+ return;
+}
+
+static void dc_init(xsc)
+ void *xsc;
+{
+ struct dc_softc *sc = xsc;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ /*struct mii_data *mii;*/
+
+
+ /*mii = device_get_softc(sc->dc_miibus);*/
+
+ /*
+ * Cancel pending I/O and free all RX/TX buffers.
+ */
+ dc_stop(sc);
+ dc_reset(sc);
+
+ /*
+ * Set cache alignment and burst length.
+ */
+ if (DC_IS_ASIX(sc) || DC_IS_DAVICOM(sc))
+ CSR_WRITE_4(sc, DC_BUSCTL, 0);
+ else
+ CSR_WRITE_4(sc, DC_BUSCTL, DC_BUSCTL_MRME|DC_BUSCTL_MRLE);
+ /*
+ * Evenly share the bus between receive and transmit process.
+ */
+ if (DC_IS_INTEL(sc))
+ DC_SETBIT(sc, DC_BUSCTL, DC_BUSCTL_ARBITRATION);
+ if (DC_IS_DAVICOM(sc) || DC_IS_INTEL(sc)) {
+ DC_SETBIT(sc, DC_BUSCTL, DC_BURSTLEN_USECA);
+ } else {
+ DC_SETBIT(sc, DC_BUSCTL, DC_BURSTLEN_16LONG);
+ }
+ if (sc->dc_flags & DC_TX_POLL)
+ DC_SETBIT(sc, DC_BUSCTL, DC_TXPOLL_1);
+ switch(sc->dc_cachesize) {
+ case 32:
+ DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_32LONG);
+ break;
+ case 16:
+ DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_16LONG);
+ break;
+ case 8:
+ DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_8LONG);
+ break;
+ case 0:
+ default:
+ DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_NONE);
+ break;
+ }
+
+ if (sc->dc_flags & DC_TX_STORENFWD)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD);
+ else {
+ if (sc->dc_txthresh > DC_TXTHRESH_MAX) {
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD);
+ } else {
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD);
+ DC_SETBIT(sc, DC_NETCFG, sc->dc_txthresh);
+ }
+ }
+
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_NO_RXCRC);
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_BACKOFF);
+
+ if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) {
+ /*
+ * The app notes for the 98713 and 98715A say that
+ * in order to have the chips operate properly, a magic
+ * number must be written to CSR16. Macronix does not
+ * document the meaning of these bits so there's no way
+ * to know exactly what they do. The 98713 has a magic
+ * number all its own; the rest all use a different one.
+ */
+ DC_CLRBIT(sc, DC_MX_MAGICPACKET, 0xFFFF0000);
+ if (sc->dc_type == DC_TYPE_98713)
+ DC_SETBIT(sc, DC_MX_MAGICPACKET, DC_MX_MAGIC_98713);
+ else
+ DC_SETBIT(sc, DC_MX_MAGICPACKET, DC_MX_MAGIC_98715);
+ }
+
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_THRESH);
+ DC_SETBIT(sc, DC_NETCFG, DC_TXTHRESH_MIN);
+
+ /* Init circular RX list. */
+ if (dc_list_rx_init(sc) == ENOBUFS) {
+ printk("dc%d: initialization failed: no "
+ "memory for rx buffers\n", sc->dc_unit);
+ dc_stop(sc);
+ return;
+ }
+
+ /*
+ * Init tx descriptors.
+ */
+ dc_list_tx_init(sc);
+
+ /*
+ * Load the address of the RX list.
+ */
+ CSR_WRITE_4(sc, DC_RXADDR, vtophys(&sc->dc_ldata->dc_rx_list[0]));
+ CSR_WRITE_4(sc, DC_TXADDR, vtophys(&sc->dc_ldata->dc_tx_list[0]));
+
+ /*
+ * Enable interrupts.
+ */
+#ifdef DEVICE_POLLING
+ /*
+ * ... but only if we are not polling, and make sure they are off in
+ * the case of polling. Some cards (e.g. fxp) turn interrupts on
+ * after a reset.
+ */
+ if (ifp->if_ipending & IFF_POLLING)
+ CSR_WRITE_4(sc, DC_IMR, 0x00000000);
+ else
+#endif
+ /* Enable interrupts */
+ CSR_WRITE_4(sc, DC_IMR, DC_INTRS);
+ CSR_WRITE_4(sc, DC_ISR, 0xFFFFFFFF);
+
+ /* Enable transmitter. */
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON);
+
+ /*
+ * If this is an Intel 21143 and we're not using the
+ * MII port, program the LED control pins so we get
+ * link and activity indications.
+ */
+ if (sc->dc_flags & DC_TULIP_LEDS) {
+ CSR_WRITE_4(sc, DC_WATCHDOG,
+ DC_WDOG_CTLWREN|DC_WDOG_LINK|DC_WDOG_ACTIVITY);
+ CSR_WRITE_4(sc, DC_WATCHDOG, 0);
+ }
+
+ /*
+ * Load the RX/multicast filter. We do this sort of late
+ * because the filter programming scheme on the 21143 and
+ * some clones requires DMAing a setup frame via the TX
+ * engine, and we need the transmitter enabled for that.
+ */
+ dc_setfilt(sc);
+
+ /* Enable receiver. */
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ON);
+ CSR_WRITE_4(sc, DC_RXSTART, 0xFFFFFFFF);
+
+ /*mii_mediachg(mii);*/
+ dc_setcfg(sc, sc->dc_if_media);
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+
+#if 0
+
+ /* Don't start the ticker if this is a homePNA link. */
+ if (IFM_SUBTYPE(mii->mii_media.ifm_media) == IFM_homePNA)
+ sc->dc_link = 1;
+ else {
+ if (sc->dc_flags & DC_21143_NWAY)
+ sc->dc_stat_ch = timeout(dc_tick, sc, hz/10);
+ else
+ sc->dc_stat_ch = timeout(dc_tick, sc, hz);
+ }
+
+#ifdef SRM_MEDIA
+ if(sc->dc_srm_media) {
+ struct ifreq ifr;
+
+ ifr.ifr_media = sc->dc_srm_media;
+ ifmedia_ioctl(ifp, &ifr, &mii->mii_media, SIOCSIFMEDIA);
+ sc->dc_srm_media = 0;
+ }
+#endif
+#endif /* end if (0) */
+ return;
+}
+
+
+#if 0
+/*
+ * Set media options.
+ */
+static int dc_ifmedia_upd(ifp)
+ struct ifnet *ifp;
+{
+ struct dc_softc *sc;
+ struct mii_data *mii;
+ struct ifmedia *ifm;
+
+ sc = ifp->if_softc;
+ mii = device_get_softc(sc->dc_miibus);
+ mii_mediachg(mii);
+ ifm = &mii->mii_media;
+
+ if (DC_IS_DAVICOM(sc) &&
+ IFM_SUBTYPE(ifm->ifm_media) == IFM_homePNA)
+ dc_setcfg(sc, ifm->ifm_media);
+ else
+ sc->dc_link = 0;
+
+ return(0);
+}
+
+/*
+ * Report current media status.
+ */
+static void dc_ifmedia_sts(ifp, ifmr)
+ struct ifnet *ifp;
+ struct ifmediareq *ifmr;
+{
+ struct dc_softc *sc;
+ struct mii_data *mii;
+ struct ifmedia *ifm;
+
+ sc = ifp->if_softc;
+ mii = device_get_softc(sc->dc_miibus);
+ mii_pollstat(mii);
+ ifm = &mii->mii_media;
+ if (DC_IS_DAVICOM(sc)) {
+ if (IFM_SUBTYPE(ifm->ifm_media) == IFM_homePNA) {
+ ifmr->ifm_active = ifm->ifm_media;
+ ifmr->ifm_status = 0;
+ return;
+ }
+ }
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+
+ return;
+}
+#endif
+
+
+static int dc_ioctl(ifp, command, data)
+ struct ifnet *ifp;
+ ioctl_command_t command;
+ caddr_t data;
+{
+ struct dc_softc *sc = ifp->if_softc;
+ /*struct ifreq *ifr = (struct ifreq *) data;
+ struct mii_data *mii;*/
+ int error = 0;
+
+
+ switch(command) {
+ case SIOCSIFADDR:
+ case SIOCGIFADDR:
+ case SIOCSIFMTU:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ int need_setfilt = (ifp->if_flags ^ sc->dc_if_flags) &
+ (IFF_PROMISC | IFF_ALLMULTI);
+ if (ifp->if_flags & IFF_RUNNING) {
+ if (need_setfilt)
+ dc_setfilt(sc);
+ } else {
+ sc->dc_txthresh = 0;
+ dc_init(sc);
+ }
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ dc_stop(sc);
+ }
+ sc->dc_if_flags = ifp->if_flags;
+ error = 0;
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ dc_setfilt(sc);
+ error = 0;
+ break;
+#if 0
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ mii = device_get_softc(sc->dc_miibus);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+#ifdef SRM_MEDIA
+ if (sc->dc_srm_media)
+ sc->dc_srm_media = 0;
+#endif
+ break;
+#endif
+ default:
+ error = EINVAL;
+ break;
+ }
+
+
+ return(error);
+}
+
+static void dc_watchdog(ifp)
+ struct ifnet *ifp;
+{
+ struct dc_softc *sc;
+
+ sc = ifp->if_softc;
+
+ ifp->if_oerrors++;
+ printk("dc%d: watchdog timeout\n", sc->dc_unit);
+
+ dc_stop(sc);
+ dc_reset(sc);
+ dc_init(sc);
+
+ if (ifp->if_snd.ifq_head != NULL)
+ dc_start(ifp);
+
+ return;
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+static void dc_stop(sc)
+ struct dc_softc *sc;
+{
+ register int i;
+ struct ifnet *ifp;
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_timer = 0;
+
+ /*untimeout(dc_tick, sc, sc->dc_stat_ch);*/
+
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+#ifdef DEVICE_POLLING
+ ether_poll_deregister(ifp);
+#endif
+
+ DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_RX_ON|DC_NETCFG_TX_ON));
+ CSR_WRITE_4(sc, DC_IMR, 0x00000000);
+ CSR_WRITE_4(sc, DC_TXADDR, 0x00000000);
+ CSR_WRITE_4(sc, DC_RXADDR, 0x00000000);
+ sc->dc_link = 0;
+
+ /*
+ * Free data in the RX lists.
+ */
+ for (i = 0; i < DC_RX_LIST_CNT; i++) {
+ if (sc->dc_cdata.dc_rx_chain[i] != NULL) {
+ m_freem(sc->dc_cdata.dc_rx_chain[i]);
+ sc->dc_cdata.dc_rx_chain[i] = NULL;
+ }
+ }
+ bzero((char *)&sc->dc_ldata->dc_rx_list,
+ sizeof(sc->dc_ldata->dc_rx_list));
+
+ /*
+ * Free the TX list buffers.
+ */
+ for (i = 0; i < DC_TX_LIST_CNT; i++) {
+ if (sc->dc_cdata.dc_tx_chain[i] != NULL) {
+ if (sc->dc_ldata->dc_tx_list[i].dc_ctl &
+ DC_TXCTL_SETUP) {
+ sc->dc_cdata.dc_tx_chain[i] = NULL;
+ continue;
+ }
+ m_freem(sc->dc_cdata.dc_tx_chain[i]);
+ sc->dc_cdata.dc_tx_chain[i] = NULL;
+ }
+ }
+
+ bzero((char *)&sc->dc_ldata->dc_tx_list,
+ sizeof(sc->dc_ldata->dc_tx_list));
+
+ return;
+}
+
+
+#if 0
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static void dc_shutdown(dev)
+ device_t dev;
+{
+ struct dc_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ dc_stop(sc);
+
+ return;
+}
+
+/*
+ * Device suspend routine. Stop the interface and save some PCI
+ * settings in case the BIOS doesn't restore them properly on
+ * resume.
+ */
+static int dc_suspend(dev)
+ device_t dev;
+{
+ register int i;
+ int s;
+ struct dc_softc *sc;
+
+
+ sc = device_get_softc(dev);
+
+ dc_stop(sc);
+
+ for (i = 0; i < 5; i++)
+ sc->saved_maps[i] = pci_read_config(dev, PCIR_MAPS + i * 4, 4);
+ sc->saved_biosaddr = pci_read_config(dev, PCIR_BIOS, 4);
+ sc->saved_intline = pci_read_config(dev, PCIR_INTLINE, 1);
+ sc->saved_cachelnsz = pci_read_config(dev, PCIR_CACHELNSZ, 1);
+ sc->saved_lattimer = pci_read_config(dev, PCIR_LATTIMER, 1);
+
+ sc->suspended = 1;
+
+ return (0);
+}
+
+/*
+ * Device resume routine. Restore some PCI settings in case the BIOS
+ * doesn't, re-enable busmastering, and restart the interface if
+ * appropriate.
+ */
+static int dc_resume(dev)
+ device_t dev;
+{
+ register int i;
+ int s;
+ struct dc_softc *sc;
+ struct ifnet *ifp;
+
+
+ sc = device_get_softc(dev);
+ ifp = &sc->arpcom.ac_if;
+
+ dc_acpi(dev);
+
+ /* better way to do this? */
+ for (i = 0; i < 5; i++)
+ pci_write_config(dev, PCIR_MAPS + i * 4, sc->saved_maps[i], 4);
+ pci_write_config(dev, PCIR_BIOS, sc->saved_biosaddr, 4);
+ pci_write_config(dev, PCIR_INTLINE, sc->saved_intline, 1);
+ pci_write_config(dev, PCIR_CACHELNSZ, sc->saved_cachelnsz, 1);
+ pci_write_config(dev, PCIR_LATTIMER, sc->saved_lattimer, 1);
+
+ /* reenable busmastering */
+ pci_enable_busmaster(dev);
+ pci_enable_io(dev, DC_RES);
+
+ /* reinitialize interface if necessary */
+ if (ifp->if_flags & IFF_UP)
+ dc_init(sc);
+
+ sc->suspended = 0;
+
+ return (0);
+}
+#endif
+
+#endif /* end if supported */
diff --git a/bsps/shared/net/if_fxp.c b/bsps/shared/net/if_fxp.c
new file mode 100644
index 0000000..2bf9907
--- /dev/null
+++ b/bsps/shared/net/if_fxp.c
@@ -0,0 +1,2339 @@
+/*-
+ * Copyright (c) 1995, David Greenman
+ * Copyright (c) 2001 Jonathan Lemon <jlemon@freebsd.org>
+ * All rights reserved.
+ *
+ * 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 unmodified, 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 AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD: src/sys/dev/fxp/if_fxp.c,v 1.118 2001/09/05 23:33:58 brooks Exp $
+ */
+
+/*
+ * Intel EtherExpress Pro/100B PCI Fast Ethernet driver
+ */
+
+/*
+ * RTEMS Revision Preliminary History
+ *
+ * July XXX, 2002 W. Eric Norum <eric.norum@usask.ca>
+ * Placed in RTEMS CVS repository. All further modifications will be
+ * noted in the CVS log and not in this comment.
+ *
+ * July 11, 2002 W. Eric Norum <eric.norum@usask.ca>
+ * Minor modifications to get driver working with NIC on VersaLogic
+ * Bobcat PC-104 single-board computer. The Bobcat has no video
+ * driver so printf/printk calls are directed to COM2:. This
+ * arrangement seems to require delays after the printk calls or
+ * else things lock up. Perhaps the RTEMS pc386 console code
+ * should be modified to insert these delays itself.
+ *
+ * June 27, 2002 W. Eric Norum <eric.norum@usask.ca>
+ * Obtained from Thomas Doerfler <Thomas.Doerfler@imd-systems.de>.
+ * A big thank-you to Thomas for making this available.
+ *
+ * October 01, 2001 Thomas Doerfler <Thomas.Doerfler@imd-systems.de>
+ * Original RTEMS modifications.
+ */
+
+#include <machine/rtems-bsd-kernel-space.h>
+
+#if defined(__i386__)
+
+/*#define DEBUG_OUT 0*/
+
+#include <rtems.h>
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+#include <bsp.h>
+#include <inttypes.h>
+
+#include <errno.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <bsp.h>
+#include <bsp/irq.h>
+#include <bsp/irq-generic.h>
+#include <rtems/pci.h>
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#include <net/bpf.h>
+
+#include <vm/vm.h> /* for vtophys */
+
+#include <net/if_types.h>
+
+#include "if_fxpreg.h"
+#include <libchip/if_fxpvar.h>
+
+/*
+ * some adaptation replacements for RTEMS
+ */
+static rtems_interval fxp_ticksPerSecond;
+#define device_printf(device,format,args...) printk(format,## args)
+#define DELAY(n) rtems_task_wake_after(((n)*fxp_ticksPerSecond/1000000)+1)
+#ifdef DEBUG_OUT
+#define DBGLVL_PRINTK(LVL,format, args...) \
+if (DEBUG_OUT >= (LVL)) { \
+ printk(format, ## args); \
+}
+#else
+#define DBGLVL_PRINTK(LVL,format, args...)
+#endif
+
+/*
+ * RTEMS event used by interrupt handler to signal driver tasks.
+ * This must not be any of the events used by the network task synchronization.
+ */
+#define INTERRUPT_EVENT RTEMS_EVENT_1
+
+/*
+ * remapping between PCI device and CPU memmory address view...
+ */
+#if defined(__i386)
+#define vtophys(p) (u_int32_t)(p)
+#else
+#define vtophys(p) vtophys(p)
+#endif
+
+#define NFXPDRIVER 1
+static struct fxp_softc fxp_softc[NFXPDRIVER];
+static bool fxp_is_verbose = true;
+/*
+ * NOTE! On the Alpha, we have an alignment constraint. The
+ * card DMAs the packet immediately following the RFA. However,
+ * the first thing in the packet is a 14-byte Ethernet header.
+ * This means that the packet is misaligned. To compensate,
+ * we actually offset the RFA 2 bytes into the cluster. This
+ * alignes the packet after the Ethernet header at a 32-bit
+ * boundary. HOWEVER! This means that the RFA is misaligned!
+ */
+#define RFA_ALIGNMENT_FUDGE 2
+
+/*
+ * Set initial transmit threshold at 64 (512 bytes). This is
+ * increased by 64 (512 bytes) at a time, to maximum of 192
+ * (1536 bytes), if an underrun occurs.
+ */
+static int tx_threshold = 64;
+
+/*
+ * The configuration byte map has several undefined fields which
+ * must be one or must be zero. Set up a template for these bits
+ * only, (assuming a 82557 chip) leaving the actual configuration
+ * to fxp_init.
+ *
+ * See struct fxp_cb_config for the bit definitions.
+ */
+static u_char fxp_cb_config_template[] = {
+ 0x0, 0x0, /* cb_status */
+ 0x0, 0x0, /* cb_command */
+ 0x0, 0x0, 0x0, 0x0, /* link_addr */
+ 0x0, /* 0 */
+ 0x0, /* 1 */
+ 0x0, /* 2 */
+ 0x0, /* 3 */
+ 0x0, /* 4 */
+ 0x0, /* 5 */
+ 0x32, /* 6 */
+ 0x0, /* 7 */
+ 0x0, /* 8 */
+ 0x0, /* 9 */
+ 0x6, /* 10 */
+ 0x0, /* 11 */
+ 0x0, /* 12 */
+ 0x0, /* 13 */
+ 0xf2, /* 14 */
+ 0x48, /* 15 */
+ 0x0, /* 16 */
+ 0x40, /* 17 */
+ 0xf0, /* 18 */
+ 0x0, /* 19 */
+ 0x3f, /* 20 */
+ 0x5 /* 21 */
+};
+
+struct fxp_ident {
+ u_int16_t devid;
+ char *name;
+ int warn;
+};
+
+#define UNTESTED 1
+
+/*
+ * Claim various Intel PCI device identifiers for this driver. The
+ * sub-vendor and sub-device field are extensively used to identify
+ * particular variants, but we don't currently differentiate between
+ * them.
+ */
+static struct fxp_ident fxp_ident_table[] = {
+ { 0x1229, "Intel Pro 10/100B/100+ Ethernet", 0 },
+ { 0x2449, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1209, "Intel Embedded 10/100 Ethernet", 0 },
+ { 0x1029, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1030, "Intel Pro/100 Ethernet", 0 },
+ { 0x1031, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1032, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1033, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1034, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1035, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1036, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1037, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1038, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x103B, "Intel Pro/100 Ethernet (82801BD PRO/100 VM (LOM))", 0 },
+ { 0, NULL, 0 }
+};
+
+#if 0
+static int fxp_probe(device_t dev);
+static int fxp_attach(device_t dev);
+static int fxp_detach(device_t dev);
+static int fxp_shutdown(device_t dev);
+#endif
+int fxp_output (struct ifnet *,
+ struct mbuf *, struct sockaddr *, struct rtentry *);
+
+
+static void fxp_intr(void *arg);
+static void fxp_init(void *xsc);
+static void fxp_tick(void *xsc);
+static void fxp_start(struct ifnet *ifp);
+static void fxp_stop(struct fxp_softc *sc);
+static void fxp_release(struct fxp_softc *sc);
+static int fxp_ioctl(struct ifnet *ifp, ioctl_command_t command,
+ caddr_t data);
+static void fxp_watchdog(struct ifnet *ifp);
+static int fxp_add_rfabuf(struct fxp_softc *sc, struct mbuf *oldm);
+static void fxp_mc_setup(struct fxp_softc *sc);
+static u_int16_t fxp_eeprom_getword(struct fxp_softc *sc, int offset,
+ int autosize);
+static void fxp_eeprom_putword(struct fxp_softc *sc, int offset,
+ u_int16_t data);
+static void fxp_autosize_eeprom(struct fxp_softc *sc);
+static void fxp_read_eeprom(struct fxp_softc *sc, u_short *data,
+ int offset, int words);
+static void fxp_write_eeprom(struct fxp_softc *sc, u_short *data,
+ int offset, int words);
+#ifdef NOTUSED
+static int fxp_ifmedia_upd(struct ifnet *ifp);
+static void fxp_ifmedia_sts(struct ifnet *ifp,
+ struct ifmediareq *ifmr);
+static int fxp_serial_ifmedia_upd(struct ifnet *ifp);
+static void fxp_serial_ifmedia_sts(struct ifnet *ifp,
+ struct ifmediareq *ifmr);
+static volatile int fxp_miibus_readreg(device_t dev, int phy, int reg);
+static void fxp_miibus_writereg(device_t dev, int phy, int reg,
+ int value);
+#endif
+static __inline void fxp_lwcopy(volatile u_int32_t *src,
+ volatile u_int32_t *dst);
+static __inline void fxp_scb_wait(struct fxp_softc *sc);
+static __inline void fxp_scb_cmd(struct fxp_softc *sc, int cmd);
+static __inline void fxp_dma_wait(volatile u_int16_t *status,
+ struct fxp_softc *sc);
+
+/*
+ * Inline function to copy a 16-bit aligned 32-bit quantity.
+ */
+static __inline void
+fxp_lwcopy(volatile u_int32_t *src, volatile u_int32_t *dst)
+{
+#ifdef __i386__
+ *dst = *src;
+#else
+ volatile u_int16_t *a = (volatile u_int16_t*)src;
+ volatile u_int16_t *b = (volatile u_int16_t*)dst;
+
+ b[0] = a[0];
+ b[1] = a[1];
+#endif
+}
+
+/*
+ * inline access functions to pci space registers
+ */
+static __inline u_int8_t fxp_csr_read_1(struct fxp_softc *sc,int reg) {
+ u_int8_t val;
+ if (sc->pci_regs_are_io) {
+ inport_byte(sc->pci_regs_base + reg,val);
+ }
+ else {
+ val = *(volatile u_int8_t*)(sc->pci_regs_base+reg);
+ }
+ return val;
+}
+static __inline u_int32_t fxp_csr_read_2(struct fxp_softc *sc,int reg) {
+ u_int16_t val;
+ if (sc->pci_regs_are_io) {
+ inport_word(sc->pci_regs_base + reg,val);
+ }
+ else {
+ val = *(volatile u_int16_t*)(sc->pci_regs_base+reg);
+ }
+ return val;
+}
+static __inline u_int32_t fxp_csr_read_4(struct fxp_softc *sc,int reg) {
+ u_int32_t val;
+ if (sc->pci_regs_are_io) {
+ inport_long(sc->pci_regs_base + reg,val);
+ }
+ else {
+ val = *(volatile u_int32_t*)(sc->pci_regs_base+reg);
+ }
+ return val;
+}
+
+/*
+ * Wait for the previous command to be accepted (but not necessarily
+ * completed).
+ */
+static __inline void
+fxp_scb_wait(struct fxp_softc *sc)
+{
+ int i = 10000;
+
+ while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --i)
+ DELAY(2);
+ if (i == 0)
+ device_printf(sc->dev, "SCB timeout: 0x%d 0x%d"
+ "0x%d" PRIx32 "0x%" PRIx32 "\n",
+ CSR_READ_1(sc, FXP_CSR_SCB_COMMAND),
+ CSR_READ_1(sc, FXP_CSR_SCB_STATACK),
+ CSR_READ_1(sc, FXP_CSR_SCB_RUSCUS),
+ CSR_READ_2(sc, FXP_CSR_FLOWCONTROL));
+}
+
+static __inline void
+fxp_scb_cmd(struct fxp_softc *sc, int cmd)
+{
+
+ if (cmd == FXP_SCB_COMMAND_CU_RESUME && sc->cu_resume_bug) {
+ CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_CB_COMMAND_NOP);
+ fxp_scb_wait(sc);
+ }
+ CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, cmd);
+}
+
+static __inline void
+fxp_dma_wait(volatile u_int16_t *status, struct fxp_softc *sc)
+{
+ int i = 10000;
+
+ while (!(*status & FXP_CB_STATUS_C) && --i)
+ DELAY(2);
+ if (i == 0)
+ device_printf(sc->dev, "DMA timeout\n");
+}
+
+
+#define FXP_PCI_CONF_ACCESSOR(_confop, _baseop, _type) \
+ \
+ static inline int _confop ( \
+ struct fxp_softc *sc, \
+ int offset, \
+ _type data ) \
+ { \
+ _baseop( \
+ sc->pci_bus, \
+ sc->pci_dev, \
+ sc->pci_fun, \
+ offset, \
+ data \
+ ); \
+ return PCIB_ERR_SUCCESS; \
+ }
+
+FXP_PCI_CONF_ACCESSOR( fxp_pci_conf_read8, pci_read_config_byte, uint8_t * );
+FXP_PCI_CONF_ACCESSOR( fxp_pci_conf_read16, pci_read_config_word, uint16_t * );
+FXP_PCI_CONF_ACCESSOR( fxp_pci_conf_read32, pci_read_config_dword, uint32_t * );
+FXP_PCI_CONF_ACCESSOR( fxp_pci_conf_write8, pci_write_config_byte, uint8_t );
+FXP_PCI_CONF_ACCESSOR( fxp_pci_conf_write16, pci_write_config_word, uint16_t );
+FXP_PCI_CONF_ACCESSOR( fxp_pci_conf_write32, pci_write_config_dword, uint32_t );
+
+static __inline unsigned int fxp_pci_get_vendor(struct fxp_softc *sc) {
+ u_int16_t vendor;
+ fxp_pci_conf_read16(sc, PCI_VENDOR_ID, &vendor);
+ return vendor;
+}
+
+static __inline unsigned int fxp_pci_get_device(struct fxp_softc *sc) {
+ u_int16_t device;
+ fxp_pci_conf_read16(sc, PCI_DEVICE_ID, &device);
+ return device;
+}
+
+static __inline unsigned int fxp_pci_get_subvendor(struct fxp_softc *sc) {
+ u_int16_t subvendor;
+ fxp_pci_conf_read16(sc, PCI_SUBSYSTEM_VENDOR_ID, &subvendor);
+ return subvendor;
+}
+
+static __inline unsigned int fxp_pci_get_subdevice(struct fxp_softc *sc) {
+ u_int16_t subdevice;
+ fxp_pci_conf_read16(sc, PCI_SUBSYSTEM_ID, &subdevice);
+ return subdevice;
+}
+
+static __inline unsigned int fxp_pci_get_revid(struct fxp_softc *sc) {
+ u_int8_t revid;
+ fxp_pci_conf_read8(sc, PCI_REVISION_ID, &revid);
+ return revid;
+}
+
+/* Prototype to avoid warning. This must be a global symbol. */
+int rtems_fxp_attach(struct rtems_bsdnet_ifconfig *config, int attaching);
+
+int
+rtems_fxp_attach(struct rtems_bsdnet_ifconfig *config, int attaching)
+{
+ int error = 0;
+ struct fxp_softc *sc;
+ struct ifnet *ifp;
+ uint16_t val16;
+ uint32_t val32;
+ uint16_t data;
+ int i;
+ int s;
+ int unitNumber;
+ char *unitName;
+ u_int16_t dev_id;
+ u_int8_t interrupt;
+ int mtu;
+
+ /*
+ * Set up some timing values
+ */
+ fxp_ticksPerSecond = rtems_clock_get_ticks_per_second();
+ DBGLVL_PRINTK(1,"fxp_attach called\n");
+
+ /*
+ * Parse driver name
+ */
+ if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0)
+ return 0;
+
+ /*
+ * Is driver free?
+ */
+ if ((unitNumber <= 0) || (unitNumber > NFXPDRIVER)) {
+ device_printf(dev,"Bad FXP unit number.\n");
+ return 0;
+ }
+ sc = &fxp_softc[unitNumber - 1];
+ ifp = &sc->arpcom.ac_if;
+ if (ifp->if_softc != NULL) {
+ device_printf(dev,"FXP Driver already in use.\n");
+ return 0;
+ }
+
+ memset(sc, 0, sizeof(*sc));
+#ifdef NOTUSED
+ sc->dev = dev;
+ callout_handle_init(&sc->stat_ch);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_DEF | MTX_RECURSE);
+#endif
+ s = splimp();
+
+ /*
+ * find device on pci bus
+ */
+ { int j; int pbus, pdev, pfun;
+
+ for (j=0; fxp_ident_table[j].devid; j++ ) {
+ i = pci_find_device( 0x8086, fxp_ident_table[j].devid,
+ unitNumber-1, &pbus, &pdev, &pfun );
+ sc->pci_bus = pbus;
+ sc->pci_dev = pdev;
+ sc->pci_fun = pfun;
+ DBGLVL_PRINTK(2,"fxp_attach: find_devid returned %d ,"
+ "pci bus %d dev %d fun %d \n",
+ i, sc->pci_bus, sc->pci_dev, sc->pci_fun);
+ if (PCIB_ERR_SUCCESS == i) {
+ if ( UNTESTED == fxp_ident_table[j].warn ) {
+ device_printf(dev,
+"WARNING: this chip version has NOT been reported to work under RTEMS yet.\n");
+ device_printf(dev,
+" If it works OK, report it as tested in 'c/src/libchip/network/if_fxp.c'\n");
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * FIXME: add search for more device types...
+ */
+ if (i != PCIB_ERR_SUCCESS) {
+ device_printf(dev, "could not find 82559ER device\n");
+ return 0;
+ }
+
+
+ /*
+ * Enable bus mastering. Enable memory space too, in case
+ * BIOS/Prom forgot about it.
+ */
+ fxp_pci_conf_read16(sc, PCI_COMMAND,&val16);
+ val16 |= (PCI_COMMAND_MEMORY|PCI_COMMAND_MASTER);
+ fxp_pci_conf_write16(sc, PCI_COMMAND, val16);
+ DBGLVL_PRINTK(3,"fxp_attach: PCI_COMMAND_write = 0x%x\n",val16);
+ fxp_pci_conf_read16(sc, PCI_COMMAND,&val16);
+ DBGLVL_PRINTK(4,"fxp_attach: PCI_COMMAND_read = 0x%x\n",val16);
+
+ /*
+ * Figure out which we should try first - memory mapping or i/o mapping?
+ * We default to memory mapping. Then we accept an override from the
+ * command line. Then we check to see which one is enabled.
+ */
+#ifdef NOTUSED
+ m1 = PCI_COMMAND_MEMORY;
+ m2 = PCI_COMMAND_IO;
+ prefer_iomap = 0;
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "prefer_iomap", &prefer_iomap) == 0 && prefer_iomap != 0) {
+ m1 = PCI_COMMAND_IO;
+ m2 = PCI_COMMAND_MEMORY;
+ }
+
+ if (val & m1) {
+ sc->rtp = ((m1 == PCI_COMMAND_MEMORY)
+ ? SYS_RES_MEMORY : SYS_RES_IOPORT);
+ sc->rgd = ((m1 == PCI_COMMAND_MEMORY)
+ ? FXP_PCI_MMBA : FXP_PCI_IOBA);
+ sc->mem = bus_alloc_resource(dev, sc->rtp, &sc->rgd,
+ 0, ~0, 1, RF_ACTIVE);
+ }
+ if (sc->mem == NULL && (val & m2)) {
+ sc->rtp = ((m2 == PCI_COMMAND_MEMORY)
+ ? SYS_RES_MEMORY : SYS_RES_IOPORT);
+ sc->rgd = ((m2 == PCI_COMMAND_MEMORY)
+ ? FXP_PCI_MMBA : FXP_PCI_IOBA);
+ sc->mem = bus_alloc_resource(dev, sc->rtp, &sc->rgd,
+ 0, ~0, 1, RF_ACTIVE);
+ }
+
+ if (!sc->mem) {
+ device_printf(dev, "could not map device registers\n");
+ error = ENXIO;
+ goto fail;
+ }
+ if (fxp_is_verbose) {
+ device_printf(dev, "using %s space register mapping\n",
+ sc->rtp == SYS_RES_MEMORY? "memory" : "I/O");
+ }
+
+ sc->sc_st = rman_get_bustag(sc->mem);
+ sc->sc_sh = rman_get_bushandle(sc->mem);
+
+ /*
+ * Allocate our interrupt.
+ */
+ rid = 0;
+ sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->irq == NULL) {
+ device_printf(dev, "could not map interrupt\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET,
+ fxp_intr, sc, &sc->ih);
+ if (error) {
+ device_printf(dev, "could not setup irq\n");
+ goto fail;
+ }
+#endif
+
+ /*
+ * get mapping and base address of registers
+ */
+ fxp_pci_conf_read16(sc, PCI_COMMAND,&val16);
+ DBGLVL_PRINTK(4,"fxp_attach: PCI_COMMAND_read = 0x%x\n",val16);
+ if((val16 & PCI_COMMAND_IO) != 0) {
+ sc->pci_regs_are_io = true;
+ fxp_pci_conf_read32(sc, PCI_BASE_ADDRESS_1, &val32);
+ sc->pci_regs_base = val32 & PCI_BASE_ADDRESS_IO_MASK;
+ }
+ else {
+ sc->pci_regs_are_io = false;
+ fxp_pci_conf_read32(sc, PCI_BASE_ADDRESS_0, &val32);
+ sc->pci_regs_base = val32 & PCI_BASE_ADDRESS_MEM_MASK;
+ }
+ DBGLVL_PRINTK(3,"fxp_attach: CSR registers are mapped in %s space"
+ " at address 0x%x\n",
+ sc->pci_regs_are_io ? "I/O" : "MEM",
+ sc->pci_regs_base);
+
+ /*
+ * get interrupt level to be used
+ */
+ fxp_pci_conf_read8(sc, PCI_INTERRUPT_LINE, &interrupt);
+ DBGLVL_PRINTK(3,"fxp_attach: interrupt = 0x%x\n",interrupt);
+ sc->irq_num = interrupt;
+ /*
+ * Reset to a stable state.
+ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET);
+ */
+ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SOFTWARE_RESET);
+ DELAY(10);
+
+ sc->cbl_base = malloc(sizeof(struct fxp_cb_tx) * FXP_NTXCB,
+ M_DEVBUF, M_NOWAIT);
+ DBGLVL_PRINTK(3,"fxp_attach: sc->cbl_base = 0x%x\n",sc->cbl_base);
+ if (sc->cbl_base == NULL)
+ goto failmem;
+ else
+ memset(sc->cbl_base, 0, sizeof(struct fxp_cb_tx) * FXP_NTXCB);
+
+ sc->fxp_stats = malloc(sizeof(struct fxp_stats), M_DEVBUF,
+ M_NOWAIT);
+ DBGLVL_PRINTK(3,"fxp_attach: sc->fxp_stats = 0x%x\n",sc->fxp_stats);
+ if (sc->fxp_stats == NULL)
+ goto failmem;
+ else
+ memset(sc->fxp_stats, 0, sizeof(struct fxp_stats));
+
+ sc->mcsp = malloc(sizeof(struct fxp_cb_mcs), M_DEVBUF, M_NOWAIT);
+ DBGLVL_PRINTK(3,"fxp_attach: sc->mcsp = 0x%x\n",sc->mcsp);
+ if (sc->mcsp == NULL)
+ goto failmem;
+
+ /*
+ * Pre-allocate our receive buffers.
+ */
+ for (i = 0; i < FXP_NRFABUFS; i++) {
+ if (fxp_add_rfabuf(sc, NULL) != 0) {
+ goto failmem;
+ }
+ }
+
+ /*
+ * Find out how large of an SEEPROM we have.
+ */
+ DBGLVL_PRINTK(3,"fxp_attach: calling fxp_autosize_eeprom\n");
+ fxp_autosize_eeprom(sc);
+
+ /*
+ * Determine whether we must use the 503 serial interface.
+ */
+ fxp_read_eeprom(sc, &data, 6, 1);
+ if ((data & FXP_PHY_DEVICE_MASK) != 0 &&
+ (data & FXP_PHY_SERIAL_ONLY))
+ sc->flags |= FXP_FLAG_SERIAL_MEDIA;
+
+ /*
+ * Find out the basic controller type; we currently only
+ * differentiate between a 82557 and greater.
+ */
+ fxp_read_eeprom(sc, &data, 5, 1);
+ if ((data >> 8) == 1)
+ sc->chip = FXP_CHIP_82557;
+ DBGLVL_PRINTK(3,"fxp_attach: sc->chip = %d\n",sc->chip);
+
+ /*
+ * Enable workarounds for certain chip revision deficiencies.
+ *
+ * Systems based on the ICH2/ICH2-M chip from Intel have a defect
+ * where the chip can cause a PCI protocol violation if it receives
+ * a CU_RESUME command when it is entering the IDLE state. The
+ * workaround is to disable Dynamic Standby Mode, so the chip never
+ * deasserts CLKRUN#, and always remains in an active state.
+ *
+ * See Intel 82801BA/82801BAM Specification Update, Errata #30.
+ */
+#ifdef NOTUSED
+ i = fxp_pci_get_device(dev);
+#else
+ fxp_pci_conf_read16(sc, PCI_DEVICE_ID, &dev_id);
+ DBGLVL_PRINTK(3,"fxp_attach: device id = 0x%x\n",dev_id);
+#endif
+ if (dev_id == 0x2449 || (dev_id > 0x1030 && dev_id < 0x1039)) {
+ device_printf(dev, "*** See Intel 82801BA/82801BAM Specification Update, Errata #30. ***\n");
+ fxp_read_eeprom(sc, &data, 10, 1);
+ if (data & 0x02) { /* STB enable */
+ u_int16_t cksum;
+ int i;
+
+ device_printf(dev,
+ "*** DISABLING DYNAMIC STANDBY MODE IN EEPROM ***\n");
+ data &= ~0x02;
+ fxp_write_eeprom(sc, &data, 10, 1);
+ device_printf(dev, "New EEPROM ID: 0x%x\n", data);
+ cksum = 0;
+ for (i = 0; i < (1 << sc->eeprom_size) - 1; i++) {
+ fxp_read_eeprom(sc, &data, i, 1);
+ cksum += data;
+ }
+ i = (1 << sc->eeprom_size) - 1;
+ cksum = 0xBABA - cksum;
+ fxp_read_eeprom(sc, &data, i, 1);
+ fxp_write_eeprom(sc, &cksum, i, 1);
+ device_printf(dev,
+ "EEPROM checksum @ 0x%x: 0x%x -> 0x%x\n",
+ i, data, cksum);
+ /*
+ * We need to do a full PCI reset here. A software
+ * reset to the port doesn't cut it, but let's try
+ * anyway.
+ */
+ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SOFTWARE_RESET);
+ DELAY(50);
+ device_printf(dev,
+ "*** PLEASE REBOOT THE SYSTEM NOW FOR CORRECT OPERATION ***\n");
+#if 1
+ /*
+ * If the user elects to continue, try the software
+ * workaround, as it is better than nothing.
+ */
+ sc->flags |= FXP_FLAG_CU_RESUME_BUG;
+#endif
+ }
+ }
+
+ /*
+ * If we are not a 82557 chip, we can enable extended features.
+ */
+ if (sc->chip != FXP_CHIP_82557) {
+ u_int8_t tmp_val;
+ /*
+ * If MWI is enabled in the PCI configuration, and there
+ * is a valid cacheline size (8 or 16 dwords), then tell
+ * the board to turn on MWI.
+ */
+ fxp_pci_conf_read8(sc, PCI_CACHE_LINE_SIZE, &tmp_val);
+ DBGLVL_PRINTK(3,"fxp_attach: CACHE_LINE_SIZE = %d\n",tmp_val);
+ if (val16 & PCI_COMMAND_MEMORY &&
+ tmp_val != 0)
+ sc->flags |= FXP_FLAG_MWI_ENABLE;
+
+ /* turn on the extended TxCB feature */
+ sc->flags |= FXP_FLAG_EXT_TXCB;
+
+ /* enable reception of long frames for VLAN */
+ sc->flags |= FXP_FLAG_LONG_PKT_EN;
+ DBGLVL_PRINTK(3,"fxp_attach: sc->flags = 0x%x\n",
+ sc->flags);
+ }
+
+ /*
+ * Read MAC address.
+ */
+ fxp_read_eeprom(sc, (u_int16_t*)sc->arpcom.ac_enaddr, 0, 3);
+ if (fxp_is_verbose) {
+ device_printf(dev, "Ethernet address %x:%x:%x:%x:%x:%x %s \n",
+ ((u_int8_t*)sc->arpcom.ac_enaddr)[0],
+ ((u_int8_t*)sc->arpcom.ac_enaddr)[1],
+ ((u_int8_t*)sc->arpcom.ac_enaddr)[2],
+ ((u_int8_t*)sc->arpcom.ac_enaddr)[3],
+ ((u_int8_t*)sc->arpcom.ac_enaddr)[4],
+ ((u_int8_t*)sc->arpcom.ac_enaddr)[5],
+ sc->flags & FXP_FLAG_SERIAL_MEDIA ? ", 10Mbps" : "");
+ device_printf(dev, "PCI IDs: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ fxp_pci_get_vendor(sc), fxp_pci_get_device(sc),
+ fxp_pci_get_subvendor(sc), fxp_pci_get_subdevice(sc),
+ fxp_pci_get_revid(sc));
+ device_printf(dev, "Chip Type: %d\n", sc->chip);
+ }
+
+#ifdef NOTUSED /* do not set up interface at all... */
+ /*
+ * If this is only a 10Mbps device, then there is no MII, and
+ * the PHY will use a serial interface instead.
+ *
+ * The Seeq 80c24 AutoDUPLEX(tm) Ethernet Interface Adapter
+ * doesn't have a programming interface of any sort. The
+ * media is sensed automatically based on how the link partner
+ * is configured. This is, in essence, manual configuration.
+ */
+ if (sc->flags & FXP_FLAG_SERIAL_MEDIA) {
+ ifmedia_init(&sc->sc_media, 0, fxp_serial_ifmedia_upd,
+ fxp_serial_ifmedia_sts);
+ ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_MANUAL, 0, NULL);
+ ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_MANUAL);
+ } else {
+ if (mii_phy_probe(dev, &sc->miibus, fxp_ifmedia_upd,
+ fxp_ifmedia_sts)) {
+ device_printf(dev, "MII without any PHY!\n");
+ error = ENXIO;
+ goto fail;
+ }
+ }
+#endif
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ ifp->if_softc = sc;
+ ifp->if_unit = unitNumber;
+ ifp->if_name = unitName;
+ ifp->if_mtu = mtu;
+ ifp->if_baudrate = 100000000;
+ ifp->if_init = fxp_init;
+ ifp->if_ioctl = fxp_ioctl;
+ ifp->if_start = fxp_start;
+ ifp->if_output = ether_output;
+ ifp->if_watchdog = fxp_watchdog;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX /*| IFF_MULTICAST*/;
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ /*
+ * Attach the interface.
+ */
+ DBGLVL_PRINTK(3,"fxp_attach: calling if_attach\n");
+ if_attach (ifp);
+ DBGLVL_PRINTK(3,"fxp_attach: calling ether_if_attach\n");
+ ether_ifattach(ifp);
+ DBGLVL_PRINTK(3,"fxp_attach: return from ether_if_attach\n");
+
+#ifdef NOTUSED
+ /*
+ * Tell the upper layer(s) we support long frames.
+ */
+ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
+#endif
+ /*
+ * Let the system queue as many packets as we have available
+ * TX descriptors.
+ */
+ ifp->if_snd.ifq_maxlen = FXP_NTXCB - 1;
+
+ splx(s);
+ return (0);
+
+failmem:
+ device_printf(dev, "Failed to malloc memory\n");
+ error = ENOMEM;
+#ifdef NOTUSED
+fail:
+#endif
+ splx(s);
+ fxp_release(sc);
+ return (error);
+}
+
+/*
+ * release all resources
+ */
+static void
+fxp_release(struct fxp_softc *sc)
+{
+
+#ifdef NOTUSED
+ bus_generic_detach(sc->dev);
+ if (sc->miibus)
+ device_delete_child(sc->dev, sc->miibus);
+#endif
+ if (sc->cbl_base)
+ free(sc->cbl_base, M_DEVBUF);
+ if (sc->fxp_stats)
+ free(sc->fxp_stats, M_DEVBUF);
+ if (sc->mcsp)
+ free(sc->mcsp, M_DEVBUF);
+ if (sc->rfa_headm)
+ m_freem(sc->rfa_headm);
+
+#ifdef NOTUSED
+ if (sc->ih)
+ bus_teardown_intr(sc->dev, sc->irq, sc->ih);
+ if (sc->irq)
+ bus_release_resource(sc->dev, SYS_RES_IRQ, 0, sc->irq);
+ if (sc->mem)
+ bus_release_resource(sc->dev, sc->rtp, sc->rgd, sc->mem);
+ mtx_destroy(&sc->sc_mtx);
+#endif
+}
+
+#if NOTUSED
+/*
+ * Detach interface.
+ */
+static int
+fxp_detach(device_t dev)
+{
+ struct fxp_softc *sc = device_get_softc(dev);
+ int s;
+
+ /* disable interrupts */
+ CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL, FXP_SCB_INTR_DISABLE);
+
+ s = splimp();
+
+ /*
+ * Stop DMA and drop transmit queue.
+ */
+ fxp_stop(sc);
+
+ /*
+ * Close down routes etc.
+ */
+ ether_ifdetach(&sc->arpcom.ac_if, ETHER_BPF_SUPPORTED);
+
+ /*
+ * Free all media structures.
+ */
+ ifmedia_removeall(&sc->sc_media);
+
+ splx(s);
+
+ /* Release our allocated resources. */
+ fxp_release(sc);
+
+ return (0);
+}
+
+/*
+ * Device shutdown routine. Called at system shutdown after sync. The
+ * main purpose of this routine is to shut off receiver DMA so that
+ * kernel memory doesn't get clobbered during warmboot.
+ */
+static int
+fxp_shutdown(device_t dev)
+{
+ /*
+ * Make sure that DMA is disabled prior to reboot. Not doing
+ * do could allow DMA to corrupt kernel memory during the
+ * reboot before the driver initializes.
+ */
+ fxp_stop((struct fxp_softc *) device_get_softc(dev));
+ return (0);
+}
+#endif
+
+/*
+ * Show interface statistics
+ */
+static void
+fxp_stats(struct fxp_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_if;
+
+ printf (" Output packets:%-8" PRIu64, ifp->if_opackets);
+ printf (" Collisions:%-8" PRIu64, ifp->if_collisions);
+ printf (" Output errors:%-8" PRIu64 "\n", ifp->if_oerrors);
+ printf (" Input packets:%-8" PRIu64, ifp->if_ipackets);
+ printf (" Input errors:%-8" PRIu64 "\n", ifp->if_ierrors);
+}
+
+static void
+fxp_eeprom_shiftin(struct fxp_softc *sc, int data, int length)
+{
+ u_int16_t reg;
+ int x;
+
+ /*
+ * Shift in data.
+ */
+ for (x = 1 << (length - 1); x; x >>= 1) {
+ if (data & x)
+ reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI;
+ else
+ reg = FXP_EEPROM_EECS;
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
+ DELAY(1);
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK);
+ DELAY(1);
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
+ DELAY(1);
+ }
+}
+
+/*
+ * Read from the serial EEPROM. Basically, you manually shift in
+ * the read opcode (one bit at a time) and then shift in the address,
+ * and then you shift out the data (all of this one bit at a time).
+ * The word size is 16 bits, so you have to provide the address for
+ * every 16 bits of data.
+ */
+static u_int16_t
+fxp_eeprom_getword(struct fxp_softc *sc, int offset, int autosize)
+{
+ u_int16_t reg, data;
+ int x;
+
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
+ /*
+ * Shift in read opcode.
+ */
+ fxp_eeprom_shiftin(sc, FXP_EEPROM_OPC_READ, 3);
+ /*
+ * Shift in address.
+ */
+ data = 0;
+ for (x = 1 << (sc->eeprom_size - 1); x; x >>= 1) {
+ if (offset & x)
+ reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI;
+ else
+ reg = FXP_EEPROM_EECS;
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
+ DELAY(1);
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK);
+ DELAY(1);
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
+ DELAY(1);
+ reg = CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO;
+ data++;
+ if (autosize && reg == 0) {
+ sc->eeprom_size = data;
+ break;
+ }
+ }
+ /*
+ * Shift out data.
+ */
+ data = 0;
+ reg = FXP_EEPROM_EECS;
+ for (x = 1 << 15; x; x >>= 1) {
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK);
+ DELAY(1);
+ if (CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO)
+ data |= x;
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
+ DELAY(1);
+ }
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0);
+ DELAY(1);
+
+ return (data);
+}
+
+static void
+fxp_eeprom_putword(struct fxp_softc *sc, int offset, u_int16_t data)
+{
+ int i;
+
+ /*
+ * Erase/write enable.
+ */
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
+ fxp_eeprom_shiftin(sc, 0x4, 3);
+ fxp_eeprom_shiftin(sc, 0x03 << (sc->eeprom_size - 2), sc->eeprom_size);
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0);
+ DELAY(1);
+ /*
+ * Shift in write opcode, address, data.
+ */
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
+ fxp_eeprom_shiftin(sc, FXP_EEPROM_OPC_WRITE, 3);
+ fxp_eeprom_shiftin(sc, offset, sc->eeprom_size);
+ fxp_eeprom_shiftin(sc, data, 16);
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0);
+ DELAY(1);
+ /*
+ * Wait for EEPROM to finish up.
+ */
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
+ DELAY(1);
+ for (i = 0; i < 1000; i++) {
+ if (CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO)
+ break;
+ DELAY(50);
+ }
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0);
+ DELAY(1);
+ /*
+ * Erase/write disable.
+ */
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
+ fxp_eeprom_shiftin(sc, 0x4, 3);
+ fxp_eeprom_shiftin(sc, 0, sc->eeprom_size);
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0);
+ DELAY(1);
+}
+
+/*
+ * From NetBSD:
+ *
+ * Figure out EEPROM size.
+ *
+ * 559's can have either 64-word or 256-word EEPROMs, the 558
+ * datasheet only talks about 64-word EEPROMs, and the 557 datasheet
+ * talks about the existance of 16 to 256 word EEPROMs.
+ *
+ * The only known sizes are 64 and 256, where the 256 version is used
+ * by CardBus cards to store CIS information.
+ *
+ * The address is shifted in msb-to-lsb, and after the last
+ * address-bit the EEPROM is supposed to output a `dummy zero' bit,
+ * after which follows the actual data. We try to detect this zero, by
+ * probing the data-out bit in the EEPROM control register just after
+ * having shifted in a bit. If the bit is zero, we assume we've
+ * shifted enough address bits. The data-out should be tri-state,
+ * before this, which should translate to a logical one.
+ */
+static void
+fxp_autosize_eeprom(struct fxp_softc *sc)
+{
+
+ /* guess maximum size of 256 words */
+ sc->eeprom_size = 8;
+
+ /* autosize */
+ (void) fxp_eeprom_getword(sc, 0, 1);
+}
+
+static void
+fxp_read_eeprom(struct fxp_softc *sc, u_short *data, int offset, int words)
+{
+ int i;
+
+ for (i = 0; i < words; i++) {
+ data[i] = fxp_eeprom_getword(sc, offset + i, 0);
+ DBGLVL_PRINTK(4,"fxp_eeprom_read(off=0x%x)=0x%x\n",
+ offset+i,data[i]);
+ }
+}
+
+static void
+fxp_write_eeprom(struct fxp_softc *sc, u_short *data, int offset, int words)
+{
+ int i;
+
+ for (i = 0; i < words; i++)
+ fxp_eeprom_putword(sc, offset + i, data[i]);
+ DBGLVL_PRINTK(4,"fxp_eeprom_write(off=0x%x,0x%x)\n",
+ offset+i,data[i]);
+}
+
+/*
+ * Start packet transmission on the interface.
+ */
+static void
+fxp_start(struct ifnet *ifp)
+{
+ struct fxp_softc *sc = ifp->if_softc;
+ struct fxp_cb_tx *txp;
+
+ DBGLVL_PRINTK(3,"fxp_start called\n");
+
+ /*
+ * See if we need to suspend xmit until the multicast filter
+ * has been reprogrammed (which can only be done at the head
+ * of the command chain).
+ */
+ if (sc->need_mcsetup) {
+ DBGLVL_PRINTK(3,"fxp_start need_mcsetup\n");
+ return;
+ }
+
+ txp = NULL;
+
+ /*
+ * We're finished if there is nothing more to add to the list or if
+ * we're all filled up with buffers to transmit.
+ * NOTE: One TxCB is reserved to guarantee that fxp_mc_setup() can add
+ * a NOP command when needed.
+ */
+ while (ifp->if_snd.ifq_head != NULL && sc->tx_queued < FXP_NTXCB - 1) {
+ struct mbuf *m, *mb_head;
+ int segment;
+
+ /*
+ * Grab a packet to transmit.
+ */
+ IF_DEQUEUE(&ifp->if_snd, mb_head);
+
+ /*
+ * Get pointer to next available tx desc.
+ */
+ txp = sc->cbl_last->next;
+
+ /*
+ * Go through each of the mbufs in the chain and initialize
+ * the transmit buffer descriptors with the physical address
+ * and size of the mbuf.
+ */
+tbdinit:
+ for (m = mb_head, segment = 0; m != NULL; m = m->m_next) {
+ if (m->m_len != 0) {
+ if (segment == FXP_NTXSEG)
+ break;
+ txp->tbd[segment].tb_addr =
+ vtophys(mtod(m, vm_offset_t));
+ txp->tbd[segment].tb_size = m->m_len;
+ segment++;
+ }
+ }
+ if (m != NULL) {
+ struct mbuf *mn;
+
+ /*
+ * We ran out of segments. We have to recopy this
+ * mbuf chain first. Bail out if we can't get the
+ * new buffers.
+ */
+ MGETHDR(mn, M_DONTWAIT, MT_DATA);
+ if (mn == NULL) {
+ m_freem(mb_head);
+ break;
+ }
+ if (mb_head->m_pkthdr.len > MHLEN) {
+ MCLGET(mn, M_DONTWAIT);
+ if ((mn->m_flags & M_EXT) == 0) {
+ m_freem(mn);
+ m_freem(mb_head);
+ break;
+ }
+ }
+ m_copydata(mb_head, 0, mb_head->m_pkthdr.len,
+ mtod(mn, caddr_t));
+ mn->m_pkthdr.len = mn->m_len = mb_head->m_pkthdr.len;
+ m_freem(mb_head);
+ mb_head = mn;
+ goto tbdinit;
+ }
+
+ txp->tbd_number = segment;
+ txp->mb_head = mb_head;
+ txp->cb_status = 0;
+ if (sc->tx_queued != FXP_CXINT_THRESH - 1) {
+ txp->cb_command =
+ FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF |
+ FXP_CB_COMMAND_S;
+ } else {
+ txp->cb_command =
+ FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF |
+ FXP_CB_COMMAND_S | FXP_CB_COMMAND_I;
+ /*
+ * Set a 5 second timer just in case we don't hear
+ * from the card again.
+ */
+ ifp->if_timer = 5;
+ }
+ txp->tx_threshold = tx_threshold;
+
+ /*
+ * Advance the end of list forward.
+ */
+
+#ifdef __alpha__
+ /*
+ * On platforms which can't access memory in 16-bit
+ * granularities, we must prevent the card from DMA'ing
+ * up the status while we update the command field.
+ * This could cause us to overwrite the completion status.
+ */
+ atomic_clear_short(&sc->cbl_last->cb_command,
+ FXP_CB_COMMAND_S);
+#else
+ sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S;
+#endif /*__alpha__*/
+ sc->cbl_last = txp;
+
+ /*
+ * Advance the beginning of the list forward if there are
+ * no other packets queued (when nothing is queued, cbl_first
+ * sits on the last TxCB that was sent out).
+ */
+ if (sc->tx_queued == 0)
+ sc->cbl_first = txp;
+
+ sc->tx_queued++;
+
+#ifdef NOTUSED
+ /*
+ * Pass packet to bpf if there is a listener.
+ */
+ if (ifp->if_bpf)
+ bpf_mtap(ifp, mb_head);
+#endif
+ }
+
+ /*
+ * We're finished. If we added to the list, issue a RESUME to get DMA
+ * going again if suspended.
+ */
+ if (txp != NULL) {
+ fxp_scb_wait(sc);
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_RESUME);
+ }
+
+ /*
+ * reenable interrupts
+ */
+ RTEMS_COMPILER_MEMORY_BARRIER();
+ CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL,0);
+ bsp_interrupt_vector_enable(sc->irq_num);
+ RTEMS_COMPILER_MEMORY_BARRIER();
+}
+
+/*
+ * Process interface interrupts.
+ */
+static void fxp_intr(void *arg)
+{
+ /*
+ * Obtain device state
+ */
+ struct fxp_softc *sc = (struct fxp_softc *)arg;
+
+ /*
+ * disable interrupts
+ */
+ CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL, FXP_SCB_INTR_DISABLE);
+ /*
+ * send event to deamon
+ */
+ rtems_bsdnet_event_send (sc->daemonTid, INTERRUPT_EVENT);
+}
+
+static void fxp_daemon(void *xsc)
+{
+ struct fxp_softc *sc = xsc;
+ struct ifnet *ifp = &sc->sc_if;
+ u_int8_t statack;
+ rtems_event_set events;
+
+#ifdef NOTUSED
+ if (sc->suspended) {
+ return;
+ }
+#endif
+ for (;;) {
+
+ DBGLVL_PRINTK(4,"fxp_daemon waiting for event, INTRCNTL 0x%02x\n",
+ CSR_READ_1(sc, FXP_CSR_SCB_INTRCNTL));
+ /*
+ * wait for event to receive from interrupt function
+ */
+ rtems_bsdnet_event_receive (INTERRUPT_EVENT,
+ RTEMS_WAIT|RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events);
+ while ((statack = CSR_READ_1(sc, FXP_CSR_SCB_STATACK)) != 0) {
+ DBGLVL_PRINTK(4,"fxp_daemon: processing event, statack = 0x%x\n",
+ statack);
+#ifdef NOTUSED
+ /*
+ * It should not be possible to have all bits set; the
+ * FXP_SCB_INTR_SWI bit always returns 0 on a read. If
+ * all bits are set, this may indicate that the card has
+ * been physically ejected, so ignore it.
+ */
+ if (statack == 0xff)
+ return;
+#endif
+
+ /*
+ * First ACK all the interrupts in this pass.
+ */
+ CSR_WRITE_1(sc, FXP_CSR_SCB_STATACK, statack);
+
+ /*
+ * Free any finished transmit mbuf chains.
+ *
+ * Handle the CNA event likt a CXTNO event. It used to
+ * be that this event (control unit not ready) was not
+ * encountered, but it is now with the SMPng modifications.
+ * The exact sequence of events that occur when the interface
+ * is brought up are different now, and if this event
+ * goes unhandled, the configuration/rxfilter setup sequence
+ * can stall for several seconds. The result is that no
+ * packets go out onto the wire for about 5 to 10 seconds
+ * after the interface is ifconfig'ed for the first time.
+ */
+ if (statack & (FXP_SCB_STATACK_CXTNO | FXP_SCB_STATACK_CNA)) {
+ struct fxp_cb_tx *txp;
+
+ for (txp = sc->cbl_first; sc->tx_queued &&
+ (txp->cb_status & FXP_CB_STATUS_C) != 0;
+ txp = txp->next) {
+ if (txp->mb_head != NULL) {
+ m_freem(txp->mb_head);
+ txp->mb_head = NULL;
+ }
+ sc->tx_queued--;
+ }
+ sc->cbl_first = txp;
+ ifp->if_timer = 0;
+ if (sc->tx_queued == 0) {
+ if (sc->need_mcsetup)
+ fxp_mc_setup(sc);
+ }
+ /*
+ * Try to start more packets transmitting.
+ */
+ if (ifp->if_snd.ifq_head != NULL)
+ fxp_start(ifp);
+ }
+ /*
+ * Process receiver interrupts. If a no-resource (RNR)
+ * condition exists, get whatever packets we can and
+ * re-start the receiver.
+ */
+ if (statack & (FXP_SCB_STATACK_FR | FXP_SCB_STATACK_RNR)) {
+ struct mbuf *m;
+ struct fxp_rfa *rfa;
+rcvloop:
+ m = sc->rfa_headm;
+ rfa = (struct fxp_rfa *)(m->m_ext.ext_buf +
+ RFA_ALIGNMENT_FUDGE);
+
+ if (rfa->rfa_status & FXP_RFA_STATUS_C) {
+ /*
+ * Remove first packet from the chain.
+ */
+ sc->rfa_headm = m->m_next;
+ m->m_next = NULL;
+
+ /*
+ * Add a new buffer to the receive chain.
+ * If this fails, the old buffer is recycled
+ * instead.
+ */
+ if (fxp_add_rfabuf(sc, m) == 0) {
+ struct ether_header *eh;
+ int total_len;
+
+ total_len = rfa->actual_size &
+ (MCLBYTES - 1);
+ if (total_len <
+ sizeof(struct ether_header)) {
+ m_freem(m);
+ goto rcvloop;
+ }
+
+ /*
+ * Drop the packet if it has CRC
+ * errors. This test is only needed
+ * when doing 802.1q VLAN on the 82557
+ * chip.
+ */
+ if (rfa->rfa_status &
+ FXP_RFA_STATUS_CRC) {
+ m_freem(m);
+ goto rcvloop;
+ }
+
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = total_len;
+ eh = mtod(m, struct ether_header *);
+ m->m_data +=
+ sizeof(struct ether_header);
+ m->m_len -=
+ sizeof(struct ether_header);
+ m->m_pkthdr.len = m->m_len;
+ ether_input(ifp, eh, m);
+ }
+ goto rcvloop;
+ }
+ if (statack & FXP_SCB_STATACK_RNR) {
+ fxp_scb_wait(sc);
+ CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL,
+ vtophys(sc->rfa_headm->m_ext.ext_buf) +
+ RFA_ALIGNMENT_FUDGE);
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_RU_START);
+ }
+ }
+ }
+ /*
+ * reenable interrupts
+ */
+ RTEMS_COMPILER_MEMORY_BARRIER();
+ CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL,0);
+ RTEMS_COMPILER_MEMORY_BARRIER();
+ }
+}
+
+/*
+ * Update packet in/out/collision statistics. The i82557 doesn't
+ * allow you to access these counters without doing a fairly
+ * expensive DMA to get _all_ of the statistics it maintains, so
+ * we do this operation here only once per second. The statistics
+ * counters in the kernel are updated from the previous dump-stats
+ * DMA and then a new dump-stats DMA is started. The on-chip
+ * counters are zeroed when the DMA completes. If we can't start
+ * the DMA immediately, we don't wait - we just prepare to read
+ * them again next time.
+ */
+static void
+fxp_tick(void *xsc)
+{
+ struct fxp_softc *sc = xsc;
+ struct ifnet *ifp = &sc->sc_if;
+ struct fxp_stats *sp = sc->fxp_stats;
+ struct fxp_cb_tx *txp;
+ int s;
+
+ DBGLVL_PRINTK(4,"fxp_tick called\n");
+
+ ifp->if_opackets += sp->tx_good;
+ ifp->if_collisions += sp->tx_total_collisions;
+ if (sp->rx_good) {
+ ifp->if_ipackets += sp->rx_good;
+ sc->rx_idle_secs = 0;
+ } else {
+ /*
+ * Receiver's been idle for another second.
+ */
+ sc->rx_idle_secs++;
+ }
+ ifp->if_ierrors +=
+ sp->rx_crc_errors +
+ sp->rx_alignment_errors +
+ sp->rx_rnr_errors +
+ sp->rx_overrun_errors;
+ /*
+ * If any transmit underruns occured, bump up the transmit
+ * threshold by another 512 bytes (64 * 8).
+ */
+ if (sp->tx_underruns) {
+ ifp->if_oerrors += sp->tx_underruns;
+ if (tx_threshold < 192)
+ tx_threshold += 64;
+ }
+ s = splimp();
+ /*
+ * Release any xmit buffers that have completed DMA. This isn't
+ * strictly necessary to do here, but it's advantagous for mbufs
+ * with external storage to be released in a timely manner rather
+ * than being defered for a potentially long time. This limits
+ * the delay to a maximum of one second.
+ */
+ for (txp = sc->cbl_first; sc->tx_queued &&
+ (txp->cb_status & FXP_CB_STATUS_C) != 0;
+ txp = txp->next) {
+ if (txp->mb_head != NULL) {
+ m_freem(txp->mb_head);
+ txp->mb_head = NULL;
+ }
+ sc->tx_queued--;
+ }
+ sc->cbl_first = txp;
+ /*
+ * If we haven't received any packets in FXP_MAC_RX_IDLE seconds,
+ * then assume the receiver has locked up and attempt to clear
+ * the condition by reprogramming the multicast filter. This is
+ * a work-around for a bug in the 82557 where the receiver locks
+ * up if it gets certain types of garbage in the syncronization
+ * bits prior to the packet header. This bug is supposed to only
+ * occur in 10Mbps mode, but has been seen to occur in 100Mbps
+ * mode as well (perhaps due to a 10/100 speed transition).
+ */
+ if (sc->rx_idle_secs > FXP_MAX_RX_IDLE) {
+ sc->rx_idle_secs = 0;
+ fxp_mc_setup(sc);
+ }
+ /*
+ * If there is no pending command, start another stats
+ * dump. Otherwise punt for now.
+ */
+ if (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) == 0) {
+ /*
+ * Start another stats dump.
+ */
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_DUMPRESET);
+ } else {
+ /*
+ * A previous command is still waiting to be accepted.
+ * Just zero our copy of the stats and wait for the
+ * next timer event to update them.
+ */
+ sp->tx_good = 0;
+ sp->tx_underruns = 0;
+ sp->tx_total_collisions = 0;
+
+ sp->rx_good = 0;
+ sp->rx_crc_errors = 0;
+ sp->rx_alignment_errors = 0;
+ sp->rx_rnr_errors = 0;
+ sp->rx_overrun_errors = 0;
+ }
+#ifdef NOTUSED
+ if (sc->miibus != NULL)
+ mii_tick(device_get_softc(sc->miibus));
+#endif
+ splx(s);
+ /*
+ * Schedule another timeout one second from now.
+ */
+ if (sc->stat_ch == fxp_timeout_running) {
+ timeout(fxp_tick, sc, hz);
+ }
+ else if (sc->stat_ch == fxp_timeout_stop_rq) {
+ sc->stat_ch = fxp_timeout_stopped;
+ }
+}
+
+/*
+ * Stop the interface. Cancels the statistics updater and resets
+ * the interface.
+ */
+static void
+fxp_stop(struct fxp_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_if;
+ struct fxp_cb_tx *txp;
+ int i;
+
+ DBGLVL_PRINTK(2,"fxp_stop called\n");
+
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+ ifp->if_timer = 0;
+
+ /*
+ * stop stats updater.
+ */
+ if (sc->stat_ch == fxp_timeout_running) {
+ DBGLVL_PRINTK(3,"fxp_stop: trying to stop stat update tick\n");
+ sc->stat_ch = fxp_timeout_stop_rq;
+ while(sc->stat_ch != fxp_timeout_stopped) {
+ rtems_bsdnet_semaphore_release();
+ rtems_task_wake_after(fxp_ticksPerSecond);
+ rtems_bsdnet_semaphore_obtain();
+ }
+ DBGLVL_PRINTK(3,"fxp_stop: stat update tick stopped\n");
+ }
+ /*
+ * Issue software reset
+ */
+ DBGLVL_PRINTK(3,"fxp_stop: issue software reset\n");
+ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET);
+ DELAY(10);
+
+ /*
+ * Release any xmit buffers.
+ */
+ DBGLVL_PRINTK(3,"fxp_stop: releasing xmit buffers\n");
+ txp = sc->cbl_base;
+ if (txp != NULL) {
+ for (i = 0; i < FXP_NTXCB; i++) {
+ if (txp[i].mb_head != NULL) {
+ m_freem(txp[i].mb_head);
+ txp[i].mb_head = NULL;
+ }
+ }
+ }
+ sc->tx_queued = 0;
+
+ /*
+ * Free all the receive buffers then reallocate/reinitialize
+ */
+ DBGLVL_PRINTK(3,"fxp_stop: free and reinit all receive buffers\n");
+ if (sc->rfa_headm != NULL)
+ m_freem(sc->rfa_headm);
+ sc->rfa_headm = NULL;
+ sc->rfa_tailm = NULL;
+ for (i = 0; i < FXP_NRFABUFS; i++) {
+ if (fxp_add_rfabuf(sc, NULL) != 0) {
+ /*
+ * This "can't happen" - we're at splimp()
+ * and we just freed all the buffers we need
+ * above.
+ */
+ panic("fxp_stop: no buffers!");
+ }
+ }
+ DBGLVL_PRINTK(2,"fxp_stop: finished\n");
+}
+
+/*
+ * Watchdog/transmission transmit timeout handler. Called when a
+ * transmission is started on the interface, but no interrupt is
+ * received before the timeout. This usually indicates that the
+ * card has wedged for some reason.
+ */
+static void
+fxp_watchdog(struct ifnet *ifp)
+{
+ struct fxp_softc *sc = ifp->if_softc;
+
+ device_printf(sc->dev, "device timeout\n");
+ ifp->if_oerrors++;
+
+ fxp_init(sc);
+}
+
+static void
+fxp_init(void *xsc)
+{
+ struct fxp_softc *sc = xsc;
+ struct ifnet *ifp = &sc->sc_if;
+ struct fxp_cb_config *cbp;
+ struct fxp_cb_ias *cb_ias;
+ struct fxp_cb_tx *txp;
+ int i, prm, s;
+ rtems_status_code statcode;
+
+rtems_task_wake_after(100);
+ DBGLVL_PRINTK(2,"fxp_init called\n");
+
+ s = splimp();
+ /*
+ * Cancel any pending I/O
+ */
+ /*
+ * E. Norum 2004-10-11
+ * Add line suggested by "Eugene Denisov" <dea@sendmail.ru>.
+ * Prevents lockup at initialization.
+ */
+ sc->stat_ch = fxp_timeout_stopped;
+ fxp_stop(sc);
+
+ prm = (ifp->if_flags & IFF_PROMISC) ? 1 : 0;
+
+ DBGLVL_PRINTK(5,"fxp_init: Initializing base of CBL and RFA memory\n");
+ /*
+ * Initialize base of CBL and RFA memory. Loading with zero
+ * sets it up for regular linear addressing.
+ */
+ CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, 0);
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_BASE);
+
+ fxp_scb_wait(sc);
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_RU_BASE);
+
+ /*
+ * Initialize base of dump-stats buffer.
+ */
+ DBGLVL_PRINTK(5,"fxp_init: Initializing base of dump-stats buffer\n");
+ fxp_scb_wait(sc);
+ CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(sc->fxp_stats));
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_DUMP_ADR);
+
+ /*
+ * We temporarily use memory that contains the TxCB list to
+ * construct the config CB. The TxCB list memory is rebuilt
+ * later.
+ */
+ cbp = (struct fxp_cb_config *) sc->cbl_base;
+ DBGLVL_PRINTK(5,"fxp_init: cbp = 0x%x\n",cbp);
+
+ /*
+ * This memcpy is kind of disgusting, but there are a bunch of must be
+ * zero and must be one bits in this structure and this is the easiest
+ * way to initialize them all to proper values.
+ */
+ memcpy( (void *)(u_int32_t*)(volatile void *)&cbp->cb_status,
+ fxp_cb_config_template,
+ sizeof(fxp_cb_config_template));
+
+ cbp->cb_status = 0;
+ cbp->cb_command = FXP_CB_COMMAND_CONFIG | FXP_CB_COMMAND_EL;
+ cbp->link_addr = -1; /* (no) next command */
+ cbp->byte_count = 22; /* (22) bytes to config */
+ cbp->rx_fifo_limit = 8; /* rx fifo threshold (32 bytes) */
+ cbp->tx_fifo_limit = 0; /* tx fifo threshold (0 bytes) */
+ cbp->adaptive_ifs = 0; /* (no) adaptive interframe spacing */
+ cbp->mwi_enable = sc->flags & FXP_FLAG_MWI_ENABLE ? 1 : 0;
+ cbp->type_enable = 0; /* actually reserved */
+ cbp->read_align_en = sc->flags & FXP_FLAG_READ_ALIGN ? 1 : 0;
+ cbp->end_wr_on_cl = sc->flags & FXP_FLAG_WRITE_ALIGN ? 1 : 0;
+ cbp->rx_dma_bytecount = 0; /* (no) rx DMA max */
+ cbp->tx_dma_bytecount = 0; /* (no) tx DMA max */
+ cbp->dma_mbce = 0; /* (disable) dma max counters */
+ cbp->late_scb = 0; /* (don't) defer SCB update */
+ cbp->direct_dma_dis = 1; /* disable direct rcv dma mode */
+ cbp->tno_int_or_tco_en =0; /* (disable) tx not okay interrupt */
+ cbp->ci_int = 1; /* interrupt on CU idle */
+ cbp->ext_txcb_dis = sc->flags & FXP_FLAG_EXT_TXCB ? 0 : 1;
+ cbp->ext_stats_dis = 1; /* disable extended counters */
+ cbp->keep_overrun_rx = 0; /* don't pass overrun frames to host */
+ cbp->save_bf = sc->chip == FXP_CHIP_82557 ? 1 : prm;
+ cbp->disc_short_rx = !prm; /* discard short packets */
+ cbp->underrun_retry = 1; /* retry mode (once) on DMA underrun */
+ cbp->two_frames = 0; /* do not limit FIFO to 2 frames */
+ cbp->dyn_tbd = 0; /* (no) dynamic TBD mode */
+ cbp->mediatype = sc->flags & FXP_FLAG_SERIAL_MEDIA ? 0 : 1;
+ cbp->csma_dis = 0; /* (don't) disable link */
+ cbp->tcp_udp_cksum = 0; /* (don't) enable checksum */
+ cbp->vlan_tco = 0; /* (don't) enable vlan wakeup */
+ cbp->link_wake_en = 0; /* (don't) assert PME# on link change */
+ cbp->arp_wake_en = 0; /* (don't) assert PME# on arp */
+ cbp->mc_wake_en = 0; /* (don't) enable PME# on mcmatch */
+ cbp->nsai = 1; /* (don't) disable source addr insert */
+ cbp->preamble_length = 2; /* (7 byte) preamble */
+ cbp->loopback = 0; /* (don't) loopback */
+ cbp->linear_priority = 0; /* (normal CSMA/CD operation) */
+ cbp->linear_pri_mode = 0; /* (wait after xmit only) */
+ cbp->interfrm_spacing = 6; /* (96 bits of) interframe spacing */
+ cbp->promiscuous = prm; /* promiscuous mode */
+ cbp->bcast_disable = 0; /* (don't) disable broadcasts */
+ cbp->wait_after_win = 0; /* (don't) enable modified backoff alg*/
+ cbp->ignore_ul = 0; /* consider U/L bit in IA matching */
+ cbp->crc16_en = 0; /* (don't) enable crc-16 algorithm */
+ cbp->crscdt = sc->flags & FXP_FLAG_SERIAL_MEDIA ? 1 : 0;
+
+ cbp->stripping = !prm; /* truncate rx packet to byte count */
+ cbp->padding = 1; /* (do) pad short tx packets */
+ cbp->rcv_crc_xfer = 0; /* (don't) xfer CRC to host */
+ cbp->long_rx_en = sc->flags & FXP_FLAG_LONG_PKT_EN ? 1 : 0;
+ cbp->ia_wake_en = 0; /* (don't) wake up on address match */
+ cbp->magic_pkt_dis = 0; /* (don't) disable magic packet */
+ /* must set wake_en in PMCSR also */
+ cbp->force_fdx = 0; /* (don't) force full duplex */
+ cbp->fdx_pin_en = 1; /* (enable) FDX# pin */
+ cbp->multi_ia = 0; /* (don't) accept multiple IAs */
+ cbp->mc_all = sc->flags & FXP_FLAG_ALL_MCAST ? 1 : 0;
+
+ DBGLVL_PRINTK(5,"fxp_init: cbp initialized\n");
+ if (sc->chip == FXP_CHIP_82557) {
+ /*
+ * The 82557 has no hardware flow control, the values
+ * below are the defaults for the chip.
+ */
+ cbp->fc_delay_lsb = 0;
+ cbp->fc_delay_msb = 0x40;
+ cbp->pri_fc_thresh = 3;
+ cbp->tx_fc_dis = 0;
+ cbp->rx_fc_restop = 0;
+ cbp->rx_fc_restart = 0;
+ cbp->fc_filter = 0;
+ cbp->pri_fc_loc = 1;
+ } else {
+ cbp->fc_delay_lsb = 0x1f;
+ cbp->fc_delay_msb = 0x01;
+ cbp->pri_fc_thresh = 3;
+ cbp->tx_fc_dis = 0; /* enable transmit FC */
+ cbp->rx_fc_restop = 1; /* enable FC restop frames */
+ cbp->rx_fc_restart = 1; /* enable FC restart frames */
+ cbp->fc_filter = !prm; /* drop FC frames to host */
+ cbp->pri_fc_loc = 1; /* FC pri location (byte31) */
+ }
+
+ /*
+ * Start the config command/DMA.
+ */
+ DBGLVL_PRINTK(5,"fxp_init: starting config command/DMA\n");
+ fxp_scb_wait(sc);
+ CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&cbp->cb_status));
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_START);
+ /* ...and wait for it to complete. */
+ fxp_dma_wait(&cbp->cb_status, sc);
+
+ /*
+ * Now initialize the station address. Temporarily use the TxCB
+ * memory area like we did above for the config CB.
+ */
+ DBGLVL_PRINTK(5,"fxp_init: initialize station address\n");
+ cb_ias = (struct fxp_cb_ias *) sc->cbl_base;
+ cb_ias->cb_status = 0;
+ cb_ias->cb_command = FXP_CB_COMMAND_IAS | FXP_CB_COMMAND_EL;
+ cb_ias->link_addr = -1;
+ memcpy((void *)(u_int32_t*)(volatile void *)cb_ias->macaddr,
+ sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr));
+
+ /*
+ * Start the IAS (Individual Address Setup) command/DMA.
+ */
+ DBGLVL_PRINTK(5,"fxp_init: start IAS command/DMA\n");
+ fxp_scb_wait(sc);
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_START);
+ /* ...and wait for it to complete. */
+ fxp_dma_wait(&cb_ias->cb_status, sc);
+
+ /*
+ * Initialize transmit control block (TxCB) list.
+ */
+
+ DBGLVL_PRINTK(5,"fxp_init: initialize TxCB list\n");
+ txp = sc->cbl_base;
+ memset(txp, 0, sizeof(struct fxp_cb_tx) * FXP_NTXCB);
+ for (i = 0; i < FXP_NTXCB; i++) {
+ txp[i].cb_status = FXP_CB_STATUS_C | FXP_CB_STATUS_OK;
+ txp[i].cb_command = FXP_CB_COMMAND_NOP;
+ txp[i].link_addr =
+ vtophys(&txp[(i + 1) & FXP_TXCB_MASK].cb_status);
+ if (sc->flags & FXP_FLAG_EXT_TXCB)
+ txp[i].tbd_array_addr = vtophys(&txp[i].tbd[2]);
+ else
+ txp[i].tbd_array_addr = vtophys(&txp[i].tbd[0]);
+ txp[i].next = &txp[(i + 1) & FXP_TXCB_MASK];
+ }
+ /*
+ * Set the suspend flag on the first TxCB and start the control
+ * unit. It will execute the NOP and then suspend.
+ */
+ DBGLVL_PRINTK(5,"fxp_init: setup suspend flag\n");
+ txp->cb_command = FXP_CB_COMMAND_NOP | FXP_CB_COMMAND_S;
+ sc->cbl_first = sc->cbl_last = txp;
+ sc->tx_queued = 1;
+
+ fxp_scb_wait(sc);
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_START);
+
+ /*
+ * Initialize receiver buffer area - RFA.
+ */
+ DBGLVL_PRINTK(5,"fxp_init: initialize RFA\n");
+ fxp_scb_wait(sc);
+ CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL,
+ vtophys(sc->rfa_headm->m_ext.ext_buf) + RFA_ALIGNMENT_FUDGE);
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_RU_START);
+
+#ifdef NOTUSED
+ /*
+ * Set current media.
+ */
+ if (sc->miibus != NULL)
+ mii_mediachg(device_get_softc(sc->miibus));
+#endif
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ if (sc->daemonTid == 0) {
+ /*
+ * Start driver task
+ */
+ sc->daemonTid = rtems_bsdnet_newproc ("FXPd", 4096, fxp_daemon, sc);
+
+ /*
+ * Set up interrupts
+ */
+ statcode = rtems_interrupt_handler_install(
+ sc->irq_num,
+ "fxp_intr",
+ RTEMS_INTERRUPT_SHARED,
+ fxp_intr,
+ sc
+ );
+
+ if ( statcode != RTEMS_SUCCESSFUL ) {
+ rtems_panic ("Can't attach fxp interrupt handler for irq %d\n",
+ sc->irq_num);
+ }
+ }
+
+ /*
+ * Enable interrupts.
+ */
+ CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL, 0);
+ splx(s);
+
+ /*
+ * Start stats updater.
+ */
+ sc->stat_ch = fxp_timeout_running;
+ DBGLVL_PRINTK(2,"fxp_init: stats updater timeout called with hz=%d\n", hz);
+ timeout(fxp_tick, sc, hz);
+ DBGLVL_PRINTK(2,"fxp_init finished\n");
+}
+
+#ifdef NOTUSED
+static int
+fxp_serial_ifmedia_upd(struct ifnet *ifp)
+{
+
+ return (0);
+}
+
+static void
+fxp_serial_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+
+ ifmr->ifm_active = IFM_ETHER|IFM_MANUAL;
+}
+
+/*
+ * Change media according to request.
+ */
+static int
+fxp_ifmedia_upd(struct ifnet *ifp)
+{
+ struct fxp_softc *sc = ifp->if_softc;
+ struct mii_data *mii;
+
+ mii = device_get_softc(sc->miibus);
+ mii_mediachg(mii);
+ return (0);
+}
+
+/*
+ * Notify the world which media we're using.
+ */
+static void
+fxp_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct fxp_softc *sc = ifp->if_softc;
+ struct mii_data *mii;
+
+ mii = device_get_softc(sc->miibus);
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+
+ if (ifmr->ifm_status & IFM_10_T && sc->flags & FXP_FLAG_CU_RESUME_BUG)
+ sc->cu_resume_bug = 1;
+ else
+ sc->cu_resume_bug = 0;
+}
+#endif
+
+/*
+ * Add a buffer to the end of the RFA buffer list.
+ * Return 0 if successful, 1 for failure. A failure results in
+ * adding the 'oldm' (if non-NULL) on to the end of the list -
+ * tossing out its old contents and recycling it.
+ * The RFA struct is stuck at the beginning of mbuf cluster and the
+ * data pointer is fixed up to point just past it.
+ */
+static int
+fxp_add_rfabuf(struct fxp_softc *sc, struct mbuf *oldm)
+{
+ u_int32_t v;
+ struct mbuf *m;
+ struct fxp_rfa *rfa, *p_rfa;
+
+ DBGLVL_PRINTK(4,"fxp_add_rfabuf called\n");
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m != NULL) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_freem(m);
+ if (oldm == NULL)
+ return 1;
+ m = oldm;
+ m->m_data = m->m_ext.ext_buf;
+ }
+ } else {
+ if (oldm == NULL)
+ return 1;
+ m = oldm;
+ m->m_data = m->m_ext.ext_buf;
+ }
+
+ /*
+ * Move the data pointer up so that the incoming data packet
+ * will be 32-bit aligned.
+ */
+ m->m_data += RFA_ALIGNMENT_FUDGE;
+
+ /*
+ * Get a pointer to the base of the mbuf cluster and move
+ * data start past it.
+ */
+ rfa = mtod(m, struct fxp_rfa *);
+ m->m_data += sizeof(struct fxp_rfa);
+ rfa->size = (u_int16_t)(MCLBYTES - sizeof(struct fxp_rfa) - RFA_ALIGNMENT_FUDGE);
+
+ /*
+ * Initialize the rest of the RFA. Note that since the RFA
+ * is misaligned, we cannot store values directly. Instead,
+ * we use an optimized, inline copy.
+ */
+
+ rfa->rfa_status = 0;
+ rfa->rfa_control = FXP_RFA_CONTROL_EL;
+ rfa->actual_size = 0;
+
+ v = -1;
+ fxp_lwcopy(&v, (volatile u_int32_t*) rfa->link_addr);
+ fxp_lwcopy(&v, (volatile u_int32_t*) rfa->rbd_addr);
+
+ /*
+ * If there are other buffers already on the list, attach this
+ * one to the end by fixing up the tail to point to this one.
+ */
+ if (sc->rfa_headm != NULL) {
+ p_rfa = (struct fxp_rfa *) (sc->rfa_tailm->m_ext.ext_buf +
+ RFA_ALIGNMENT_FUDGE);
+ sc->rfa_tailm->m_next = m;
+ v = vtophys(rfa);
+ fxp_lwcopy(&v, (volatile u_int32_t*) p_rfa->link_addr);
+ p_rfa->rfa_control = 0;
+ } else {
+ sc->rfa_headm = m;
+ }
+ sc->rfa_tailm = m;
+
+ return (m == oldm);
+}
+
+#ifdef NOTUSED
+static volatile int
+fxp_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct fxp_softc *sc = device_get_softc(dev);
+ int count = 10000;
+ int value;
+
+ CSR_WRITE_4(sc, FXP_CSR_MDICONTROL,
+ (FXP_MDI_READ << 26) | (reg << 16) | (phy << 21));
+
+ while (((value = CSR_READ_4(sc, FXP_CSR_MDICONTROL)) & 0x10000000) == 0
+ && count--)
+ DELAY(10);
+
+ if (count <= 0)
+ device_printf(dev, "fxp_miibus_readreg: timed out\n");
+
+ return (value & 0xffff);
+}
+
+static void
+fxp_miibus_writereg(device_t dev, int phy, int reg, int value)
+{
+ struct fxp_softc *sc = device_get_softc(dev);
+ int count = 10000;
+
+ CSR_WRITE_4(sc, FXP_CSR_MDICONTROL,
+ (FXP_MDI_WRITE << 26) | (reg << 16) | (phy << 21) |
+ (value & 0xffff));
+
+ while ((CSR_READ_4(sc, FXP_CSR_MDICONTROL) & 0x10000000) == 0 &&
+ count--)
+ DELAY(10);
+
+ if (count <= 0)
+ device_printf(dev, "fxp_miibus_writereg: timed out\n");
+}
+#endif
+
+static int
+fxp_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct fxp_softc *sc = ifp->if_softc;
+#ifdef NOTUSED
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct mii_data *mii;
+#endif
+ int s, error = 0;
+
+ DBGLVL_PRINTK(2,"fxp_ioctl called\n");
+
+ s = splimp();
+
+ switch (command) {
+ case SIOCSIFADDR:
+ case SIOCGIFADDR:
+ case SIOCSIFMTU:
+ error = ether_ioctl(ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_ALLMULTI)
+ sc->flags |= FXP_FLAG_ALL_MCAST;
+ else
+ sc->flags &= ~FXP_FLAG_ALL_MCAST;
+
+ /*
+ * If interface is marked up and not running, then start it.
+ * If it is marked down and running, stop it.
+ * XXX If it's up then re-initialize it. This is so flags
+ * such as IFF_PROMISC are handled.
+ */
+ if (ifp->if_flags & IFF_UP) {
+ fxp_init(sc);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ fxp_stop(sc);
+ }
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if (ifp->if_flags & IFF_ALLMULTI)
+ sc->flags |= FXP_FLAG_ALL_MCAST;
+ else
+ sc->flags &= ~FXP_FLAG_ALL_MCAST;
+ /*
+ * Multicast list has changed; set the hardware filter
+ * accordingly.
+ */
+ if ((sc->flags & FXP_FLAG_ALL_MCAST) == 0)
+ fxp_mc_setup(sc);
+ /*
+ * fxp_mc_setup() can set FXP_FLAG_ALL_MCAST, so check it
+ * again rather than else {}.
+ */
+ if (sc->flags & FXP_FLAG_ALL_MCAST)
+ fxp_init(sc);
+ error = 0;
+ break;
+
+#ifdef NOTUSED
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ if (sc->miibus != NULL) {
+ mii = device_get_softc(sc->miibus);
+ error = ifmedia_ioctl(ifp, ifr,
+ &mii->mii_media, command);
+ } else {
+ error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, command);
+ }
+ break;
+#endif
+
+ case SIO_RTEMS_SHOW_STATS:
+ fxp_stats(sc);
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ splx(s);
+ return (error);
+}
+
+/*
+ * Program the multicast filter.
+ *
+ * We have an artificial restriction that the multicast setup command
+ * must be the first command in the chain, so we take steps to ensure
+ * this. By requiring this, it allows us to keep up the performance of
+ * the pre-initialized command ring (esp. link pointers) by not actually
+ * inserting the mcsetup command in the ring - i.e. its link pointer
+ * points to the TxCB ring, but the mcsetup descriptor itself is not part
+ * of it. We then can do 'CU_START' on the mcsetup descriptor and have it
+ * lead into the regular TxCB ring when it completes.
+ *
+ * This function must be called at splimp.
+ */
+static void
+fxp_mc_setup(struct fxp_softc *sc)
+{
+ struct fxp_cb_mcs *mcsp = sc->mcsp;
+ struct ifnet *ifp = &sc->sc_if;
+#ifdef NOTUSED
+ struct ifmultiaddr *ifma;
+#endif
+ int nmcasts;
+ int count;
+
+ DBGLVL_PRINTK(2,"fxp_mc_setup called\n");
+
+ /*
+ * If there are queued commands, we must wait until they are all
+ * completed. If we are already waiting, then add a NOP command
+ * with interrupt option so that we're notified when all commands
+ * have been completed - fxp_start() ensures that no additional
+ * TX commands will be added when need_mcsetup is true.
+ */
+ if (sc->tx_queued) {
+ struct fxp_cb_tx *txp;
+
+ /*
+ * need_mcsetup will be true if we are already waiting for the
+ * NOP command to be completed (see below). In this case, bail.
+ */
+ if (sc->need_mcsetup)
+ return;
+ sc->need_mcsetup = 1;
+
+ /*
+ * Add a NOP command with interrupt so that we are notified when all
+ * TX commands have been processed.
+ */
+ txp = sc->cbl_last->next;
+ txp->mb_head = NULL;
+ txp->cb_status = 0;
+ txp->cb_command = FXP_CB_COMMAND_NOP |
+ FXP_CB_COMMAND_S | FXP_CB_COMMAND_I;
+ /*
+ * Advance the end of list forward.
+ */
+ sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S;
+ sc->cbl_last = txp;
+ sc->tx_queued++;
+ /*
+ * Issue a resume in case the CU has just suspended.
+ */
+ fxp_scb_wait(sc);
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_RESUME);
+ /*
+ * Set a 5 second timer just in case we don't hear from the
+ * card again.
+ */
+ ifp->if_timer = 5;
+
+ return;
+ }
+ sc->need_mcsetup = 0;
+
+ /*
+ * Initialize multicast setup descriptor.
+ */
+ mcsp->next = sc->cbl_base;
+ mcsp->mb_head = NULL;
+ mcsp->cb_status = 0;
+ mcsp->cb_command = FXP_CB_COMMAND_MCAS |
+ FXP_CB_COMMAND_S | FXP_CB_COMMAND_I;
+ mcsp->link_addr = vtophys(&sc->cbl_base->cb_status);
+
+ nmcasts = 0;
+#ifdef NOTUSED /* FIXME: Multicast not supported? */
+ if ((sc->flags & FXP_FLAG_ALL_MCAST) == 0) {
+#if __FreeBSD_version < 500000
+ LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+#else
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+#endif
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ if (nmcasts >= MAXMCADDR) {
+ sc->flags |= FXP_FLAG_ALL_MCAST;
+ nmcasts = 0;
+ break;
+ }
+ memcpy((void *)(uintptr_t)(volatile void *)
+ &sc->mcsp->mc_addr[nmcasts][0],
+ LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 6);
+ nmcasts++;
+ }
+ }
+#endif
+ mcsp->mc_cnt = nmcasts * 6;
+ sc->cbl_first = sc->cbl_last = (struct fxp_cb_tx *) mcsp;
+ sc->tx_queued = 1;
+
+ /*
+ * Wait until command unit is not active. This should never
+ * be the case when nothing is queued, but make sure anyway.
+ */
+ count = 100;
+ while ((CSR_READ_1(sc, FXP_CSR_SCB_RUSCUS) >> 6) ==
+ FXP_SCB_CUS_ACTIVE && --count)
+ DELAY(10);
+ if (count == 0) {
+ device_printf(sc->dev, "command queue timeout\n");
+ return;
+ }
+
+ /*
+ * Start the multicast setup command.
+ */
+ fxp_scb_wait(sc);
+ CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&mcsp->cb_status));
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_START);
+
+ ifp->if_timer = 2;
+ return;
+ }
+
+#endif /* defined(__i386__) */
diff --git a/bsps/shared/net/if_fxpreg.h b/bsps/shared/net/if_fxpreg.h
new file mode 100644
index 0000000..9bf4e59
--- /dev/null
+++ b/bsps/shared/net/if_fxpreg.h
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 1995, David Greenman
+ * Copyright (c) 2001 Jonathan Lemon <jlemon@freebsd.org>
+ * All rights reserved.
+ *
+ * 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 unmodified, 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 AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD: src/sys/dev/fxp/if_fxpreg.h,v 1.23.2.4 2001/08/31 02:17:02 jlemon Exp $
+ */
+
+#define FXP_VENDORID_INTEL 0x8086
+
+#define FXP_PCI_MMBA 0x10
+#define FXP_PCI_IOBA 0x14
+
+/*
+ * Control/status registers.
+ */
+#define FXP_CSR_SCB_RUSCUS 0 /* scb_rus/scb_cus (1 byte) */
+#define FXP_CSR_SCB_STATACK 1 /* scb_statack (1 byte) */
+#define FXP_CSR_SCB_COMMAND 2 /* scb_command (1 byte) */
+#define FXP_CSR_SCB_INTRCNTL 3 /* scb_intrcntl (1 byte) */
+#define FXP_CSR_SCB_GENERAL 4 /* scb_general (4 bytes) */
+#define FXP_CSR_PORT 8 /* port (4 bytes) */
+#define FXP_CSR_FLASHCONTROL 12 /* flash control (2 bytes) */
+#define FXP_CSR_EEPROMCONTROL 14 /* eeprom control (2 bytes) */
+#define FXP_CSR_MDICONTROL 16 /* mdi control (4 bytes) */
+#define FXP_CSR_FLOWCONTROL 0x19 /* flow control (2 bytes) */
+#define FXP_CSR_GENCONTROL 0x1C /* general control (1 byte) */
+
+/*
+ * FOR REFERENCE ONLY, the old definition of FXP_CSR_SCB_RUSCUS:
+ *
+ * volatile u_int8_t :2,
+ * scb_rus:4,
+ * scb_cus:2;
+ */
+
+#define FXP_PORT_SOFTWARE_RESET 0
+#define FXP_PORT_SELFTEST 1
+#define FXP_PORT_SELECTIVE_RESET 2
+#define FXP_PORT_DUMP 3
+
+#define FXP_SCB_RUS_IDLE 0
+#define FXP_SCB_RUS_SUSPENDED 1
+#define FXP_SCB_RUS_NORESOURCES 2
+#define FXP_SCB_RUS_READY 4
+#define FXP_SCB_RUS_SUSP_NORBDS 9
+#define FXP_SCB_RUS_NORES_NORBDS 10
+#define FXP_SCB_RUS_READY_NORBDS 12
+
+#define FXP_SCB_CUS_IDLE 0
+#define FXP_SCB_CUS_SUSPENDED 1
+#define FXP_SCB_CUS_ACTIVE 2
+
+#define FXP_SCB_INTR_DISABLE 0x01 /* Disable all interrupts */
+#define FXP_SCB_INTR_SWI 0x02 /* Generate SWI */
+#define FXP_SCB_INTMASK_FCP 0x04
+#define FXP_SCB_INTMASK_ER 0x08
+#define FXP_SCB_INTMASK_RNR 0x10
+#define FXP_SCB_INTMASK_CNA 0x20
+#define FXP_SCB_INTMASK_FR 0x40
+#define FXP_SCB_INTMASK_CXTNO 0x80
+
+#define FXP_SCB_STATACK_FCP 0x01 /* Flow Control Pause */
+#define FXP_SCB_STATACK_ER 0x02 /* Early Receive */
+#define FXP_SCB_STATACK_SWI 0x04
+#define FXP_SCB_STATACK_MDI 0x08
+#define FXP_SCB_STATACK_RNR 0x10
+#define FXP_SCB_STATACK_CNA 0x20
+#define FXP_SCB_STATACK_FR 0x40
+#define FXP_SCB_STATACK_CXTNO 0x80
+
+#define FXP_SCB_COMMAND_CU_NOP 0x00
+#define FXP_SCB_COMMAND_CU_START 0x10
+#define FXP_SCB_COMMAND_CU_RESUME 0x20
+#define FXP_SCB_COMMAND_CU_DUMP_ADR 0x40
+#define FXP_SCB_COMMAND_CU_DUMP 0x50
+#define FXP_SCB_COMMAND_CU_BASE 0x60
+#define FXP_SCB_COMMAND_CU_DUMPRESET 0x70
+
+#define FXP_SCB_COMMAND_RU_NOP 0
+#define FXP_SCB_COMMAND_RU_START 1
+#define FXP_SCB_COMMAND_RU_RESUME 2
+#define FXP_SCB_COMMAND_RU_ABORT 4
+#define FXP_SCB_COMMAND_RU_LOADHDS 5
+#define FXP_SCB_COMMAND_RU_BASE 6
+#define FXP_SCB_COMMAND_RU_RBDRESUME 7
+
+/*
+ * Command block definitions
+ */
+struct fxp_cb_nop {
+ void *fill[2];
+ volatile u_int16_t cb_status;
+ volatile u_int16_t cb_command;
+ volatile u_int32_t link_addr;
+};
+struct fxp_cb_ias {
+ void *fill[2];
+ volatile u_int16_t cb_status;
+ volatile u_int16_t cb_command;
+ volatile u_int32_t link_addr;
+ volatile u_int8_t macaddr[6];
+};
+/* I hate bit-fields :-( */
+struct fxp_cb_config {
+ void *fill[2];
+ volatile u_int16_t cb_status;
+ volatile u_int16_t cb_command;
+ volatile u_int32_t link_addr;
+ volatile u_int byte_count:6,
+ :2;
+ volatile u_int rx_fifo_limit:4,
+ tx_fifo_limit:3,
+ :1;
+ volatile u_int8_t adaptive_ifs;
+ volatile u_int mwi_enable:1, /* 8,9 */
+ type_enable:1, /* 8,9 */
+ read_align_en:1, /* 8,9 */
+ end_wr_on_cl:1, /* 8,9 */
+ :4;
+ volatile u_int rx_dma_bytecount:7,
+ :1;
+ volatile u_int tx_dma_bytecount:7,
+ dma_mbce:1;
+ volatile u_int late_scb:1, /* 7 */
+ direct_dma_dis:1, /* 8,9 */
+ tno_int_or_tco_en:1, /* 7,9 */
+ ci_int:1,
+ ext_txcb_dis:1, /* 8,9 */
+ ext_stats_dis:1, /* 8,9 */
+ keep_overrun_rx:1,
+ save_bf:1;
+ volatile u_int disc_short_rx:1,
+ underrun_retry:2,
+ :3,
+ two_frames:1, /* 8,9 */
+ dyn_tbd:1; /* 8,9 */
+ volatile u_int mediatype:1, /* 7 */
+ :6,
+ csma_dis:1; /* 8,9 */
+ volatile u_int tcp_udp_cksum:1, /* 9 */
+ :3,
+ vlan_tco:1, /* 8,9 */
+ link_wake_en:1, /* 8,9 */
+ arp_wake_en:1, /* 8 */
+ mc_wake_en:1; /* 8 */
+ volatile u_int :3,
+ nsai:1,
+ preamble_length:2,
+ loopback:2;
+ volatile u_int linear_priority:3, /* 7 */
+ :5;
+ volatile u_int linear_pri_mode:1, /* 7 */
+ :3,
+ interfrm_spacing:4;
+ volatile u_int :8;
+ volatile u_int :8;
+ volatile u_int promiscuous:1,
+ bcast_disable:1,
+ wait_after_win:1, /* 8,9 */
+ :1,
+ ignore_ul:1, /* 8,9 */
+ crc16_en:1, /* 9 */
+ :1,
+ crscdt:1;
+ volatile u_int fc_delay_lsb:8; /* 8,9 */
+ volatile u_int fc_delay_msb:8; /* 8,9 */
+ volatile u_int stripping:1,
+ padding:1,
+ rcv_crc_xfer:1,
+ long_rx_en:1, /* 8,9 */
+ pri_fc_thresh:3, /* 8,9 */
+ :1;
+ volatile u_int ia_wake_en:1, /* 8 */
+ magic_pkt_dis:1, /* 8,9,!9ER */
+ tx_fc_dis:1, /* 8,9 */
+ rx_fc_restop:1, /* 8,9 */
+ rx_fc_restart:1, /* 8,9 */
+ fc_filter:1, /* 8,9 */
+ force_fdx:1,
+ fdx_pin_en:1;
+ volatile u_int :5,
+ pri_fc_loc:1, /* 8,9 */
+ multi_ia:1,
+ :1;
+ volatile u_int :3,
+ mc_all:1,
+ :4;
+};
+
+#define MAXMCADDR 80
+struct fxp_cb_mcs {
+ struct fxp_cb_tx *next;
+ struct mbuf *mb_head;
+ volatile u_int16_t cb_status;
+ volatile u_int16_t cb_command;
+ volatile u_int32_t link_addr;
+ volatile u_int16_t mc_cnt;
+ volatile u_int8_t mc_addr[MAXMCADDR][6];
+};
+
+/*
+ * Number of DMA segments in a TxCB. Note that this is carefully
+ * chosen to make the total struct size an even power of two. It's
+ * critical that no TxCB be split across a page boundry since
+ * no attempt is made to allocate physically contiguous memory.
+ *
+ */
+#ifdef __alpha__ /* XXX - should be conditional on pointer size */
+#define FXP_NTXSEG 28
+#else
+#define FXP_NTXSEG 29
+#endif
+
+struct fxp_tbd {
+ volatile u_int32_t tb_addr;
+ volatile u_int32_t tb_size;
+};
+struct fxp_cb_tx {
+ struct fxp_cb_tx *next;
+ struct mbuf *mb_head;
+ volatile u_int16_t cb_status;
+ volatile u_int16_t cb_command;
+ volatile u_int32_t link_addr;
+ volatile u_int32_t tbd_array_addr;
+ volatile u_int16_t byte_count;
+ volatile u_int8_t tx_threshold;
+ volatile u_int8_t tbd_number;
+ /*
+ * The following structure isn't actually part of the TxCB,
+ * unless the extended TxCB feature is being used. In this
+ * case, the first two elements of the structure below are
+ * fetched along with the TxCB.
+ */
+ volatile struct fxp_tbd tbd[FXP_NTXSEG];
+};
+
+/*
+ * Control Block (CB) definitions
+ */
+
+/* status */
+#define FXP_CB_STATUS_OK 0x2000
+#define FXP_CB_STATUS_C 0x8000
+/* commands */
+#define FXP_CB_COMMAND_NOP 0x0
+#define FXP_CB_COMMAND_IAS 0x1
+#define FXP_CB_COMMAND_CONFIG 0x2
+#define FXP_CB_COMMAND_MCAS 0x3
+#define FXP_CB_COMMAND_XMIT 0x4
+#define FXP_CB_COMMAND_RESRV 0x5
+#define FXP_CB_COMMAND_DUMP 0x6
+#define FXP_CB_COMMAND_DIAG 0x7
+/* command flags */
+#define FXP_CB_COMMAND_SF 0x0008 /* simple/flexible mode */
+#define FXP_CB_COMMAND_I 0x2000 /* generate interrupt on completion */
+#define FXP_CB_COMMAND_S 0x4000 /* suspend on completion */
+#define FXP_CB_COMMAND_EL 0x8000 /* end of list */
+
+/*
+ * RFA definitions
+ */
+
+struct fxp_rfa {
+ volatile u_int16_t rfa_status;
+ volatile u_int16_t rfa_control;
+ volatile u_int8_t link_addr[4];
+ volatile u_int8_t rbd_addr[4];
+ volatile u_int16_t actual_size;
+ volatile u_int16_t size;
+};
+#define FXP_RFA_STATUS_RCOL 0x0001 /* receive collision */
+#define FXP_RFA_STATUS_IAMATCH 0x0002 /* 0 = matches station address */
+#define FXP_RFA_STATUS_S4 0x0010 /* receive error from PHY */
+#define FXP_RFA_STATUS_TL 0x0020 /* type/length */
+#define FXP_RFA_STATUS_FTS 0x0080 /* frame too short */
+#define FXP_RFA_STATUS_OVERRUN 0x0100 /* DMA overrun */
+#define FXP_RFA_STATUS_RNR 0x0200 /* no resources */
+#define FXP_RFA_STATUS_ALIGN 0x0400 /* alignment error */
+#define FXP_RFA_STATUS_CRC 0x0800 /* CRC error */
+#define FXP_RFA_STATUS_OK 0x2000 /* packet received okay */
+#define FXP_RFA_STATUS_C 0x8000 /* packet reception complete */
+#define FXP_RFA_CONTROL_SF 0x08 /* simple/flexible memory mode */
+#define FXP_RFA_CONTROL_H 0x10 /* header RFD */
+#define FXP_RFA_CONTROL_S 0x4000 /* suspend after reception */
+#define FXP_RFA_CONTROL_EL 0x8000 /* end of list */
+
+/*
+ * Statistics dump area definitions
+ */
+struct fxp_stats {
+ volatile u_int32_t tx_good;
+ volatile u_int32_t tx_maxcols;
+ volatile u_int32_t tx_latecols;
+ volatile u_int32_t tx_underruns;
+ volatile u_int32_t tx_lostcrs;
+ volatile u_int32_t tx_deffered;
+ volatile u_int32_t tx_single_collisions;
+ volatile u_int32_t tx_multiple_collisions;
+ volatile u_int32_t tx_total_collisions;
+ volatile u_int32_t rx_good;
+ volatile u_int32_t rx_crc_errors;
+ volatile u_int32_t rx_alignment_errors;
+ volatile u_int32_t rx_rnr_errors;
+ volatile u_int32_t rx_overrun_errors;
+ volatile u_int32_t rx_cdt_errors;
+ volatile u_int32_t rx_shortframes;
+ volatile u_int32_t completion_status;
+};
+#define FXP_STATS_DUMP_COMPLETE 0xa005
+#define FXP_STATS_DR_COMPLETE 0xa007
+
+/*
+ * Serial EEPROM control register bits
+ */
+#define FXP_EEPROM_EESK 0x01 /* shift clock */
+#define FXP_EEPROM_EECS 0x02 /* chip select */
+#define FXP_EEPROM_EEDI 0x04 /* data in */
+#define FXP_EEPROM_EEDO 0x08 /* data out */
+
+/*
+ * Serial EEPROM opcodes, including start bit
+ */
+#define FXP_EEPROM_OPC_ERASE 0x4
+#define FXP_EEPROM_OPC_WRITE 0x5
+#define FXP_EEPROM_OPC_READ 0x6
+
+/*
+ * Management Data Interface opcodes
+ */
+#define FXP_MDI_WRITE 0x1
+#define FXP_MDI_READ 0x2
+
+/*
+ * PHY device types
+ */
+#define FXP_PHY_DEVICE_MASK 0x3f00
+#define FXP_PHY_SERIAL_ONLY 0x8000
+#define FXP_PHY_NONE 0
+#define FXP_PHY_82553A 1
+#define FXP_PHY_82553C 2
+#define FXP_PHY_82503 3
+#define FXP_PHY_DP83840 4
+#define FXP_PHY_80C240 5
+#define FXP_PHY_80C24 6
+#define FXP_PHY_82555 7
+#define FXP_PHY_DP83840A 10
+#define FXP_PHY_82555B 11
diff --git a/bsps/shared/net/open_eth.c b/bsps/shared/net/open_eth.c
new file mode 100644
index 0000000..2335b7f
--- /dev/null
+++ b/bsps/shared/net/open_eth.c
@@ -0,0 +1,767 @@
+/*
+ * RTEMS driver for Opencores Ethernet Controller
+ *
+ * Weakly based on dec21140 rtems driver and open_eth linux driver
+ * Written by Jiri Gaisler, Gaisler Research
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ *
+ */
+
+/*
+ * This driver current only supports architectures with the old style
+ * exception processing. The following checks try to keep this
+ * from being compiled on systems which can't support this driver.
+ *
+ * NOTE: The i386, ARM, and PowerPC use a different interrupt API than
+ * that used by this driver.
+ */
+
+#include <machine/rtems-bsd-kernel-space.h>
+
+#if defined(__i386__) || defined(__arm__) || defined(__PPC__)
+ #define OPENETH_NOT_SUPPORTED
+#endif
+
+#if !defined(OPENETH_NOT_SUPPORTED)
+#include <bsp.h>
+#include <rtems.h>
+
+#include <bsp.h>
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+#include <libchip/open_eth.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>
+
+#ifdef malloc
+#undef malloc
+#endif
+#ifdef free
+#undef free
+#endif
+
+extern rtems_isr_entry set_vector( rtems_isr_entry, rtems_vector_number, int );
+
+ /*
+#define OPEN_ETH_DEBUG
+ */
+
+#ifdef CPU_U32_FIX
+extern void ipalign(struct mbuf *m);
+#endif
+
+/* message descriptor entry */
+struct MDTX
+{
+ char *buf;
+};
+
+struct MDRX
+{
+ struct mbuf *m;
+};
+
+/*
+ * Number of OCs supported by this driver
+ */
+#define NOCDRIVER 1
+
+/*
+ * Receive buffer size -- Allow for a full ethernet packet including CRC
+ */
+#define RBUF_SIZE 1536
+
+#define ET_MINLEN 64 /* minimum message length */
+
+/*
+ * RTEMS event used by interrupt handler to signal driver tasks.
+ * This must not be any of the events used by the network task synchronization.
+ */
+#define INTERRUPT_EVENT RTEMS_EVENT_1
+
+/*
+ * RTEMS event used to start transmit daemon.
+ * This must not be the same as INTERRUPT_EVENT.
+ */
+#define START_TRANSMIT_EVENT RTEMS_EVENT_2
+
+ /* event to send when tx buffers become available */
+#define OPEN_ETH_TX_WAIT_EVENT RTEMS_EVENT_3
+
+ /* suspend when all TX descriptors exhausted */
+ /*
+#define OETH_SUSPEND_NOTXBUF
+ */
+
+#if (MCLBYTES < RBUF_SIZE)
+# error "Driver must have MCLBYTES > RBUF_SIZE"
+#endif
+
+/*
+ * Per-device data
+ */
+struct open_eth_softc
+{
+
+ struct arpcom arpcom;
+
+ oeth_regs *regs;
+
+ int acceptBroadcast;
+ rtems_id rxDaemonTid;
+ rtems_id txDaemonTid;
+
+ unsigned int tx_ptr;
+ unsigned int rx_ptr;
+ unsigned int txbufs;
+ unsigned int rxbufs;
+ struct MDTX *txdesc;
+ struct MDRX *rxdesc;
+ rtems_vector_number vector;
+ unsigned int en100MHz;
+
+ /*
+ * Statistics
+ */
+ unsigned long rxInterrupts;
+ unsigned long rxPackets;
+ unsigned long rxLengthError;
+ unsigned long rxNonOctet;
+ unsigned long rxBadCRC;
+ unsigned long rxOverrun;
+ unsigned long rxMiss;
+ unsigned long rxCollision;
+
+ unsigned long txInterrupts;
+ unsigned long txDeferred;
+ unsigned long txHeartbeat;
+ unsigned long txLateCollision;
+ unsigned long txRetryLimit;
+ unsigned long txUnderrun;
+ unsigned long txLostCarrier;
+ unsigned long txRawWait;
+};
+
+static struct open_eth_softc oc;
+
+/* OPEN_ETH interrupt handler */
+
+static rtems_isr
+open_eth_interrupt_handler (rtems_vector_number v)
+{
+ uint32_t status;
+
+ /* read and clear interrupt cause */
+
+ status = oc.regs->int_src;
+ oc.regs->int_src = status;
+
+ /* Frame received? */
+
+ if (status & (OETH_INT_RXF | OETH_INT_RXE))
+ {
+ oc.rxInterrupts++;
+ rtems_bsdnet_event_send (oc.rxDaemonTid, INTERRUPT_EVENT);
+ }
+#ifdef OETH_SUSPEND_NOTXBUF
+ if (status & (OETH_INT_MASK_TXB | OETH_INT_MASK_TXC | OETH_INT_MASK_TXE))
+ {
+ oc.txInterrupts++;
+ rtems_bsdnet_event_send (oc.txDaemonTid, OPEN_ETH_TX_WAIT_EVENT);
+ }
+#endif
+ /*
+#ifdef __leon__
+ LEON_Clear_interrupt(v-0x10);
+#endif
+ */
+}
+
+static uint32_t read_mii(uint32_t addr)
+{
+ while (oc.regs->miistatus & OETH_MIISTATUS_BUSY) {}
+ oc.regs->miiaddress = addr << 8;
+ oc.regs->miicommand = OETH_MIICOMMAND_RSTAT;
+ while (oc.regs->miistatus & OETH_MIISTATUS_BUSY) {}
+ if (!(oc.regs->miistatus & OETH_MIISTATUS_NVALID))
+ return(oc.regs->miirx_data);
+ else {
+ printf("open_eth: failed to read mii\n");
+ return (0);
+ }
+}
+
+static void write_mii(uint32_t addr, uint32_t data)
+{
+ while (oc.regs->miistatus & OETH_MIISTATUS_BUSY) {}
+ oc.regs->miiaddress = addr << 8;
+ oc.regs->miitx_data = data;
+ oc.regs->miicommand = OETH_MIICOMMAND_WCTRLDATA;
+ while (oc.regs->miistatus & OETH_MIISTATUS_BUSY) {}
+}
+/*
+ * Initialize the ethernet hardware
+ */
+static void
+open_eth_initialize_hardware (struct open_eth_softc *sc)
+{
+ struct mbuf *m;
+ int i;
+ int mii_cr = 0;
+
+ oeth_regs *regs;
+
+ regs = sc->regs;
+
+ /* Reset the controller. */
+
+ regs->ctrlmoder = 0;
+ regs->moder = OETH_MODER_RST; /* Reset ON */
+ regs->moder = 0; /* Reset OFF */
+
+ /* reset PHY and wait for complettion */
+ mii_cr = 0x3300;
+ if (!sc->en100MHz) mii_cr = 0;
+ write_mii(0, mii_cr | 0x8000);
+ while (read_mii(0) & 0x8000) {}
+ if (!sc->en100MHz) write_mii(0, 0);
+ mii_cr = read_mii(0);
+ printf("open_eth: driver attached, PHY config : 0x%04" PRIx32 "\n", read_mii(0));
+
+#ifdef OPEN_ETH_DEBUG
+ printf("mii_cr: %04x\n", mii_cr);
+ for (i=0;i<21;i++)
+ printf("mii_reg %2d : 0x%04x\n", i, read_mii(i));
+#endif
+
+ /* Setting TXBD base to sc->txbufs */
+
+ regs->tx_bd_num = sc->txbufs;
+
+ /* Initialize rx/tx pointers. */
+
+ sc->rx_ptr = 0;
+ sc->tx_ptr = 0;
+
+ /* Set min/max packet length */
+ regs->packet_len = 0x00400600;
+
+ /* Set IPGT register to recomended value */
+ regs->ipgt = 0x00000015;
+
+ /* Set IPGR1 register to recomended value */
+ regs->ipgr1 = 0x0000000c;
+
+ /* Set IPGR2 register to recomended value */
+ regs->ipgr2 = 0x00000012;
+
+ /* Set COLLCONF register to recomended value */
+ regs->collconf = 0x000f003f;
+
+ /* initialize TX descriptors */
+
+ sc->txdesc = calloc(sc->txbufs, sizeof(*sc->txdesc));
+ for (i = 0; i < sc->txbufs; i++)
+ {
+ sc->regs->xd[i].len_status = OETH_TX_BD_PAD | OETH_TX_BD_CRC;
+ sc->txdesc[i].buf = calloc(1, OETH_MAXBUF_LEN);
+#ifdef OPEN_ETH_DEBUG
+ printf("TXBUF: %08x\n", (int) sc->txdesc[i].buf);
+#endif
+ }
+ sc->regs->xd[sc->txbufs - 1].len_status |= OETH_TX_BD_WRAP;
+
+ /* allocate RX buffers */
+
+ sc->rxdesc = calloc(sc->rxbufs, sizeof(*sc->rxdesc));
+ for (i = 0; i < sc->rxbufs; i++)
+ {
+
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ sc->rxdesc[i].m = m;
+ sc->regs->xd[i + sc->txbufs].addr = mtod (m, uint32_t*);
+ sc->regs->xd[i + sc->txbufs].len_status =
+ OETH_RX_BD_EMPTY | OETH_RX_BD_IRQ;
+#ifdef OPEN_ETH_DEBUG
+ printf("RXBUF: %08x\n", (int) sc->rxdesc[i].m);
+#endif
+ }
+ sc->regs->xd[sc->rxbufs + sc->txbufs - 1].len_status |= OETH_RX_BD_WRAP;
+
+
+ /* set ethernet address. */
+
+ regs->mac_addr1 = sc->arpcom.ac_enaddr[0] << 8 | sc->arpcom.ac_enaddr[1];
+
+ uint32_t mac_addr0;
+ mac_addr0 = sc->arpcom.ac_enaddr[2];
+ mac_addr0 <<= 8;
+ mac_addr0 |= sc->arpcom.ac_enaddr[3];
+ mac_addr0 <<= 8;
+ mac_addr0 |= sc->arpcom.ac_enaddr[4];
+ mac_addr0 <<= 8;
+ mac_addr0 |= sc->arpcom.ac_enaddr[5];
+
+ regs->mac_addr0 = mac_addr0;
+
+ /* install interrupt vector */
+ set_vector (open_eth_interrupt_handler, sc->vector, 1);
+
+ /* clear all pending interrupts */
+
+ regs->int_src = 0xffffffff;
+
+ /* MAC mode register: PAD, IFG, CRCEN */
+
+ regs->moder = OETH_MODER_PAD | OETH_MODER_CRCEN | ((mii_cr & 0x100) << 2);
+
+ /* enable interrupts */
+
+ regs->int_mask = OETH_INT_MASK_RXF | OETH_INT_MASK_RXE | OETH_INT_MASK_RXC;
+
+#ifdef OETH_SUSPEND_NOTXBUF
+ regs->int_mask |= OETH_INT_MASK_TXB | OETH_INT_MASK_TXC | OETH_INT_MASK_TXE | OETH_INT_BUSY;*/
+ sc->regs->xd[(sc->txbufs - 1)/2].len_status |= OETH_TX_BD_IRQ;
+ sc->regs->xd[sc->txbufs - 1].len_status |= OETH_TX_BD_IRQ;
+#endif
+
+ regs->moder |= OETH_MODER_RXEN | OETH_MODER_TXEN;
+}
+
+static void
+open_eth_rxDaemon (void *arg)
+{
+ struct ether_header *eh;
+ struct open_eth_softc *dp = (struct open_eth_softc *) &oc;
+ struct ifnet *ifp = &dp->arpcom.ac_if;
+ struct mbuf *m;
+ unsigned int len;
+ uint32_t len_status;
+ unsigned int bad;
+ rtems_event_set events;
+
+
+ for (;;)
+ {
+
+ rtems_bsdnet_event_receive (INTERRUPT_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT, &events);
+#ifdef OPEN_ETH_DEBUG
+ printf ("r\n");
+#endif
+
+ while (!
+ ((len_status =
+ dp->regs->xd[dp->rx_ptr+dp->txbufs].len_status) & OETH_RX_BD_EMPTY))
+ {
+ bad = 0;
+ if (len_status & (OETH_RX_BD_TOOLONG | OETH_RX_BD_SHORT))
+ {
+ dp->rxLengthError++;
+ bad = 1;
+ }
+ if (len_status & OETH_RX_BD_DRIBBLE)
+ {
+ dp->rxNonOctet++;
+ bad = 1;
+ }
+ if (len_status & OETH_RX_BD_CRCERR)
+ {
+ dp->rxBadCRC++;
+ bad = 1;
+ }
+ if (len_status & OETH_RX_BD_OVERRUN)
+ {
+ dp->rxOverrun++;
+ bad = 1;
+ }
+ if (len_status & OETH_RX_BD_MISS)
+ {
+ dp->rxMiss++;
+ bad = 1;
+ }
+ if (len_status & OETH_RX_BD_LATECOL)
+ {
+ dp->rxCollision++;
+ bad = 1;
+ }
+
+ if (!bad)
+ {
+ /* pass on the packet in the receive buffer */
+ len = len_status >> 16;
+ m = (struct mbuf *) (dp->rxdesc[dp->rx_ptr].m);
+ m->m_len = m->m_pkthdr.len =
+ len - sizeof (struct ether_header);
+ eh = mtod (m, struct ether_header *);
+ m->m_data += sizeof (struct ether_header);
+#ifdef CPU_U32_FIX
+ ipalign(m); /* Align packet on 32-bit boundary */
+#endif
+
+ ether_input (ifp, eh, m);
+
+ /* get a new mbuf */
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = ifp;
+ dp->rxdesc[dp->rx_ptr].m = m;
+ dp->regs->xd[dp->rx_ptr + dp->txbufs].addr =
+ (uint32_t*) mtod (m, void *);
+ dp->rxPackets++;
+ }
+
+ dp->regs->xd[dp->rx_ptr+dp->txbufs].len_status =
+ (dp->regs->xd[dp->rx_ptr+dp->txbufs].len_status &
+ ~OETH_TX_BD_STATS) | OETH_TX_BD_READY;
+ dp->rx_ptr = (dp->rx_ptr + 1) % dp->rxbufs;
+ }
+ }
+}
+
+static int inside = 0;
+static void
+sendpacket (struct ifnet *ifp, struct mbuf *m)
+{
+ struct open_eth_softc *dp = ifp->if_softc;
+ unsigned char *temp;
+ struct mbuf *n;
+ uint32_t len, len_status;
+
+ if (inside) printf ("error: sendpacket re-entered!!\n");
+ inside = 1;
+ /*
+ * Waiting for Transmitter ready
+ */
+ n = m;
+
+ while (dp->regs->xd[dp->tx_ptr].len_status & OETH_TX_BD_READY)
+ {
+#ifdef OETH_SUSPEND_NOTXBUF
+ rtems_event_set events;
+ rtems_bsdnet_event_receive (OPEN_ETH_TX_WAIT_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_MILLISECONDS_TO_TICKS(500), &events);
+#endif
+ }
+
+ len = 0;
+ temp = (unsigned char *) dp->txdesc[dp->tx_ptr].buf;
+ dp->regs->xd[dp->tx_ptr].addr = (uint32_t*) temp;
+
+#ifdef OPEN_ETH_DEBUG
+ printf("TXD: 0x%08x\n", (int) m->m_data);
+#endif
+ for (;;)
+ {
+#ifdef OPEN_ETH_DEBUG
+ int i;
+ printf("MBUF: 0x%08x : ", (int) m->m_data);
+ for (i=0;i<m->m_len;i++)
+ printf("%x%x", (m->m_data[i] >> 4) & 0x0ff, m->m_data[i] & 0x0ff);
+ printf("\n");
+#endif
+ len += m->m_len;
+ if (len <= RBUF_SIZE)
+ memcpy ((void *) temp, (char *) m->m_data, m->m_len);
+ temp += m->m_len;
+ if ((m = m->m_next) == NULL)
+ break;
+ }
+
+ m_freem (n);
+
+ /* don't send long packets */
+
+ if (len <= RBUF_SIZE) {
+
+ /* Clear all of the status flags. */
+ len_status = dp->regs->xd[dp->tx_ptr].len_status & ~OETH_TX_BD_STATS;
+
+ /* If the frame is short, tell CPM to pad it. */
+ if (len < ET_MINLEN) {
+ len_status |= OETH_TX_BD_PAD;
+ len = ET_MINLEN;
+ }
+ else
+ len_status &= ~OETH_TX_BD_PAD;
+
+ /* write buffer descriptor length and status */
+ len_status &= 0x0000ffff;
+ len_status |= (len << 16) | (OETH_TX_BD_READY | OETH_TX_BD_CRC);
+ dp->regs->xd[dp->tx_ptr].len_status = len_status;
+ dp->tx_ptr = (dp->tx_ptr + 1) % dp->txbufs;
+
+ }
+ inside = 0;
+}
+
+/*
+ * Driver transmit daemon
+ */
+static void
+open_eth_txDaemon (void *arg)
+{
+ struct open_eth_softc *sc = (struct open_eth_softc *) arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ rtems_event_set events;
+
+ for (;;)
+ {
+ /*
+ * Wait for packet
+ */
+
+ rtems_bsdnet_event_receive (START_TRANSMIT_EVENT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT, &events);
+#ifdef OPEN_ETH_DEBUG
+ printf ("t\n");
+#endif
+
+ /*
+ * Send packets till queue is empty
+ */
+ for (;;)
+ {
+ /*
+ * Get the next mbuf chain to transmit.
+ */
+ IF_DEQUEUE (&ifp->if_snd, m);
+ if (!m)
+ break;
+ sendpacket (ifp, m);
+ }
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+}
+
+
+static void
+open_eth_start (struct ifnet *ifp)
+{
+ struct open_eth_softc *sc = ifp->if_softc;
+
+ rtems_bsdnet_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT);
+ ifp->if_flags |= IFF_OACTIVE;
+}
+
+/*
+ * Initialize and start the device
+ */
+static void
+open_eth_init (void *arg)
+{
+ struct open_eth_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ if (sc->txDaemonTid == 0)
+ {
+
+ /*
+ * Set up OPEN_ETH hardware
+ */
+ open_eth_initialize_hardware (sc);
+
+ /*
+ * Start driver tasks
+ */
+ sc->rxDaemonTid = rtems_bsdnet_newproc ("DCrx", 4096,
+ open_eth_rxDaemon, sc);
+ sc->txDaemonTid = rtems_bsdnet_newproc ("DCtx", 4096,
+ open_eth_txDaemon, sc);
+ }
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+
+}
+
+/*
+ * Stop the device
+ */
+static void
+open_eth_stop (struct open_eth_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ sc->regs->moder = 0; /* RX/TX OFF */
+ sc->regs->moder = OETH_MODER_RST; /* Reset ON */
+ sc->regs->moder = 0; /* Reset OFF */
+}
+
+
+/*
+ * Show interface statistics
+ */
+static void
+open_eth_stats (struct open_eth_softc *sc)
+{
+ printf (" Rx Packets:%-8lu", sc->rxPackets);
+ printf (" Rx Interrupts:%-8lu", sc->rxInterrupts);
+ printf (" Length:%-8lu", sc->rxLengthError);
+ printf (" Non-octet:%-8lu\n", sc->rxNonOctet);
+ printf (" Bad CRC:%-8lu", sc->rxBadCRC);
+ printf (" Overrun:%-8lu", sc->rxOverrun);
+ printf (" Miss:%-8lu", sc->rxMiss);
+ printf (" Collision:%-8lu\n", sc->rxCollision);
+
+ printf (" Tx Interrupts:%-8lu", sc->txInterrupts);
+ printf (" Deferred:%-8lu", sc->txDeferred);
+ printf (" Missed Hearbeat:%-8lu\n", sc->txHeartbeat);
+ printf (" No Carrier:%-8lu", sc->txLostCarrier);
+ printf ("Retransmit Limit:%-8lu", sc->txRetryLimit);
+ printf (" Late Collision:%-8lu\n", sc->txLateCollision);
+ printf (" Underrun:%-8lu", sc->txUnderrun);
+ printf (" Raw output wait:%-8lu\n", sc->txRawWait);
+}
+
+/*
+ * Driver ioctl handler
+ */
+static int
+open_eth_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct open_eth_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command)
+ {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl (ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING))
+ {
+ case IFF_RUNNING:
+ open_eth_stop (sc);
+ break;
+
+ case IFF_UP:
+ open_eth_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ open_eth_stop (sc);
+ open_eth_init (sc);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ open_eth_stats (sc);
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+/*
+ * Attach an OPEN_ETH driver to the system
+ */
+int
+rtems_open_eth_driver_attach (struct rtems_bsdnet_ifconfig *config,
+ open_eth_configuration_t * chip)
+{
+ struct open_eth_softc *sc;
+ struct ifnet *ifp;
+ int mtu;
+ int unitNumber;
+ char *unitName;
+
+ /* parse driver name */
+ if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0)
+ return 0;
+
+ sc = &oc;
+ ifp = &sc->arpcom.ac_if;
+ memset (sc, 0, sizeof (*sc));
+
+ if (config->hardware_address)
+ {
+ memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
+ ETHER_ADDR_LEN);
+ }
+ else
+ {
+ memset (sc->arpcom.ac_enaddr, 0x08, ETHER_ADDR_LEN);
+ }
+
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ sc->acceptBroadcast = !config->ignore_broadcast;
+ sc->regs = chip->base_address;
+ sc->vector = chip->vector;
+ sc->txbufs = chip->txd_count;
+ sc->rxbufs = chip->rxd_count;
+ sc->en100MHz = chip->en100MHz;
+
+
+ /*
+ * Set up network interface values
+ */
+ ifp->if_softc = sc;
+ ifp->if_unit = unitNumber;
+ ifp->if_name = unitName;
+ ifp->if_mtu = mtu;
+ ifp->if_init = open_eth_init;
+ ifp->if_ioctl = open_eth_ioctl;
+ ifp->if_start = open_eth_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);
+
+#ifdef OPEN_ETH_DEBUG
+ printf ("OPEN_ETH : driver has been attached\n");
+#endif
+ return 1;
+};
+
+#endif /* OPENETH_NOT_SUPPORTED */
diff --git a/bsps/shared/net/smc91111.c b/bsps/shared/net/smc91111.c
new file mode 100644
index 0000000..61703c7
--- /dev/null
+++ b/bsps/shared/net/smc91111.c
@@ -0,0 +1,1653 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+#include <rtems.h>
+#include <errno.h>
+
+#include <bsp.h>
+
+/*
+ * This driver currently only supports SPARC with the old style
+ * exception processing and the Phytec Phycore MPC5554.
+ * This test keeps it from being compiled on systems which haven't been
+ * tested.
+ *
+ */
+
+#if defined(HAS_SMC91111)
+ #define SMC91111_SUPPORTED
+#endif
+
+#if defined(HAS_SMC91111)
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+#include <rtems/irq-extension.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>
+
+#define SMC91111_INTERRUPT_EVENT RTEMS_EVENT_1 /* RTEMS event used by interrupt handler to signal driver tasks. This must not be any of the events used by the network task synchronization. */
+#define SMC91111_START_TRANSMIT_EVENT RTEMS_EVENT_2 /* RTEMS event used to start transmit/receive daemon. This must not be the same as INTERRUPT_EVENT. */
+#define SMC91111_TX_WAIT_EVENT RTEMS_EVENT_3 /* event to send when tx buffers become available */
+
+/* Set to perms of:
+ 0 disables all debug output
+ 1 for process debug output
+ 2 for added data IO output: get_reg, put_reg
+ 4 for packet allocation/free output
+ 8 for only startup status, so we can tell we're installed OK
+ 16 dump phy read/write
+ 32 precise register dump
+ 64 dump packets
+*/
+/*#define DEBUG (-1)*/
+/*#define DEBUG (-1 & ~(16))*/
+#define DEBUG (0)
+/*#define DEBUG (1)*/
+
+#include "smc91111config.h"
+#include <libchip/smc91111.h>
+
+#ifdef BSP_FEATURE_IRQ_EXTENSION
+ #include <rtems/irq-extension.h>
+#endif
+
+struct lan91cxx_priv_data smc91111;
+
+int lan91cxx_hardware_init(struct lan91cxx_priv_data *cpd);
+static uint16_t lan91cxx_read_phy(struct lan91cxx_priv_data *cpd,
+ uint8_t phyaddr, uint8_t phyreg);
+static void lan91cxx_write_phy(struct lan91cxx_priv_data *cpd,
+ uint8_t phyaddr, uint8_t phyreg,
+ uint16_t value);
+static void lan91cxx_start(struct ifnet *ifp);
+static void smc91111_start(struct ifnet *ifp);
+static int smc_probe(struct lan91cxx_priv_data *cpd);
+static void smc91111_stop(struct lan91cxx_priv_data *cpd);
+static void smc91111_init(void *arg);
+#ifndef BSP_FEATURE_IRQ_EXTENSION
+static void lan91cxx_finish_sent(struct lan91cxx_priv_data *cpd);
+#endif
+#if 0
+static int lan91cxx_phy_fixed(struct lan91cxx_priv_data *cpd);
+static void lan91cxx_phy_configure(struct lan91cxx_priv_data *cpd);
+#endif
+
+#define min(l,r) ((l) < (r) ? (l) : (r))
+#define max(l,r) ((l) > (r) ? (l) : (r))
+
+#ifndef BSP_FEATURE_IRQ_EXTENSION
+/* \ ------------- Interrupt ------------- \ */
+static void lan91cxx_interrupt_handler(void *arg)
+{
+ struct lan91cxx_priv_data *cpd = arg;
+ unsigned short irq, event;
+ unsigned short oldbase;
+ unsigned short oldpointer;
+ INCR_STAT(cpd, interrupts);
+ DEBUG_FUNCTION();
+
+ HAL_READ_UINT16(cpd->base + (LAN91CXX_BS), oldbase);
+ oldpointer = get_reg(cpd, LAN91CXX_POINTER);
+
+ /* Get the (unmasked) requests */
+ irq = get_reg(cpd, LAN91CXX_INTERRUPT);
+ event = irq & (irq >> 8) & 0xff;
+ if (0 == event)
+ return;
+
+ /*put_reg(cpd, LAN91CXX_INTERRUPT, irq ); */ /* ack interrupts */
+
+ if (event & LAN91CXX_INTERRUPT_ERCV_INT) {
+ db_printf("Early receive interrupt");
+ } else if (event & LAN91CXX_INTERRUPT_EPH_INT) {
+ db_printf("ethernet protocol handler failures");
+ } else if (event & LAN91CXX_INTERRUPT_RX_OVRN_INT) {
+ db_printf("receive overrun");
+ } else if (event & LAN91CXX_INTERRUPT_ALLOC_INT) {
+ db_printf("allocation interrupt");
+ } else {
+
+ if (event & LAN91CXX_INTERRUPT_TX_SET) {
+ db_printf("#*tx irq\n");
+ lan91cxx_finish_sent(cpd);
+ put_reg(cpd, LAN91CXX_INTERRUPT,
+ (irq & 0xff00) | LAN91CXX_INTERRUPT_TX_INT);
+
+ /*rtems_bsdnet_event_send (cpd->txDaemonTid, SMC91111_INTERRUPT_EVENT); */
+ /*put_reg(cpd, LAN91CXX_INTERRUPT, (irq & 0xff00) | LAN91CXX_INTERRUPT_TX_INT); */
+ /*rtems_bsdnet_event_send (cpd->txDaemonTid, SMC91111_TX_WAIT_EVENT); */
+ }
+ if (event & LAN91CXX_INTERRUPT_RCV_INT) {
+ db_printf("#*rx irq\n");
+ rtems_bsdnet_event_send(cpd->rxDaemonTid,
+ SMC91111_INTERRUPT_EVENT);
+ }
+ if (event &
+ ~(LAN91CXX_INTERRUPT_TX_SET | LAN91CXX_INTERRUPT_RCV_INT))
+ db_printf("Unknown interrupt\n");
+ }
+ db_printf("out %s\n", __FUNCTION__);
+
+ put_reg(cpd, LAN91CXX_POINTER, oldpointer);
+ HAL_WRITE_UINT16(cpd->base + (LAN91CXX_BS), oldbase);
+}
+#endif
+
+/* \ ------------- Rx receive ------------- \ */
+
+ /**/
+/* This function is called as a result of the "readpacket()" call.*/
+/* Its job is to actually fetch data for a packet from the hardware once*/
+/* memory buffers have been allocated for the packet. Note that the buffers*/
+/* may come in pieces, using a mbuf list. */
+static void lan91cxx_recv(struct lan91cxx_priv_data *cpd, struct mbuf *m)
+{
+ struct ifnet *ifp = &cpd->arpcom.ac_if;
+ struct ether_header *eh;
+ short mlen = 0, plen;
+ char *start;
+ rxd_t *data = NULL, val;
+#if DEBUG & 64
+ rxd_t lp = 0;
+#else
+ /* start is only read with debug enabled */
+ (void)start;
+#endif
+ struct mbuf *n;
+ dbg_prefix = "<";
+
+ DEBUG_FUNCTION();
+ INCR_STAT(cpd, rx_deliver);
+
+ /* ############ read packet ############ */
+
+ put_reg(cpd, LAN91CXX_POINTER,
+ (LAN91CXX_POINTER_RCV | LAN91CXX_POINTER_READ |
+ LAN91CXX_POINTER_AUTO_INCR));
+ val = get_data(cpd);
+
+ /* packet length (minus header/footer) */
+#ifdef LAN91CXX_32BIT_RX
+ val = CYG_LE32_TO_CPU(val);
+ plen = (val >> 16) - 6;
+#else
+ val = CYG_LE16_TO_CPU(val);
+ plen = get_data(cpd);
+ plen = CYG_LE16_TO_CPU(plen) - 6;
+#endif
+
+ if ( cpd->c111_reva || LAN91CXX_RX_STATUS_IS_ODD(cpd, val) ) /* RevA Odd-bit BUG */
+ plen++;
+
+ for (n = m; n; n = n->m_next) {
+#ifdef LAN91CXX_32BIT_RX
+ if (mlen == 2) {
+#if DEBUG & 64
+ db_printf("Appending to last apacket\n");
+#endif
+
+ val = get_data(cpd);
+ *((unsigned short *)data) = (val >> 16) & 0xffff;
+ plen -= 2;
+ data = (rxd_t *) n->m_data;
+ start = (char *)data;
+ mlen = n->m_len;
+ if ((data) && (mlen > 1)) {
+ *(unsigned short *)data = (val & 0xffff);
+ data = (rxd_t *)((unsigned short *)data + 1);
+ plen -= 2;
+ mlen -= 2;
+ }
+ } else {
+ data = (rxd_t *) n->m_data;
+ start = (char *)data;
+ mlen = n->m_len;
+ }
+#else
+ data = (rxd_t *) n->m_data;
+ start = (char *)data;
+ mlen = n->m_len;
+#endif
+
+ db1_printf("<[packet : mlen 0x%x, plen 0x%x]\n", mlen, plen);
+
+ if (data) {
+ while (mlen >= sizeof(*data)) {
+#ifdef LAN91CXX_32BIT_RX
+ val = get_data(cpd);
+ *(unsigned short *)data = (val >> 16) & 0xffff;
+ data = (rxd_t *)((unsigned short *)data + 1);
+ *(unsigned short *)data = (val & 0xffff);
+ data = (rxd_t *)((unsigned short *)data + 1);
+#else
+ *data++ = get_data(cpd);
+#endif
+ mlen -= sizeof(*data);
+ plen -= sizeof(*data);
+ }
+ } else { /* must actively discard ie. read it from the chip anyway. */
+ while (mlen >= sizeof(*data)) {
+ (void)get_data(cpd);
+ mlen -= sizeof(*data);
+ plen -= sizeof(*data);
+ }
+ }
+
+#if DEBUG & 64
+ lp = 0;
+ while (((int)start) < ((int)data)) {
+ unsigned char a = *(start++);
+ unsigned char b = *(start++);
+ db64_printf("%02x %02x ", a, b);
+ lp += 2;
+ if (lp >= 16) {
+ db64_printf("\n");
+ lp = 0;
+ }
+ }
+ db64_printf(" \n");
+#endif
+ }
+ val = get_data(cpd); /* Read control word (and potential data) unconditionally */
+#ifdef LAN91CXX_32BIT_RX
+ if (plen & 2) {
+ if (data && (mlen>1) ) {
+ *(unsigned short *)data = (val >> 16) & 0xffff;
+ data = (rxd_t *)((unsigned short *)data + 1);
+ val <<= 16;
+ mlen-=2;
+ }
+ }
+ if ( (plen & 1) && data && (mlen>0) )
+ *(unsigned char *)data = val >> 24;
+#else
+ val = CYG_LE16_TO_CPU(val);
+ cp = (unsigned char *)data;
+
+ CYG_ASSERT(val & LAN91CXX_CONTROLBYTE_RX, "Controlbyte is not for Rx");
+ CYG_ASSERT((1 == mlen) == (0 != LAN91CXX_CONTROLBYTE_IS_ODD(cpd, val)),
+ "Controlbyte does not match");
+ if (data && (1 == mlen) && LAN91CXX_CONTROLBYTE_IS_ODD(cpd, val)) {
+ cval = val & 0x00ff; /* last byte contains data */
+ *cp = cval;
+ }
+#endif
+
+ val = get_reg(cpd, LAN91CXX_FIFO_PORTS);
+ if (0x8000 & val) { /* Then the Rx FIFO is empty */
+ db4_printf
+ ("<+Rx packet NOT freed, stat is %x (expected %x)\n",
+ val, cpd->rxpacket);
+ } else {
+ db4_printf("<+Rx packet freed %x (expected %x)\n",
+ 0xff & (val >> 8), cpd->rxpacket);
+ }
+
+ CYG_ASSERT((0xff & (val >> 8)) == cpd->rxpacket,
+ "Unexpected rx packet");
+
+ /* ############ free packet ############ */
+ /* Free packet */
+ put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_remrel_rx_frame);
+
+ dbg_prefix = "";
+
+ /* Remove the mac header. This is different from the NetBSD stack. */
+ eh = mtod(m, struct ether_header *);
+ m->m_data += sizeof(struct ether_header);
+ m->m_len -= sizeof(struct ether_header);
+ m->m_pkthdr.len -= sizeof(struct ether_header);
+
+ ether_input(ifp, eh, m);
+
+}
+
+/* allocate mbuf chain */
+static struct mbuf *smc91111_allocmbufchain(int totlen, struct ifnet *ifp)
+{
+
+ struct mbuf *m, *m0, *newm;
+ int len;
+
+ MGETHDR(m0, M_DONTWAIT, MT_DATA);
+ if (m0 == 0)
+ return (0);
+ m0->m_pkthdr.rcvif = ifp;
+ m0->m_pkthdr.len = totlen;
+ len = MHLEN;
+ m = m0;
+
+ /* This loop goes through and allocates mbufs for all the data we will be copying in. */
+ while (totlen > 0) {
+ if (totlen >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0)
+ goto bad;
+ len = MCLBYTES;
+ }
+
+ if (m == m0) {
+ caddr_t newdata = (caddr_t)
+ ALIGN(m->m_data +
+ sizeof(struct ether_header)) -
+ sizeof(struct ether_header);
+ len -= newdata - m->m_data;
+ m->m_data = newdata;
+ }
+
+ m->m_len = len = min(totlen, len);
+
+ totlen -= len;
+ if (totlen > 0) {
+ MGET(newm, M_DONTWAIT, MT_DATA);
+ if (newm == 0)
+ goto bad;
+ len = MLEN;
+ m = m->m_next = newm;
+ }
+ }
+ return (m0);
+
+ bad:
+ m_freem(m0);
+ return (0);
+}
+
+static int readpacket(struct lan91cxx_priv_data *cpd)
+{
+ struct mbuf *m;
+ unsigned short stat, complen;
+ struct ifnet *ifp = &cpd->arpcom.ac_if;
+#ifdef LAN91CXX_32BIT_RX
+ uint32_t val;
+#endif
+
+ DEBUG_FUNCTION();
+
+ /* ############ read packet nr ############ */
+ stat = get_reg(cpd, LAN91CXX_FIFO_PORTS);
+ db1_printf("+LAN91CXX_FIFO_PORTS: 0x%04x\n", stat);
+
+ if (0x8000 & stat) {
+ /* Then the Rx FIFO is empty */
+ db4_printf("!RxEvent with empty fifo\n");
+ return 0;
+ }
+
+ INCR_STAT(cpd, rx_count);
+
+ db4_printf("+Rx packet allocated %x (previous %x)\n",
+ 0xff & (stat >> 8), cpd->rxpacket);
+
+ /* There is an Rx Packet ready */
+ cpd->rxpacket = 0xff & (stat >> 8);
+
+ /* ############ read packet header ############ */
+ /* Read status and (word) length */
+ put_reg(cpd, LAN91CXX_POINTER,
+ (LAN91CXX_POINTER_RCV | LAN91CXX_POINTER_READ |
+ LAN91CXX_POINTER_AUTO_INCR | 0x0000));
+#ifdef LAN91CXX_32BIT_RX
+ val = get_data(cpd);
+ val = CYG_LE32_TO_CPU(val);
+ stat = val & 0xffff;
+ complen = ((val >> 16) & 0xffff) - 6; /* minus header/footer words */
+#else
+ stat = get_data(cpd);
+ stat = CYG_LE16_TO_CPU(stat);
+ complen = get_data(cpd);
+ complen = CYG_LE16_TO_CPU(len) - 6; /* minus header/footer words */
+#endif
+
+#ifdef KEEP_STATISTICS
+ if (stat & LAN91CXX_RX_STATUS_ALIGNERR)
+ INCR_STAT(cpd, rx_align_errors);
+ /*if ( stat & LAN91CXX_RX_STATUS_BCAST ) INCR_STAT( ); */
+ if (stat & LAN91CXX_RX_STATUS_BADCRC)
+ INCR_STAT(cpd, rx_crc_errors);
+ if (stat & LAN91CXX_RX_STATUS_TOOLONG)
+ INCR_STAT(cpd, rx_too_long_frames);
+ if (stat & LAN91CXX_RX_STATUS_TOOSHORT)
+ INCR_STAT(cpd, rx_short_frames);
+ /*if ( stat & LAN91CXX_RX_STATUS_MCAST ) INCR_STAT( ); */
+#endif /* KEEP_STATISTICS */
+
+ if ((stat & LAN91CXX_RX_STATUS_BAD) == 0) {
+ INCR_STAT(cpd, rx_good);
+ /* Then it's OK */
+
+ if (cpd->c111_reva || LAN91CXX_RX_STATUS_IS_ODD(cpd, stat)) /* RevA Odd-bit BUG */
+ complen++;
+
+#if DEBUG & 1
+ db_printf("good rx - stat: 0x%04x, len: 0x%04x\n", stat,
+ complen);
+#endif
+ /* Check for bogusly short packets; can happen in promisc mode: */
+ /* Asserted against and checked by upper layer driver. */
+ if (complen > sizeof(struct ether_header)) {
+ /* then it is acceptable; offer the data to the network stack */
+
+ complen = ((complen + 3) & ~3);
+
+ m = smc91111_allocmbufchain(complen, ifp);
+ {
+ struct mbuf *n = m;
+ db_printf("mbuf-chain:");
+ while (n) {
+ db_printf("[%" PRIxPTR ":%x]",
+ mtod(n, uintptr_t),
+ (unsigned int)(n->m_len));
+ n = n->m_next;
+ }
+ db_printf("\n");
+ }
+
+ if (m) {
+ /* fetch packet data into mbuf chain */
+ lan91cxx_recv(cpd, m);
+ return 1;
+ }
+ }
+ /*(sc->funs->eth_drv->recv)(sc, len); */
+ }
+
+ /* Not OK for one reason or another... */
+ db1_printf("!bad rx: stat: 0x%04x, len: 0x%04x\n", stat, complen);
+
+ /* ############ free packet ############ */
+ /* Free packet */
+ put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_remrel_rx_frame);
+ return 1;
+
+}
+
+static void smc91111_rxDaemon(void *arg)
+{
+ struct lan91cxx_priv_data *cpd = arg;
+ rtems_event_set events;
+ DEBUG_FUNCTION();
+
+ for (;;) {
+ rtems_bsdnet_event_receive(INTERRUPT_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT, &events);
+
+ /* read until read fifo is empty */
+ while (!(get_reg(cpd, LAN91CXX_FIFO_PORTS) & 0x8000)) {
+ readpacket(cpd);
+ }
+ }
+}
+
+/* \ ------------- Tx send ------------- \ */
+
+static void sendpacket(struct ifnet *ifp, struct mbuf *m)
+{
+ struct lan91cxx_priv_data *cpd = ifp->if_softc;
+ int i, len, plen, tcr, odd;
+ struct mbuf *n = m;
+ unsigned short *sdata = NULL;
+ unsigned short ints, control;
+ uint16_t packet, status;
+ dbg_prefix = ">";
+ DEBUG_FUNCTION();
+
+ cpd->txbusy = 1;
+
+ /* Worry about the TX engine stopping. */
+ tcr = get_reg(cpd, LAN91CXX_TCR);
+ if (0 == (LAN91CXX_TCR_TXENA & tcr)) {
+ db1_printf("> ENGINE RESTART: tcr %x\n", tcr);
+ tcr |= LAN91CXX_TCR_TXENA;
+ put_reg(cpd, LAN91CXX_TCR, tcr);
+ }
+
+ /* ############ packet allocation ############ */
+
+ /* Find packet length */
+ plen = 0;
+ while (n) {
+ plen += n->m_len;
+ n = n->m_next;
+ }
+
+ /* Alloc new TX packet */
+ do {
+ put_reg(cpd, LAN91CXX_MMU_COMMAND,
+ LAN91CXX_MMU_alloc_for_tx | ((plen >> 8) & 0x07));
+
+ i = 1024 * 1024;
+ do {
+ status = get_reg(cpd, LAN91CXX_INTERRUPT);
+ } while (0 == (status & LAN91CXX_INTERRUPT_ALLOC_INT)
+ && (--i > 0));
+ if (i)
+ packet = get_reg(cpd, LAN91CXX_PNR);
+ else
+ packet = 0xffff;
+ db1_printf(">+allocated packet %04x\n", packet);
+
+ packet = packet >> 8;
+ if (packet & 0x80) {
+ /* Hm.. Isn't this a dead end? */
+ db1_printf("Allocation failed! Retrying...\n");
+ continue;
+ }
+ } while (0);
+
+ db4_printf(">+Tx packet allocated %x (previous %x)\n",
+ packet, cpd->txpacket);
+
+ cpd->txpacket = packet;
+
+ /* ############ assemble packet data ############ */
+
+ /* prepare send */
+ put_reg(cpd, LAN91CXX_PNR, packet);
+ /* Note: Check FIFO state here before continuing? */
+ put_reg(cpd, LAN91CXX_POINTER, LAN91CXX_POINTER_AUTO_INCR | 0x0000);
+ /* Pointer is now set, and the proper bank is selected for */
+ /* data writes. */
+
+ /* Prepare header: */
+ put_data(cpd, CYG_CPU_TO_LE16(0)); /* reserve space for status word */
+ /* packet length (includes status, byte-count and control shorts) */
+ put_data(cpd, CYG_CPU_TO_LE16(0x7FE & (plen + 6))); /* Always even, always < 15xx(dec) */
+
+ /* Put data into buffer */
+ odd = 0;
+ n = m;
+ while (n) {
+ sdata = (unsigned short *)n->m_data;
+ len = n->m_len;
+
+ CYG_ASSERT(sdata, "!No sg data pointer here");
+
+ /* start on an odd offset?
+ * If last byte also (1byte mbuf with different pointer should not occur)
+ * let following code handle it
+ */
+ if ( ((unsigned int)sdata & 1) && (len>1) ){
+ put_data8(cpd,*(unsigned char *)sdata);
+ sdata = (unsigned short *)((unsigned int)sdata + 1);
+ odd = ~odd;
+ len--;
+ }
+
+ /* speed up copying a bit, never copy last word */
+ while(len >= 17){
+ put_data(cpd, *(sdata));
+ put_data(cpd, *(sdata+1));
+ put_data(cpd, *(sdata+2));
+ put_data(cpd, *(sdata+3));
+ put_data(cpd, *(sdata+4));
+ put_data(cpd, *(sdata+5));
+ put_data(cpd, *(sdata+6));
+ put_data(cpd, *(sdata+7));
+ sdata += 8;
+ len -= 16;
+ }
+
+ /* copy word wise, skip last word */
+ while (len >= 3) {
+ put_data(cpd, *sdata++);
+ len -= sizeof(*sdata);
+ }
+
+ /* one or two bytes left to put into fifo */
+ if ( len > 1 ){
+ /* the last 2bytes */
+ if ( !odd || n->m_next ){
+ put_data(cpd, *sdata++);
+ len -= sizeof(*sdata);
+ }else{
+ /* write next byte, mark that we are not at an odd offset any more,
+ * remaining byte will be written outside while together with control byte.
+ */
+ put_data8(cpd,*(unsigned char *)sdata);
+ sdata = (unsigned short *)((unsigned int)sdata + 1);
+ odd = 0;
+ len--;
+ /*break;*/
+ }
+ }else if ( (len>0) && (n->m_next) ){
+ /* one byte left to write, and more bytes is comming in next mbuf */
+ put_data8(cpd,*(unsigned char *)sdata);
+ odd = ~odd;
+ }
+
+ n = n->m_next;
+ }
+
+ /* Lay down the control short unconditionally at the end. */
+ /* (or it might use random memory contents) */
+ control = 0;
+ if ( len > 0 ){
+ if ( !odd ) {
+ /* Need to set ODD flag and insert the data */
+ unsigned char onebyte = *(unsigned char *)sdata;
+ control = onebyte;
+ control |= LAN91CXX_CONTROLBYTE_ODD;
+ }else{
+ put_data8(cpd,*(unsigned char *)sdata);
+ }
+ }
+ control |= LAN91CXX_CONTROLBYTE_CRC; /* Just in case... */
+ put_data(cpd, CYG_CPU_TO_LE16(control));
+
+ m_freem(m);
+ CYG_ASSERT(sdata, "!No sg data pointer outside");
+
+ /* ############ start transmit ############ */
+
+ /* Ack TX empty int and unmask it. */
+ ints = get_reg(cpd, LAN91CXX_INTERRUPT) & 0xff00;
+ put_reg(cpd, LAN91CXX_INTERRUPT,
+ ints | LAN91CXX_INTERRUPT_TX_EMPTY_INT);
+ put_reg(cpd, LAN91CXX_INTERRUPT, ints | LAN91CXX_INTERRUPT_TX_INT_M); /* notify on error only (Autorelease) */
+
+ /* Enqueue the packet */
+ put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_enq_packet);
+
+ ints = get_reg(cpd, LAN91CXX_INTERRUPT);
+ db1_printf(">END: ints at TX: %04x\n", ints);
+ dbg_prefix = "";
+}
+
+static void smc91111_txDaemon(void *arg)
+{
+ struct lan91cxx_priv_data *cpd = arg;
+ struct ifnet *ifp = &cpd->arpcom.ac_if;
+ struct mbuf *m;
+ rtems_event_set events;
+ DEBUG_FUNCTION();
+
+ for (;;) {
+ /*
+ * Wait for packet
+ */
+
+ rtems_bsdnet_event_receive
+ (SMC91111_START_TRANSMIT_EVENT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events);
+
+ /*IF_DEQUEUE (&ifp->if_snd, m);
+ if (m) {
+ sendpacket (ifp, m);
+ } */
+
+ for (;;) {
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (!m)
+ break;
+ sendpacket(ifp, m);
+ }
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ }
+
+}
+
+/* start transmit */
+static void smc91111_start(struct ifnet *ifp)
+{
+ struct lan91cxx_priv_data *cpd = ifp->if_softc;
+
+ if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+ return;
+
+ rtems_bsdnet_event_send(cpd->txDaemonTid, START_TRANSMIT_EVENT);
+ ifp->if_flags |= IFF_OACTIVE;
+
+}
+
+#ifndef BSP_FEATURE_IRQ_EXTENSION
+/* called after a tx error interrupt, freet the packet */
+static void lan91cxx_finish_sent(struct lan91cxx_priv_data *cpd)
+{
+ unsigned short packet, tcr;
+ int saved_packet;
+
+ DEBUG_FUNCTION();
+
+ INCR_STAT(cpd, tx_complete);
+
+ saved_packet = get_reg(cpd, LAN91CXX_PNR);
+
+ /* Ack and mask TX interrupt set */
+ /*ints = get_reg(cpd, LAN91CXX_INTERRUPT) & 0xff00;
+ ints |= LAN91CXX_INTERRUPT_TX_SET_ACK;
+ ints &= ~LAN91CXX_INTERRUPT_TX_SET_M;
+ put_reg(cpd, LAN91CXX_INTERRUPT, ints); */
+
+ /* Get number of completed packet and read the status word */
+ packet = get_reg(cpd, LAN91CXX_FIFO_PORTS);
+ db1_printf("%s:START: fifo %04x \n", __FUNCTION__, packet);
+
+ {
+ unsigned short reg;
+
+ reg = get_reg(cpd, LAN91CXX_EPH_STATUS);
+
+ /* Covering each bit in turn... */
+ if (reg & LAN91CXX_STATUS_TX_UNRN)
+ INCR_STAT(cpd, tx_underrun);
+ if (reg & LAN91CXX_STATUS_LOST_CARR)
+ INCR_STAT(cpd, tx_carrier_loss);
+ if (reg & LAN91CXX_STATUS_LATCOL)
+ INCR_STAT(cpd, tx_late_collisions);
+ if (reg & LAN91CXX_STATUS_TX_DEFR)
+ INCR_STAT(cpd, tx_deferred);
+ if (reg & LAN91CXX_STATUS_SQET)
+ INCR_STAT(cpd, tx_sqetesterrors);
+ if (reg & LAN91CXX_STATUS_16COL)
+ INCR_STAT(cpd, tx_max_collisions);
+ if (reg & LAN91CXX_STATUS_MUL_COL)
+ INCR_STAT(cpd, tx_mult_collisions);
+ if (reg & LAN91CXX_STATUS_SNGL_COL)
+ INCR_STAT(cpd, tx_single_collisions);
+ if (reg & LAN91CXX_STATUS_TX_SUC)
+ INCR_STAT(cpd, tx_good);
+
+ cpd->stats.tx_total_collisions =
+ cpd->stats.tx_late_collisions +
+ cpd->stats.tx_max_collisions +
+ cpd->stats.tx_mult_collisions +
+ cpd->stats.tx_single_collisions;
+
+ /* We do not need to look in the Counter Register (LAN91CXX_COUNTER)
+ because it just mimics the info we already have above. */
+ }
+
+ /* We do not really care about Tx failure. Ethernet is not a reliable
+ medium. But we do care about the TX engine stopping. */
+ tcr = get_reg(cpd, LAN91CXX_TCR);
+ if (0 == (LAN91CXX_TCR_TXENA & tcr)) {
+ db1_printf("%s: ENGINE RESTART: tcr %x \n", __FUNCTION__, tcr);
+ tcr |= LAN91CXX_TCR_TXENA;
+ put_reg(cpd, LAN91CXX_TCR, tcr);
+ }
+
+ packet &= 0xff;
+
+ /* and then free the packet */
+ put_reg(cpd, LAN91CXX_PNR, cpd->txpacket);
+ put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_rel_packet);
+
+ while (get_reg(cpd, LAN91CXX_MMU_COMMAND) & LAN91CXX_MMU_COMMAND_BUSY) ;
+ /* Don't change Packet Number Reg until busy bit is cleared */
+ /* Per LAN91C111 Spec, Page 50 */
+ put_reg(cpd, LAN91CXX_PNR, saved_packet);
+
+}
+#endif
+
+/* \ ------------- Helpers ------------- \ */
+
+/*
+ * Show interface statistics
+ */
+static void smc91111_stats(struct lan91cxx_priv_data *priv)
+{
+ printf("tx_good :%-8d\n", priv->stats.tx_good);
+ printf("tx_max_collisions :%-8d\n", priv->stats.tx_max_collisions);
+ printf("tx_late_collisions :%-8d\n", priv->stats.tx_late_collisions);
+ printf("tx_underrun :%-8d\n", priv->stats.tx_underrun);
+ printf("tx_carrier_loss :%-8d\n", priv->stats.tx_carrier_loss);
+ printf("tx_deferred :%-8d\n", priv->stats.tx_deferred);
+ printf("tx_sqetesterrors :%-8d\n", priv->stats.tx_sqetesterrors);
+ printf("tx_single_collisions:%-8d\n", priv->stats.tx_single_collisions);
+ printf("tx_mult_collisions :%-8d\n", priv->stats.tx_mult_collisions);
+ printf("tx_total_collisions :%-8d\n", priv->stats.tx_total_collisions);
+ printf("rx_good :%-8d\n", priv->stats.rx_good);
+ printf("rx_crc_errors :%-8d\n", priv->stats.rx_crc_errors);
+ printf("rx_align_errors :%-8d\n", priv->stats.rx_align_errors);
+ printf("rx_resource_errors :%-8d\n", priv->stats.rx_resource_errors);
+ printf("rx_overrun_errors :%-8d\n", priv->stats.rx_overrun_errors);
+ printf("rx_collisions :%-8d\n", priv->stats.rx_collisions);
+ printf("rx_short_frames :%-8d\n", priv->stats.rx_short_frames);
+ printf("rx_too_long_frames :%-8d\n", priv->stats.rx_too_long_frames);
+ printf("rx_symbol_errors :%-8d\n", priv->stats.rx_symbol_errors);
+ printf("interrupts :%-8d\n", priv->stats.interrupts);
+ printf("rx_count :%-8d\n", priv->stats.rx_count);
+ printf("rx_deliver :%-8d\n", priv->stats.rx_deliver);
+ printf("rx_resource :%-8d\n", priv->stats.rx_resource);
+ printf("rx_restart :%-8d\n", priv->stats.rx_restart);
+ printf("tx_count :%-8d\n", priv->stats.tx_count);
+ printf("tx_complete :%-8d\n", priv->stats.tx_complete);
+ printf("tx_dropped :%-8d\n", priv->stats.tx_dropped);
+}
+
+/*
+ * Driver ioctl handler
+ */
+static int smc91111_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct lan91cxx_priv_data *cpd = ifp->if_softc;
+ int error = 0;
+ DEBUG_FUNCTION();
+
+ switch (command) {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ db_printf("SIOCSIFADDR\n");
+ ether_ioctl(ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ db_printf("SIOCSIFFLAGS\n");
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
+ case IFF_RUNNING:
+ smc91111_stop(cpd);
+ break;
+
+ case IFF_UP:
+ smc91111_init(cpd);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ smc91111_stop(cpd);
+ smc91111_init(cpd);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ db_printf("SIO_RTEMS_SHOW_STATS\n");
+ smc91111_stats(cpd);
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+/*
+ * Attach an SMC91111 driver to the system
+ */
+int _rtems_smc91111_driver_attach (struct rtems_bsdnet_ifconfig *config,
+ struct scmv91111_configuration * chip)
+{
+ struct ifnet *ifp;
+ struct lan91cxx_priv_data *cpd;
+ int unitNumber;
+ char *unitName;
+ int mtu;
+ DEBUG_FUNCTION();
+
+ /* parse driver name */
+ if ((unitNumber = rtems_bsdnet_parse_driver_name(config, &unitName)) < 0) {
+ db_printf("Unitnumber < 0: %d\n", unitNumber);
+ return 0;
+ }
+
+ db_printf("Unitnumber: %d, baseaddr: 0x%p\n", unitNumber, chip->baseaddr);
+
+ cpd = &smc91111;
+ ifp = &cpd->arpcom.ac_if;
+ memset(cpd, 0, sizeof(*cpd));
+
+ cpd->config = *chip;
+ cpd->base = chip->baseaddr;
+
+ if (smc_probe(cpd)) {
+ return 0;
+ }
+
+ if (config->hardware_address) {
+ memcpy(cpd->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
+ } else {
+#ifdef SMC91111_ENADDR_IS_SETUP
+ /* The address was put in the chip at reset time. Retrieve it. */
+ int i;
+ for (i = 0; i < sizeof(cpd->enaddr); i += 2) {
+ unsigned short r = get_reg(cpd, LAN91CXX_IA01 + i / 2);
+ cpd->arpcom.ac_enaddr[i] = r;
+ cpd->arpcom.ac_enaddr[i+1] = r >> 8;
+ }
+#else
+ /* dummy default address */
+ cpd->arpcom.ac_enaddr[0] = 0x12;
+ cpd->arpcom.ac_enaddr[1] = 0x13;
+ cpd->arpcom.ac_enaddr[2] = 0x14;
+ cpd->arpcom.ac_enaddr[3] = 0x15;
+ cpd->arpcom.ac_enaddr[4] = 0x16;
+ cpd->arpcom.ac_enaddr[5] = 0x17;
+#endif
+ }
+
+ cpd->enaddr[0] = cpd->arpcom.ac_enaddr[0];
+ cpd->enaddr[1] = cpd->arpcom.ac_enaddr[1];
+ cpd->enaddr[2] = cpd->arpcom.ac_enaddr[2];
+ cpd->enaddr[3] = cpd->arpcom.ac_enaddr[3];
+ cpd->enaddr[4] = cpd->arpcom.ac_enaddr[4];
+ cpd->enaddr[5] = cpd->arpcom.ac_enaddr[5];
+ cpd->rpc_cur_mode =
+ LAN91CXX_RPCR_LEDA_RX | LAN91CXX_RPCR_LEDB_LINK |
+ LAN91CXX_RPCR_ANEG;
+
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ /*
+ * Set up network interface values
+ */
+ ifp->if_softc = cpd;
+ ifp->if_unit = unitNumber;
+ ifp->if_name = unitName;
+ ifp->if_mtu = mtu;
+ ifp->if_init = smc91111_init;
+ ifp->if_ioctl = smc91111_ioctl;
+ ifp->if_start = smc91111_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);
+
+#if DEBUG
+ printf("SMC91111 : driver has been attached\n");
+#endif
+
+ return 1;
+};
+
+/* \ ------------- Initialization ------------- \ */
+
+/*
+ * Initialize and start the device
+ */
+static void smc91111_init(void *arg)
+{
+ struct lan91cxx_priv_data *cpd = arg;
+ struct ifnet *ifp = &cpd->arpcom.ac_if;
+ DEBUG_FUNCTION();
+
+ if (cpd->txDaemonTid == 0) {
+
+ lan91cxx_hardware_init(cpd);
+ lan91cxx_start(ifp);
+
+ cpd->rxDaemonTid = rtems_bsdnet_newproc("DCrx", 4096,
+ smc91111_rxDaemon, cpd);
+ cpd->txDaemonTid =
+ rtems_bsdnet_newproc("DCtx", 4096, smc91111_txDaemon, cpd);
+ } else {
+ lan91cxx_start(ifp);
+ }
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+}
+
+/*
+ * Stop the device
+ */
+static void smc91111_stop(struct lan91cxx_priv_data *cpd)
+{
+ struct ifnet *ifp = &cpd->arpcom.ac_if;
+ DEBUG_FUNCTION();
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /* Reset chip */
+ put_reg(cpd, LAN91CXX_RCR, LAN91CXX_RCR_SOFT_RST);
+ put_reg(cpd, LAN91CXX_RCR, 0);
+ cpd->txbusy = cpd->within_send = 0;
+
+}
+
+int lan91cxx_hardware_init(struct lan91cxx_priv_data *cpd)
+{
+ unsigned short val;
+ int i;
+
+ DEBUG_FUNCTION();
+
+ cpd->txbusy = cpd->within_send = 0;
+
+ /* install interrupt vector */
+#ifdef BSP_FEATURE_IRQ_EXTENSION
+ {
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ sc = rtems_interrupt_handler_install(
+ cpd->config.vector,
+ cpd->config.info,
+ cpd->config.options,
+ cpd->config.interrupt_wrapper,
+ cpd
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ printf("rtems_interrupt_handler_install returned %d.\n", sc);
+ return 0;
+ }
+ }
+#else
+ {
+ int rc;
+
+ db_printf("Install lan91cxx isr at vec/irq %" PRIu32 "\n", cpd->config.vector);
+ rc = rtems_interrupt_handler_install(cpd->config.vector, "smc91cxx",
+ RTEMS_INTERRUPT_SHARED, lan91cxx_interrupt_handler, cpd);
+ if (rc != RTEMS_SUCCESSFUL)
+ return 0;
+ }
+#endif
+
+ /* Reset chip */
+ put_reg(cpd, LAN91CXX_RCR, LAN91CXX_RCR_SOFT_RST);
+ put_reg(cpd, LAN91CXX_RCR, 0);
+ HAL_DELAY_US(100000);
+ put_reg(cpd, LAN91CXX_CONFIG, 0x9000);
+ put_reg(cpd, LAN91CXX_RCR, 0);
+ put_reg(cpd, LAN91CXX_TCR, 0);
+ put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_reset_mmu);
+
+ val = get_reg(cpd, LAN91CXX_EPH_STATUS);
+ /* probe chip by reading the signature in BS register */
+ val = get_banksel(cpd);
+ db9_printf("LAN91CXX - supposed BankReg @ %x = %04x\n",
+ (unsigned int)(cpd->base + LAN91CXX_BS), val);
+
+ if ((0xff00 & val) != 0x3300) {
+ printf("No 91Cxx signature");
+ printf("smsc_lan91cxx_init: No 91Cxx signature found\n");
+ return 0;
+ }
+
+ val = get_reg(cpd, LAN91CXX_REVISION);
+
+ db9_printf("LAN91CXX - type: %01x, rev: %01x\n",
+ (val >> 4) & 0xf, val & 0xf);
+
+ /* Set RevA flag for LAN91C111 so we can cope with the odd-bit bug. */
+ cpd->c111_reva = (val == 0x3390); /* 90=A, 91=B, 92=C */
+
+ /* The controller may provide a function used to set up the ESA */
+ if (cpd->config_enaddr)
+ (*cpd->config_enaddr) (cpd);
+
+ db9_printf("LAN91CXX - status: %04x\n", val);
+ /* Use statically configured ESA from the private data */
+ db9_printf
+ ("LAN91CXX - static ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ cpd->enaddr[0], cpd->enaddr[1], cpd->enaddr[2],
+ cpd->enaddr[3], cpd->enaddr[4], cpd->enaddr[5]);
+ /* Set up hardware address */
+ for (i = 0; i < sizeof(cpd->enaddr); i += 2)
+ put_reg(cpd, LAN91CXX_IA01 + i / 2,
+ cpd->enaddr[i] | (cpd->enaddr[i + 1] << 8));
+
+ return 1;
+}
+
+/*
+ This function is called to "start up" the interface. It may be called
+ multiple times, even when the hardware is already running. It will be
+ called whenever something "hardware oriented" changes and should leave
+ the hardware ready to send/receive packets.
+*/
+static void lan91cxx_start(struct ifnet *ifp)
+{
+ struct lan91cxx_priv_data *cpd = ifp->if_softc;
+
+ uint16_t intr;
+ uint16_t phy_ctl;
+ int delay;
+ DEBUG_FUNCTION();
+
+ HAL_DELAY_US(100000);
+
+ /* 91C111 Errata. Internal PHY comes up disabled. Must enable here. */
+ phy_ctl = lan91cxx_read_phy(cpd, 0, LAN91CXX_PHY_CTRL);
+ phy_ctl &= ~LAN91CXX_PHY_CTRL_MII_DIS;
+ lan91cxx_write_phy(cpd, 0, LAN91CXX_PHY_CTRL, phy_ctl);
+
+ /* Start auto-negotiation */
+ put_reg(cpd, LAN91CXX_RPCR,
+ LAN91CXX_RPCR_LEDA_RX | LAN91CXX_RPCR_LEDB_LINK |
+ LAN91CXX_RPCR_ANEG);
+ cpd->rpc_cur_mode =
+ LAN91CXX_RPCR_LEDA_RX | LAN91CXX_RPCR_LEDB_LINK |
+ LAN91CXX_RPCR_ANEG;
+
+ /* wait for auto-negotiation to finish. */
+ /* give it ~5 seconds before giving up (no cable?) */
+ delay = 50;
+ while (!(lan91cxx_read_phy(cpd, 0, LAN91CXX_PHY_STAT) & 0x20)) {
+ if (--delay <= 0) {
+ printf("Timeout autonegotiation\n");
+ break;
+ }
+ HAL_DELAY_US(100000);
+ }
+
+ put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_reset_mmu);
+
+ put_reg(cpd, LAN91CXX_INTERRUPT, 0); /* disable interrupts */
+ intr = get_reg(cpd, LAN91CXX_INTERRUPT);
+ put_reg(cpd, LAN91CXX_INTERRUPT, intr & /* ack old interrupts */
+ (LAN91CXX_INTERRUPT_TX_INT |
+ LAN91CXX_INTERRUPT_TX_EMPTY_INT |
+ LAN91CXX_INTERRUPT_RX_OVRN_INT | LAN91CXX_INTERRUPT_ERCV_INT));
+ put_reg(cpd, LAN91CXX_RCR,
+ LAN91CXX_RCR_STRIP_CRC | LAN91CXX_RCR_RXEN |
+ LAN91CXX_RCR_ALMUL);
+ put_reg(cpd, LAN91CXX_TCR, LAN91CXX_TCR_TXENA | LAN91CXX_TCR_PAD_EN);
+ put_reg(cpd, LAN91CXX_CONTROL, LAN91CXX_CONTROL_AUTO_RELEASE); /* */
+ put_reg(cpd, LAN91CXX_INTERRUPT, /* enable interrupts */
+ LAN91CXX_INTERRUPT_RCV_INT_M);
+
+ if ((0
+#ifdef ETH_DRV_FLAGS_PROMISC_MODE
+ != (flags & ETH_DRV_FLAGS_PROMISC_MODE)
+#endif
+ ) || (ifp->if_flags & IFF_PROMISC)
+ ) {
+ /* Then we select promiscuous mode. */
+ unsigned short rcr;
+ rcr = get_reg(cpd, LAN91CXX_RCR);
+ rcr |= LAN91CXX_RCR_PRMS;
+ put_reg(cpd, LAN91CXX_RCR, rcr);
+ }
+}
+
+/* \ ------------- Probe ------------- \ */
+
+static const char *chip_ids[15] = {
+ NULL, NULL, NULL,
+ /* 3 */ "SMC91C90/91C92",
+ /* 4 */ "SMC91C94",
+ /* 5 */ "SMC91C95",
+ /* 6 */ "SMC91C96",
+ /* 7 */ "SMC91C100",
+ /* 8 */ "SMC91C100FD",
+ /* 9 */ "SMC91C11xFD",
+ NULL, NULL,
+ NULL, NULL, NULL
+};
+
+static int smc_probe(struct lan91cxx_priv_data *cpd)
+{
+ unsigned short bank;
+ unsigned short revision_register;
+
+ DEBUG_FUNCTION();
+
+ /* First, see if the high byte is 0x33 */
+ HAL_READ_UINT16(cpd->base + (LAN91CXX_BS), bank);
+ bank = CYG_LE16_TO_CPU(bank);
+ if ((bank & 0xFF00) != 0x3300) {
+ db_printf("<1>Smc probe bank check 1 failed.\n");
+ return -ENODEV;
+ }
+ /* The above MIGHT indicate a device, but I need to write to further
+ test this. */
+ HAL_WRITE_UINT16(cpd->base + (LAN91CXX_BS), CYG_CPU_TO_LE16(0 >> 3));
+ HAL_READ_UINT16(cpd->base + (LAN91CXX_BS), bank);
+ bank = CYG_LE16_TO_CPU(bank);
+ if ((bank & 0xFF00) != 0x3300) {
+ db_printf("<1>Smc probe bank check 2 failed.\n");
+ return -ENODEV;
+ }
+#if SMC_DEBUG > 3
+ {
+ unsigned short bank16, bank16_0, bank16_1;
+ HAL_READ_UINT16(cpd->base + (LAN91CXX_BS), bank16);
+ bank = CYG_LE16_TO_CPU(bank);
+ HAL_READ_UINT8(cpd->base + (LAN91CXX_BS), bank16_0);
+ HAL_READ_UINT8(cpd->base + (LAN91CXX_BS + 1), bank16_1);
+
+ db_printf
+ ("smc_probe:Bank read as a 16 bit value:0x%04x\n", bank16);
+ db_printf
+ ("smc_probe:Bank read as an 8 bit value:0x%02x\n",
+ bank16_0);
+ db_printf
+ ("smc_probe:Bank + 1 read as an 8 bit value:0x%02x\n",
+ bank16_1);
+ }
+#endif
+
+ /* check if the revision register is something that I recognize.
+ These might need to be added to later, as future revisions
+ could be added. */
+ revision_register = get_reg(cpd, LAN91CXX_REVISION);
+ if (!chip_ids[(revision_register >> 4) & 0xF]) {
+ /* I don't recognize this chip, so... */
+ db_printf
+ ("smc_probe: IO %" PRIxPTR ": Unrecognized revision register:"
+ " %x, Contact author. \n", (uintptr_t)cpd->base,
+ revision_register);
+
+ return -ENODEV;
+ }
+ db_printf("LAN91CXX(0x%x) - type: %s, rev: %01x\n",
+ revision_register,
+ chip_ids[(revision_register >> 4) & 0xF],
+ revision_register & 0xf);
+
+ /* Set RevA flag for LAN91C111 so we can cope with the odd-bit bug. */
+ if (revision_register == 0x3390) {
+ db_printf("!Revision A\n");
+ }
+
+ return 0;
+}
+
+#if 0
+/* \ ------------- PHY read/write ------------- \ */
+/*Sets the PHY to a configuration as determined by the user*/
+static int lan91cxx_phy_fixed(struct lan91cxx_priv_data *cpd)
+{
+ int my_fixed_caps;
+ int cfg1;
+
+ DEBUG_FUNCTION();
+ db4_printf("lan91cxx_phy_fixed: full duplex: %d, speed: %d\n",
+ cpd->config.ctl_rfduplx, cpd->config.ctl_rspeed);
+
+ /* Enter Link Disable state */
+ cfg1 = lan91cxx_read_phy(cpd, 0, LAN91CXX_PHY_CONFIG1);
+ cfg1 |= PHY_CFG1_LNKDIS;
+ lan91cxx_write_phy(cpd, 0, LAN91CXX_PHY_CONFIG1, cfg1);
+
+ /* Set our fixed capabilities, Disable auto-negotiation */
+ my_fixed_caps = 0;
+
+ if (cpd->config.ctl_rfduplx)
+ my_fixed_caps |= LAN91CXX_PHY_CTRL_DPLX;
+
+ if (cpd->config.ctl_rspeed == 100)
+ my_fixed_caps |= LAN91CXX_PHY_CTRL_SPEED;
+
+ /* Write capabilities to the phy control register */
+ lan91cxx_write_phy(cpd, 0, LAN91CXX_PHY_CTRL, my_fixed_caps);
+
+ /* Re-Configure the Receive/Phy Control register */
+ put_reg(cpd, LAN91CXX_RPCR, cpd->rpc_cur_mode);
+
+ return (1);
+}
+#endif
+
+#if 0
+/*Configures the specified PHY using Autonegotiation. */
+static void lan91cxx_phy_configure(struct lan91cxx_priv_data *cpd)
+{
+
+ unsigned int phyaddr;
+ unsigned int my_phy_caps; /* My PHY capabilities */
+ unsigned int my_ad_caps; /* My Advertised capabilities */
+ unsigned int status = 0;
+ int failed = 0, delay;
+
+ DEBUG_FUNCTION();
+
+ /* Set the blocking flag */
+ cpd->autoneg_active = 1;
+
+ /* Get the detected phy address */
+ phyaddr = cpd->phyaddr;
+
+ /* Reset the PHY, setting all other bits to zero */
+ lan91cxx_write_phy(cpd, 0, PHY_CNTL_REG, PHY_CNTL_RST);
+
+ /* Wait for the reset to complete, or time out */
+ delay = 50;
+ while (delay--) {
+ if (!(lan91cxx_read_phy(cpd, 0, PHY_CNTL_REG)
+ & PHY_CNTL_RST)) {
+ break;
+ }
+ HAL_DELAY_US(100000);
+ }
+
+ if (delay < 1) {
+ db_printf("smc91111:!PHY reset timed out\n");
+ goto smc_phy_configure_exit;
+ }
+
+ /* Read PHY Register 18, Status Output */
+ cpd->lastPhy18 = lan91cxx_read_phy(cpd, 0, PHY_INT_REG);
+
+ /* Enable PHY Interrupts (for register 18) */
+ /* Interrupts listed here are disabled */
+ lan91cxx_write_phy(cpd, 0, PHY_MASK_REG,
+ PHY_INT_LOSSSYNC | PHY_INT_CWRD | PHY_INT_SSD
+ | PHY_INT_ESD | PHY_INT_RPOL | PHY_INT_JAB |
+ PHY_INT_SPDDET | PHY_INT_DPLXDET);
+
+ /* Configure the Receive/Phy Control register */
+ put_reg(cpd, LAN91CXX_RPCR, cpd->rpc_cur_mode);
+
+ /* Copy our capabilities from PHY_STAT_REG to PHY_AD_REG */
+ my_phy_caps = lan91cxx_read_phy(cpd, phyaddr, PHY_STAT_REG);
+ my_ad_caps = PHY_AD_CSMA; /* I am CSMA capable */
+
+ if (my_phy_caps & PHY_STAT_CAP_T4)
+ my_ad_caps |= PHY_AD_T4;
+
+ if (my_phy_caps & PHY_STAT_CAP_TXF)
+ my_ad_caps |= PHY_AD_TX_FDX;
+
+ if (my_phy_caps & PHY_STAT_CAP_TXH)
+ my_ad_caps |= PHY_AD_TX_HDX;
+
+ if (my_phy_caps & PHY_STAT_CAP_TF)
+ my_ad_caps |= PHY_AD_10_FDX;
+
+ if (my_phy_caps & PHY_STAT_CAP_TH)
+ my_ad_caps |= PHY_AD_10_HDX;
+
+ /* Disable capabilities not selected by our user */
+ if (cpd->config.ctl_rspeed != 100) {
+ my_ad_caps &= ~(PHY_AD_T4 | PHY_AD_TX_FDX | PHY_AD_TX_HDX);
+ }
+
+ if (!cpd->config.ctl_rfduplx) {
+ my_ad_caps &= ~(PHY_AD_TX_FDX | PHY_AD_10_FDX);
+ }
+
+ /* Update our Auto-Neg Advertisement Register */
+ lan91cxx_write_phy(cpd, 0, PHY_AD_REG, my_ad_caps);
+
+ db4_printf("smc91111:phy caps=%x\n", my_phy_caps);
+ db4_printf("smc91111:phy advertised caps=%x\n", my_ad_caps);
+
+ /* If the user requested no auto neg, then go set his request */
+ if (!(cpd->config.ctl_autoneg)) {
+ lan91cxx_phy_fixed(cpd);
+
+ goto smc_phy_configure_exit;
+ }
+
+ /* Restart auto-negotiation process in order to advertise my caps */
+ lan91cxx_write_phy(cpd, 0, PHY_CNTL_REG,
+ PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST);
+
+ /* wait for auto-negotiation to finish. */
+ /* give it ~5 seconds before giving up (no cable?) */
+ delay = 50;
+ while (!
+ ((status =
+ lan91cxx_read_phy(cpd, 0, LAN91CXX_PHY_STAT)) & 0x20)) {
+ if (--delay <= 0) {
+ printf("Timeout autonegotiation\n");
+ failed = 1;
+ break;
+ }
+
+ /* Restart auto-negotiation if remote fault */
+ if (status & PHY_STAT_REM_FLT) {
+ db_printf("smc91111:PHY remote fault detected\n");
+
+ /* Restart auto-negotiation */
+ db_printf("smc91111:PHY restarting auto-negotiation\n");
+ lan91cxx_write_phy(cpd, 0, PHY_CNTL_REG,
+ PHY_CNTL_ANEG_EN |
+ PHY_CNTL_ANEG_RST |
+ PHY_CNTL_SPEED | PHY_CNTL_DPLX);
+ }
+ HAL_DELAY_US(100000);
+ }
+
+ /* Fail if we detected an auto-negotiate remote fault */
+ if (status & PHY_STAT_REM_FLT) {
+ db_printf("smc91111:PHY remote fault detected\n");
+ failed = 1;
+ }
+
+ /* The smc_phy_interrupt() routine will be called to update lastPhy18 */
+
+ /* Set our sysctl parameters to match auto-negotiation results */
+ if (cpd->lastPhy18 & PHY_INT_SPDDET) {
+ db_printf("smc91111:PHY 100BaseT\n");
+ cpd->rpc_cur_mode |= LAN91CXX_RPCR_SPEED;
+ } else {
+ db_printf("smc91111:PHY 10BaseT\n");
+ cpd->rpc_cur_mode &= ~LAN91CXX_RPCR_SPEED;
+ }
+
+ if (cpd->lastPhy18 & PHY_INT_DPLXDET) {
+ db_printf("smc91111:PHY Full Duplex\n");
+ cpd->rpc_cur_mode |= LAN91CXX_RPCR_DPLX;
+ } else {
+ db_printf("smc91111:PHY Half Duplex\n");
+ cpd->rpc_cur_mode &= ~LAN91CXX_RPCR_DPLX;
+ }
+
+ /* Re-Configure the Receive/Phy Control register */
+ put_reg(cpd, LAN91CXX_RPCR, cpd->rpc_cur_mode);
+
+ smc_phy_configure_exit:
+
+ /* Exit auto-negotiation */
+ cpd->autoneg_active = 0;
+}
+#endif
+
+static uint16_t
+lan91cxx_read_phy(struct lan91cxx_priv_data *cpd, uint8_t phyaddr,
+ uint8_t phyreg)
+{
+ int i, mask, input_idx, clk_idx = 0;
+ uint16_t mii_reg, value;
+ uint8_t bits[64];
+
+ /* 32 consecutive ones on MDO to establish sync */
+ for (i = 0; i < 32; ++i)
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+
+ /* Start code <01> */
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+
+ /* Read command <10> */
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+
+ /* Output the PHY address, msb first */
+ for (mask = 0x10; mask; mask >>= 1) {
+ if (phyaddr & mask)
+ bits[clk_idx++] =
+ LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+ else
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+ }
+
+ /* Output the phy register number, msb first */
+ for (mask = 0x10; mask; mask >>= 1) {
+ if (phyreg & mask)
+ bits[clk_idx++] =
+ LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+ else
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+ }
+
+ /* Tristate and turnaround (1 bit times) */
+ 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;
+
+ /* Get the current MII register value */
+ mii_reg = get_reg(cpd, LAN91CXX_MGMT);
+
+ /* Turn off all MII Interface bits */
+ mii_reg &= ~(LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MCLK |
+ LAN91CXX_MGMT_MDI | LAN91CXX_MGMT_MDO);
+ HAL_DELAY_US(50);
+
+ /* Clock all 64 cycles */
+ for (i = 0; i < sizeof(bits); ++i) {
+ /* Clock Low - output data */
+ put_reg(cpd, LAN91CXX_MGMT, mii_reg | bits[i]);
+ HAL_DELAY_US(50);
+
+ /* Clock Hi - input data */
+ put_reg(cpd, LAN91CXX_MGMT,
+ mii_reg | bits[i] | LAN91CXX_MGMT_MCLK);
+ HAL_DELAY_US(50);
+
+ bits[i] |= get_reg(cpd, LAN91CXX_MGMT) & LAN91CXX_MGMT_MDI;
+ }
+
+ /* Return to idle state */
+ put_reg(cpd, LAN91CXX_MGMT, mii_reg);
+ HAL_DELAY_US(50);
+
+ /* Recover input data */
+ for (value = 0, i = 0; i < 16; ++i) {
+ value <<= 1;
+ if (bits[input_idx++] & LAN91CXX_MGMT_MDI)
+ value |= 1;
+ }
+
+ db16_printf("phy_read : %d : %04x\n", phyreg, value);
+ return value;
+}
+
+static void
+lan91cxx_write_phy(struct lan91cxx_priv_data *cpd, uint8_t phyaddr,
+ uint8_t phyreg, uint16_t value)
+{
+ int i, mask, clk_idx = 0;
+ uint16_t mii_reg;
+ uint8_t bits[65];
+
+ /* 32 consecutive ones on MDO to establish sync */
+ for (i = 0; i < 32; ++i)
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+
+ /* Start code <01> */
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+
+ /* Write command <01> */
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+
+ /* Output the PHY address, msb first */
+ for (mask = 0x10; mask; mask >>= 1) {
+ if (phyaddr & mask)
+ bits[clk_idx++] =
+ LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+ else
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+ }
+
+ /* Output the phy register number, msb first */
+ for (mask = 0x10; mask; mask >>= 1) {
+ if (phyreg & mask)
+ bits[clk_idx++] =
+ LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+ else
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+ }
+
+ /* Tristate and turnaround (2 bit times) */
+ bits[clk_idx++] = 0;
+ bits[clk_idx++] = 0;
+
+ /* Write out 16 bits of data, msb first */
+ for (mask = 0x8000; mask; mask >>= 1) {
+ if (value & mask)
+ bits[clk_idx++] =
+ LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+ else
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+ }
+
+ /* Final clock bit (tristate) */
+ bits[clk_idx++] = 0;
+
+ /* Get the current MII register value */
+ mii_reg = get_reg(cpd, LAN91CXX_MGMT);
+
+ /* Turn off all MII Interface bits */
+ mii_reg &= ~(LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MCLK |
+ LAN91CXX_MGMT_MDI | LAN91CXX_MGMT_MDO);
+ HAL_DELAY_US(50);
+
+ /* Clock all cycles */
+ for (i = 0; i < sizeof(bits); ++i) {
+ /* Clock Low - output data */
+ put_reg(cpd, LAN91CXX_MGMT, mii_reg | bits[i]);
+ HAL_DELAY_US(50);
+
+ /* Clock Hi - input data */
+ put_reg(cpd, LAN91CXX_MGMT,
+ mii_reg | bits[i] | LAN91CXX_MGMT_MCLK);
+ HAL_DELAY_US(50);
+
+/* bits[i] |= get_reg(cpd, LAN91CXX_MGMT) & LAN91CXX_MGMT_MDI;*/
+ }
+
+ /* Return to idle state */
+ put_reg(cpd, LAN91CXX_MGMT, mii_reg);
+ HAL_DELAY_US(50);
+
+ db16_printf("phy_write: %d : %04x\n", phyreg, value);
+}
+
+#if 0
+void lan91cxx_print_bank(int bank){
+ struct lan91cxx_priv_data *cpd = &smc91111;
+ int regno;
+ unsigned short regval[8];
+ int i;
+
+ if ( bank >= 4 )
+ return;
+ for(i=0; i<8; i++){
+ regno=i+bank<<3;
+ regval[i] = get_reg(cpd, regno);
+ }
+ printk("---- BANK %d ----\n\r",bank);
+ for(i=0; i<8; i++){
+ printk("0x%x: 0x%x\n\r",i,regval[i]);
+ }
+
+}
+#endif
+
+#endif
diff --git a/bsps/shared/net/smc91111config.h b/bsps/shared/net/smc91111config.h
new file mode 100644
index 0000000..8340ca2
--- /dev/null
+++ b/bsps/shared/net/smc91111config.h
@@ -0,0 +1,118 @@
+#ifndef _SMC91111_CONFIG_H_
+#define _SMC91111_CONFIG_H_
+
+/*
+ * RTEMS event used by interrupt handler to signal driver tasks.
+ * This must not be any of the events used by the network task synchronization.
+ */
+#define INTERRUPT_EVENT RTEMS_EVENT_1
+
+/*
+ * RTEMS event used to start transmit daemon.
+ * This must not be the same as INTERRUPT_EVENT.
+ */
+#define START_TRANSMIT_EVENT RTEMS_EVENT_2
+
+ /* event to send when tx buffers become available */
+#define SMC91111_TX_WAIT_EVENT RTEMS_EVENT_3
+
+
+/* Number of OCs supported by this driver*/
+#define NOCDRIVER 1
+
+/* Receive buffer size -- Allow for a full ethernet packet including CRC */
+#define RBUF_SIZE 1536
+
+#define ET_MINLEN 64 /* minimum message length */
+
+#if (MCLBYTES < RBUF_SIZE)
+# error "Driver must have MCLBYTES > RBUF_SIZE"
+#endif
+
+/* ----------------- cygdriver params ----------------- */
+
+#define LAN91CXX_32BIT_RX
+#define LAN91CXX_IS_LAN91C111
+
+/* ----------------- compat layer ----------------- */
+
+#include <stdint.h>
+
+typedef uint32_t CYG_WORD;
+typedef uint8_t CYG_BYTE;
+typedef uint16_t CYG_WORD16;
+typedef uint32_t CYG_WORD32;
+
+#ifndef CYG_SWAP16
+# define CYG_SWAP16(_x_) \
+ ({ uint16_t _x = (_x_); ((_x << 8) | (_x >> 8)); })
+#endif
+
+#ifndef CYG_SWAP32
+# define CYG_SWAP32(_x_) \
+ ({ uint32_t _x = (_x_); \
+ ((_x << 24) | \
+ ((0x0000FF00UL & _x) << 8) | \
+ ((0x00FF0000UL & _x) >> 8) | \
+ (_x >> 24)); })
+#endif
+
+# define CYG_CPU_TO_BE16(_x_) (_x_)
+# define CYG_CPU_TO_BE32(_x_) (_x_)
+# define CYG_BE16_TO_CPU(_x_) (_x_)
+# define CYG_BE32_TO_CPU(_x_) (_x_)
+
+# define CYG_CPU_TO_LE16(_x_) CYG_SWAP16((_x_))
+# define CYG_CPU_TO_LE32(_x_) CYG_SWAP32((_x_))
+# define CYG_LE16_TO_CPU(_x_) CYG_SWAP16((_x_))
+# define CYG_LE32_TO_CPU(_x_) CYG_SWAP32((_x_))
+
+#define CYG_MACRO_START do {
+#define CYG_MACRO_END } while (0)
+#define HAL_IO_BARRIER() \
+ __asm__ volatile ( "" : : : "memory" )
+
+#define HAL_READ_UINT8( _register_, _value_ ) \
+ CYG_MACRO_START \
+ ((_value_) = *((volatile CYG_BYTE *)(_register_))); \
+ HAL_IO_BARRIER (); \
+ CYG_MACRO_END
+
+#define HAL_WRITE_UINT8( _register_, _value_ ) \
+ CYG_MACRO_START \
+ (*((volatile CYG_BYTE *)(_register_)) = (_value_)); \
+ HAL_IO_BARRIER (); \
+ CYG_MACRO_END
+
+#define HAL_READ_UINT16( _register_, _value_ ) \
+ CYG_MACRO_START \
+ ((_value_) = *((volatile CYG_WORD16 *)(_register_))); \
+ HAL_IO_BARRIER (); \
+ CYG_MACRO_END
+
+#define HAL_WRITE_UINT16( _register_, _value_ ) \
+ CYG_MACRO_START \
+ (*((volatile CYG_WORD16 *)(_register_)) = (_value_)); \
+ HAL_IO_BARRIER (); \
+ CYG_MACRO_END
+
+#define HAL_READ_UINT32( _register_, _value_ ) \
+ CYG_MACRO_START \
+ ((_value_) = *((volatile CYG_WORD32 *)(_register_))); \
+ HAL_IO_BARRIER (); \
+ CYG_MACRO_END
+
+#define HAL_READ_UINT16( _register_, _value_ ) \
+ CYG_MACRO_START \
+ ((_value_) = *((volatile CYG_WORD16 *)(_register_))); \
+ HAL_IO_BARRIER (); \
+ CYG_MACRO_END
+
+#define CYG_ASSERT(c,p) do { if (!(c)) { while(1) { printf(p);} }; } while(0)
+
+#define HAL_DELAY_US(p) rtems_task_wake_after (RTEMS_MICROSECONDS_TO_TICKS (p))
+
+
+#endif /* _SMC_91111_CONFIG_H_ */
+
+
diff --git a/bsps/shared/net/sonic.c b/bsps/shared/net/sonic.c
new file mode 100644
index 0000000..77e3651
--- /dev/null
+++ b/bsps/shared/net/sonic.c
@@ -0,0 +1,1685 @@
+/*
+ * RTEMS NETWORK DRIVER FOR NATIONAL DP83932 `SONIC'
+ * SYSTEMS-ORIENTED NETWORK INTERFACE CONTROLLER
+ *
+ * REUSABLE CHIP DRIVER
+ *
+ * References:
+ *
+ * 1) DP83932C-20/25/33 MHz SONIC(TM) Systems-Oriented Network Interface
+ * Controller data sheet. TL/F/10492, RRD-B30M105, National Semiconductor,
+ * 1995.
+ *
+ * 2) Software Driver Programmer's Guide for the DP83932 SONIC(TM),
+ * Application Note 746, Wesley Lee and Mike Lui, TL/F/11140,
+ * RRD-B30M75, National Semiconductor, March, 1991.
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ *
+ * This driver was originally written and tested on a DY-4 DMV177,
+ * which had a 100 Mhz PPC603e.
+ *
+ * This driver also works with DP83934CVUL-20/25 MHz, tested on
+ * Tharsys ERC32 VME board.
+ *
+ * Rehaul to fix lost interrupts and buffers, and to use to use
+ * interrupt-free transmission by Jiri, 22/03/1999.
+ */
+
+#include <machine/rtems-bsd-kernel-space.h>
+
+#include <rtems.h>
+#include <rtems/rtems_bsdnet.h>
+#include <libchip/sonic.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <errno.h>
+#include <rtems/error.h>
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+extern rtems_isr_entry set_vector( rtems_isr_entry, rtems_vector_number, int );
+
+#if (SONIC_DEBUG & SONIC_DEBUG_DUMP_MBUFS)
+#include <rtems/dumpbuf.h>
+#endif
+
+/*
+ * Use the top line if you want more symbols.
+ */
+
+#define SONIC_STATIC static
+
+/*
+ * Number of devices supported by this driver
+ */
+#ifndef NSONIC
+# define NSONIC 1
+#endif
+
+/*
+ *
+ * As suggested by National Application Note 746, make the
+ * receive resource area bigger than the receive descriptor area.
+ *
+ * NOTE: Changing this may break this driver since it currently
+ * assumes a 1<->1 mapping.
+ */
+#define RRA_EXTRA_COUNT 0
+
+/*
+ * RTEMS event used by interrupt handler to signal daemons.
+ */
+#define INTERRUPT_EVENT RTEMS_EVENT_1
+
+/*
+ * RTEMS event used to start transmit daemon.
+ * This must not be the same as INTERRUPT_EVENT.
+ */
+#define START_TRANSMIT_EVENT RTEMS_EVENT_2
+
+/*
+ * Largest Ethernet frame.
+ */
+#define MAXIMUM_FRAME_SIZE 1518
+
+/*
+ * Receive buffer size.
+ * Allow for a pointer, plus a full ethernet frame (including Frame
+ * Check Sequence) rounded up to a 4-byte boundary.
+ */
+#define RBUF_SIZE ((sizeof (void *) + (MAXIMUM_FRAME_SIZE) + 3) & ~3)
+/* #define RBUF_WC ((((MAXIMUM_FRAME_SIZE) + 3) & ~3) / 2) */
+#define RBUF_WC (RBUF_SIZE / 2)
+
+/*
+ * Macros for manipulating 32-bit pointers as 16-bit fragments
+ */
+#define LSW(p) ((uint16_t)((uintptr_t)(p)))
+#define MSW(p) ((uint16_t)((uintptr_t)(p) >> 16))
+#define PTR(m,l) ((void*)(((uint16_t)(m)<<16)|(uint16_t)(l)))
+
+/*
+ * Hardware-specific storage
+ */
+struct sonic_softc {
+ /*
+ * Connection to networking code
+ * This entry *must* be the first in the sonic_softc structure.
+ */
+ struct arpcom arpcom;
+
+ /*
+ * Default location of device registers
+ * ===CACHE===
+ * This area must be non-cacheable, guarded.
+ */
+ void *sonic;
+
+ /*
+ * Register access routines
+ */
+ sonic_write_register_t write_register;
+ sonic_read_register_t read_register;
+
+ /*
+ * Interrupt vector
+ */
+ rtems_vector_number vector;
+
+ /*
+ * Data Configuration Register values
+ */
+ uint32_t dcr_value;
+ uint32_t dc2_value;
+
+ /*
+ * Indicates configuration
+ */
+ int acceptBroadcast;
+
+ /*
+ * Task waiting for interrupts
+ */
+ rtems_id rxDaemonTid;
+ rtems_id txDaemonTid;
+
+ /*
+ * Receive resource area
+ */
+ int rdaCount;
+ ReceiveResourcePointer_t rsa;
+ ReceiveResourcePointer_t rea;
+ CamDescriptorPointer_t cdp;
+ ReceiveDescriptorPointer_t rda;
+ ReceiveDescriptorPointer_t rdp_last;
+
+ /*
+ * Transmit descriptors
+ */
+ int tdaCount;
+ TransmitDescriptorPointer_t tdaHead; /* Last filled */
+ TransmitDescriptorPointer_t tdaTail; /* Next to retire */
+
+ /*
+ * 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;
+};
+SONIC_STATIC struct sonic_softc sonic_softc[NSONIC];
+
+
+/*
+ ******************************************************************
+ * *
+ * Debug Routines *
+ * *
+ ******************************************************************
+ */
+
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_DESCRIPTORS)
+void sonic_print_tx_descriptor(
+ TransmitDescriptorPointer_t tdp
+)
+{
+ printf( "TXD ==> %p", tdp );
+ printf( " pkt_config = 0x%04x", tdp->pkt_config & 0xffff);
+ printf( " pkt_size = 0x%04x\n", tdp->pkt_size & 0xffff );
+ printf( " frag_count = %d", tdp->frag_count & 0xffff );
+ /* could print all the fragments */
+ printf( " next = %p", tdp->next );
+ printf( " linkp = %p\n", tdp->linkp );
+ printf( " mbufp = %p", tdp->mbufp );
+ if ( tdp->mbufp )
+ printf( " mbufp->data = %p", mtod ( tdp->mbufp, void *) );
+ puts("");
+}
+
+void sonic_print_rx_descriptor(
+ ReceiveDescriptorPointer_t rdp
+)
+{
+ printf( "RXD ==> %p\n", rdp );
+ printf( " status = 0x%04x", rdp->status & 0xffff );
+ printf( " byte_count = 0x%04x\n", rdp->byte_count & 0xffff );
+ printf( " pkt = 0x%04x%04x", rdp->pkt_msw, rdp->pkt_lsw );
+ printf( " seq_no = %d", rdp->seq_no );
+ printf( " link = %d\n", rdp->link );
+ printf( " in_use = %d", rdp->in_use );
+ printf( " next = %p", rdp->next );
+ printf( " mbufp = %p", rdp->mbufp );
+ if ( rdp->mbufp )
+ printf( " mbufp->data = %p", mtod ( rdp->mbufp, void *) );
+ puts("");
+}
+#endif
+
+/*
+ ******************************************************************
+ * *
+ * Support Routines *
+ * *
+ ******************************************************************
+ */
+
+static void sonic_enable_interrupts(
+ struct sonic_softc *sc,
+ uint32_t mask
+)
+{
+ void *rp = sc->sonic;
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable( level );
+ (*sc->write_register)(
+ rp,
+ SONIC_REG_IMR,
+ (*sc->read_register)(rp, SONIC_REG_IMR) | mask
+ );
+ rtems_interrupt_enable( level );
+}
+
+static void sonic_disable_interrupts(
+ struct sonic_softc *sc,
+ uint32_t mask
+)
+{
+ void *rp = sc->sonic;
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable( level );
+ (*sc->write_register)(
+ rp,
+ SONIC_REG_IMR,
+ (*sc->read_register)(rp, SONIC_REG_IMR) & ~mask
+ );
+ rtems_interrupt_enable( level );
+}
+
+static void sonic_clear_interrupts(
+ struct sonic_softc *sc,
+ uint32_t mask
+)
+{
+ void *rp = sc->sonic;
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable( level );
+ (*sc->write_register)( rp, SONIC_REG_ISR, mask);
+ rtems_interrupt_enable( level );
+}
+
+static void sonic_command(
+ struct sonic_softc *sc,
+ uint32_t mask
+)
+{
+ void *rp = sc->sonic;
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable( level );
+ (*sc->write_register)( rp, SONIC_REG_CR, mask);
+ rtems_interrupt_enable( level );
+}
+
+/*
+ * Allocate non-cacheable memory on a single 64k page.
+ * Very simple minded -- just keeps trying till the memory is on a single page.
+ */
+SONIC_STATIC void * sonic_allocate(unsigned int nbytes)
+{
+ void *p;
+ unsigned long a1, a2;
+
+ for (;;) {
+ /*
+ * ===CACHE===
+ * Change malloc to malloc_noncacheable_guarded.
+ */
+ p = malloc( nbytes, M_MBUF, M_NOWAIT );
+ if (p == NULL)
+ rtems_panic ("No memory!");
+ memset (p, '\0', nbytes);
+ a1 = (unsigned long)p;
+ a2 = a1 + nbytes - 1;
+ if ((a1 >> 16) == (a2 >> 16))
+ break;
+ }
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_ALLOCATE)
+ printf( "sonic_allocate %d bytes at %p\n", nbytes, p );
+#endif
+ return p;
+}
+
+/*
+ * Shut down the interface.
+ */
+
+SONIC_STATIC void sonic_stop (struct sonic_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /*
+ * Stop the transmitter and receiver.
+ */
+ sonic_command(sc, CR_HTX | CR_RXDIS );
+}
+
+/*
+ * Show interface statistics
+ */
+SONIC_STATIC void sonic_stats (struct sonic_softc *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);
+}
+
+/*
+ ******************************************************************
+ * *
+ * Interrupt Handler *
+ * *
+ ******************************************************************
+ */
+
+SONIC_STATIC rtems_isr sonic_interrupt_handler (rtems_vector_number v)
+{
+ struct sonic_softc *sc = sonic_softc;
+ uint32_t isr, imr;
+ void *rp;
+
+#if (NSONIC > 1)
+ /*
+ * Find the device which requires service
+ */
+ for (;;) {
+ if (sc->vector == v)
+ break;
+ if (++sc == &sonic[NSONIC])
+ return; /* Spurious interrupt? */
+ }
+#endif /* NSONIC > 1 */
+
+ /*
+ * Get pointer to SONIC registers
+ */
+ rp = sc->sonic;
+
+ sc->Interrupts++;
+
+ isr = (*sc->read_register)( rp, SONIC_REG_ISR );
+ imr = (*sc->read_register)( rp, SONIC_REG_IMR );
+
+ /*
+ * Packet received or receive buffer area exceeded?
+ */
+ if (imr & isr & (IMR_PRXEN | IMR_RBAEEN)) {
+ imr &= ~(IMR_PRXEN | IMR_RBAEEN);
+ sc->rxInterrupts++;
+ rtems_bsdnet_event_send (sc->rxDaemonTid, INTERRUPT_EVENT);
+ (*sc->write_register)( rp, SONIC_REG_IMR, imr );
+ (*sc->write_register)( rp, SONIC_REG_ISR, isr & ISR_PKTRX );
+ }
+
+ /*
+ * Packet started, transmitter done or transmitter error?
+ * TX interrupts only occur after an error or when all TDA's are
+ * exhausted and we are waiting for buffer to come free.
+ */
+ if (imr & isr & (IMR_PINTEN | IMR_TXEREN)) {
+ sc->txInterrupts++;
+ rtems_bsdnet_event_send (sc->txDaemonTid, INTERRUPT_EVENT);
+ (*sc->write_register)( rp, SONIC_REG_ISR, ISR_PINT | ISR_TXDN | ISR_TXER );
+ }
+
+}
+
+/*
+ ******************************************************************
+ * *
+ * Transmitter Routines *
+ * *
+ ******************************************************************
+ */
+
+/*
+ * Soak up transmit descriptors that have been sent.
+ */
+
+SONIC_STATIC void sonic_retire_tda (struct sonic_softc *sc)
+{
+ uint16_t status;
+ unsigned int collisions;
+ struct mbuf *m, *n;
+
+ /*
+ * Repeat for all completed transmit descriptors.
+ */
+ while ((status = sc->tdaTail->status) != 0) {
+
+#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
+ printf( "retire TDA %p (0x%04x)\n", sc->tdaTail, status );
+#endif
+
+#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
+ /*
+ * If there is an error that was not a collision,
+ * then someone may want to see it.
+ */
+
+ if ( (status & ~(TDA_STATUS_COLLISION_MASK|TDA_STATUS_DEF)) != 0x0001 )
+ printf( "ERROR: retire TDA %p (0x%08x)\n",
+ sc->tdaTail, sc->tdaTail->status );
+#endif
+
+ /*
+ * Check for errors which stop the transmitter.
+ */
+ if (status & (TDA_STATUS_EXD |
+ TDA_STATUS_EXC |
+ TDA_STATUS_FU |
+ TDA_STATUS_BCM)) {
+ /*
+ * Restart the transmitter if there are
+ * packets waiting to go.
+ */
+ uint16_t link;
+#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
+ printf("restarting sonic after error\n");
+#endif
+
+ link = *(sc->tdaTail->linkp);
+
+ if ((link & TDA_LINK_EOL) == 0) {
+ void *rp = sc->sonic;
+
+ (*sc->write_register)( rp, SONIC_REG_CTDA, link );
+ sonic_command(sc, CR_TXP );
+ }
+ }
+
+ /*
+ * Update network statistics
+ */
+ collisions = (status & TDA_STATUS_COLLISION_MASK) >> TDA_STATUS_COLLISION_SHIFT;
+ if (collisions) {
+ if (collisions == 1)
+ sc->txSingleCollision++;
+ else
+ sc->txMultipleCollision++;
+ sc->txCollision += collisions;
+ }
+ if (status & TDA_STATUS_EXC)
+ sc->txExcessiveCollision++;
+ if (status & TDA_STATUS_OWC)
+ sc->txLateCollision++;
+ if (status & TDA_STATUS_EXD)
+ sc->txExcessiveDeferral++;
+ if (status & TDA_STATUS_DEF)
+ sc->txDeferred++;
+ if (status & TDA_STATUS_FU)
+ sc->txUnderrun++;
+ if (status & TDA_STATUS_CRSL)
+ sc->txLostCarrier++;
+
+ /*
+ * Free the packet and reset a couple of fields
+ */
+ m = sc->tdaTail->mbufp;
+ while ( m ) {
+ MFREE(m, n);
+ m = n;
+ }
+
+ /*
+ sc->tdaTail->frag[0].frag_link = LSW(sc->tdaTail->link_pad);
+ sc->tdaTail->frag_count = 0;
+ */
+ sc->tdaTail->status = 0;
+
+ /*
+ * Move to the next transmit descriptor
+ */
+ sc->tdaTail = sc->tdaTail->next;
+#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
+ printf( "next TDA %p\n", sc->tdaTail );
+#endif
+ }
+}
+
+/*
+ * Send packet
+ */
+SONIC_STATIC void sonic_sendpacket (struct ifnet *ifp, struct mbuf *m)
+{
+ struct sonic_softc *sc = ifp->if_softc;
+ struct mbuf *l = NULL;
+ TransmitDescriptorPointer_t tdp;
+ volatile struct TransmitDescriptorFragLink *fp;
+ unsigned int packetSize;
+ int i;
+ rtems_event_set events;
+ static char padBuf[64];
+
+ /* printf( "sonic_sendpacket %p\n", m ); */
+
+
+ /*
+ * Wait for transmit descriptor to become available. Only retire TDA's
+ * if there are no more free buffers to minimize TX latency. Retire TDA'a
+ * on the way out.
+ */
+
+ while (sc->tdaHead->next->status != 0) {
+
+ /*
+ * Free up transmit descriptors
+ */
+ sonic_retire_tda (sc);
+
+ if (sc->tdaHead->next->status == 0)
+ break;
+
+#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
+ printf("blocking until TDAs are available\n");
+#endif
+ /*
+ * Enable PINT interrupts.
+ sonic_clear_interrupts( sc, ISR_PINT );
+ sonic_enable_interrupts( sc, IMR_PINTEN );
+ */
+
+ /*
+ * Wait for PINT TX interrupt. Every fourth TX buffer will raise PINT.
+ */
+ rtems_bsdnet_event_receive (INTERRUPT_EVENT,
+ RTEMS_WAIT|RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events);
+ sonic_disable_interrupts( sc, IMR_PINTEN );
+ sonic_retire_tda (sc);
+ }
+
+ /*
+ * Fill in the transmit descriptor fragment descriptors.
+ * ===CACHE===
+ * If data cache is operating in write-back mode, flush cached
+ * data to memory.
+ */
+ tdp = sc->tdaHead->next;
+ tdp->mbufp = m;
+ packetSize = 0;
+ fp = tdp->frag;
+ for (i = 0 ; i < MAXIMUM_FRAGS_PER_DESCRIPTOR ; i++, fp++) {
+ /*
+ * Throw away empty mbufs
+ */
+ if (m->m_len) {
+ void *p = mtod (m, void *);
+ fp->frag_lsw = LSW(p);
+ fp->frag_msw = MSW(p);
+ fp->frag_size = m->m_len;
+ packetSize += m->m_len;
+#if (SONIC_DEBUG & SONIC_DEBUG_FRAGMENTS)
+ printf( "fp %p 0x%04x%04x %d=%d .. %d\n",
+ fp, fp->frag_msw, fp->frag_lsw, fp->frag_size, m->m_len, packetSize );
+#endif
+#if (SONIC_DEBUG & SONIC_DEBUG_DUMP_TX_MBUFS)
+ rtems_print_buffer(
+ p,
+ (fp->frag_size > MAXIMUM_FRAME_SIZE) ? MAXIMUM_FRAME_SIZE : fp->frag_size
+ );
+#endif
+ l = m;
+ m = m->m_next;
+ }
+ else {
+ struct mbuf *n;
+ MFREE (m, n);
+ m = n;
+ if (l != NULL)
+ l->m_next = m;
+ }
+ /*
+ * Break out of the loop if this mbuf is the last in the frame.
+ */
+ if (m == NULL)
+ break;
+ }
+
+ /*
+ * Pad short packets.
+ */
+ if ((packetSize < 64) && (i < MAXIMUM_FRAGS_PER_DESCRIPTOR)) {
+ int padSize = 64 - packetSize;
+ fp++;
+ fp->frag_lsw = LSW(padBuf);
+ fp->frag_msw = MSW(padBuf);
+ fp->frag_size = padSize;
+#if (SONIC_DEBUG & SONIC_DEBUG_FRAGMENTS)
+ printf( "PAD fp %p 0x%04x%04x %d\n",
+ fp, fp->frag_msw, fp->frag_lsw, fp->frag_size );
+#endif
+ packetSize += padSize;
+ i++;
+ }
+
+ /*
+ * Fill Transmit Descriptor
+ */
+ tdp->pkt_size = packetSize;
+ tdp->frag_count = i + 1;
+ tdp->status = 0;
+
+ /*
+ * Chain onto list and start transmission.
+ */
+
+ tdp->linkp = &(fp+1)->frag_link;
+ *tdp->linkp = LSW(tdp->next) | TDA_LINK_EOL;
+ if ( sc->tdaHead->frag_count )
+ *sc->tdaHead->linkp &= ~TDA_LINK_EOL;
+ sc->tdaHead = tdp;
+
+ /* Start transmission */
+
+ sonic_command(sc, CR_TXP );
+
+ /*
+ * Free up transmit descriptors on the way out.
+ */
+ sonic_retire_tda (sc);
+}
+
+/*
+ * Driver transmit daemon
+ */
+SONIC_STATIC void sonic_txDaemon (void *arg)
+{
+ struct sonic_softc *sc = (struct sonic_softc *)arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ rtems_event_set events;
+
+ for (;;) {
+ /*
+ * Wait for packet
+ */
+ rtems_bsdnet_event_receive (
+ START_TRANSMIT_EVENT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &events
+ );
+
+ /*
+ * Send packets till queue is empty
+ */
+ for (;;) {
+ /*
+ * Get the next mbuf chain to transmit.
+ */
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (!m)
+ break;
+ sonic_sendpacket (ifp, m);
+ }
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+}
+
+/*
+ ******************************************************************
+ * *
+ * Receiver Routines *
+ * *
+ ******************************************************************
+ */
+
+/*
+ * Wait for SONIC to hand over a Receive Descriptor.
+ */
+
+SONIC_STATIC void sonic_rda_wait(
+ struct sonic_softc *sc,
+ ReceiveDescriptorPointer_t rdp
+)
+{
+ int i;
+ void *rp = sc->sonic;
+ rtems_event_set events;
+
+ /*
+ * Wait for Receive Descriptor.
+ * The order of the tests is very important.
+ * The RDA is checked after RBAE is detected. This ensures that
+ * the driver processes all RDA entries before reusing the RRA
+ * entry holding the giant packet.
+ * The event wait is done after the RDA and RBAE checks. This
+ * catches the possibility that a Receive Descriptor became ready
+ * between the call to this function and the clearing of the
+ * interrupt status register bit.
+ */
+ for (;;) {
+ /*
+ * Has a giant packet arrived?
+ * The National DP83932C data sheet is very vague on what
+ * happens under this condition. The description of the
+ * Interrupt Status Register (Section 4.3.6) states,
+ * ``Reception is aborted and the SONIC fetches the next
+ * available resource descriptors in the RRA. The buffer
+ * space is not re-used and an RDA is not setup for the
+ * truncated packet.''
+ * I take ``Reception is aborted'' to mean that the RXEN
+ * bit in the Command Register is cleared and must be set
+ * by the driver to begin reception again.
+ * Unfortunately, an alternative interpretation could be
+ * that only reception of the current packet is aborted.
+ * This would be more difficult to recover from....
+ */
+ if ((*sc->read_register)( rp, SONIC_REG_ISR ) & ISR_RBAE) {
+
+#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
+ printf( "ERROR: looks like a giant packet -- RBAE\n" );
+#endif
+
+ /*
+ * One more check to soak up any Receive Descriptors
+ * that may already have been handed back to the driver.
+ */
+ if (rdp->in_use == RDA_IN_USE) {
+#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
+ printf( "ERROR: nope just an RBAE\n" );
+#endif
+ break;
+ }
+
+ /*
+ * Check my interpretation of the SONIC manual.
+ */
+ if ((*sc->read_register)( rp, SONIC_REG_CR ) & CR_RXEN)
+ rtems_panic ("SONIC RBAE/RXEN");
+
+ /*
+ * Update statistics
+ */
+ sc->rxGiant++;
+
+ /*
+ * Reuse receive buffer.
+ * Again, the manual is subject to interpretation. The
+ * RRP register is described as, `the lower address of
+ * the next descriptor the SONIC will read.''
+ * Since, acording to the ISR/RBAE notes, the SONIC has
+ * ``fetched the next available resource descriptor in
+ * the RRA'', I interpret this to mean that that the
+ * driver has to move the RRP back *two* entries to
+ * reuse the receive buffer holding the giant packet.
+ */
+ for (i = 0; i < 2; ++i) {
+ uint32_t rrp = (*sc->read_register)( rp, SONIC_REG_RRP );
+ const uint32_t rsa = (*sc->read_register)( rp, SONIC_REG_RSA );
+
+ if (rrp == rsa) {
+ const uint32_t rea = (*sc->read_register)( rp, SONIC_REG_REA );
+ (*sc->write_register)( rp, SONIC_REG_RRP, rea );
+ }
+
+ rrp = (*sc->read_register)( rp, SONIC_REG_RRP );
+ (*sc->write_register)( rp, SONIC_REG_RRP, rrp - sizeof(ReceiveResource_t) );
+ }
+
+ /*
+ * Restart reception
+ */
+ sonic_clear_interrupts( sc, ISR_RBAE );
+ sonic_command( sc, CR_RXEN );
+ }
+
+ /*
+ * Has Receive Descriptor become available?
+ */
+ if (rdp->in_use == RDA_IN_USE)
+ break;
+
+ /*
+ * Enable interrupts.
+ */
+ sonic_enable_interrupts( sc, (IMR_PRXEN | IMR_RBAEEN) );
+
+ /*
+ * Wait for interrupt.
+ */
+ rtems_bsdnet_event_receive(
+ INTERRUPT_EVENT,
+ RTEMS_WAIT|RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events
+ );
+ }
+#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
+ printf( "RDA %p\n", rdp );
+#endif
+
+#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
+ if (rdp->status & 0x000E)
+ printf( "ERROR: RDA %p (0x%04x)\n", rdp, rdp->status );
+#endif
+
+}
+
+#ifdef CPU_U32_FIX
+
+/*
+ * Routine to align the received packet so that the ip header
+ * is on a 32-bit boundary. Necessary for cpu's that do not
+ * allow unaligned loads and stores and when the 32-bit DMA
+ * mode is used.
+ *
+ * Transfers are done on word basis to avoid possibly slow byte
+ * and half-word writes.
+ */
+
+void ipalign(struct mbuf *m)
+{
+ unsigned int *first, *last, data;
+ unsigned int tmp = 0;
+
+ if ((((int) m->m_data) & 2) && (m->m_len)) {
+ last = (unsigned int *) ((((int) m->m_data) + m->m_len + 8) & ~3);
+ first = (unsigned int *) (((int) m->m_data) & ~3);
+ tmp = *first << 16;
+ first++;
+ do {
+ data = *first;
+ *first = tmp | (data >> 16);
+ tmp = data << 16;
+ first++;
+ } while (first <= last);
+
+ m->m_data = (caddr_t)(((int) m->m_data) + 2);
+ }
+}
+#endif
+
+/*
+ * SONIC reader task
+ */
+SONIC_STATIC void sonic_rxDaemon (void *arg)
+{
+ struct sonic_softc *sc = (struct sonic_softc *)arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ void *rp = sc->sonic;
+ struct mbuf *m;
+ uint16_t status;
+ ReceiveDescriptorPointer_t rdp;
+ ReceiveResourcePointer_t rwp, rea;
+ uint16_t newMissedTally, oldMissedTally;
+
+ rwp = sc->rsa;
+ rea = sc->rea;
+ rdp = sc->rda;
+
+ /*
+ * Start the receiver
+ */
+ oldMissedTally = (*sc->read_register)( rp, SONIC_REG_MPT );
+
+ /*
+ * Input packet handling loop
+ */
+ for (;;) {
+ /*
+ * Wait till SONIC supplies a Receive Descriptor.
+ */
+ if (rdp->in_use == RDA_FREE) {
+ sonic_rda_wait (sc, rdp);
+ }
+
+#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
+ printf( "Incoming packet %p status=0x%04x\n", rdp, rdp->status );
+#endif
+
+ /*
+ * Check that packet is valid
+ */
+ status = rdp->status;
+ if (status & RDA_STATUS_PRX) {
+ struct ether_header *eh;
+ void *p;
+
+ /*
+ * Pass the packet up the chain.
+ * The mbuf count is reduced to remove
+ * the frame check sequence at the end
+ * of the packet.
+ * ===CACHE===
+ * Invalidate cache entries for this memory.
+ */
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_DESCRIPTORS)
+ sonic_print_rx_descriptor( rdp );
+ if ((LSW(rdp->mbufp->m_data) != rdp->pkt_lsw)
+ || (MSW(rdp->mbufp->m_data) != rdp->pkt_msw))
+ printf ("SONIC RDA/RRA %p, %08x\n",rdp->mbufp->m_data,(rdp->pkt_msw << 16) |
+ (rdp->pkt_lsw & 0x0ffff));
+#endif
+ rdp->byte_count &= 0x0ffff; /* ERC32 pollutes msb of byte_count */
+ m = rdp->mbufp;
+ m->m_len = m->m_pkthdr.len = rdp->byte_count -
+ sizeof(uint32_t) -
+ sizeof(struct ether_header);
+ eh = mtod (m, struct ether_header *);
+ m->m_data += sizeof(struct ether_header);
+
+#ifdef CPU_U32_FIX
+ ipalign(m); /* Align packet on 32-bit boundary */
+#endif
+
+#if (SONIC_DEBUG & SONIC_DEBUG_DUMP_RX_MBUFS)
+ rtems_print_buffer( (void *) eh, sizeof(struct ether_header) );
+ rtems_print_buffer( (void *) m, 96 /* m->m_len*/ );
+#endif
+
+ /* printf( "ether_input %p\n", m ); */
+ /*
+ printf( "pkt %p, seq %04x, mbuf %p, m_data %p\n", rdp, rdp->seq_no, m, m->m_data );
+ printf( "%u, %u\n", ((int*)m->m_data)[6], ((int*)m->m_data)[7]);
+ */
+ ether_input (ifp, eh, m);
+ /*
+ */
+
+ /*
+ * Sanity check that Receive Resource Area is
+ * still in sync with Receive Descriptor Area
+ * The buffer reported in the Receive Descriptor
+ * should be the same as the buffer in the Receive
+ * Resource we are about to reuse.
+ */
+/* XXX figure out whether this is valid or not */
+#if 0
+ if ((LSW(p) != rwp->buff_ptr_lsw)
+ || (MSW(p) != rwp->buff_ptr_msw))
+ rtems_panic ("SONIC RDA/RRA");
+#endif
+
+ /*
+ * Allocate a new mbuf.
+ */
+
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = ifp;
+ rdp->mbufp = m;
+ p = mtod (m, void *);
+
+ /*
+ * Reuse Receive Resource.
+ */
+
+ rwp->buff_ptr_lsw = LSW(p);
+ rwp->buff_ptr_msw = MSW(p);
+ rwp->buff_wc_lsw = RBUF_WC;
+ rwp->buff_wc_msw = 0;
+ rwp++;
+
+ if (rwp == rea) {
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
+ printf( "Wrapping RWP from %p to %p\n", rwp, sc->rsa );
+#endif
+ rwp = sc->rsa;
+ }
+ (*sc->write_register)( rp, SONIC_REG_RWP , LSW(rwp) );
+
+ /*
+ * Tell the SONIC to reread the RRA.
+ */
+ if ((*sc->read_register)( rp, SONIC_REG_ISR ) & ISR_RBE)
+ sonic_clear_interrupts( sc, ISR_RBE );
+ }
+ else {
+ if (status & RDA_STATUS_COL)
+ sc->rxCollision++;
+ if (status & RDA_STATUS_FAER)
+ sc->rxNonOctet++;
+ else if (status & RDA_STATUS_CRCR)
+ sc->rxBadCRC++;
+ }
+
+ /*
+ * Count missed packets
+ */
+ newMissedTally = (*sc->read_register)( rp, SONIC_REG_MPT );
+ if (newMissedTally != oldMissedTally) {
+ sc->rxMissed += (newMissedTally - oldMissedTally) & 0xFFFF;
+ newMissedTally = oldMissedTally;
+ }
+
+ /*
+ * Move to next receive descriptor and update EOL
+ */
+
+ rdp->link |= RDA_LINK_EOL;
+ rdp->in_use = RDA_FREE;
+ sc->rdp_last->link &= ~RDA_LINK_EOL;
+ sc->rdp_last = rdp;
+ rdp = rdp->next;
+
+ }
+}
+
+/*
+ ******************************************************************
+ * *
+ * Initialization Routines *
+ * *
+ ******************************************************************
+ */
+
+/*
+ * Initialize the SONIC hardware
+ */
+SONIC_STATIC void sonic_initialize_hardware(struct sonic_softc *sc)
+{
+ void *rp = sc->sonic;
+ int i;
+ unsigned char *hwaddr;
+ TransmitDescriptorPointer_t tdp;
+ ReceiveDescriptorPointer_t ordp, rdp;
+ ReceiveResourcePointer_t rwp;
+ struct mbuf *m;
+ void *p;
+ CamDescriptorPointer_t cdp;
+
+ /*
+ * The Revision B SONIC has a horrible bug known as the "Zero
+ * Length Packet bug". The initial board used to develop this
+ * driver had a newer revision of the SONIC so there was no reason
+ * to check for this. If you have the Revision B SONIC chip, then
+ * you need to add some code to the RX path to handle this weirdness.
+ */
+
+ if ( (*sc->read_register)( rp, SONIC_REG_SR ) <= SONIC_REVISION_B ) {
+ rtems_fatal_error_occurred( 0x0BADF00D ); /* don't eat this part :) */
+ }
+
+ /*
+ * Set up circular linked list in Transmit Descriptor Area.
+ * Use the PINT bit in the transmit configuration field to
+ * request an interrupt on every other transmitted packet.
+ *
+ * NOTE: sonic_allocate() zeroes all of the memory allocated.
+ */
+
+ sc->tdaTail = sonic_allocate(sc->tdaCount * sizeof *tdp);
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
+ printf( "tdaTail = %p\n", sc->tdaTail );
+#endif
+ tdp = sc->tdaTail;
+ for (i = 0 ; i < sc->tdaCount ; i++) {
+ /*
+ * Start off with the table of outstanding mbuf's
+ */
+
+ /*
+ * status, pkt_config, pkt_size, and all fragment fields
+ * are set to zero by sonic_allocate.
+ */
+
+/* XXX not used by the BSD drivers
+ tdp->frag[0].frag_link = LSW(tdp + 1);
+*/
+ if (i & 3)
+ tdp->pkt_config = TDA_CONFIG_PINT;
+
+ tdp->status = 0;
+ tdp->frag_count = 0;
+ tdp->link_pad = LSW(tdp + 1) | TDA_LINK_EOL;
+ tdp->linkp = &((tdp + 1)->frag[0].frag_link);
+ tdp->next = (TransmitDescriptor_t *)(tdp + 1);
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_DESCRIPTORS)
+ sonic_print_tx_descriptor( tdp );
+#endif
+ tdp++;
+ }
+ tdp--;
+ sc->tdaHead = tdp;
+ tdp->link_pad = LSW(sc->tdaTail) | TDA_LINK_EOL;
+ tdp->next = (TransmitDescriptor_t *)sc->tdaTail;
+ tdp->linkp = &sc->tdaTail->frag[0].frag_link;
+
+ /*
+ * Set up circular linked list in Receive Descriptor Area.
+ * Leaves sc->rda pointing at the `beginning' of the list.
+ *
+ * NOTE: The RDA and CDP must have the same MSW for their addresses.
+ */
+
+ sc->rda = sonic_allocate(
+ (sc->rdaCount * sizeof(ReceiveDescriptor_t)) +
+ sizeof(CamDescriptor_t) );
+ sc->cdp = (CamDescriptorPointer_t) ((unsigned char *)sc->rda +
+ (sc->rdaCount * sizeof(ReceiveDescriptor_t)));
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
+ printf( "rda area = %p\n", sc->rda );
+ printf( "cdp area = %p\n", sc->cdp );
+#endif
+
+ ordp = rdp = sc->rda;
+ for (i = 0 ; i < sc->rdaCount ; i++) {
+ /*
+ * status, byte_count, pkt_ptr0, pkt_ptr1, and seq_no are set
+ * to zero by sonic_allocate.
+ */
+ rdp->link = LSW(rdp + 1);
+ rdp->in_use = RDA_FREE;
+ rdp->next = (ReceiveDescriptor_t *)(rdp + 1);
+ ordp = rdp;
+ rdp++;
+ }
+ /*
+ * Link the last desriptor to the 1st one and mark it as the end
+ * of the list.
+ */
+ ordp->next = sc->rda;
+ ordp->link = LSW(sc->rda) | RDA_LINK_EOL;
+ sc->rdp_last = ordp;
+
+ /*
+ * Allocate the receive resource area.
+ * In accordance with National Application Note 746, make the
+ * receive resource area bigger than the receive descriptor area.
+ * This has the useful side effect of making the receive resource
+ * area big enough to hold the CAM descriptor area.
+ */
+
+ sc->rsa = sonic_allocate((sc->rdaCount + RRA_EXTRA_COUNT) * sizeof *sc->rsa);
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
+ printf( "rsa area = %p\n", sc->rsa );
+#endif
+
+ /*
+ * Set up list in Receive Resource Area.
+ * Allocate space for incoming packets.
+ */
+
+ rwp = sc->rsa;
+ for (i = 0 ; i < (sc->rdaCount + RRA_EXTRA_COUNT) ; i++, rwp++) {
+
+ /*
+ * Allocate memory for buffer.
+ * Place a pointer to the mbuf at the beginning of the buffer
+ * so we can find the mbuf when the SONIC returns the buffer
+ * to the driver.
+ */
+
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ sc->rda[i].mbufp = m;
+
+ p = mtod (m, void *);
+
+ /*
+ * Set up RRA entry
+ */
+ rwp->buff_ptr_lsw = LSW(p);
+ rwp->buff_ptr_msw = MSW(p);
+ rwp->buff_wc_lsw = RBUF_WC;
+ rwp->buff_wc_msw = 0;
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_DESCRIPTORS)
+ sonic_print_rx_descriptor( &sc->rda[i] );
+#endif
+ }
+ sc->rea = rwp;
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
+ printf( "rea area = %p\n", sc->rea );
+#endif
+
+
+ /*
+ * Issue a software reset.
+ */
+ (*sc->write_register)( rp, SONIC_REG_CR, CR_RST | CR_STP | CR_RXDIS | CR_HTX );
+
+ /*
+ * Set up data configuration registers.
+ */
+ (*sc->write_register)( rp, SONIC_REG_DCR, sc->dcr_value );
+ (*sc->write_register)( rp, SONIC_REG_DCR2, sc->dc2_value );
+
+ (*sc->write_register)( rp, SONIC_REG_CR, CR_STP | CR_RXDIS | CR_HTX );
+
+ /*
+ * Mask all interrupts
+ */
+ (*sc->write_register)( rp, SONIC_REG_IMR, 0x0 ); /* XXX was backwards */
+
+ /*
+ * Clear outstanding interrupts.
+ */
+ (*sc->write_register)( rp, SONIC_REG_ISR, 0x7FFF );
+
+ /*
+ * Clear the tally counters
+ */
+
+ (*sc->write_register)( rp, SONIC_REG_CRCT, 0xFFFF );
+ (*sc->write_register)( rp, SONIC_REG_FAET, 0xFFFF );
+ (*sc->write_register)( rp, SONIC_REG_MPT, 0xFFFF );
+ (*sc->write_register)( rp, SONIC_REG_RSC, 0 );
+
+ /*
+ * Set the Receiver mode
+ *
+ * Enable/disable reception of broadcast packets
+ */
+
+ if (sc->acceptBroadcast)
+ (*sc->write_register)( rp, SONIC_REG_RCR, RCR_BRD );
+ else
+ (*sc->write_register)( rp, SONIC_REG_RCR, 0 );
+
+ /*
+ * Set up Resource Area pointers
+ */
+
+ (*sc->write_register)( rp, SONIC_REG_URRA, MSW(sc->rsa) );
+ (*sc->write_register)( rp, SONIC_REG_RSA, LSW(sc->rsa) );
+
+ (*sc->write_register)( rp, SONIC_REG_REA, LSW(sc->rea) );
+
+ (*sc->write_register)( rp, SONIC_REG_RRP, LSW(sc->rsa) );
+ (*sc->write_register)( rp, SONIC_REG_RWP, LSW(sc->rsa) ); /* XXX was rea */
+
+ (*sc->write_register)( rp, SONIC_REG_URDA, MSW(sc->rda) );
+ (*sc->write_register)( rp, SONIC_REG_CRDA, LSW(sc->rda) );
+
+ (*sc->write_register)( rp, SONIC_REG_UTDA, MSW(sc->tdaTail) );
+ (*sc->write_register)( rp, SONIC_REG_CTDA, LSW(sc->tdaTail) );
+
+ /*
+ * Set End Of Buffer Count register to the value recommended
+ * in Note 1 of Section 3.4.4.4 of the SONIC data sheet.
+ */
+
+ (*sc->write_register)( rp, SONIC_REG_EOBC, RBUF_WC - 2 );
+
+ /*
+ * Issue the load RRA command
+ */
+
+ (*sc->write_register)( rp, SONIC_REG_CR, CR_RRRA );
+ while ((*sc->read_register)( rp, SONIC_REG_CR ) & CR_RRRA)
+ continue;
+
+ /*
+ * Remove device reset
+ */
+
+ (*sc->write_register)( rp, SONIC_REG_CR, 0 );
+
+ /*
+ * Set up the SONIC CAM with our hardware address.
+ */
+
+ hwaddr = sc->arpcom.ac_enaddr;
+ cdp = sc->cdp;
+
+#if (SONIC_DEBUG & SONIC_DEBUG_CAM)
+ printf( "hwaddr: %2x:%2x:%2x:%2x:%2x:%2x\n",
+ hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5] );
+#endif
+
+ cdp->cep = 0; /* Fill first and only entry in CAM */
+ cdp->cap0 = hwaddr[1] << 8 | hwaddr[0];
+ cdp->cap1 = hwaddr[3] << 8 | hwaddr[2];
+ cdp->cap2 = hwaddr[5] << 8 | hwaddr[4];
+ cdp->ce = 0x0001; /* Enable first entry in CAM */
+
+ (*sc->write_register)( rp, SONIC_REG_CDC, 1 ); /* 1 entry in CDA */
+ (*sc->write_register)( rp, SONIC_REG_CDP, LSW(cdp) );
+ (*sc->write_register)( rp, SONIC_REG_CR, CR_LCAM ); /* Load the CAM */
+
+ while ((*sc->read_register)( rp, SONIC_REG_CR ) & CR_LCAM)
+ continue;
+
+ /*
+ * Verify that CAM was properly loaded.
+ */
+
+ (*sc->write_register)( rp, SONIC_REG_CR, CR_RST | CR_STP | CR_RXDIS | CR_HTX );
+
+#if (SONIC_DEBUG & SONIC_DEBUG_CAM)
+ (*sc->write_register)( rp, SONIC_REG_CEP, 0 ); /* Select first entry in CAM */
+ printf ("Loaded Ethernet address into SONIC CAM.\n"
+ " Wrote %04x%04x%04x - %#x\n"
+ " Read %04x%04x%04x - %#x\n",
+ cdp->cap2, cdp->cap1, cdp->cap0, cdp->ce,
+ (*sc->read_register)( rp, SONIC_REG_CAP2 ),
+ (*sc->read_register)( rp, SONIC_REG_CAP1 ),
+ (*sc->read_register)( rp, SONIC_REG_CAP0 ),
+ (*sc->read_register)( rp, SONIC_REG_CE ));
+
+ (*sc->write_register)( rp, SONIC_REG_CEP, 0 ); /* Select first entry in CAM */
+ if (((*sc->read_register)( rp, SONIC_REG_CAP2 ) != cdp->cap2)
+ || ((*sc->read_register)( rp, SONIC_REG_CAP1 ) != cdp->cap1)
+ || ((*sc->read_register)( rp, SONIC_REG_CAP0 ) != cdp->cap0)
+ || ((*sc->read_register)( rp, SONIC_REG_CE ) != cdp->ce)) {
+ printf ("Failed to load Ethernet address into SONIC CAM.\n"
+ " Wrote %04x%04x%04x - %#x\n"
+ " Read %04x%04x%04x - %#x\n",
+ cdp->cap2, cdp->cap1, cdp->cap0, cdp->ce,
+ (*sc->read_register)( rp, SONIC_REG_CAP2 ),
+ (*sc->read_register)( rp, SONIC_REG_CAP1 ),
+ (*sc->read_register)( rp, SONIC_REG_CAP0 ),
+ (*sc->read_register)( rp, SONIC_REG_CE ));
+ rtems_panic ("SONIC LCAM");
+ }
+#endif
+
+ (*sc->write_register)(rp, SONIC_REG_CR, /* CR_TXP | */CR_RXEN | CR_STP);
+
+ /*
+ * Attach SONIC interrupt handler
+ */
+/* XXX
+ (*sc->write_register)( rp, SONIC_REG_IMR, 0 );
+*/
+
+ /* Ignore returned old handler */
+ (void) set_vector(sonic_interrupt_handler, sc->vector, 1);
+
+ /*
+ * Remainder of hardware initialization is
+ * done by the receive and transmit daemons.
+ */
+}
+
+/*
+ * Send packet (caller provides header).
+ */
+
+SONIC_STATIC void sonic_start(struct ifnet *ifp)
+{
+ struct sonic_softc *sc = ifp->if_softc;
+
+ rtems_bsdnet_event_send(sc->txDaemonTid, START_TRANSMIT_EVENT);
+ ifp->if_flags |= IFF_OACTIVE;
+}
+
+/*
+ * Initialize and start the device
+ */
+
+SONIC_STATIC void sonic_init (void *arg)
+{
+ struct sonic_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ void *rp = sc->sonic;
+ int rcr;
+
+ if (sc->txDaemonTid == 0) {
+
+ /*
+ * Set up SONIC hardware
+ */
+ sonic_initialize_hardware (sc);
+
+ /*
+ * Start driver tasks
+ */
+ sc->rxDaemonTid = rtems_bsdnet_newproc ("SNrx", 4096, sonic_rxDaemon, sc);
+ sc->txDaemonTid = rtems_bsdnet_newproc ("SNtx", 4096, sonic_txDaemon, sc);
+ }
+
+ /*
+ * Set flags appropriately
+ */
+ rcr = (*sc->read_register)( rp, SONIC_REG_RCR );
+ if (ifp->if_flags & IFF_PROMISC)
+ rcr |= RCR_PRO;
+ else
+ rcr &= ~RCR_PRO;
+ (*sc->write_register)( rp, SONIC_REG_RCR, rcr);
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+
+ /*
+ * Enable receiver and transmitter
+ */
+ sonic_enable_interrupts( sc, IMR_TXEREN | (IMR_PRXEN | IMR_RBAEEN) );
+ sonic_command( sc, CR_RXEN );
+}
+
+/*
+ * Driver ioctl handler
+ */
+static int
+sonic_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct sonic_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command) {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl (ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
+ case IFF_RUNNING:
+ sonic_stop (sc);
+ break;
+
+ case IFF_UP:
+ sonic_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ sonic_stop (sc);
+ sonic_init (sc);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ sonic_stats (sc);
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+/*
+ * Attach an SONIC driver to the system
+ * This is the only `extern' function in the driver.
+ */
+
+int
+rtems_sonic_driver_attach (
+ struct rtems_bsdnet_ifconfig *config,
+ sonic_configuration_t *chip
+)
+{
+ struct sonic_softc *sc;
+ struct ifnet *ifp;
+ int mtu;
+ int unitNumber;
+ char *unitName;
+
+ /*
+ * Parse driver name
+ */
+ if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0)
+ return 0;
+
+ /*
+ * Is driver free?
+ */
+ if ((unitNumber <= 0) || (unitNumber > NSONIC)) {
+ printf ("Bad SONIC unit number.\n");
+ return 0;
+ }
+ sc = &sonic_softc[unitNumber - 1];
+ ifp = &sc->arpcom.ac_if;
+ if (ifp->if_softc != NULL) {
+ printf ("Driver already in use.\n");
+ return 0;
+ }
+
+ /*
+ * zero out the control structure
+ */
+
+ memset( sc, 0, sizeof(*sc) );
+
+
+ /*
+ * Process options
+ */
+ if (config->hardware_address) {
+ memcpy (sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
+ }
+ else {
+ memset (sc->arpcom.ac_enaddr, 0x08, ETHER_ADDR_LEN);
+ }
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+ if (config->rbuf_count)
+ sc->rdaCount = config->rbuf_count;
+ else
+ sc->rdaCount = chip->rda_count;
+ if (config->xbuf_count)
+ sc->tdaCount = config->xbuf_count;
+ else
+ sc->tdaCount = chip->tda_count;
+ sc->acceptBroadcast = !config->ignore_broadcast;
+
+ sc->sonic = chip->base_address;
+ sc->vector = chip->vector;
+ sc->dcr_value = chip->dcr_value;
+ sc->dc2_value = chip->dc2_value;
+ sc->write_register = chip->write_register;
+ sc->read_register = chip->read_register;
+
+ /*
+ * Set up network interface values
+ */
+ ifp->if_softc = sc;
+ ifp->if_unit = unitNumber;
+ ifp->if_name = unitName;
+ ifp->if_mtu = mtu;
+ ifp->if_init = sonic_init;
+ ifp->if_ioctl = sonic_ioctl;
+ ifp->if_start = sonic_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ /*
+ * Attach the interface
+ */
+ if_attach (ifp);
+ ether_ifattach (ifp);
+ return 1;
+}
+
+#if (SONIC_DEBUG & SONIC_DEBUG_PRINT_REGISTERS)
+#include <stdio.h>
+
+char SONIC_Reg_name[64][6]= {
+ "CR", /* 0x00 */
+ "DCR", /* 0x01 */
+ "RCR", /* 0x02 */
+ "TCR", /* 0x03 */
+ "IMR", /* 0x04 */
+ "ISR", /* 0x05 */
+ "UTDA", /* 0x06 */
+ "CTDA", /* 0x07 */
+ "0x08", /* 0x08 */
+ "0x09", /* 0x09 */
+ "0x0A", /* 0x0A */
+ "0x0B", /* 0x0B */
+ "0x0C", /* 0x0C */
+ "URDA", /* 0x0D */
+ "CRDA", /* 0x0E */
+ "0x0F", /* 0x0F */
+ "0x10", /* 0x10 */
+ "0x11", /* 0x11 */
+ "0x12", /* 0x12 */
+ "EOBC", /* 0x13 */
+ "URRA", /* 0x14 */
+ "RSA", /* 0x15 */
+ "REA", /* 0x16 */
+ "RRP", /* 0x17 */
+ "RWP", /* 0x18 */
+ "0x19", /* 0x19 */
+ "0x1A", /* 0x1A */
+ "0x1B", /* 0x1B */
+ "0x1C", /* 0x1C */
+ "0x0D", /* 0x1D */
+ "0x1E", /* 0x1E */
+ "0x1F", /* 0x1F */
+ "0x20", /* 0x20 */
+ "CEP", /* 0x21 */
+ "CAP2", /* 0x22 */
+ "CAP1", /* 0x23 */
+ "CAP0", /* 0x24 */
+ "CE", /* 0x25 */
+ "CDP", /* 0x26 */
+ "CDC", /* 0x27 */
+ "SR", /* 0x28 */
+ "WT0", /* 0x29 */
+ "WT1", /* 0x2A */
+ "RSC", /* 0x2B */
+ "CRCT", /* 0x2C */
+ "FAET", /* 0x2D */
+ "MPT", /* 0x2E */
+ "MDT", /* 0x2F */
+ "0x30", /* 0x30 */
+ "0x31", /* 0x31 */
+ "0x32", /* 0x32 */
+ "0x33", /* 0x33 */
+ "0x34", /* 0x34 */
+ "0x35", /* 0x35 */
+ "0x36", /* 0x36 */
+ "0x37", /* 0x37 */
+ "0x38", /* 0x38 */
+ "0x39", /* 0x39 */
+ "0x3A", /* 0x3A */
+ "0x3B", /* 0x3B */
+ "0x3C", /* 0x3C */
+ "0x3D", /* 0x3D */
+ "0x3E", /* 0x3E */
+ "DCR2" /* 0x3F */
+};
+#endif
diff --git a/lnetworking.py b/lnetworking.py
index 6965a7e..f81f4fc 100644
--- a/lnetworking.py
+++ b/lnetworking.py
@@ -37,6 +37,7 @@ exclude_headers = ['rtems-bsd-user-space.h', 'rtems-bsd-kernel-space.h']
for root, dirs, files in os.walk("."):
[dirs.remove(d) for d in list(dirs) if d in exclude_dirs]
+ dirs.append('./bsps/shared/net')
include_files[root[2:]] = []
for name in files:
if name[-2:] == '.c':
@@ -52,9 +53,9 @@ for root, dirs, files in os.walk('./testsuites'):
def build(bld):
include_path = []
ip = ''
- BSP = bld.env.RTEMS_ARCH_BSP.split('-')[-1]
+ bsp = bld.env.RTEMS_ARCH_BSP.split('-')[-1]
- bsp_dirs, bsp_sources, bsp_archs = bsp_drivers.bsp_files(bld)
+ bsp_dirs, bsp_sources = bsp_drivers.bsp_files(bld)
include_path.extend(['.',
os.path.relpath(bld.env.PREFIX),
@@ -68,18 +69,20 @@ def build(bld):
include_path.append(os.path.relpath(os.path.join(bld.env.PREFIX,
arch_lib_path,
'include')))
- if BSP in bsp_dirs:
- include_path.extend(bsp_dirs[BSP])
+ include_path.append('./bsps/include/libchip')
+
+ if bsp in bsp_dirs:
+ include_path.extend(bsp_dirs[bsp])
for i in include_path:
ip = ip + i + ' '
- if (BSP in bsp_sources):
+ if (bsp in bsp_sources):
bld(target = 'bsp_objs',
features = 'c',
cflags = ['-O2', '-g'],
includes = ip,
- source = bsp_sources[BSP])
+ source = bsp_sources[bsp])
bld(target = 'network_objects',
features = 'c',
@@ -97,6 +100,10 @@ def build(bld):
use = 'networking',
source = test_source)
- bld.install_files(os.path.join('${PREFIX}', arch_lib_path), ["libnetworking.a"])
+ bld.install_files(os.path.join('${PREFIX}', arch_lib_path),
+ ["libnetworking.a"])
+ bld.install_files(os.path.join('${PREFIX}', arch_lib_path),
+ [os.path.join('./bsps/include/libchip/', f)
+ for f in os.listdir('./bsps/include/libchip/')])
for i in include_files:
bld.install_files(os.path.join('${PREFIX}', arch_lib_path, i), include_files[i])