if_lan91cxx.c

来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,486 行 · 第 1/4 页

C
1,486
字号
//==========================================================================
//
//      dev/if_lan91cxx.c
//
//      Ethernet device driver for SMSC LAN91CXX compatible controllers
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2003 Nick Garnett 
// Copyright (C) 2004 Andrew Lunn
// Copyright (C) 2004 eCosCentric Ltd.
//
// 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.
//
// -------------------------------------------
//####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, based on lan900 (for LAN91C110) driver by jskov
//               jskov, based on CS8900 driver by Gary Thomas
// Contributors: gthomas, jskov, hmt, jco@ict.es, nickg
// Date:         2001-01-22
// Purpose:      
// Description:  hardware driver for LAN91CXX "LAN9000" ethernet
// Notes:        Pointer register is not saved/restored on receive interrupts.
//               The pointer is shared by both receive/transmit code.
//               But the net stack manages atomicity for you here.
//
//               The controller has an autorelease mode that allows TX packets
//               to be freed automatically on successful transmission - but
//               that is not used since we're only sending one packet at a
//               time anyway.
//               We may want to pingpong in future for throughput reasons.
//
//               <jco@ict.es> Added support for PCMCIA mode and shifted
//               address buses.
//
//####DESCRIPTIONEND####
//
//==========================================================================

// Based on LAN91C110 and LAN91C96

#include <pkgconf/system.h>
#include <pkgconf/devs_eth_smsc_lan91cxx.h>
#include <pkgconf/io_eth_drivers.h>

#include <cyg/infra/cyg_type.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/hal_diag.h>
#include <cyg/infra/cyg_ass.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 <cyg/kernel/kapi.h>
#include <net/if.h>  /* Needed for struct ifnet */
#endif

#ifdef CYGPKG_INFRA_DEBUG
// Then we log, OOI, the number of times we get a bad packet number
// from the tx done fifo.
int lan91cxx_txfifo_good = 0;
int lan91cxx_txfifo_bad = 0;
#endif

// Set to perms of:
// 0 disables all debug output
// 1 for process debug output
// 2 for added data IO output: get_reg, put_reg
// 4 for packet allocation/free output
// 8 for only startup status, so we can tell we're installed OK
#define DEBUG 0


#if DEBUG
#if defined(CYGPKG_REDBOOT)
static void db_printf( char *fmt, ... )
{
    extern int start_console(void);
    extern void end_console(int);
    va_list a;
    int old_console;
    va_start( a, fmt );
    old_console = start_console();  
    diag_vprintf( fmt, a );
    end_console(old_console);
    va_end( a );
}
#else
#if 0
static void db_printf( char *fmt, ... )
{
    va_list a;
    int old_console = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
    va_start( a, fmt );
    CYGACC_CALL_IF_SET_CONSOLE_COMM( 0 );
    diag_vprintf( fmt, a );
    CYGACC_CALL_IF_SET_CONSOLE_COMM(old_console);
    va_end( a );
}
#else
#define db_printf diag_printf
#endif
#endif
#else
#if 0
static void db_printf( char *fmt, ... )
{
    va_list a;
    int old_console = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
    va_start( a, fmt );
    CYGACC_CALL_IF_SET_CONSOLE_COMM( 0 );
    diag_vprintf( fmt, a );
    CYGACC_CALL_IF_SET_CONSOLE_COMM(old_console);
    va_end( a );
}
#else
#define db_printf( fmt, ... )
#endif
#endif


#if DEBUG & 1
#define DEBUG_FUNCTION() do { db_printf("%s\n", __FUNCTION__); } while (0)
#else
#define DEBUG_FUNCTION() do {} while(0)
#endif

#if defined(ETH_DRV_GET_IF_STATS) || defined (ETH_DRV_GET_IF_STATS_UD)
#define KEEP_STATISTICS
#endif

#ifdef KEEP_STATISTICS
#define INCR_STAT( _x_ )        (cpd->stats. _x_ ++)
#else
#define INCR_STAT( _x_ )        CYG_EMPTY_STATEMENT
#endif

