From d1517076487ea677799764efcb4313760b68c05e Mon Sep 17 00:00:00 2001 From: Vijay Kumar Banerjee Date: Thu, 18 Feb 2021 19:31:10 -0700 Subject: bsps: Add support to build from shared bsp folder for specific BSPs --- bsp_drivers.py | 22 +- bsps/arm/shared/lpc-ethernet.c | 1839 ++++++++++++ bsps/include/libchip/i82586var.h | 319 ++ bsps/include/libchip/if_dcreg.h | 1120 +++++++ bsps/include/libchip/if_fxpvar.h | 203 ++ bsps/include/libchip/smc91111.h | 558 ++++ bsps/lm32/include/system_conf.h | 329 +++ bsps/shared/grlib/net/README | 7 + bsps/shared/grlib/net/greth.c | 1655 +++++++++++ bsps/shared/grlib/net/network_interface_add.c | 62 + bsps/shared/net/README | 12 + bsps/shared/net/README.3com | 3 + bsps/shared/net/README.cs8900 | 26 + bsps/shared/net/README.dec21140 | 116 + bsps/shared/net/README.i82586 | 1 + bsps/shared/net/README.open_eth | 72 + bsps/shared/net/README.sonic | 135 + bsps/shared/net/README.tulipclone | 101 + bsps/shared/net/cs8900.c | 1216 ++++++++ bsps/shared/net/cs8900.c.bsp | 510 ++++ bsps/shared/net/cs8900.h.bsp | 38 + bsps/shared/net/dec21140.c | 1112 +++++++ bsps/shared/net/elnk.c | 3553 +++++++++++++++++++++++ bsps/shared/net/greth2.c | 1200 ++++++++ bsps/shared/net/i82586.c | 2198 ++++++++++++++ bsps/shared/net/i82586reg.h | 448 +++ bsps/shared/net/if_dc.c | 3844 +++++++++++++++++++++++++ bsps/shared/net/if_fxp.c | 2339 +++++++++++++++ bsps/shared/net/if_fxpreg.h | 370 +++ bsps/shared/net/open_eth.c | 767 +++++ bsps/shared/net/smc91111.c | 1653 +++++++++++ bsps/shared/net/smc91111config.h | 118 + bsps/shared/net/sonic.c | 1685 +++++++++++ lnetworking.py | 21 +- 34 files changed, 27641 insertions(+), 11 deletions(-) create mode 100644 bsps/arm/shared/lpc-ethernet.c create mode 100644 bsps/include/libchip/i82586var.h create mode 100644 bsps/include/libchip/if_dcreg.h create mode 100644 bsps/include/libchip/if_fxpvar.h create mode 100644 bsps/include/libchip/smc91111.h create mode 100644 bsps/lm32/include/system_conf.h create mode 100644 bsps/shared/grlib/net/README create mode 100644 bsps/shared/grlib/net/greth.c create mode 100644 bsps/shared/grlib/net/network_interface_add.c create mode 100644 bsps/shared/net/README create mode 100644 bsps/shared/net/README.3com create mode 100644 bsps/shared/net/README.cs8900 create mode 100644 bsps/shared/net/README.dec21140 create mode 100644 bsps/shared/net/README.i82586 create mode 100644 bsps/shared/net/README.open_eth create mode 100644 bsps/shared/net/README.sonic create mode 100644 bsps/shared/net/README.tulipclone create mode 100644 bsps/shared/net/cs8900.c create mode 100644 bsps/shared/net/cs8900.c.bsp create mode 100644 bsps/shared/net/cs8900.h.bsp create mode 100644 bsps/shared/net/dec21140.c create mode 100644 bsps/shared/net/elnk.c create mode 100644 bsps/shared/net/greth2.c create mode 100644 bsps/shared/net/i82586.c create mode 100644 bsps/shared/net/i82586reg.h create mode 100644 bsps/shared/net/if_dc.c create mode 100644 bsps/shared/net/if_fxp.c create mode 100644 bsps/shared/net/if_fxpreg.h create mode 100644 bsps/shared/net/open_eth.c create mode 100644 bsps/shared/net/smc91111.c create mode 100644 bsps/shared/net/smc91111config.h create mode 100644 bsps/shared/net/sonic.c 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 + * + * + * 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 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 . 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 +#include + +#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. + * 2007-09-07, Ported GBIT support from 4.6.5 + */ + +#include + +#include +#define CPU_U32_FIX +#include + +#ifdef GRETH_SUPPORTED + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef malloc +#undef malloc +#endif +#ifdef free +#undef free +#endif + +#include + +#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; im_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;im_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; im_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 +#include + +#include + +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 +#include +#include + +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), *************** +******** *************** +***************************************************************** + +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 + +#include +#include +#include + +#include + +/* + * 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 + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "cs8900.h" + +#include + +/* + * 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 + +#include +#include + +/* + * 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 + +#if !defined(PCI_DRAM_OFFSET) + #undef DEC21140_SUPPORTED +#endif + +#if defined(DEC21140_SUPPORTED) +#include + +#if defined(__PPC__) +#include +#include +#endif + +#if defined(__i386__) +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#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 ; inumRxbuffers; 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 ; inumTxbuffers; 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 . 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 + +#include + +/* + * 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 + +#if !defined(PCI_DRAM_OFFSET) + #undef ELNK_SUPPORTED +#endif + +/* #undef ELNK_SUPPORTED */ + + +#if defined(ELNK_SUPPORTED) +#include + +#if defined(__PPC__) +#include +#include +#endif + +#if defined(__i386__) +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 ; inumRxbuffers; 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 ; inumTxbuffers; 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 ; inumRxbuffers; 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 ; inumTxbuffers; 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 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], ""); + + 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 + +#include +#include + +#ifdef GRETH_SUPPORTED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#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; im_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;im_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; im_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 + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "i82586reg.h" +#include + +#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 + * -- only tested with i386 bsp. + * -- supports *one* card (until the PCI & IRQ APIs get sorted out ;-)) + * + * + * Copyright (c) 1997, 1998, 1999 + * Bill Paul . 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 + * 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 + +#if defined(DRIVER_SUPPORTED) + #undef DRIVER_SUPPORTED +#endif + +#if defined(__i386__) + #define DRIVER_SUPPORTED +#endif + +#if defined(__PPC__) + #define DRIVER_SUPPORTED +#endif + +#include + +#if !defined(PCI_DRAM_OFFSET) + #undef DRIVER_SUPPORTED +#endif + +#if defined(DRIVER_SUPPORTED) /* this covers the file "globally"... */ +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* moved to cpukit/include/rtems in CVS current ! */ +/*#include "if_media.h" */ +/*#include "pci.h" */ +#include +#include +/* +#include +#include +*/ + +#include /* for vtophys */ + + +#define vtophys(p) (uintptr_t)(p) + +/* +#include +#include +#include +*/ + +#if 0 +#include /* for vtophys */ +#include /* for DELAY */ +#include +#include +#include +#include +#include +#include +#endif +#include +#if 0 +#include + +#include +#include +#endif + +/* NOTE: use mem space mapping (for now ...) +#define DC_USEIOSPACE +*/ + +#ifdef __alpha__ +#define SRM_MEDIA +#endif + +#include + + +#include + + +#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)ðerbroadcastaddr); + 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 + * 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 + * 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 + * 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 + * Obtained from Thomas Doerfler . + * A big thank-you to Thomas for making this available. + * + * October 01, 2001 Thomas Doerfler + * Original RTEMS modifications. + */ + +#include + +#if defined(__i386__) + +/*#define DEBUG_OUT 0*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef NS +#include +#include +#endif + +#include + +#include /* for vtophys */ + +#include + +#include "if_fxpreg.h" +#include + +/* + * 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" . + * 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 + * 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 + +#if defined(__i386__) || defined(__arm__) || defined(__PPC__) + #define OPENETH_NOT_SUPPORTED +#endif + +#if !defined(OPENETH_NOT_SUPPORTED) +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#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;im_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 + +#include +#include + +#include + +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#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 + +#ifdef BSP_FEATURE_IRQ_EXTENSION + #include +#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 + +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 + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +extern rtems_isr_entry set_vector( rtems_isr_entry, rtems_vector_number, int ); + +#if (SONIC_DEBUG & SONIC_DEBUG_DUMP_MBUFS) +#include +#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 + +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]) -- cgit v1.2.3