From 031df3914990db0336a0d386fb53558b05de467e Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Mon, 23 Apr 2018 09:53:31 +0200 Subject: bsps: Move legacy network drivers to bsps This patch is a part of the BSP source reorganization. Update #3285. --- c/src/lib/libbsp/powerpc/mvme3100/Makefile.am | 2 +- c/src/lib/libbsp/powerpc/mvme3100/network/tsec.c | 3215 ---------------------- 2 files changed, 1 insertion(+), 3216 deletions(-) delete mode 100644 c/src/lib/libbsp/powerpc/mvme3100/network/tsec.c (limited to 'c/src/lib/libbsp/powerpc/mvme3100') diff --git a/c/src/lib/libbsp/powerpc/mvme3100/Makefile.am b/c/src/lib/libbsp/powerpc/mvme3100/Makefile.am index 6cd02dceab..88b1f81212 100644 --- a/c/src/lib/libbsp/powerpc/mvme3100/Makefile.am +++ b/c/src/lib/libbsp/powerpc/mvme3100/Makefile.am @@ -81,7 +81,7 @@ librtemsbsp_a_SOURCES += ../../../../../../bsps/powerpc/mvme3100/i2c/i2c_init.c librtemsbsp_a_SOURCES += ../shared/motorola/vpd.c if HAS_NETWORKING -librtemsbsp_a_SOURCES += network/tsec.c +librtemsbsp_a_SOURCES += ../../../../../../bsps/powerpc/mvme3100/net/tsec.c endif librtemsbsp_a_SOURCES += ../../../../../../bsps/powerpc/shared/cache/cache.c diff --git a/c/src/lib/libbsp/powerpc/mvme3100/network/tsec.c b/c/src/lib/libbsp/powerpc/mvme3100/network/tsec.c deleted file mode 100644 index 37a24099c0..0000000000 --- a/c/src/lib/libbsp/powerpc/mvme3100/network/tsec.c +++ /dev/null @@ -1,3215 +0,0 @@ -/* - * Authorship - * ---------- - * This software ('mvme3100' RTEMS BSP) was created by - * - * Till Straumann , 2005-2007, - * Stanford Linear Accelerator Center, Stanford University. - * - * Acknowledgement of sponsorship - * ------------------------------ - * The 'mvme3100' BSP was produced by - * the Stanford Linear Accelerator Center, Stanford University, - * under Contract DE-AC03-76SFO0515 with the Department of Energy. - * - * Government disclaimer of liability - * ---------------------------------- - * Neither the United States nor the United States Department of Energy, - * nor any of their employees, makes any warranty, express or implied, or - * assumes any legal liability or responsibility for the accuracy, - * completeness, or usefulness of any data, apparatus, product, or process - * disclosed, or represents that its use would not infringe privately owned - * rights. - * - * Stanford disclaimer of liability - * -------------------------------- - * Stanford University makes no representations or warranties, express or - * implied, nor assumes any liability for the use of this software. - * - * Stanford disclaimer of copyright - * -------------------------------- - * Stanford University, owner of the copyright, hereby disclaims its - * copyright and all other rights in this software. Hence, anyone may - * freely use it for any purpose without restriction. - * - * Maintenance of notices - * ---------------------- - * In the interest of clarity regarding the origin and status of this - * SLAC software, this and all the preceding Stanford University notices - * are to remain affixed to any copy or derivative of this software made - * or distributed by the recipient and are to be affixed to any copy of - * software made or distributed by the recipient that contains a copy or - * derivative of this software. - * - * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 - */ - -#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef KERNEL -#define KERNEL -#endif -#ifndef _KERNEL -#define _KERNEL -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define STATIC -#define PARANOIA -#undef DEBUG - - -#ifdef TEST_MII_TIMING - -#include - -SPR_RO(TBRL) - -static inline uint32_t tb_rd() -{ - return _read_TBRL(); -} -#endif - -struct tsec_private; - -/* Forward declarations */ -static void -phy_init_irq( int install, struct tsec_private *mp, void (*tsec_lisr)(rtems_irq_hdl_param) ); - -static void -phy_en_irq(struct tsec_private *mp); - -static void -phy_en_irq_at_phy(struct tsec_private *mp); - -static void -phy_dis_irq(struct tsec_private *mp); - -static void -phy_dis_irq_at_phy(struct tsec_private *mp); - -static int -phy_irq_pending(struct tsec_private *mp); - -static uint32_t -phy_ack_irq(struct tsec_private *mp); - -static void -tsec_update_mcast(struct ifnet *ifp); - -#if defined(PARANOIA) || defined(DEBUG) -void tsec_dump_tring(struct tsec_private *mp); -void tsec_dump_rring(struct tsec_private *mp); -#endif - -#ifdef DEBUG -#ifdef TSEC_RX_RING_SIZE -#undef TSEC_RX_RING_SIZE -#endif -#define TSEC_RX_RING_SIZE 4 - -#ifdef TSEC_TX_RING_SIZE -#undef TSEC_TX_RING_SIZE -#endif -#define TSEC_TX_RING_SIZE 2 -#else -#ifndef TSEC_RX_RING_SIZE -#define TSEC_RX_RING_SIZE 40 -#endif -#ifndef TSEC_TX_RING_SIZE -#define TSEC_TX_RING_SIZE 200 -#endif -#endif - -/********** Helper Macros and Definitions ******/ - -/* - * Align 'p' up to a multiple of 'a' which must be - * a power of two. Result is cast to (uintptr_t) - */ -#define ALIGNTO(p,a) ((((uintptr_t)(p)) + (a) - 1) & ~((a)-1)) - - -/* - * Not obvious from the doc: RX buffers apparently must be 32-byte - * aligned :-(; TX buffers have no alignment requirement. - * I found this by testing, T.S, 11/2007 - */ -#define RX_BUF_ALIGNMENT 32 - -/* - * Alignment req. for buffer descriptors (BDs) - */ -#define BD_ALIGNMENT 8 - - -#define ETH_RX_OFFSET 0 -#define ETH_CRC_LEN 0 - -#define CPU2BUS_ADDR(x) (x) -#define BUS2CPU_ADDR(x) (x) - -/* - * Whether to automatically try to reclaim descriptors - * when enqueueing new packets - */ -#if 1 -#define TSEC_CLEAN_ON_SEND(mp) (BSP_tsec_swipe_tx(mp)) -#else -#define TSEC_CLEAN_ON_SEND(mp) (-1) -#endif - -#define TX_AVAILABLE_RING_SIZE(mp) (mp)->tx_ring_size - -#define DRVNAME "tsec" - -/* - * Event(s) posted by ISRs to driver task - */ -#define EV_PER_UNIT 1 - -#define TSEC_ETH_EVENT( unit ) ( 1 << (EV_PER_UNIT * (unit) )) -#if EV_PER_UNIT > 1 -#define TSEC_PHY_EVENT( unit ) ( 1 << (EV_PER_UNIT * (unit) + 1)) -#endif - -#define EV_IS_ETH(ev) ( (ev) & 1 ) -#if EV_PER_UNIT > 1 -#define EV_IS_PHY(ev) ( (ev) & 2 ) -#endif - -#ifndef MAXEBERRS -#define MAXEBERRS 10 -#endif - -#define EV_IS_ANY(ev) ( (ev) & ((1<flags ); -} - -static inline void bd_wrfl(TSEC_BD *bd, uint16_t v) -{ - st_be16( &bd->flags, v ); -} - -static inline void bd_setfl(TSEC_BD *bd, uint16_t v) -{ - bd_wrfl(bd, bd_rdfl(bd) | v ); -} - -static inline void bd_clrfl(TSEC_BD *bd, uint16_t v) -{ - bd_wrfl(bd, bd_rdfl(bd) & ~v ); -} - -static inline void bd_cslfl(TSEC_BD *bd, uint16_t s, uint16_t c) -{ - bd_wrfl( bd, ( bd_rdfl(bd) & ~c ) | s ); -} - -static inline uint32_t bd_rdbuf(TSEC_BD *bd) -{ - return BUS2CPU_ADDR( ld_be32( &bd->buf ) ); -} - -static inline void bd_wrbuf(TSEC_BD *bd, uint32_t addr) -{ - st_be32( &bd->buf, CPU2BUS_ADDR(addr) ); -} - -/* BD bit definitions */ - -#define TSEC_TXBD_R ((uint16_t)(1<<(15- 0))) -#define TSEC_TXBD_PAD_CRC ((uint16_t)(1<<(15- 1))) -#define TSEC_TXBD_W ((uint16_t)(1<<(15- 2))) -#define TSEC_TXBD_I ((uint16_t)(1<<(15- 3))) -#define TSEC_TXBD_L ((uint16_t)(1<<(15- 4))) -#define TSEC_TXBD_TC ((uint16_t)(1<<(15- 5))) -#define TSEC_TXBD_DEF ((uint16_t)(1<<(15- 6))) -#define TSEC_TXBD_TO1 ((uint16_t)(1<<(15- 7))) -#define TSEC_TXBD_HFE_LC ((uint16_t)(1<<(15- 8))) -#define TSEC_TXBD_RL ((uint16_t)(1<<(15- 9))) -#define TSEC_TXBD_RC(x) ((uint16_t)(((x)>>2)&0xf)) -#define TSEC_TXBD_UN ((uint16_t)(1<<(15-14))) -#define TSEC_TXBD_TXTRUNC ((uint16_t)(1<<(15-15))) -#define TSEC_TXBD_ERRS (TSEC_TXBD_RL | TSEC_TXBD_UN | TSEC_TXBD_TXTRUNC) - -#define TSEC_RXBD_E ((uint16_t)(1<<(15- 0))) -#define TSEC_RXBD_RO1 ((uint16_t)(1<<(15- 1))) -#define TSEC_RXBD_W ((uint16_t)(1<<(15- 2))) -#define TSEC_RXBD_I ((uint16_t)(1<<(15- 3))) -#define TSEC_RXBD_L ((uint16_t)(1<<(15- 4))) -#define TSEC_RXBD_F ((uint16_t)(1<<(15- 5))) -#define TSEC_RXBD_M ((uint16_t)(1<<(15- 7))) -#define TSEC_RXBD_BC ((uint16_t)(1<<(15- 8))) -#define TSEC_RXBD_MC ((uint16_t)(1<<(15- 9))) -#define TSEC_RXBD_LG ((uint16_t)(1<<(15-10))) -#define TSEC_RXBD_NO ((uint16_t)(1<<(15-11))) -#define TSEC_RXBD_SH ((uint16_t)(1<<(15-12))) -#define TSEC_RXBD_CR ((uint16_t)(1<<(15-13))) -#define TSEC_RXBD_OV ((uint16_t)(1<<(15-14))) -#define TSEC_RXBD_TR ((uint16_t)(1<<(15-15))) - -#define TSEC_RXBD_ERROR \ - (TSEC_RXBD_LG | TSEC_RXBD_NO | TSEC_RXBD_SH | TSEC_RXBD_CR | TSEC_RXBD_OV | TSEC_RXBD_TR ) - -/* Driver 'private' data */ - -#define NUM_MC_HASHES 256 - -struct tsec_private { - FEC_Enet_Base base; /* Controller base address */ - FEC_Enet_Base phy_base; /* Phy base address (not necessarily identical - * with controller base address); - * e.g., phy attached to 2nd controller may be - * connected to mii bus of 1st controller. - */ - unsigned phy; /* Phy address on mii bus */ - unsigned unit; /* Driver instance (one-based */ - int isfec; /* Set if a FEC (not TSEC) controller */ - struct tsec_softc *sc; /* Pointer to BSD driver struct */ - TSEC_BD *ring_area; /* Not necessarily aligned */ - TSEC_BD *tx_ring; /* Aligned array of TX BDs */ - void **tx_ring_user; /* Array of user pointers (1 per BD) */ - unsigned tx_ring_size; - unsigned tx_head; /* first 'dirty' BD; chip is working on */ - unsigned tx_tail; /* BDs between head and tail */ - unsigned tx_avail; /* Number of available/free TX BDs */ - TSEC_BD *rx_ring; /* Aligned array of RX BDs */ - void **rx_ring_user; /* Array of user pointers (1 per BD) */ - unsigned rx_tail; /* Where we left off scanning for full bufs */ - unsigned rx_ring_size; - void (*isr)(void*); - void *isr_arg; - void (*cleanup_txbuf) /* Callback to cleanup TX ring */ - (void*, void*, int); - void *cleanup_txbuf_arg; - void *(*alloc_rxbuf) /* Callback for allocating RX buffer */ - (int *psize, uintptr_t *paddr); - void (*consume_rxbuf) /* callback to consume RX buffer */ - (void*, void*, int); - void *consume_rxbuf_arg; - rtems_id tid; /* driver task ID */ - uint32_t irq_mask; - uint32_t irq_mask_cache; - uint32_t irq_pending; - rtems_event_set event; /* Task synchronization events */ - struct { /* Statistics */ - unsigned xirqs; - unsigned rirqs; - unsigned eirqs; - unsigned lirqs; - unsigned maxchain; - unsigned packet; - unsigned odrops; - unsigned repack; - unsigned eberrs; - unsigned dmarst; - } stats; - uint16_t mc_refcnt[NUM_MC_HASHES]; -}; - -#define NEXT_TXI(mp, i) (((i)+1) < (mp)->tx_ring_size ? (i)+1 : 0 ) -#define NEXT_RXI(mp, i) (((i)+1) < (mp)->rx_ring_size ? (i)+1 : 0 ) - -/* Stuff needed for bsdnet support */ -struct tsec_bsdsupp { - int oif_flags; /* old / cached if_flags */ -}; - -/* bsdnet driver data */ -struct tsec_softc { - struct arpcom arpcom; - struct tsec_bsdsupp bsd; - struct tsec_private pvt; -}; - -/* BSP glue information */ -typedef struct tsec_bsp_config { - uint32_t base; - int xirq, rirq, eirq; - uint32_t phy_base; - int phy_addr; -} TsecBspConfig; - -/********** Global Variables ********************/ - -/* You may override base addresses - * externally - but you must - * then also define TSEC_NUM_DRIVER_SLOTS. - */ -#ifndef TSEC_CONFIG - -static TsecBspConfig tsec_config[] = -{ - { - base: BSP_8540_CCSR_BASE + 0x24000, - xirq: BSP_CORE_IRQ_LOWEST_OFFSET + 13, - rirq: BSP_CORE_IRQ_LOWEST_OFFSET + 14, - eirq: BSP_CORE_IRQ_LOWEST_OFFSET + 18, - phy_base: BSP_8540_CCSR_BASE + 0x24000, - phy_addr: 1, - }, - { - base: BSP_8540_CCSR_BASE + 0x25000, - xirq: BSP_CORE_IRQ_LOWEST_OFFSET + 19, - rirq: BSP_CORE_IRQ_LOWEST_OFFSET + 20, - eirq: BSP_CORE_IRQ_LOWEST_OFFSET + 23, - /* all PHYs are on the 1st adapter's mii bus */ - phy_base: BSP_8540_CCSR_BASE + 0x24000, - phy_addr: 2, - }, -}; - -#define TSEC_CONFIG tsec_config - -#endif - -#ifndef TSEC_NUM_DRIVER_SLOTS -#define TSEC_NUM_DRIVER_SLOTS (sizeof(TSEC_CONFIG)/sizeof(TSEC_CONFIG[0])) -#endif - -/* Driver data structs */ -STATIC struct tsec_softc theTsecEths[TSEC_NUM_DRIVER_SLOTS] = { {{{0}}} }; - -/* Bsdnet driver task ID; since the BSD stack is single-threaded - * there is no point having multiple tasks. A single - * task handling all adapters (attached to BSD stack) - * is good enough. - * Note that an adapter might well be used independently - * from the BSD stack (use the low-level driver interface) - * and be serviced by a separate task. - */ -STATIC rtems_id tsec_tid = 0; - -/* If we anticipate using adapters independently - * from the BSD stack AND if all PHYs are on a single - * adapter's MII bus THEN we must mutex-protect - * that MII bus. - * If not all of these conditions hold then you - * may define TSEC_CONFIG_NO_PHY_REGLOCK and - * avoid the creation and use of a mutex. - */ -#ifndef TSEC_CONFIG_NO_PHY_REGLOCK -/* - * PHY register access protection mutex; - * multiple instances of tsec hardware - * may share e.g., the first tsec's registers - * for accessing the mii bus where all PHYs - * may be connected. If we would only deal - * with BSD networking then using the normal - * networking semaphore would be OK. However, - * we want to support standalone drivers and - * therefore might require a separate lock. - */ -STATIC rtems_id tsec_mtx = 0; -#define REGLOCK() do { \ - if ( RTEMS_SUCCESSFUL != rtems_semaphore_obtain(tsec_mtx, RTEMS_WAIT, RTEMS_NO_TIMEOUT) ) \ - rtems_panic(DRVNAME": unable to lock phy register protection mutex"); \ - } while (0) -#define REGUNLOCK() rtems_semaphore_release(tsec_mtx) -#else -#define REGLOCK() do { } while (0) -#define REGUNLOCK() do { } while (0) -#endif - -static void tsec_xisr(rtems_irq_hdl_param arg); -static void tsec_risr(rtems_irq_hdl_param arg); -static void tsec_eisr(rtems_irq_hdl_param arg); -static void tsec_lisr(rtems_irq_hdl_param arg); - -static void noop(const rtems_irq_connect_data *unused) { } -static int nopf(const rtems_irq_connect_data *unused) { return -1; } - -/********** Low-level Driver API ****************/ - -/* - * This API provides driver access to applications that - * want to use e.g., the second ethernet interface - * independently from the BSD TCP/IP stack. E.g., for - * raw ethernet packet communication... - */ - -/* - * Descriptor scavenger; cleanup the TX ring, passing all buffers - * that have been sent to the cleanup_tx() callback. - * This routine is called from BSP_tsec_send_buf(), BSP_tsec_init_hw(), - * BSP_tsec_stop_hw(). - * - * RETURNS: number of buffers processed. - */ - -int -BSP_tsec_swipe_tx(struct tsec_private *mp) -{ -int rval = 0; -int i; -TSEC_BD *bd; -uint16_t flags; -void *u; - -#if DEBUG > 2 -printf("Swipe TX entering:\n"); -tsec_dump_tring(mp); -#endif - - for ( i = mp->tx_head; bd_rdbuf( (bd = &mp->tx_ring[i]) ); i = NEXT_TXI(mp, i) ) { - - flags = bd_rdfl( bd ); - if ( (TSEC_TXBD_R & flags) ) { - /* nothing more to clean */ - break; - } - - /* tx_ring_user[i] is only set on the last descriptor in a chain; - * we only count errors in the last descriptor; - */ - if ( (u=mp->tx_ring_user[i]) ) { - mp->cleanup_txbuf(u, mp->cleanup_txbuf_arg, (flags & TSEC_TXBD_ERRS)); - mp->tx_ring_user[i] = 0; - } - - bd_wrbuf( bd, 0 ); - - mp->tx_avail++; - - rval++; - } - mp->tx_head = i; - -#if DEBUG > 2 -tsec_dump_tring(mp); -printf("Swipe TX leaving\n"); -#endif - - return rval; -} - - -/* - * Reset the controller and bring into a known state; - * all interrupts are off - */ -STATIC void -tsec_reset_hw(struct tsec_private *mp) -{ -FEC_Enet_Base b = mp->base; - - /* Make sure all interrupts are off */ - fec_wr(b, TSEC_IMASK, TSEC_IMASK_NONE); - -#ifndef TSEC_CONFIG_NO_PHY_REGLOCK - /* don't bother disabling irqs in the PHY if this is - * called before the mutex is created; - * the PHY ISR is not hooked yet and there can be no - * interrupts... - */ - if ( tsec_mtx ) -#endif - phy_dis_irq_at_phy( mp ); - - mp->irq_mask_cache = 0; - - /* Follow the manual resetting the chip */ - - /* Do graceful stop (if not in stop condition already) */ - if ( ! (TSEC_DMACTRL_GTS & fec_rd(b, TSEC_DMACTRL)) ) { - /* Make sure GTSC is clear */ - fec_wr(b, TSEC_IEVENT, TSEC_IEVENT_GTSC); - fec_set(b, TSEC_DMACTRL, TSEC_DMACTRL_GTS); - while ( ! (TSEC_IEVENT_GTSC & fec_rd(b, TSEC_IEVENT)) ) - /* wait */; - } - - /* Clear RX/TX enable in MAC */ - fec_clr(b, TSEC_MACCFG1, TSEC_MACCFG1_RX_EN | TSEC_MACCFG1_TX_EN); - - /* wait for > 8ms */ - rtems_task_wake_after(1); - - /* set GRS if not already stopped */ - if ( ! (TSEC_DMACTRL_GRS & fec_rd(b, TSEC_DMACTRL)) ) { - /* Make sure GRSC is clear */ - fec_wr(b, TSEC_IEVENT, TSEC_IEVENT_GRSC); - fec_set(b, TSEC_DMACTRL, TSEC_DMACTRL_GRS); - while ( ! (TSEC_IEVENT_GRSC & fec_rd(b, TSEC_IEVENT)) ) - /* wait */; - } - - fec_set(b, TSEC_MACCFG1, TSEC_MACCFG1_SOFT_RESET); - fec_clr(b, TSEC_MACCFG1, TSEC_MACCFG1_SOFT_RESET); - - /* clear all irqs */ - fec_wr (b, TSEC_IEVENT, TSEC_IEVENT_ALL); -} - -/* Helper to hook/unhook interrupts */ - -static void -install_remove_isrs(int install, struct tsec_private *mp, uint32_t irq_mask) -{ - rtems_irq_connect_data xxx; - int installed = 0; - int line; - int unit = mp->unit; - - xxx.on = noop; - xxx.off = noop; - xxx.isOn = nopf; - xxx.handle = mp; - - if ( irq_mask & TSEC_TXIRQ ) { - xxx.name = TSEC_CONFIG[unit-1].xirq; - xxx.hdl = tsec_xisr; - if ( ! (install ? - BSP_install_rtems_irq_handler( &xxx ) : - BSP_remove_rtems_irq_handler( &xxx ) ) ) { - rtems_panic(DRVNAME": Unable to install TX ISR\n"); - } - installed++; - } - - if ( (irq_mask & TSEC_RXIRQ) ) { - if ( (line = TSEC_CONFIG[unit-1].rirq) < 0 && ! installed ) { - /* have no dedicated RX IRQ line; install TX ISR if not already done */ - line = TSEC_CONFIG[unit-1].xirq; - } - xxx.name = line; - xxx.hdl = tsec_risr; - if ( ! (install ? - BSP_install_rtems_irq_handler( &xxx ) : - BSP_remove_rtems_irq_handler( &xxx ) ) ) { - rtems_panic(DRVNAME": Unable to install RX ISR\n"); - } - installed++; - } - - if ( (line = TSEC_CONFIG[unit-1].eirq) < 0 && ! installed ) { - /* have no dedicated RX IRQ line; install TX ISR if not already done */ - line = TSEC_CONFIG[unit-1].xirq; - } - xxx.name = line; - xxx.hdl = tsec_eisr; - if ( ! (install ? - BSP_install_rtems_irq_handler( &xxx ) : - BSP_remove_rtems_irq_handler( &xxx ) ) ) { - rtems_panic(DRVNAME": Unable to install ERR ISR\n"); - } - - if ( irq_mask & TSEC_LINK_INTR ) { - phy_init_irq( install, mp, tsec_lisr ); - } -} - -/* - * Setup an interface. - * Allocates resources for descriptor rings and sets up the driver software structure. - * - * Arguments: - * unit: - * interface # (1..2). The interface must not be attached to BSD already. - * - * driver_tid: - * ISR posts RTEMS event # ('unit' - 1) to task with ID 'driver_tid' and disables interrupts - * from this interface. - * - * void (*cleanup_txbuf)(void *user_buf, void *cleanup_txbuf_arg, int error_on_tx_occurred): - * Pointer to user-supplied callback to release a buffer that had been sent - * by BSP_tsec_send_buf() earlier. The callback is passed 'cleanup_txbuf_arg' - * and a flag indicating whether the send had been successful. - * The driver no longer accesses 'user_buf' after invoking this callback. - * CONTEXT: This callback is executed either by BSP_tsec_swipe_tx() or - * BSP_tsec_send_buf(), BSP_tsec_init_hw(), BSP_tsec_stop_hw() (the latter - * ones calling BSP_tsec_swipe_tx()). - * void *cleanup_txbuf_arg: - * Closure argument that is passed on to 'cleanup_txbuf()' callback; - * - * void *(*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr), - * Pointer to user-supplied callback to allocate a buffer for subsequent - * insertion into the RX ring by the driver. - * RETURNS: opaque handle to the buffer (which may be a more complex object - * such as an 'mbuf'). The handle is not used by the driver directly - * but passed back to the 'consume_rxbuf()' callback. - * Size of the available data area and pointer to buffer's data area - * in '*psize' and '*p_data_area', respectively. - * If no buffer is available, this routine should return NULL in which - * case the driver drops the last packet and re-uses the last buffer - * instead of handing it out to 'consume_rxbuf()'. - * CONTEXT: Called when initializing the RX ring (BSP_tsec_init_hw()) or when - * swiping it (BSP_tsec_swipe_rx()). - * - * - * void (*consume_rxbuf)(void *user_buf, void *consume_rxbuf_arg, int len); - * Pointer to user-supplied callback to pass a received buffer back to - * the user. The driver no longer accesses the buffer after invoking this - * callback (with 'len'>0, see below). 'user_buf' is the buffer handle - * previously generated by 'alloc_rxbuf()'. - * The callback is passed 'cleanup_rxbuf_arg' and a 'len' - * argument giving the number of bytes that were received. - * 'len' may be <=0 in which case the 'user_buf' argument is NULL. - * 'len' == 0 means that the last 'alloc_rxbuf()' had failed, - * 'len' < 0 indicates a receiver error. In both cases, the last packet - * was dropped/missed and the last buffer will be re-used by the driver. - * NOTE: the data are 'prefixed' with two bytes, i.e., the ethernet packet header - * is stored at offset 2 in the buffer's data area. Also, the FCS (4 bytes) - * is appended. 'len' accounts for both. - * CONTEXT: Called from BSP_tsec_swipe_rx(). - * void *cleanup_rxbuf_arg: - * Closure argument that is passed on to 'consume_rxbuf()' callback; - * - * rx_ring_size, tx_ring_size: - * How many big to make the RX and TX descriptor rings. Note that the sizes - * may be 0 in which case a reasonable default will be used. - * If either ring size is < 0 then the RX or TX will be disabled. - * Note that it is illegal in this case to use BSP_tsec_swipe_rx() or - * BSP_tsec_swipe_tx(), respectively. - * - * irq_mask: - * Interrupts to enable. OR of flags from above. - * - */ - -static struct tsec_private * -tsec_setup_internal( - int unit, - rtems_id driver_tid, - void (*isr)(void *), - void * isr_arg, - void (*cleanup_txbuf)(void *user_buf, void *cleanup_txbuf_arg, int error_on_tx_occurred), - void * cleanup_txbuf_arg, - void * (*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr), - void (*consume_rxbuf)(void *user_buf, void *consume_rxbuf_arg, int len), - void * consume_rxbuf_arg, - int rx_ring_size, - int tx_ring_size, - int irq_mask -) -{ -struct tsec_private *mp; -int i; -struct ifnet *ifp; - - if ( unit <= 0 || unit > TSEC_NUM_DRIVER_SLOTS ) { - printk(DRVNAME": Bad unit number %i; must be 1..%i\n", unit, TSEC_NUM_DRIVER_SLOTS); - return 0; - } - - ifp = &theTsecEths[unit-1].arpcom.ac_if; - if ( ifp->if_init ) { - if ( ifp->if_init ) { - printk(DRVNAME": instance %i already attached.\n", unit); - return 0; - } - } - - - if ( rx_ring_size < 0 && tx_ring_size < 0 ) - return 0; - - mp = &theTsecEths[unit - 1].pvt; - - memset(mp, 0, sizeof(*mp)); - - mp->sc = &theTsecEths[unit - 1]; - mp->unit = unit; - - mp->base = (FEC_Enet_Base)TSEC_CONFIG[unit-1].base; - mp->phy_base = (FEC_Enet_Base)TSEC_CONFIG[unit-1].phy_base; - mp->phy = TSEC_CONFIG[unit-1].phy_addr; - mp->tid = driver_tid; - /* use odd event flags for link status IRQ */ - mp->event = TSEC_ETH_EVENT((unit-1)); - - mp->cleanup_txbuf = cleanup_txbuf; - mp->cleanup_txbuf_arg = cleanup_txbuf_arg; - mp->alloc_rxbuf = alloc_rxbuf; - mp->consume_rxbuf = consume_rxbuf; - mp->consume_rxbuf_arg = consume_rxbuf_arg; - - /* stop hw prior to setting ring-size to anything nonzero - * so that the rings are not swept. - */ - BSP_tsec_stop_hw(mp); - - if ( 0 == rx_ring_size ) - rx_ring_size = TSEC_RX_RING_SIZE; - if ( 0 == tx_ring_size ) - tx_ring_size = TSEC_TX_RING_SIZE; - - mp->rx_ring_size = rx_ring_size < 0 ? 0 : rx_ring_size; - mp->tx_ring_size = tx_ring_size < 0 ? 0 : tx_ring_size; - - /* allocate ring area; add 1 entry -- room for alignment */ - assert( !mp->ring_area ); - mp->ring_area = malloc( - sizeof(*mp->ring_area) * - (mp->rx_ring_size + mp->tx_ring_size + 1), - M_DEVBUF, - M_WAIT ); - assert( mp->ring_area ); - - mp->tx_ring_user = malloc( sizeof(*mp->tx_ring_user) * - (mp->rx_ring_size + mp->tx_ring_size), - M_DEVBUF, - M_WAIT ); - assert( mp->tx_ring_user ); - - mp->rx_ring_user = mp->tx_ring_user + mp->tx_ring_size; - - /* Initialize TX ring */ - mp->tx_ring = (TSEC_BD *) ALIGNTO(mp->ring_area,BD_ALIGNMENT); - - mp->rx_ring = mp->tx_ring + mp->tx_ring_size; - - for ( i=0; itx_ring_size; i++ ) { - bd_wrbuf( &mp->tx_ring[i], 0 ); - bd_wrfl( &mp->tx_ring[i], TSEC_TXBD_I ); - mp->tx_ring_user[i] = 0; - } - /* set wrap-around flag on last BD */ - if ( mp->tx_ring_size ) - bd_setfl( &mp->tx_ring[i-1], TSEC_TXBD_W ); - - mp->tx_tail = mp->tx_head = 0; - mp->tx_avail = mp->tx_ring_size; - - /* Initialize RX ring (buffers are allocated later) */ - for ( i=0; irx_ring_size; i++ ) { - bd_wrbuf( &mp->rx_ring[i], 0 ); - bd_wrfl( &mp->rx_ring[i], TSEC_RXBD_I ); - mp->rx_ring_user[i] = 0; - } - /* set wrap-around flag on last BD */ - if ( mp->rx_ring_size ) - bd_setfl( &mp->rx_ring[i-1], TSEC_RXBD_W ); - - if ( irq_mask ) { - if ( rx_ring_size == 0 ) - irq_mask &= ~ TSEC_RXIRQ; - if ( tx_ring_size == 0 ) - irq_mask &= ~ TSEC_TXIRQ; - } - -#ifndef TSEC_CONFIG_NO_PHY_REGLOCK - if ( ! tsec_mtx ) { - rtems_status_code sc; - rtems_id new_mtx; - rtems_interrupt_level l; - sc = rtems_semaphore_create( - rtems_build_name('t','s','e','X'), - 1, - RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_INHERIT_PRIORITY | RTEMS_DEFAULT_ATTRIBUTES, - 0, - &new_mtx); - if ( RTEMS_SUCCESSFUL != sc ) { - rtems_error(sc,DRVNAME": creating mutex\n"); - rtems_panic("unable to proceed\n"); - } - rtems_interrupt_disable( l ); - if ( ! tsec_mtx ) { - tsec_mtx = new_mtx; - new_mtx = 0; - } - rtems_interrupt_enable( l ); - - if ( new_mtx ) { - /* another task was faster installing the mutex */ - rtems_semaphore_delete( new_mtx ); - } - - } -#endif - - if ( irq_mask ) { - install_remove_isrs( 1, mp, irq_mask ); - } - - mp->irq_mask = irq_mask; - - /* mark as used */ - ifp->if_init = (void*)(-1); - - return mp; -} - -struct tsec_private * -BSP_tsec_setup( - int unit, - rtems_id driver_tid, - void (*cleanup_txbuf)(void *user_buf, void *cleanup_txbuf_arg, int error_on_tx_occurred), - void * cleanup_txbuf_arg, - void * (*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr), - void (*consume_rxbuf)(void *user_buf, void *consume_rxbuf_arg, int len), - void * consume_rxbuf_arg, - int rx_ring_size, - int tx_ring_size, - int irq_mask -) -{ - if ( irq_mask && ! driver_tid ) { - printk(DRVNAME": must supply a TID if irq_mask not zero\n"); - return 0; - } - return tsec_setup_internal( - unit, - driver_tid, - 0, 0, - cleanup_txbuf, cleanup_txbuf_arg, - alloc_rxbuf, - consume_rxbuf, consume_rxbuf_arg, - rx_ring_size, - tx_ring_size, - irq_mask - ); -} - -struct tsec_private * -BSP_tsec_setup_1( - int unit, - void (*isr)(void*), - void * isr_arg, - void (*cleanup_txbuf)(void *user_buf, void *cleanup_txbuf_arg, int error_on_tx_occurred), - void * cleanup_txbuf_arg, - void * (*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr), - void (*consume_rxbuf)(void *user_buf, void *consume_rxbuf_arg, int len), - void * consume_rxbuf_arg, - int rx_ring_size, - int tx_ring_size, - int irq_mask -) -{ - if ( irq_mask && ! isr ) { - printk(DRVNAME": must supply a ISR if irq_mask not zero\n"); - return 0; - } - return tsec_setup_internal( - unit, - 0, - isr, isr_arg, - cleanup_txbuf, cleanup_txbuf_arg, - alloc_rxbuf, - consume_rxbuf, consume_rxbuf_arg, - rx_ring_size, - tx_ring_size, - irq_mask - ); -} - -void -BSP_tsec_reset_stats(struct tsec_private *mp) -{ -FEC_Enet_Base b = mp->base; -int i; - memset( &mp->stats, 0, sizeof( mp->stats ) ); - if ( mp->isfec ) - return; - for ( i=TSEC_TR64; i<=TSEC_TFRG; i+=4 ) - fec_wr( b, i, 0 ); - -} - -/* - * retrieve media status from the PHY - * and set duplex mode in MACCFG2 based - * on the result. - * - * RETURNS: media word (or -1 if BSP_tsec_media_ioctl() fails) - */ -static int -mac_set_duplex(struct tsec_private *mp) -{ -int media = IFM_MAKEWORD(0, 0, 0, 0); - - if ( 0 == BSP_tsec_media_ioctl(mp, SIOCGIFMEDIA, &media)) { - if ( IFM_LINK_OK & media ) { - /* update duplex setting in MACCFG2 */ - if ( IFM_FDX & media ) { - fec_set( mp->base, TSEC_MACCFG2, TSEC_MACCFG2_FD ); - } else { - fec_clr( mp->base, TSEC_MACCFG2, TSEC_MACCFG2_FD ); - } - } - return media; - } - return -1; -} - -/* - * Initialize interface hardware - * - * 'mp' handle obtained by from BSP_tsec_setup(). - * 'promisc' whether to set promiscuous flag. - * 'enaddr' pointer to six bytes with MAC address. Read - * from the device if NULL. - */ -void -BSP_tsec_init_hw(struct tsec_private *mp, int promisc, unsigned char *enaddr) -{ -FEC_Enet_Base b = mp->base; -unsigned i; -uint32_t v; -int sz; - - BSP_tsec_stop_hw(mp); - -#ifdef PARANOIA - assert( mp->tx_avail == mp->tx_ring_size ); - assert( mp->tx_head == mp->tx_tail ); - for ( i=0; itx_ring_size; i++ ) { - assert( mp->tx_ring_user[i] == 0 ); - } -#endif - - /* make sure RX ring is filled */ - for ( i=0; irx_ring_size; i++ ) { - uintptr_t data_area; - if ( ! (mp->rx_ring_user[i] = mp->alloc_rxbuf( &sz, &data_area)) ) { - rtems_panic(DRVNAME": unable to fill RX ring"); - } - if ( data_area & (RX_BUF_ALIGNMENT-1) ) - rtems_panic(DRVNAME": RX buffers must be %i-byte aligned", RX_BUF_ALIGNMENT); - - bd_wrbuf( &mp->rx_ring[i], data_area ); - st_be16 ( &mp->rx_ring[i].len, sz ); - bd_setfl( &mp->rx_ring[i], TSEC_RXBD_E | TSEC_RXBD_I ); - } - - mp->tx_tail = mp->tx_head = 0; - - mp->rx_tail = 0; - - /* tell chip what the ring areas are */ - fec_wr( b, TSEC_TBASE, (uint32_t)mp->tx_ring ); - fec_wr( b, TSEC_RBASE, (uint32_t)mp->rx_ring ); - - /* clear and disable IRQs */ - fec_wr( b, TSEC_IEVENT, TSEC_IEVENT_ALL ); - fec_wr( b, TSEC_IMASK, TSEC_IMASK_NONE ); - mp->irq_mask_cache = 0; - - /* bring other regs. into a known state */ - fec_wr( b, TSEC_EDIS, 0 ); - - if ( !mp->isfec ) - fec_wr( b, TSEC_ECNTRL, TSEC_ECNTRL_CLRCNT | TSEC_ECNTRL_STEN ); - - fec_wr( b, TSEC_MINFLR, 64 ); - fec_wr( b, TSEC_PTV, 0 ); - - v = TSEC_DMACTRL_WWR; - - if ( mp->tx_ring_size ) - v |= TSEC_DMACTRL_TDSEN | TSEC_DMACTRL_TBDSEN | TSEC_DMACTRL_WOP; - - fec_wr( b, TSEC_DMACTRL, v ); - - fec_wr( b, TSEC_FIFO_PAUSE_CTRL, 0 ); - fec_wr( b, TSEC_FIFO_TX_THR, 256 ); - fec_wr( b, TSEC_FIFO_TX_STARVE, 128 ); - fec_wr( b, TSEC_FIFO_TX_STARVE_SHUTOFF, 256 ); - fec_wr( b, TSEC_TCTRL, 0 ); - if ( !mp->isfec ) { - /* FIXME: use IRQ coalescing ? not sure how to - * set the timer (bad if it depends on the speed - * setting). - */ - fec_wr( b, TSEC_TXIC, 0); - } - fec_wr( b, TSEC_OSTBD, 0 ); - fec_wr( b, TSEC_RCTRL, (promisc ? TSEC_RCTRL_PROM : 0) ); - fec_wr( b, TSEC_RSTAT, TSEC_RSTAT_QHLT ); - if ( !mp->isfec ) { - /* FIXME: use IRQ coalescing ? not sure how to - * set the timer (bad if it depends on the speed - * setting). - */ - fec_wr( b, TSEC_RXIC, 0 ); - } - fec_wr( b, TSEC_MRBLR, sz & ~63 ); - - /* Reset config. as per manual */ - fec_wr( b, TSEC_IPGIFG, 0x40605060 ); - fec_wr( b, TSEC_HAFDUP, 0x00a1f037 ); - fec_wr( b, TSEC_MAXFRM, 1536 ); - - if ( enaddr ) { - union { - uint32_t u; - uint16_t s[2]; - uint8_t c[4]; - } x; - fec_wr( b, TSEC_MACSTNADDR1, ld_le32( (volatile uint32_t*)(enaddr + 2) ) ); - x.s[0] = ld_le16( (volatile uint16_t *)(enaddr) ); - fec_wr( b, TSEC_MACSTNADDR2, x.u ); - } - - for ( i=0; i<8*4; i+=4 ) { - fec_wr( b, TSEC_IADDR0 + i, 0 ); - } - - BSP_tsec_mcast_filter_clear(mp); - - BSP_tsec_reset_stats(mp); - - fec_wr( b, TSEC_ATTR, (TSEC_ATTR_RDSEN | TSEC_ATTR_RBDSEN) ); - fec_wr( b, TSEC_ATTRELI, 0 ); - - /* The interface type is probably board dependent; leave alone... - v = mp->isfec ? TSEC_MACCFG2_IF_MODE_MII : TSEC_MACCFG2_IF_MODE_GMII; - */ - - fec_clr( b, TSEC_MACCFG2, - TSEC_MACCFG2_PREAMBLE_15 - | TSEC_MACCFG2_HUGE_FRAME - | TSEC_MACCFG2_LENGTH_CHECK ); - - fec_set( b, TSEC_MACCFG2, - TSEC_MACCFG2_PREAMBLE_7 - | TSEC_MACCFG2_PAD_CRC ); - - mac_set_duplex( mp ); - - v = 0; - if ( mp->rx_ring_size ) { - v |= TSEC_MACCFG1_RX_EN; - } - if ( mp->tx_ring_size ) { - v |= TSEC_MACCFG1_TX_EN; - } - - fec_wr( b, TSEC_MACCFG1, v); - - /* The following sequency (FWIW) ensures that - * - * - PHY and TSEC interrupts are enabled atomically - * - IRQS are not globally off while accessing the PHY - * (slow MII) - */ - - if ( (TSEC_LINK_INTR & mp->irq_mask) ) { - /* disable PHY irq at PIC (fast) */ - phy_dis_irq( mp ); - /* enable PHY irq (MII operation, slow) */ - phy_en_irq_at_phy (mp ); - } - - BSP_tsec_enable_irq_mask( mp, mp->irq_mask ); -} - -static void -hash_prog(struct tsec_private *mp, uint32_t tble, const uint8_t *enaddr, int accept) -{ -uint8_t s; -uint32_t reg, bit; - - s = ether_crc32_le(enaddr, ETHER_ADDR_LEN); - - /* bit-reverse */ - s = ((s&0x0f) << 4) | ((s&0xf0) >> 4); - s = ((s&0x33) << 2) | ((s&0xcc) >> 2); - s = ((s&0x55) << 1) | ((s&0xaa) >> 1); - - reg = tble + ((s >> (5-2)) & ~3); - bit = 1 << (31 - (s & 31)); - - if ( accept ) { - if ( 0 == mp->mc_refcnt[s]++ ) - fec_set( mp->base, reg, bit ); - } else { - if ( mp->mc_refcnt[s] > 0 && 0 == --mp->mc_refcnt[s] ) - fec_clr( mp->base, reg, bit ); - } -} - -void -BSP_tsec_mcast_filter_clear(struct tsec_private *mp) -{ -int i; - for ( i=0; i<8*4; i+=4 ) { - fec_wr( mp->base, TSEC_GADDR0 + i, 0 ); - } - for ( i=0; imc_refcnt[i] = 0; -} - -void -BSP_tsec_mcast_filter_accept_all(struct tsec_private *mp) -{ -int i; - for ( i=0; i<8*4; i+=4 ) { - fec_wr( mp->base, TSEC_GADDR0 + i, 0xffffffff ); - } - for ( i=0; imc_refcnt[i]++; -} - -static void -mcast_filter_prog(struct tsec_private *mp, uint8_t *enaddr, int accept) -{ -static const uint8_t bcst[6]={0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - if ( ! (enaddr[0] & 0x01) ) { - /* not a multicast address; ignore */ - return; - } - if ( 0 == memcmp( enaddr, bcst, sizeof(bcst) ) ) { - /* broadcast; ignore */ - return; - } - hash_prog(mp, TSEC_GADDR0, enaddr, accept); -} - -void -BSP_tsec_mcast_filter_accept_add(struct tsec_private *mp, uint8_t *enaddr) -{ - mcast_filter_prog(mp, enaddr, 1 /* accept */); -} - -void -BSP_tsec_mcast_filter_accept_del(struct tsec_private *mp, uint8_t *enaddr) -{ - mcast_filter_prog(mp, enaddr, 0 /* delete */); -} - -void -BSP_tsec_dump_stats(struct tsec_private *mp, FILE *f) -{ -FEC_Enet_Base b; - - if ( !mp ) - mp = &theTsecEths[0].pvt; - - if ( ! f ) - f = stdout; - - fprintf(f, DRVNAME"%i Statistics:\n", mp->unit); - - b = mp->base; - - fprintf(f, "TX IRQS: %u\n", mp->stats.xirqs); - fprintf(f, "RX IRQS: %u\n", mp->stats.rirqs); - fprintf(f, "ERR IRQS: %u\n", mp->stats.eirqs); - fprintf(f, "bus errs: %u\n", mp->stats.eberrs); - fprintf(f, "dmawhack: %u\n", mp->stats.dmarst); - fprintf(f, "LNK IRQS: %u\n", mp->stats.lirqs); - fprintf(f, "maxchain: %u\n", mp->stats.maxchain); - fprintf(f, "xpackets: %u\n", mp->stats.packet); - fprintf(f, "odrops: %u\n", mp->stats.odrops); - fprintf(f, "repack: %u\n", mp->stats.repack); - - if ( mp->isfec ) { - fprintf(f,"FEC has no HW counters\n"); - return; - } - - fprintf(f,"TSEC MIB counters (modulo 2^32):\n"); - - fprintf(f,"RX bytes %"PRIu32"\n", fec_rd( b, TSEC_RBYT )); - fprintf(f,"RX pkts %"PRIu32"\n", fec_rd( b, TSEC_RPKT )); - fprintf(f,"RX FCS errs %"PRIu32"\n", fec_rd( b, TSEC_RFCS )); - fprintf(f,"RX mcst pkts %"PRIu32"\n", fec_rd( b, TSEC_RMCA )); - fprintf(f,"RX bcst pkts %"PRIu32"\n", fec_rd( b, TSEC_RBCA )); - fprintf(f,"RX pse frms %"PRIu32"\n", fec_rd( b, TSEC_RXPF )); - fprintf(f,"RX drop %"PRIu32"\n", fec_rd( b, TSEC_RDRP )); - fprintf(f,"TX bytes %"PRIu32"\n", fec_rd( b, TSEC_TBYT )); - fprintf(f,"TX pkts %"PRIu32"\n", fec_rd( b, TSEC_TPKT )); - fprintf(f,"TX FCS errs %"PRIu32"\n", fec_rd( b, TSEC_TFCS )); - fprintf(f,"TX mcst pkts %"PRIu32"\n", fec_rd( b, TSEC_TMCA )); - fprintf(f,"TX bcst pkts %"PRIu32"\n", fec_rd( b, TSEC_TBCA )); - fprintf(f,"TX pse frms %"PRIu32"\n", fec_rd( b, TSEC_TXPF )); - fprintf(f,"TX drop %"PRIu32"\n", fec_rd( b, TSEC_TDRP )); - fprintf(f,"TX coll %"PRIu32"\n", fec_rd( b, TSEC_TSCL )); - fprintf(f,"TX mcoll %"PRIu32"\n", fec_rd( b, TSEC_TMCL )); - fprintf(f,"TX late coll %"PRIu32"\n", fec_rd( b, TSEC_TLCL )); - fprintf(f,"TX exc coll %"PRIu32"\n", fec_rd( b, TSEC_TXCL )); - fprintf(f,"TX defer %"PRIu32"\n", fec_rd( b, TSEC_TDFR )); - fprintf(f,"TX exc defer %"PRIu32"\n", fec_rd( b, TSEC_TEDF )); - fprintf(f,"TX oversz %"PRIu32"\n", fec_rd( b, TSEC_TOVR )); - fprintf(f,"TX undersz %"PRIu32"\n", fec_rd( b, TSEC_TUND )); -} - -/* - * Shutdown hardware and clean out the rings - */ -void -BSP_tsec_stop_hw(struct tsec_private *mp) -{ -unsigned i; - /* stop and reset hardware */ - tsec_reset_hw( mp ); - - if ( mp->tx_ring_size ) { - /* should be OK to clear all ownership flags */ - for ( i=0; itx_ring_size; i++ ) { - bd_clrfl( &mp->tx_ring[i], TSEC_TXBD_R ); - } - BSP_tsec_swipe_tx(mp); -#if DEBUG > 0 - tsec_dump_tring(mp); - fflush(stderr); fflush(stdout); -#endif -#ifdef PARANOIA - assert( mp->tx_avail == mp->tx_ring_size ); - assert( mp->tx_head == mp->tx_tail ); - for ( i=0; itx_ring_size; i++ ) { - assert( !bd_rdbuf( & mp->tx_ring[i] ) ); - assert( !mp->tx_ring_user[i] ); - } -#endif - } - - if ( mp->rx_ring_size ) { - for ( i=0; irx_ring_size; i++ ) { - bd_clrfl( &mp->rx_ring[i], TSEC_RXBD_E ); - bd_wrbuf( &mp->rx_ring[i], 0 ); - if ( mp->rx_ring_user[i] ) - mp->consume_rxbuf( mp->rx_ring_user[i], mp->consume_rxbuf_arg, 0 ); - mp->rx_ring_user[i] = 0; - } - } -} - -/* - * calls BSP_tsec_stop_hw(), releases all resources and marks the interface - * as unused. - * RETURNS 0 on success, nonzero on failure. - * NOTE: the handle MUST NOT be used after successful execution of this - * routine. - */ -int -BSP_tsec_detach(struct tsec_private *mp) -{ - - if ( ! mp || !mp->sc || ! mp->sc->arpcom.ac_if.if_init ) { - fprintf(stderr,"Unit not setup -- programming error!\n"); - return -1; - } - - BSP_tsec_stop_hw(mp); - - install_remove_isrs( 0, mp, mp->irq_mask ); - - free( (void*)mp->ring_area, M_DEVBUF ); - free( (void*)mp->tx_ring_user, M_DEVBUF ); - memset(mp, 0, sizeof(*mp)); - __asm__ __volatile__("":::"memory"); - - /* mark as unused */ - mp->sc->arpcom.ac_if.if_init = 0; - - return 0; -} - -/* - * Enqueue a mbuf chain or a raw data buffer for transmission; - * RETURN: #bytes sent or -1 if there are not enough free descriptors - * - * If 'len' is <=0 then 'm_head' is assumed to point to a mbuf chain. - * OTOH, a raw data packet (or a different type of buffer) - * may be sent (non-BSD driver) by pointing data_p to the start of - * the data and passing 'len' > 0. - * 'm_head' is passed back to the 'cleanup_txbuf()' callback. - * - * Comments: software cache-flushing incurs a penalty if the - * packet cannot be queued since it is flushed anyways. - * The algorithm is slightly more efficient in the normal - * case, though. - * - * RETURNS: # bytes enqueued to device for transmission or -1 if no - * space in the TX ring was available. - */ - -#if 0 -#define NEXT_TXD(mp, bd) ((bd_rdfl( bd ) & TSEC_TXBD_W) ? mp->tx_ring : (bd + 1)) -#endif - -/* - * allocate a new cluster and copy an existing chain there; - * old chain is released... - */ -static struct mbuf * -repackage_chain(struct mbuf *m_head) -{ -struct mbuf *m; - MGETHDR(m, M_DONTWAIT, MT_DATA); - - if ( !m ) { - goto bail; - } - - MCLGET(m, M_DONTWAIT); - - if ( !(M_EXT & m->m_flags) ) { - m_freem(m); - m = 0; - goto bail; - } - - m_copydata(m_head, 0, MCLBYTES, mtod(m, caddr_t)); - m->m_pkthdr.len = m->m_len = m_head->m_pkthdr.len; - -bail: - m_freem(m_head); - return m; -} - -static inline unsigned -tsec_assign_desc( TSEC_BD *bd, uint32_t buf, unsigned len, uint32_t flags) -{ - st_be16 ( &bd->len, (uint16_t)len ); - bd_wrbuf( bd, buf ); - bd_cslfl( bd, flags, TSEC_TXBD_R | TSEC_TXBD_L ); - return len; -} - - -int -BSP_tsec_send_buf(struct tsec_private *mp, void *m_head, void *data_p, int len) -{ -int rval; -register TSEC_BD *bd; -register unsigned l,d,t; -register struct mbuf *m1; -int nmbs; -int ismbuf = (len <= 0); - -#if DEBUG > 2 - printf("send entering...\n"); - tsec_dump_tring(mp); -#endif -/* Only way to get here is when we discover that the mbuf chain - * is too long for the tx ring - */ -startover: - - rval = 0; - -#ifdef PARANOIA - assert(m_head); -#endif - - /* if no descriptor is available; try to wipe the queue */ - if ( (mp->tx_avail < 1) && TSEC_CLEAN_ON_SEND(mp)<=0 ) { - return -1; - } - - t = mp->tx_tail; - -#ifdef PARANOIA - assert( ! bd_rdbuf( &mp->tx_ring[t] ) ); - assert( ! mp->tx_ring_user[t] ); -#endif - - if ( ! (m1 = m_head) ) - return 0; - - if ( ismbuf ) { - /* find first mbuf with actual data */ - while ( 0 == m1->m_len ) { - if ( ! (m1 = m1->m_next) ) { - /* end reached and still no data to send ?? */ - m_freem(m_head); - return 0; - } - } - } - - /* Don't use the first descriptor yet because BSP_tsec_swipe_tx() - * needs bd->buf == NULL as a marker. Hence, we - * start with the second mbuf and fill the first descriptor - * last. - */ - - l = t; - d = NEXT_TXI(mp,t); - - mp->tx_avail--; - - nmbs = 1; - if ( ismbuf ) { - register struct mbuf *m; - for ( m=m1->m_next; m; m=m->m_next ) { - if ( 0 == m->m_len ) - continue; /* skip empty mbufs */ - - nmbs++; - - if ( mp->tx_avail < 1 && TSEC_CLEAN_ON_SEND(mp)<=0 ) { - /* not enough descriptors; cleanup... - * the first slot was never used, so we start - * at mp->d_tx_h->next; - */ - for ( l = NEXT_TXI(mp, t); l!=d; l=NEXT_TXI(mp, l) ) { - bd = & mp->tx_ring[l]; -#ifdef PARANOIA - assert( mp->tx_ring_user[l] == 0 ); -#endif - bd_wrbuf( bd, 0 ); - bd_clrfl( bd, TSEC_TXBD_R | TSEC_TXBD_L ); - - mp->tx_avail++; - } - mp->tx_avail++; - if ( nmbs > TX_AVAILABLE_RING_SIZE(mp) ) { - /* this chain will never fit into the ring */ - if ( nmbs > mp->stats.maxchain ) - mp->stats.maxchain = nmbs; - mp->stats.repack++; - if ( ! (m_head = repackage_chain(m_head)) ) { - /* no cluster available */ - mp->stats.odrops++; -#ifdef PARANOIA - printf("send return 0\n"); - tsec_dump_tring(mp); -#endif - return 0; - } -#ifdef PARANOIA - printf("repackaged; start over\n"); -#endif - goto startover; - } -#ifdef PARANOIA - printf("send return -1\n"); - tsec_dump_tring(mp); -#endif - return -1; - } - - mp->tx_avail--; - -#ifdef PARANOIA - assert( ! mp->tx_ring_user[d] ); - if ( d == t ) { - tsec_dump_tring(mp); - printf("l %i, d %i, t %i, nmbs %i\n", l,d,t, nmbs); - } else - assert( d != t ); - assert( ! bd_rdbuf( &mp->tx_ring[d] ) ); -#endif - - /* fill this slot */ - rval += tsec_assign_desc( &mp->tx_ring[d], mtod(m, uint32_t), m->m_len, TSEC_TXBD_R); - - FLUSH_BUF(mtod(m, uint32_t), m->m_len); - - l = d; - d = NEXT_TXI(mp, d); - } - - /* fill first slot - don't release to DMA yet */ - rval += tsec_assign_desc( &mp->tx_ring[t], mtod(m1, uint32_t), m1->m_len, 0); - - - FLUSH_BUF(mtod(m1, uint32_t), m1->m_len); - - } else { - /* fill first slot with raw buffer - don't release to DMA yet */ - rval += tsec_assign_desc( &mp->tx_ring[t], (uint32_t)data_p, len, 0); - - FLUSH_BUF( (uint32_t)data_p, len); - } - - /* tag last slot; this covers the case where 1st==last */ - bd_setfl( &mp->tx_ring[l], TSEC_TXBD_L ); - - /* mbuf goes into last desc */ - mp->tx_ring_user[l] = m_head; - - membarrier(); - - /* turn over the whole chain by flipping ownership of the first desc */ - bd_setfl( &mp->tx_ring[t], TSEC_TXBD_R ); - - membarrier(); - -#if DEBUG > 2 - printf("send return (l=%i, t=%i, d=%i) %i\n", l, t, d, rval); - tsec_dump_tring(mp); -#endif - - /* notify the device */ - fec_wr( mp->base, TSEC_TSTAT, TSEC_TSTAT_THLT ); - - /* Update softc */ - mp->stats.packet++; - if ( nmbs > mp->stats.maxchain ) - mp->stats.maxchain = nmbs; - - /* remember new tail */ - mp->tx_tail = d; - - return rval; /* #bytes sent */ -} - -/* - * Retrieve all received buffers from the RX ring, replacing them - * by fresh ones (obtained from the alloc_rxbuf() callback). The - * received buffers are passed to consume_rxbuf(). - * - * RETURNS: number of buffers processed. - */ -int -BSP_tsec_swipe_rx(struct tsec_private *mp) -{ -int rval = 0, err; -unsigned i; -uint16_t flags; -TSEC_BD *bd; -void *newbuf; -int sz; -uint16_t len; -uintptr_t baddr; - - i = mp->rx_tail; - bd = mp->rx_ring + i; - flags = bd_rdfl( bd ); - - while ( ! (TSEC_RXBD_E & flags) ) { - - /* err is not valid if not qualified by TSEC_RXBD_L */ - if ( ( err = (TSEC_RXBD_ERROR & flags) ) ) { - /* make sure error is < 0 */ - err |= 0xffff0000; - /* pass 'L' flag out so they can verify... */ - err |= (flags & TSEC_RXBD_L); - } - -#ifdef PARANOIA - assert( flags & TSEC_RXBD_L ); - assert( mp->rx_ring_user[i] ); -#endif - - if ( err || !(newbuf = mp->alloc_rxbuf(&sz, &baddr)) ) { - /* drop packet and recycle buffer */ - newbuf = mp->rx_ring_user[i]; - mp->consume_rxbuf( 0, mp->consume_rxbuf_arg, err ); - } else { - len = ld_be16( &bd->len ); - -#ifdef PARANOIA - assert( 0 == (baddr & (RX_BUF_ALIGNMENT-1)) ); - assert( len > 0 ); -#endif - - mp->consume_rxbuf( mp->rx_ring_user[i], mp->consume_rxbuf_arg, len ); - - mp->rx_ring_user[i] = newbuf; - st_be16( &bd->len, sz ); - bd_wrbuf( bd, baddr ); - } - - RTEMS_COMPILER_MEMORY_BARRIER(); - - bd_wrfl( &mp->rx_ring[i], (flags & ~TSEC_RXBD_ERROR) | TSEC_RXBD_E ); - - rval++; - - i = NEXT_RXI( mp, i ); - bd = mp->rx_ring + i; - flags = bd_rdfl( bd ); - } - - fec_wr( mp->base, TSEC_RSTAT, TSEC_RSTAT_QHLT ); - - mp->rx_tail = i; - return rval; -} - -/* read ethernet address from hw to buffer */ -void -BSP_tsec_read_eaddr(struct tsec_private *mp, unsigned char *eaddr) -{ -union { - uint32_t u; - uint16_t s[2]; - uint8_t c[4]; -} x; - st_le32( (volatile uint32_t *)(eaddr+2), fec_rd(mp->base, TSEC_MACSTNADDR1) ); - x.u = fec_rd(mp->base, TSEC_MACSTNADDR2); - st_le16( (volatile uint16_t *)(eaddr), x.s[0]); -} - -/* mdio / mii interface wrappers for rtems_mii_ioctl API */ - -/* - * Busy-wait -- this can take a while: I measured ~550 timebase-ticks - * @333333333Hz, TB divisor is 8 -> 13us - */ -static inline void mii_wait(FEC_Enet_Base b) -{ - while ( (TSEC_MIIMIND_BUSY & fec_rd( b, TSEC_MIIMIND )) ) - ; -} - -/* MII operations are rather slow :-( */ -static int -tsec_mdio_rd(int phyidx, void *uarg, unsigned reg, uint32_t *pval) -{ -uint32_t v; -#ifdef TEST_MII_TIMING -uint32_t t; -#endif -struct tsec_private *mp = uarg; -FEC_Enet_Base b = mp->phy_base; - - if ( phyidx != 0 ) - return -1; /* only one phy supported for now */ - - /* write phy and register address */ - fec_wr( b, TSEC_MIIMADD, TSEC_MIIMADD_ADDR(mp->phy,reg) ); - - /* clear READ bit */ - v = fec_rd( b, TSEC_MIIMCOM ); - fec_wr( b, TSEC_MIIMCOM, v & ~TSEC_MIIMCOM_READ ); - -#ifdef TEST_MII_TIMING - t = tb_rd(); -#endif - - /* trigger READ operation by READ-bit 0-->1 transition */ - fec_wr( b, TSEC_MIIMCOM, v | TSEC_MIIMCOM_READ ); - - /* (busy) wait for completion */ - - mii_wait( b ); - - /* Ugly workaround: I observed that the link status - * is not correctly reported when the link changes to - * a good status - a failed link is reported until - * we read twice :-( - */ - if ( MII_BMSR == reg ) { - /* trigger a second read operation */ - fec_wr( b, TSEC_MIIMCOM, v & ~TSEC_MIIMCOM_READ ); - fec_wr( b, TSEC_MIIMCOM, v | TSEC_MIIMCOM_READ ); - mii_wait( b ); - } - -#ifdef TEST_MII_TIMING - t = tb_rd() - t; - printf("Reading MII took %"PRIi32"\n", t); -#endif - /* return result */ - *pval = fec_rd( b, TSEC_MIIMSTAT ) & 0xffff; - return 0; -} - -STATIC int -tsec_mdio_wr(int phyidx, void *uarg, unsigned reg, uint32_t val) -{ -#ifdef TEST_MII_TIMING -uint32_t t; -#endif -struct tsec_private *mp = uarg; -FEC_Enet_Base b = mp->phy_base; - - if ( phyidx != 0 ) - return -1; /* only one phy supported for now */ - -#ifdef TEST_MII_TIMING - t = tb_rd(); -#endif - - fec_wr( b, TSEC_MIIMADD, TSEC_MIIMADD_ADDR(mp->phy,reg) ); - fec_wr( b, TSEC_MIIMCON, val & 0xffff ); - - mii_wait( b ); - -#ifdef TEST_MII_TIMING - t = tb_rd() - t; - printf("Writing MII took %"PRIi32"\n", t); -#endif - - return 0; -} - -/* Public, locked versions */ -uint32_t -BSP_tsec_mdio_rd(struct tsec_private *mp, unsigned reg) -{ -uint32_t val, rval; - - REGLOCK(); - rval = tsec_mdio_rd(0, mp, reg, &val ) ? -1 : val; - REGUNLOCK(); - - return rval; -} - -int -BSP_tsec_mdio_wr(struct tsec_private *mp, unsigned reg, uint32_t val) -{ -int rval; - - REGLOCK(); - rval = tsec_mdio_wr(0, mp, reg, val); - REGUNLOCK(); - - return rval; -} - -static struct rtems_mdio_info tsec_mdio = { - mdio_r: tsec_mdio_rd, - mdio_w: tsec_mdio_wr, - has_gmii: 1, -}; - - -/* - * read/write media word. - * 'cmd': can be SIOCGIFMEDIA, SIOCSIFMEDIA, 0 or 1. The latter - * are aliased to the former for convenience. - * 'parg': pointer to media word. - * - * RETURNS: 0 on success, nonzero on error - */ -int -BSP_tsec_media_ioctl(struct tsec_private *mp, int cmd, int *parg) -{ -int rval; - /* alias cmd == 0,1 for convenience when calling from shell */ - switch ( cmd ) { - case 0: cmd = SIOCGIFMEDIA; - break; - case 1: cmd = SIOCSIFMEDIA; - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - break; - default: return -1; - } - REGLOCK(); - tsec_mdio.has_gmii = mp->isfec ? 0 : 1; - rval = rtems_mii_ioctl(&tsec_mdio, mp, cmd, parg); - REGUNLOCK(); - return rval; -} - -/* Interrupt related routines */ - -/* - * When it comes to interrupts the chip has two rather - * annoying features: - * 1 once an IRQ is pending, clearing the IMASK does not - * de-assert the interrupt line. - * 2 the chip has three physical interrupt lines even though - * all events are reported in a single register. Rather - * useless; we must hook 3 ISRs w/o any real benefit. - * In fact, it makes our life a bit more difficult: - * - * Hence, for (1) we would have to mask interrupts at the PIC - * but to re-enable them we would have to do that three times - * because of (2). - * - * Therefore, we take the following approach: - * - * ISR masks interrupts on the TSEC, acks/clears them - * and stores the acked irqs in the device struct where - * it is picked up by BSP_tsec_ack_irq_mask(). - * - */ - -static inline uint32_t -tsec_dis_irqs(struct tsec_private *mp, uint32_t mask) -{ -uint32_t rval; - - rval = mp->irq_mask_cache; - if ( (TSEC_LINK_INTR & mask & mp->irq_mask_cache) ) - phy_dis_irq( mp ); - mp->irq_mask_cache = rval & ~mask; - fec_wr( mp->base, TSEC_IMASK, (mp->irq_mask_cache & ~TSEC_LINK_INTR) ); - - return rval; -} - -static inline uint32_t -tsec_dis_clr_irqs(struct tsec_private *mp) -{ -uint32_t rval; -FEC_Enet_Base b = mp->base; - - rval = fec_rd( b, TSEC_IEVENT); - - /* Make sure we mask out the link intr */ - rval &= ~TSEC_LINK_INTR; - - tsec_dis_irqs( mp, rval ); - fec_wr( b, TSEC_IEVENT, rval ); - - return rval; -} - -/* - * We have 3 different ISRs just so we can count - * interrupt types independently... - */ - -static void tsec_xisr(rtems_irq_hdl_param arg) -{ -struct tsec_private *mp = (struct tsec_private *)arg; -rtems_interrupt_level l; - - rtems_interrupt_disable( l ); - mp->irq_pending |= tsec_dis_clr_irqs( mp ); - rtems_interrupt_enable( l ); - - mp->stats.xirqs++; - - if ( mp->isr ) - mp->isr( mp->isr_arg ); - else - rtems_bsdnet_event_send( mp->tid, mp->event ); -} - -static void tsec_risr(rtems_irq_hdl_param arg) -{ -struct tsec_private *mp = (struct tsec_private *)arg; -rtems_interrupt_level l; - - rtems_interrupt_disable( l ); - mp->irq_pending |= tsec_dis_clr_irqs( mp ); - rtems_interrupt_enable( l ); - - mp->stats.rirqs++; - - if ( mp->isr ) - mp->isr( mp->isr_arg ); - else - rtems_bsdnet_event_send( mp->tid, mp->event ); -} - -static void tsec_eisr(rtems_irq_hdl_param arg) -{ -struct tsec_private *mp = (struct tsec_private *)arg; -rtems_interrupt_level l; -uint32_t pending; - - rtems_interrupt_disable( l ); - /* make local copy since ISR may ack and clear mp->pending; - * also, we want the fresh bits not the ORed state including - * the past... - */ - mp->irq_pending |= (pending = tsec_dis_clr_irqs( mp )); - rtems_interrupt_enable( l ); - - mp->stats.eirqs++; - - if ( mp->isr ) - mp->isr( mp->isr_arg ); - else - rtems_bsdnet_event_send( mp->tid, mp->event ); - - if ( (TSEC_IEVENT_TXE & pending) ) { - if ( (TSEC_IEVENT_EBERR & pending) && ++mp->stats.eberrs > MAXEBERRS ) { - printk(DRVNAME" BAD error: max # of DMA bus errors reached\n"); - } else { - /* Restart DMA -- do we have to clear DMACTRL[GTS], too ?? */ - fec_wr( mp->base, TSEC_TSTAT, TSEC_TSTAT_THLT ); - mp->stats.dmarst++; - } - } -} - -static void tsec_lisr(rtems_irq_hdl_param arg) -{ -struct tsec_private *mp = (struct tsec_private *)arg; -rtems_interrupt_level l; - - if ( phy_irq_pending( mp ) ) { - - rtems_interrupt_disable( l ); - tsec_dis_irqs( mp, TSEC_LINK_INTR ); - mp->irq_pending |= TSEC_LINK_INTR; - rtems_interrupt_enable( l ); - - mp->stats.lirqs++; - - if ( mp->isr ) - mp->isr( mp->isr_arg ); - else - rtems_bsdnet_event_send( mp->tid, mp->event ); - } -} - -/* Enable interrupts at device */ -void -BSP_tsec_enable_irq_mask(struct tsec_private *mp, uint32_t mask) -{ -rtems_interrupt_level l; - - mask &= mp->irq_mask; - - rtems_interrupt_disable( l ); - if ( (TSEC_LINK_INTR & mask) && ! (TSEC_LINK_INTR & mp->irq_mask_cache) ) - phy_en_irq( mp ); - mp->irq_mask_cache |= mask; - fec_wr( mp->base, TSEC_IMASK, (mp->irq_mask_cache & ~TSEC_LINK_INTR) ); - rtems_interrupt_enable( l ); -} - -void -BSP_tsec_enable_irqs(struct tsec_private *mp) -{ - BSP_tsec_enable_irq_mask(mp, -1); -} - -/* Disable interrupts at device */ -uint32_t -BSP_tsec_disable_irq_mask(struct tsec_private *mp, uint32_t mask) -{ -uint32_t rval; -rtems_interrupt_level l; - - rtems_interrupt_disable( l ); - rval = tsec_dis_irqs(mp, mask); - rtems_interrupt_enable( l ); - - return rval; -} - -void -BSP_tsec_disable_irqs(struct tsec_private *mp) -{ -rtems_interrupt_level l; - - rtems_interrupt_disable( l ); - tsec_dis_irqs(mp, -1); - rtems_interrupt_enable( l ); -} - -/* - * Acknowledge (and clear) interrupts. - * RETURNS: interrupts that were raised. - */ -uint32_t -BSP_tsec_ack_irq_mask(struct tsec_private *mp, uint32_t mask) -{ -uint32_t rval; -rtems_interrupt_level l; - - rtems_interrupt_disable( l ); - rval = mp->irq_pending; - mp->irq_pending &= ~ mask; - rtems_interrupt_enable( l ); - - if ( (rval & TSEC_LINK_INTR & mask) ) { - /* interacting with the PHY is slow so - * we do it only if we have to... - */ - phy_ack_irq( mp ); - } - - return rval & mp->irq_mask; -} - -uint32_t -BSP_tsec_ack_irqs(struct tsec_private *mp) -{ - return BSP_tsec_ack_irq_mask(mp, -1); -} - -/* Retrieve the driver daemon TID that was passed to - * BSP_tsec_setup(). - */ - -rtems_id -BSP_tsec_get_tid(struct tsec_private *mp) -{ - return mp->tid; -} - -struct tsec_private * -BSP_tsec_getp(unsigned index) -{ - if ( index >= TSEC_NUM_DRIVER_SLOTS ) - return 0; - return & theTsecEths[index].pvt; -} - -/* - * - * Example driver task loop (note: no synchronization of - * buffer access shown!). - * RTEMS_EVENTx = 0,1 or 2 depending on IF unit. - * - * / * setup (obtain handle) and initialize hw here * / - * - * do { - * / * ISR disables IRQs and posts event * / - * rtems_event_receive( RTEMS_EVENTx, RTEMS_WAIT | RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, &evs ); - * irqs = BSP_tsec_ack_irqs(handle); - * if ( irqs & BSP_TSEC_IRQ_TX ) { - * BSP_tsec_swipe_tx(handle); / * cleanup_txbuf() callback executed * / - * } - * if ( irqs & BSP_TSEC_IRQ_RX ) { - * BSP_tsec_swipe_rx(handle); / * alloc_rxbuf() and consume_rxbuf() executed * / - * } - * BSP_tsec_enable_irq_mask(handle, -1); - * } while (1); - * - */ - -/* BSDNET SUPPORT/GLUE ROUTINES */ - -STATIC void -tsec_stop(struct tsec_softc *sc) -{ - BSP_tsec_stop_hw(&sc->pvt); - sc->arpcom.ac_if.if_timer = 0; -} - -/* allocate a mbuf for RX with a properly aligned data buffer - * RETURNS 0 if allocation fails - */ -static void * -alloc_mbuf_rx(int *psz, uintptr_t *paddr) -{ -struct mbuf *m; -unsigned long l,o; - - MGETHDR(m, M_DONTWAIT, MT_DATA); - if ( !m ) - return 0; - MCLGET(m, M_DONTWAIT); - if ( ! (m->m_flags & M_EXT) ) { - m_freem(m); - return 0; - } - - o = mtod(m, unsigned long); - l = ALIGNTO(o, RX_BUF_ALIGNMENT) - o; - - /* align start of buffer */ - m->m_data += l; - - /* reduced length */ - l = MCLBYTES - l; - - m->m_len = m->m_pkthdr.len = l; - *psz = m->m_len; - *paddr = mtod(m, unsigned long); - - return (void*) m; -} - -static void consume_rx_mbuf(void *buf, void *arg, int len) -{ -struct ifnet *ifp = arg; -struct mbuf *m = buf; - - if ( len <= 0 ) { - ifp->if_iqdrops++; - if ( len < 0 ) { - ifp->if_ierrors++; - } - if ( m ) - m_freem(m); - } else { - struct ether_header *eh; - - eh = (struct ether_header *)(mtod(m, unsigned long) + ETH_RX_OFFSET); - m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header) - ETH_RX_OFFSET - ETH_CRC_LEN; - m->m_data += sizeof(struct ether_header) + ETH_RX_OFFSET; - m->m_pkthdr.rcvif = ifp; - - ifp->if_ipackets++; - ifp->if_ibytes += m->m_pkthdr.len; - - /* send buffer upwards */ - if (0) { - /* Low-level debugging */ - int i; - for (i=0; i<13; i++) { - printf("%02X:",((char*)eh)[i]); - } - printf("%02X\n",((char*)eh)[i]); - for (i=0; im_len; i++) { - if ( !(i&15) ) - printf("\n"); - printf("0x%02x ",mtod(m,char*)[i]); - } - printf("\n"); - } - - if (0) /* Low-level debugging/testing without bsd stack */ - m_freem(m); - else - ether_input(ifp, eh, m); - } -} - -static void release_tx_mbuf(void *buf, void *arg, int err) -{ -struct ifnet *ifp = arg; -struct mbuf *mb = buf; - - if ( err ) { - ifp->if_oerrors++; - } else { - ifp->if_opackets++; - } - ifp->if_obytes += mb->m_pkthdr.len; - m_freem(mb); -} - -/* BSDNET DRIVER CALLBACKS */ - -static void -tsec_init(void *arg) -{ -struct tsec_softc *sc = arg; -struct ifnet *ifp = &sc->arpcom.ac_if; -int media; - - BSP_tsec_init_hw(&sc->pvt, ifp->if_flags & IFF_PROMISC, sc->arpcom.ac_enaddr); - - /* Determine initial link status and block sender if there is no link */ - media = IFM_MAKEWORD(0, 0, 0, 0); - if ( 0 == BSP_tsec_media_ioctl(&sc->pvt, SIOCGIFMEDIA, &media) ) { - if ( (IFM_LINK_OK & media) ) { - ifp->if_flags &= ~IFF_OACTIVE; - } else { - ifp->if_flags |= IFF_OACTIVE; - } - } - - tsec_update_mcast(ifp); - ifp->if_flags |= IFF_RUNNING; - sc->arpcom.ac_if.if_timer = 0; -} - -/* bsdnet driver entry to start transmission */ -static void -tsec_start(struct ifnet *ifp) -{ -struct tsec_softc *sc = ifp->if_softc; -struct mbuf *m = 0; - - while ( ifp->if_snd.ifq_head ) { - IF_DEQUEUE( &ifp->if_snd, m ); - if ( BSP_tsec_send_buf(&sc->pvt, m, 0, 0) < 0 ) { - IF_PREPEND( &ifp->if_snd, m); - ifp->if_flags |= IFF_OACTIVE; - break; - } - /* need to do this really only once - * but it's cheaper this way. - */ - ifp->if_timer = 2*IFNET_SLOWHZ; - } -} - -/* bsdnet driver entry; */ -static void -tsec_watchdog(struct ifnet *ifp) -{ -struct tsec_softc *sc = ifp->if_softc; - - ifp->if_oerrors++; - printk(DRVNAME"%i: watchdog timeout; resetting\n", ifp->if_unit); - - tsec_init(sc); - tsec_start(ifp); -} - -static void -tsec_update_mcast(struct ifnet *ifp) -{ -struct tsec_softc *sc = ifp->if_softc; -struct ether_multi *enm; -struct ether_multistep step; - - if ( IFF_ALLMULTI & ifp->if_flags ) { - BSP_tsec_mcast_filter_accept_all( &sc->pvt ); - } else { - BSP_tsec_mcast_filter_clear( &sc->pvt ); - - ETHER_FIRST_MULTI(step, (struct arpcom *)ifp, enm); - - while ( enm ) { - if ( memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN) ) - assert( !"Should never get here; IFF_ALLMULTI should be set!" ); - - BSP_tsec_mcast_filter_accept_add(&sc->pvt, enm->enm_addrlo); - - ETHER_NEXT_MULTI(step, enm); - } - } -} - -/* bsdnet driver ioctl entry */ -static int -tsec_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data) -{ -struct tsec_softc *sc = ifp->if_softc; -struct ifreq *ifr = (struct ifreq *)data; -#if 0 -uint32_t v; -#endif -int error = 0; -int f; - - switch ( cmd ) { - case SIOCSIFFLAGS: - f = ifp->if_flags; - if ( f & IFF_UP ) { - if ( ! ( f & IFF_RUNNING ) ) { - tsec_init(sc); - } else { - if ( (f & IFF_PROMISC) != (sc->bsd.oif_flags & IFF_PROMISC) ) { - /* Hmm - the manual says we must change the RCTRL - * register only after a reset or if DMACTRL[GRS] - * is cleared which is the normal operating - * condition. I hope this is legal ?? - */ - if ( (f & IFF_PROMISC) ) { - fec_set( sc->pvt.base, TSEC_RCTRL, TSEC_RCTRL_PROM ); - } else { - fec_clr( sc->pvt.base, TSEC_RCTRL, TSEC_RCTRL_PROM ); - } - } - /* FIXME: other flag changes are ignored/unimplemented */ - } - } else { - if ( f & IFF_RUNNING ) { - tsec_stop(sc); - ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); - } - } - sc->bsd.oif_flags = ifp->if_flags; - break; - - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - error = BSP_tsec_media_ioctl(&sc->pvt, cmd, &ifr->ifr_media); - break; - - case SIOCADDMULTI: - case SIOCDELMULTI: - error = (cmd == SIOCADDMULTI) - ? ether_addmulti(ifr, &sc->arpcom) - : ether_delmulti(ifr, &sc->arpcom); - - if (error == ENETRESET) { - if (ifp->if_flags & IFF_RUNNING) { - tsec_update_mcast(ifp); - } - error = 0; - } - break; - - case SIO_RTEMS_SHOW_STATS: - BSP_tsec_dump_stats( &sc->pvt, stdout ); - break; - - default: - error = ether_ioctl(ifp, cmd, data); - break; - } - - return error; -} - -/* DRIVER TASK */ - -/* Daemon task does all the 'interrupt' work */ -static void tsec_daemon(void *arg) -{ -struct tsec_softc *sc; -struct ifnet *ifp; -rtems_event_set evs; - for (;;) { - rtems_bsdnet_event_receive( EV_MSK, RTEMS_WAIT | RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, &evs ); - evs &= EV_MSK; - for ( sc = theTsecEths; evs; evs>>=EV_PER_UNIT, sc++ ) { - if ( EV_IS_ANY(evs) ) { - - register uint32_t x; - - ifp = &sc->arpcom.ac_if; - - if ( !(ifp->if_flags & IFF_UP) ) { - tsec_stop(sc); - ifp->if_flags &= ~(IFF_UP|IFF_RUNNING); - continue; - } - - if ( !(ifp->if_flags & IFF_RUNNING) ) { - /* event could have been pending at the time hw was stopped; - * just ignore... - */ - continue; - } - - x = BSP_tsec_ack_irqs(&sc->pvt); - - if ( TSEC_LINK_INTR & x ) { - /* phy status changed */ - int media; - -#ifdef DEBUG - printf("LINK INTR\n"); -#endif - if ( -1 != (media = mac_set_duplex( &sc->pvt )) ) { -#ifdef DEBUG - rtems_ifmedia2str( media, 0, 0 ); - printf("\n"); -#endif - if ( IFM_LINK_OK & media ) { - ifp->if_flags &= ~IFF_OACTIVE; - tsec_start(ifp); - } else { - /* stop sending */ - ifp->if_flags |= IFF_OACTIVE; - } - } - } - - /* free tx chain */ - if ( (TSEC_TXIRQ & x) && BSP_tsec_swipe_tx(&sc->pvt) ) { - ifp->if_flags &= ~IFF_OACTIVE; - if ( TX_AVAILABLE_RING_SIZE(&sc->pvt) == sc->pvt.tx_avail ) - ifp->if_timer = 0; - tsec_start(ifp); - } - if ( (TSEC_RXIRQ & x) ) - BSP_tsec_swipe_rx(&sc->pvt); - - BSP_tsec_enable_irq_mask(&sc->pvt, -1); - } - } - } -} - -/* PUBLIC RTEMS BSDNET ATTACH FUNCTION */ -int -rtems_tsec_attach(struct rtems_bsdnet_ifconfig *ifcfg, int attaching) -{ -char *unitName; -int unit,i,cfgUnits; -struct tsec_softc *sc; -struct ifnet *ifp; - - unit = rtems_bsdnet_parse_driver_name(ifcfg, &unitName); - if ( unit <= 0 || unit > TSEC_NUM_DRIVER_SLOTS ) { - printk(DRVNAME": Bad unit number %i; must be 1..%i\n", unit, TSEC_NUM_DRIVER_SLOTS); - return 1; - } - - sc = &theTsecEths[unit-1]; - ifp = &sc->arpcom.ac_if; - - if ( attaching ) { - if ( ifp->if_init ) { - printk(DRVNAME": instance %i already attached.\n", unit); - return -1; - } - - for ( i=cfgUnits = 0; irbuf_count, - ifcfg->xbuf_count, - TSEC_RXIRQ | TSEC_TXIRQ | TSEC_LINK_INTR) ) { - return -1; - } - - if ( nmbclusters < sc->pvt.rx_ring_size * cfgUnits + 60 /* arbitrary */ ) { - printk(DRVNAME"%i: (tsec ethernet) Your application has not enough mbuf clusters\n", unit); - printk( " configured for this driver.\n"); - return -1; - } - - if ( ifcfg->hardware_address ) { - memcpy(sc->arpcom.ac_enaddr, ifcfg->hardware_address, ETHER_ADDR_LEN); - } else { - /* read back from hardware assuming that MotLoad already had set it up */ - BSP_tsec_read_eaddr(&sc->pvt, sc->arpcom.ac_enaddr); - } - - ifp->if_softc = sc; - ifp->if_unit = unit; - ifp->if_name = unitName; - - ifp->if_mtu = ifcfg->mtu ? ifcfg->mtu : ETHERMTU; - - ifp->if_init = tsec_init; - ifp->if_ioctl = tsec_ioctl; - ifp->if_start = tsec_start; - ifp->if_output = ether_output; - /* - * While nonzero, the 'if->if_timer' is decremented - * (by the networking code) at a rate of IFNET_SLOWHZ (1hz) and 'if_watchdog' - * is called when it expires. - * If either of those fields is 0 the feature is disabled. - */ - ifp->if_watchdog = tsec_watchdog; - ifp->if_timer = 0; - - sc->bsd.oif_flags = /* ... */ - ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX; - - /* - * if unset, this set to 10Mbps by ether_ifattach; seems to be unused by bsdnet stack; - * could be updated along with phy speed, though... - ifp->if_baudrate = 10000000; - */ - - /* NOTE: ether_output drops packets if ifq_len >= ifq_maxlen - * but this is the packet count, not the fragment count! - ifp->if_snd.ifq_maxlen = sc->pvt.tx_ring_size; - */ - ifp->if_snd.ifq_maxlen = ifqmaxlen; - -#ifdef TSEC_DETACH_HACK - if ( !ifp->if_addrlist ) /* do only the first time [reattach hack] */ -#endif - { - if_attach(ifp); - ether_ifattach(ifp); - } - - } else { -#ifdef TSEC_DETACH_HACK - if ( !ifp->if_init ) { - printk(DRVNAME": instance %i not attached.\n", unit); - return -1; - } - return tsec_detach(sc); -#else - printk(DRVNAME": interface detaching not implemented\n"); - return -1; -#endif - } - - return 0; -} - -/* PHY stuff should really not be in this driver but separate :-( - * However, I don't have time right now to implement clean - * boundaries: - * - PHY driver should only know about the PHY - * - TSEC driver only provides MII access and knows - * how to deal with a PHY interrupt. - * - BSP knows how interrupts are routed. E.g., the MVME3100 - * shares a single IRQ line among 3 PHYs (for the three ports) - * and provides a special 'on-board' register for determining - * what PHY raised an interrupt w/o the need to do any MII IO. - * Integrating all these bits in a clean way is not as easy as - * just hacking away, sorry... - */ - -/* - * Broadcom 54xx PHY register definitions. Unfriendly Broadcom doesn't - * release specs for their products :-( -- valuable info comes from - * the linux driver by - * Maciej W. Rozycki - * Amy Fong - */ - -#define BCM54xx_GBCR 0x09 /* gigabit control */ -#define BCM54xx_GBCR_FD (1<<9) /* full-duplex cap. */ - -#define BCM54xx_ECR 0x10 /* extended control */ -#define BCM54xx_ECR_IM (1<<12) /* IRQ mask */ -#define BCM54xx_ECR_IF (1<<12) /* IRQ force */ - -#define BCM54xx_ESR 0x11 /* extended status */ -#define BCM54xx_ESR_IRQ (1<<12) /* IRQ pending */ - -#define BCM54xx_AUXCR 0x18 /* AUX control */ -#define BCM54xx_AUXCR_PWR10BASET (1<<2) - -#define BCM54xx_AUXST 0x19 /* AUX status */ -#define BCM54xx_AUXST_LNKMM (7<<8) /* link mode mask */ - -/* link mode (linux' syngem_phy.c helped here...) - * - * 0: no link - * 1: 10BT half - * 2: 10BT full - * 3: 100BT half - * 4: 100BT half - * 5: 100BT full - * 6: 1000BT full - * 7: 1000BT full - */ - -#define BCM54xx_ISR 0x1a /* IRQ status */ -#define BCM54xx_IMR 0x1b /* IRQ mask */ -#define BCM54xx_IRQ_CRC (1<< 0) /* CRC error */ -#define BCM54xx_IRQ_LNK (1<< 1) /* LINK status chg. */ -#define BCM54xx_IRQ_SPD (1<< 2) /* SPEED change */ -#define BCM54xx_IRQ_DUP (1<< 3) /* LINK status chg. */ -#define BCM54xx_IRQ_LRS (1<< 4) /* Lcl. RX status chg.*/ -#define BCM54xx_IRQ_RRS (1<< 5) /* Rem. RX status chg.*/ -#define BCM54xx_IRQ_SSE (1<< 6) /* Scrambler sync err */ -#define BCM54xx_IRQ_UHCD (1<< 7) /* Unsupp. HCD neg. */ -#define BCM54xx_IRQ_NHCD (1<< 8) /* No HCD */ -#define BCM54xx_IRQ_HCDL (1<< 9) /* No HCD Link */ -#define BCM54xx_IRQ_ANPR (1<<10) /* Aneg. pg. req. */ -#define BCM54xx_IRQ_LC (1<<11) /* All ctrs. < 128 */ -#define BCM54xx_IRQ_HC (1<<12) /* Ctr > 32768 */ -#define BCM54xx_IRQ_MDIX (1<<13) /* MDIX status chg. */ -#define BCM54xx_IRQ_PSERR (1<<14) /* Pair swap error */ - -#define PHY_IRQS ( BCM54xx_IRQ_LNK | BCM54xx_IRQ_SPD | BCM54xx_IRQ_DUP ) - - -static void -phy_en_irq_at_phy( struct tsec_private *mp ) -{ -uint32_t ecr; - - REGLOCK(); - tsec_mdio_rd( 0, mp, BCM54xx_ECR, &ecr ); - ecr &= ~BCM54xx_ECR_IM; - tsec_mdio_wr( 0, mp, BCM54xx_ECR, ecr ); - REGUNLOCK(); -} - -static void -phy_dis_irq_at_phy( struct tsec_private *mp ) -{ -uint32_t ecr; - - REGLOCK(); - tsec_mdio_rd( 0, mp, BCM54xx_ECR, &ecr ); - ecr |= BCM54xx_ECR_IM; - tsec_mdio_wr( 0, mp, BCM54xx_ECR, ecr ); - REGUNLOCK(); -} - -static void -phy_init_irq( int install, struct tsec_private *mp, void (*isr)(rtems_irq_hdl_param) ) -{ -uint32_t v; -rtems_irq_connect_data xxx; - - xxx.on = noop; - xxx.off = noop; - xxx.isOn = nopf; - xxx.name = BSP_PHY_IRQ; - xxx.handle = mp; - xxx.hdl = isr; - - phy_dis_irq_at_phy( mp ); - - REGLOCK(); - /* Select IRQs we want */ - tsec_mdio_wr( 0, mp, BCM54xx_IMR, ~ PHY_IRQS ); - /* clear pending irqs */ - tsec_mdio_rd( 0, mp, BCM54xx_ISR, &v ); - REGUNLOCK(); - - /* install shared ISR */ - if ( ! (install ? - BSP_install_rtems_shared_irq_handler( &xxx ) : - BSP_remove_rtems_irq_handler( &xxx )) ) { - rtems_panic( "Unable to %s shared irq handler (PHY)\n", install ? "install" : "remove" ); - } -} - -/* Because on the MVME3100 multiple PHYs (belonging to different - * TSEC instances) share a single interrupt line and we want - * to disable interrupts at the PIC rather than in the individual - * PHYs (because access to those is slow) we must implement - * nesting... - */ -STATIC int phy_irq_dis_level = 0; - -/* assume 'en_irq' / 'dis_irq' cannot be interrupted. - * Either because they are called from an ISR (all - * tsec + phy isrs must have the same priority) or - * from a IRQ-protected section of code - */ - -static void -phy_en_irq(struct tsec_private *mp) -{ - phy_irq_dis_level &= ~(1<unit); - if ( 0 == phy_irq_dis_level ) { - BSP_enable_irq_at_pic( BSP_PHY_IRQ ); - } -} - - -static void -phy_dis_irq(struct tsec_private *mp) -{ - phy_irq_dis_level |= (1<unit); - BSP_disable_irq_at_pic( BSP_PHY_IRQ ); -} - -static int -phy_irq_pending(struct tsec_private *mp) -{ - /* MVME3100 speciality: we can check for a pending - * PHY IRQ w/o having to access the MII bus :-) - */ - return in_8( BSP_MVME3100_IRQ_DETECT_REG ) & (1 << (mp->unit - 1)); -} - -static uint32_t -phy_ack_irq(struct tsec_private *mp) -{ -uint32_t v; - - REGLOCK(); - tsec_mdio_rd( 0, mp, BCM54xx_ISR, &v ); - REGUNLOCK(); - -#ifdef DEBUG - printf("phy_ack_irq: 0x%08"PRIx32"\n", v); -#endif - - return v; -} - -#if defined(PARANOIA) || defined(DEBUG) - -static void dumpbd(TSEC_BD *bd) -{ - printf("Flags 0x%04"PRIx16", len 0x%04"PRIx16", buf 0x%08"PRIx32"\n", - bd_rdfl( bd ), ld_be16( &bd->len ), bd_rdbuf( bd ) ); -} - -void tsec_dump_rring(struct tsec_private *mp) -{ -int i; -TSEC_BD *bd; - if ( !mp ) { - printf("Neet tsec_private * arg\n"); - return; - } - for ( i=0; irx_ring_size; i++ ) { - bd = &mp->rx_ring[i]; - printf("[%i]: ", i); - dumpbd(bd); - } -} - -void tsec_dump_tring(struct tsec_private *mp) -{ -int i; -TSEC_BD *bd; - if ( !mp ) { - printf("Neet tsec_private * arg\n"); - return; - } - for ( i=0; itx_ring_size; i++ ) { - bd = &mp->tx_ring[i]; - printf("[%i]: ", i); - dumpbd(bd); - } - printf("Avail: %i; Head %i; Tail %i\n", mp->tx_avail, mp->tx_head, mp->tx_tail); -} -#endif - - -#ifdef DEBUG - -#undef free -#undef malloc - -#include - -void cleanup_txbuf_test(void *u, void *a, int err) -{ - printf("cleanup_txbuf_test (releasing buf 0x%8p)\n", u); - free(u); - if ( err ) - printf("cleanup_txbuf_test: an error was detected\n"); -} - -void *alloc_rxbuf_test(int *p_size, uintptr_t *p_data_addr) -{ -void *rval; - - *p_size = 1600; - rval = malloc( *p_size + RX_BUF_ALIGNMENT ); - *p_data_addr = (uintptr_t)ALIGNTO(rval,RX_BUF_ALIGNMENT); - - /* PRIxPTR is still broken :-( */ - printf("alloc_rxxbuf_test: allocated %i bytes @0x%8p/0x%08"PRIx32"\n", - *p_size, rval, (uint32_t)*p_data_addr); - - return rval; -} - -void consume_rxbuf_test(void *user_buf, void *consume_rxbuf_arg, int len) -{ -int i; -uintptr_t d = (uintptr_t)ALIGNTO(user_buf,RX_BUF_ALIGNMENT); - - /* PRIxPTR is still broken */ - printf("consuming rx buf 0x%8p (data@ 0x%08"PRIx32")\n",user_buf, (uint32_t)d); - if ( len > 32 ) - len = 32; - if ( len < 0 ) - printf("consume_rxbuf_test: ERROR occurred: 0x%x\n", len); - else { - printf("RX:"); - for ( i=0; i