#include "smsc_lan91cxx.h"

#ifdef LAN91CXX_IS_LAN91C111
static void lan91cxx_write_phy(struct eth_drv_sc *sc, cyg_uint8 phyaddr,
			       cyg_uint8 phyreg, cyg_uint16 value);
static cyg_uint16 lan91cxx_read_phy(struct eth_drv_sc *sc, cyg_uint8 phyaddr,
				    cyg_uint8 phyreg);
#endif

static void lan91cxx_poll(struct eth_drv_sc *sc);


#ifdef LAN91CXX_IS_LAN91C111
// Revision A of the LAN91C111 has a bug in which it does not set the
// ODD bit in the status word and control byte of received packets. We
// work around this by assuming the bit is always set and tacking the
// extra odd byte onto the packet anyway. Higher protocol levels never
// believe the packet size reported by the driver and always use the
// values in the protocol headers. So this workaround is quite safe.
// In theory nobody should be using the RevA part now, but it appears
// that some people still have some in their parts bins.
#define LAN91CXX_RX_STATUS_IS_ODD(__cpd,__stat) \
        (((__cpd)->c111_reva)?1:((__stat) & LAN91CXX_RX_STATUS_ODDFRM))
#define LAN91CXX_CONTROLBYTE_IS_ODD(__cpd,__val) \
        (((__cpd)->c111_reva)?1:((__val) & LAN91CXX_CONTROLBYTE_ODD))
#else
#define LAN91CXX_RX_STATUS_IS_ODD(__cpd,__stat) ((__stat) & LAN91CXX_RX_STATUS_ODDFRM)
#define LAN91CXX_CONTROLBYTE_IS_ODD(__cpd,__val) ((__val) & LAN91CXX_CONTROLBYTE_ODD)
#endif

#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
static cyg_interrupt lan91cxx_interrupt;
static cyg_handle_t  lan91cxx_interrupt_handle;

// This ISR is called when the ethernet interrupt occurs
static int lan91cxx_isr(cyg_vector_t vector, cyg_addrword_t data)
             /* , HAL_SavedRegisters *regs */
{
    struct eth_drv_sc *sc = (struct eth_drv_sc *)data;
    struct lan91cxx_priv_data *cpd =
        (struct lan91cxx_priv_data *)sc->driver_private;

//    DEBUG_FUNCTION();

    INCR_STAT( interrupts );

    cyg_drv_interrupt_mask(cpd->interrupt);
    cyg_drv_interrupt_acknowledge(cpd->interrupt);

    return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR);  // Run the DSR
}
#endif

// The deliver function (ex-DSR)  handles the ethernet [logical] processing
static void
lan91cxx_deliver(struct eth_drv_sc *sc)
{
    struct lan91cxx_priv_data *cpd =
        (struct lan91cxx_priv_data *)sc->driver_private;

//    DEBUG_FUNCTION();

    // Service the interrupt:
    lan91cxx_poll(sc);
    // Allow interrupts to happen again
    cyg_drv_interrupt_unmask(cpd->interrupt);
}

static int
lan91cxx_int_vector(struct eth_drv_sc *sc)
{
    struct lan91cxx_priv_data *cpd =
        (struct lan91cxx_priv_data *)sc->driver_private;

    return (cpd->interrupt);
}

