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,
           &eth_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 *)&eth_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 + -
显示快捷键?