if_upd985xx.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,622 行 · 第 1/4 页
C
1,622 行
//==========================================================================
//
// if_upd985xx.c
//
// Ethernet drivers
// NEC UPD985XX device ethernet specific support
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//####BSDCOPYRIGHTBEGIN####
//
// -------------------------------------------
//
// Portions of this software may have been derived from OpenBSD or other sources,
// and are covered by the appropriate copyright disclaimers included herein.
//
// -------------------------------------------
//
//####BSDCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): hmt, gthomas
// Contributors:
// Date: 2001-06-28
// Purpose:
// Description: hardware driver for uPD985xx ethernet devices
//
//
//####DESCRIPTIONEND####
//
//==========================================================================
#include <pkgconf/system.h>
#include <pkgconf/devs_eth_mips_upd985xx.h>
#include <pkgconf/io_eth_drivers.h>
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/cyg_ass.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/hal_cache.h>
#include <cyg/infra/diag.h>
#include <cyg/hal/drv_api.h>
#include <cyg/io/eth/netdev.h>
#include <cyg/io/eth/eth_drv.h>
#ifdef CYGPKG_NET
#include <pkgconf/net.h>
#include <net/if.h> /* Needed for struct ifnet */
#else
#include <cyg/hal/hal_if.h>
#endif
#include <cyg/devs/eth/upd985xx_eth.h>
#include <cyg/io/eth/eth_drv_stats.h>
// ------------------------------------------------------------------------
#ifdef CYGDBG_DEVS_ETH_MIPS_UPD985XX_CHATTER
#define nDEBUG_TRAFFIC // This one prints stuff as packets come and go
#define nDEBUG_IOCTL // ioctl() call printing
#define DEBUG // Startup printing mainly
#endif
#define os_printf diag_printf
#define db_printf diag_printf
#define STATIC static
// ------------------------------------------------------------------------
// I/O access macros as inlines for later changes to >1 device?
//
// (If we need to do this, then these macros would *assume* the
// presence of a valid p_eth_upd985xx just like we always have)
static inline void OUTL( volatile cyg_uint32 *io_address, cyg_uint32 value )
{ *io_address = value; }
static inline cyg_uint32 INL( volatile cyg_uint32 *io_address )
{ return *io_address; }
// These map cachable addresses to uncachable ones and vice versa.
// This is all fixed on MIPS. 8-9xxxxxxx uncachable, A-Bxxxxxxx cachable.
#define VIRT_TO_BUS( _x_ ) virt_to_bus((cyg_uint32)(_x_))
static inline cyg_uint8 *virt_to_bus(cyg_uint32 p_memory)
{
return (cyg_uint8 *)(0xa0000000u + (p_memory & ~0xe0000000u));
}
#define BUS_TO_VIRT( _x_ ) bus_to_virt((cyg_uint32)(_x_))
static inline cyg_uint8 *bus_to_virt(cyg_uint32 p_memory)
{
return (cyg_uint8 *)(0x80000000u + (p_memory & ~0xe0000000u));
}
// ------------------------------------------------------------------------
#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_S1
#define FLUSH_WRITES() CYG_MACRO_START \
(void) INL( ETH_RXSR ); \
CYG_MACRO_END
#else
#define FLUSH_WRITES() CYG_EMPTY_STATEMENT
#endif
// ------------------------------------------------------------------------
//
// DEVICES AND PACKET QUEUES
//
// ------------------------------------------------------------------------
// 128 bytes extra for VLAN packets should be enough; AFAICT usually the
// encapsulation is only 4 or 10 bytes extra.
#define MAX_ETHERNET_PACKET_SIZE 1536 // Ethernet Rx packet size
#define MAX_OVERSIZE_PACKET_SIZE 1664 // VLAN Rx packet size
#define MAX_RX_PACKET_SIZE MAX_OVERSIZE_PACKET_SIZE
#define NUM_RXBUFS (8)
// This one is the hardware definition.
struct bufdesc {
volatile cyg_uint32 attr;
cyg_uint8 *ptr;
};
// Rx databuffer.
STATIC cyg_uint8 rx_databuf[ NUM_RXBUFS ] [ MAX_RX_PACKET_SIZE ];
#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E3
// Tx databuffer
STATIC cyg_uint8 tx_databuf[ MAX_RX_PACKET_SIZE ];
#endif
struct eth_upd985xx {
cyg_uint8 active, index, tx_busy, mac_addr_ok;
cyg_uint8 vector; // interrupt numbers are small
cyg_uint8 phy_status; // from PHY_STATUS_ flags below
cyg_uint8 hardwired_esa;
#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2
cyg_uint8 promisc;
#endif
cyg_uint8 mac_address[6];
cyg_handle_t interrupt_handle;
cyg_interrupt interrupt_object;
struct cyg_netdevtab_entry *ndp;
// these shall hold uncached addresses of the structures following...
volatile struct bufdesc *txring;
volatile struct bufdesc *rxring_active;
volatile struct bufdesc *rxring_next;
int rxring_active_index;
int rxring_next_index;
cyg_uint32 intrs;
cyg_uint32 tx_keys[1];
// -----------------------------------------------------------------
// Statistics counters
cyg_uint32 count_rx_resource;
cyg_uint32 count_rx_restart;
cyg_uint32 count_interrupts;
cyg_uint32 count_bad_isr_restarts;
cyg_uint32 count_bad_tx_completion;
// -----------------------------------------------------------------
// DO NOT ACCESS THESE DIRECTLY - THE DEVICE HAS TO SEE THEM UNCACHED
// Initially, enough for one whole transmission to be described in one go,
// plus a null link on the end.
struct bufdesc tx_bufdesc[ MAX_ETH_DRV_SG + 2 ];
// Pending rx buffers, of full size.
struct bufdesc rx_bufdesc[ NUM_RXBUFS+1 ];
// -----------------------------------------------------------------
};
struct eth_upd985xx eth_upd985xx[CYGNUM_DEVS_ETH_MIPS_UPD985XX_DEV_COUNT] = {
{
index: 0,
vector: CYGNUM_HAL_INTERRUPT_ETHER,
#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2
promisc: 0,
#endif
#ifdef CYGSEM_DEVS_ETH_UPD985XX_ETH0_SET_ESA
hardwired_esa: 1,
mac_address: CYGDAT_DEVS_ETH_UPD985XX_ETH0_ESA,
mac_addr_ok: 1,
#else
hardwired_esa: 0,
mac_addr_ok: 0,
#endif
}
};
// eth0
ETH_DRV_SC(upd985xx_sc0,
ð_upd985xx[0], // Driver specific data
CYGDAT_DEVS_ETH_UPD985XX_ETH0_NAME, // name for this interface
eth_upd985xx_start,
eth_upd985xx_stop,
eth_upd985xx_ioctl,
eth_upd985xx_can_send,
eth_upd985xx_send,
eth_upd985xx_recv,
eth_upd985xx_deliver,
eth_upd985xx_poll,
eth_upd985xx_int_vector
);
NETDEVTAB_ENTRY(upd985xx_netdev0,
"upd985xx-" CYGDAT_DEVS_ETH_UPD985XX_ETH0_NAME,
upd985xx_eth_upd985xx_init,
&upd985xx_sc0);
// This is in a macro so that if more devices arrive it can easily be changed
#define CHECK_NDP_SC_LINK() CYG_MACRO_START \
CYG_ASSERT( ((void *)ndp == (void *)&upd985xx_netdev0), "Bad ndp" ); \
CYG_ASSERT( ((void *)sc == (void *)&upd985xx_sc0), "Bad sc" ); \
CYG_ASSERT( (void *)p_eth_upd985xx == sc->driver_private, \
"sc pointer bad" ); \
CYG_ASSERT( (void *)p_eth_upd985xx == (void *)ð_upd985xx[0], \
"bad p_eth_upd985x" ); \
CYG_MACRO_END
#define NUM_ELEMENTS( _x_ ) (sizeof( (_x_) ) / sizeof( (_x_[0]) ) )
// ------------------------------------------------------------------------
//
// FUNCTION PROTOTYPES
//
// ------------------------------------------------------------------------
STATIC void InitRxRing(struct eth_upd985xx* p_eth_upd985xx);
STATIC void NextRxRing(struct eth_upd985xx* p_eth_upd985xx);
STATIC void InitTxRing(struct eth_upd985xx* p_eth_upd985xx);
STATIC void ResetTxRing(struct eth_upd985xx* p_eth_upd985xx);
#ifdef CYGPKG_NET
STATIC int eth_upd985xx_configure(struct eth_upd985xx* p_eth_upd985xx,
int promisc, int oversized);
#endif
STATIC void PacketRxReady(struct eth_upd985xx* p_eth_upd985xx);
STATIC void TxDone(struct eth_upd985xx* p_eth_upd985xx);
#define PHY_STATUS_LINK (1)
#define PHY_STATUS_FDX (2)
#define PHY_STATUS_100MBPS (4)
STATIC int eth_upd985xx_status( struct eth_upd985xx *p_eth_upd985xx );
STATIC int eth_set_mac_address( struct eth_upd985xx *p_eth_upd985xx, void *data );
// ------------------------------------------------------------------------
//
// MII ACCESS TO PHY DEVICE
//
// ------------------------------------------------------------------------
STATIC cyg_bool mii_read( cyg_uint32 reg, cyg_uint32 *pvalue,
struct eth_upd985xx *p_eth_upd985xx )
{
int i = 1000;
// wait a bit for it to be idle
while ( 0 != ((ETH_MIND_NVALID | ETH_MIND_SCANA | ETH_MIND_BUSY)
& INL(ETH_MIND)) )
if ( --i < 0 )
return false;
// Tell it the register address and PHY address
OUTL( ETH_MADR, ETH_MADR_PHY_DEVICE_PHYS_ADDRESS | reg );
OUTL( ETH_MCMD, ETH_MCMD_RSTAT ); // "do a read"
// wait for the read to complete
while ( 0 != ((ETH_MIND_NVALID | ETH_MIND_SCANA | ETH_MIND_BUSY)
& INL(ETH_MIND)) )
if ( --i < 0 )
return false;
// so get the data
*pvalue = INL( ETH_MRDD );
return true;
}
#if 0
STATIC cyg_bool mii_write( cyg_uint32 reg, cyg_uint32 value,
struct eth_upd985xx *p_eth_upd985xx )
{
int i = 1000;
// wait a bit for it to be idle
while ( 0 != ((ETH_MIND_NVALID | ETH_MIND_SCANA | ETH_MIND_BUSY)
& INL(ETH_MIND)) )
if ( --i < 0 )
return false;
// Tell it the register address and PHY address
OUTL( ETH_MADR, ETH_MADR_PHY_DEVICE_PHYS_ADDRESS | reg );
// And write the data:
OUTL( ETH_MWTD, value );
// wait a bit for it to be idle
while ( 0 != ((ETH_MIND_NVALID | ETH_MIND_SCANA | ETH_MIND_BUSY)
& INL(ETH_MIND)) )
if ( --i < 0 )
return false;
return true;
}
#endif
// ------------------------------------------------------------------------
//
// INTERRUPT HANDLERS
//
// ------------------------------------------------------------------------
STATIC cyg_uint32 eth_isr(cyg_vector_t vector, cyg_addrword_t data)
{
cyg_drv_interrupt_mask( vector );
return CYG_ISR_CALL_DSR; // schedule DSR
}
// ------------------------------------------------------------------------
// This is a callback from the higher level thread in consequence of the DSR
STATIC void
eth_upd985xx_deliver(struct eth_drv_sc *sc)
{
register int intrs;
struct eth_upd985xx *p_eth_upd985xx;
p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private;
p_eth_upd985xx->count_interrupts++;
intrs = INL( ETH_ISR ); // Read-clear
// Acknowledge once at the start anyway to prevent an interrupt loop in
// case of a transient - interrupts latch in the interrupt controller
// as well as in the ethernet device.
cyg_drv_interrupt_acknowledge(p_eth_upd985xx->vector);
#ifdef DEBUG_TRAFFIC
os_printf("\n[[[[[[[ Deliver intrs = %x\n", intrs );
#endif
// Guard possible external entry points
if ( ! p_eth_upd985xx->active )
return; // without unmasking the interrupt
while ( intrs ) {
if ( 0xffff0000 & intrs ) {
// Then something very bad has happened
p_eth_upd985xx->count_bad_isr_restarts++;
CYG_ASSERT ( p_eth_upd985xx->active, "Device not active!" );
eth_upd985xx_stop( sc );
eth_upd985xx_start( sc, NULL, 0 );
intrs = INL( ETH_ISR ); // Read-clear
}
p_eth_upd985xx->intrs = intrs;
if ( ( ETH_ISR_XMTDN | ETH_ISR_TABR ) & intrs ) {
// Scan for completed Txen and inform the stack
TxDone(p_eth_upd985xx);
}
if ( ( ETH_ISR_RCVDN | ETH_ISR_RBDRS | ETH_ISR_RBDRU ) & intrs ) {
// Pass any rx data up the stack
PacketRxReady(p_eth_upd985xx);
}
// Now we have made the interrupt causes go away, acknowledge and
// *then* read the ISR again. That way the race can result in a
// spurious interrupt rather than a lost interrupt.
cyg_drv_interrupt_acknowledge(p_eth_upd985xx->vector);
intrs = INL( ETH_ISR ); // Read-clear
#ifdef DEBUG_TRAFFIC
if ( intrs )
os_printf("------- Again intrs = %x\n", intrs );
#endif
}
#ifdef DEBUG_TRAFFIC
os_printf("]]]]]]]] Done intrs = %x\n\n", intrs );
#endif
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?