static bool 
smsc_lan91cxx_init(struct cyg_netdevtab_entry *tab)
{
    struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;
    struct lan91cxx_priv_data *cpd =
        (struct lan91cxx_priv_data *)sc->driver_private;
    unsigned short val;
    int i;
#if CYGINT_DEVS_ETH_SMSC_LAN91CXX_PCMCIA_MODE
    unsigned char ecor, ecsr;
#endif
    cyg_bool esa_configured = false;

    DEBUG_FUNCTION();

    cpd->txbusy = cpd->within_send = 0;
        
#ifdef CYGNUM_DEVS_ETH_SMSC_LAN91CXX_SHIFT_ADDR
    cpd->addrsh = CYGNUM_DEVS_ETH_SMSC_LAN91CXX_SHIFT_ADDR;
#else
    cpd->addrsh = 0;
#endif

#if CYGINT_DEVS_ETH_SMSC_LAN91CXX_PCMCIA_MODE

    // If the chip is configured in PCMCIA mode, the internal
    // registers mapped in the attribute memory should be
    // initialized (i.e. to enable the I/O map)
    
    ecor = get_att(sc, LAN91CXX_ECOR);
    
    // pulse SRESET on ECOR
    ecor |= LAN91CXX_ECOR_RESET;
    put_att(sc, LAN91CXX_ECOR, ecor);
    
    CYGACC_CALL_IF_DELAY_US(1);
    
    ecor &= ~LAN91CXX_ECOR_RESET;
    put_att(sc, LAN91CXX_ECOR, ecor);

    // then, enable I/O map
    ecor |= LAN91CXX_ECOR_ENABLE;    
    put_att(sc, LAN91CXX_ECOR, ecor);

    // verify the register contents
    if (ecor != get_att(sc, LAN91CXX_ECOR))
        db_printf("LAN91CXX - Cannot access PCMCIA attribute registers\n");
	
    ecsr = get_att(sc, LAN91CXX_ECSR);
#ifdef CYGSEM_DEVS_ETH_SMSC_LAN91CXX_8_BIT
#error "91CXX 8-bit mode not yet supported."
    ecsr |= LAN91CXX_ECSR_IOIS8;
#else
    ecsr &= ~LAN91CXX_ECSR_IOIS8;
#endif
    put_att(sc, LAN91CXX_ECSR, ecsr);
        
#endif

#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
    // Initialize environment, setup interrupt handler
    cyg_drv_interrupt_create(cpd->interrupt,
                             CYGNUM_DEVS_ETH_SMSC_LAN91CXX_INT_PRIO,
                             (cyg_addrword_t)sc, //  Data item passed to interrupt handler
                             (cyg_ISR_t *)lan91cxx_isr,
                             (cyg_DSR_t *)eth_drv_dsr, // The logical driver DSR
                             &lan91cxx_interrupt_handle,
                             &lan91cxx_interrupt);
    cyg_drv_interrupt_attach(lan91cxx_interrupt_handle);
#endif // !CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
    cyg_drv_interrupt_acknowledge(cpd->interrupt);
    cyg_drv_interrupt_unmask(cpd->interrupt);
    
    // probe chip by reading the signature in BS register
    val = get_banksel(sc);
#if DEBUG & 9
    db_printf("LAN91CXX - supposed BankReg @ %x = %04x\n",
                cpd->base+LAN91CXX_BS, val );
#endif

    if ((0xff00 & val) !=  0x3300) {
        CYG_FAIL("No 91Cxx signature" );
        diag_printf("smsc_lan91cxx_init: No 91Cxx signature found\n");
        return false;
    }

    val = get_reg(sc, LAN91CXX_REVISION);

#if DEBUG & 9
    db_printf("LAN91CXX - type: %01x, rev: %01x\n",
                (val>>4)&0xf, val & 0xf);
#endif

#ifdef LAN91CXX_IS_LAN91C111
    // Set RevA flag for LAN91C111 so we can cope with the odd-bit bug.
    cpd->c111_reva = (val == 0x3390);
#endif
    
    // The controller may provide a function used to set up the ESA
    if (cpd->config_enaddr)
        (*cpd->config_enaddr)(cpd);

    // Reset chip
    put_reg(sc, LAN91CXX_RCR, LAN91CXX_RCR_SOFT_RST);
    put_reg(sc, LAN91CXX_RCR, 0);

    val = get_reg(sc, LAN91CXX_EPH_STATUS);
#ifndef LAN91CXX_IS_LAN91C111
    // LINK_OK on 91C111 is just a general purpose input and may not
    // have anything to do with the link.
    if (!(val & LAN91CXX_STATUS_LINK_OK)) {
        db_printf("no link\n");
        return false;  // Link not connected
    }
#endif


#if DEBUG & 9

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?