if_cs8900a.c

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

C
751
字号
//==========================================================================
//
//      dev/if_cs8900a.c
//
//      Device driver for Cirrus Logic CS8900A ethernet controller
//
//==========================================================================
//####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):    gthomas
// Contributors: gthomas, jskov
// Date:         2001-11-02
// Purpose:      
// Description:  Driver for CS8900 ethernet controller
//
// Note:         Platform can define CYGSEM_DEVS_ETH_CL_CS8900A_NOINTS
//               to get a timer thread polling instead of interupt based
//               operation.
//
// Note:         Driver will need some changes to support multiple instances
//
//####DESCRIPTIONEND####
//
//==========================================================================

#include <pkgconf/system.h>
#ifdef CYGPKG_KERNEL
#include <cyg/kernel/kapi.h>
#endif
#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_endian.h>
#include <cyg/infra/diag.h>
#include <cyg/hal/drv_api.h>
#undef __ECOS
#define __ECOS
#include <cyg/io/eth/eth_drv.h>
#include <cyg/io/eth/netdev.h>

#include <cyg/io/cs8900.h>

#define __WANT_DEVS
#include CYGDAT_DEVS_ETH_CL_CS8900A_INL
#undef __WANT_DEVS

// NOINTS operation only relevant when the NET package is loaded
#if !defined(CYGPKG_NET) || !defined(CYGPKG_KERNEL)
# undef CYGSEM_DEVS_ETH_CL_CS8900A_NOINTS
#endif

#ifdef CYGSEM_DEVS_ETH_CL_CS8900A_NOINTS
#define STACK_SIZE CYGNUM_HAL_STACK_SIZE_MINIMUM
static char cs8900a_fake_int_stack[STACK_SIZE];
static cyg_thread cs8900a_fake_int_thread_data;
static cyg_handle_t cs8900a_fake_int_thread_handle;
static void cs8900a_fake_int(cyg_addrword_t);
#endif

#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
extern int cyg_io_eth_net_debug;
#endif

static void cs8900a_poll(struct eth_drv_sc *sc);
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
// This ISR is called when the ethernet interrupt occurs
static int
cs8900a_isr(cyg_vector_t vector, cyg_addrword_t data, HAL_SavedRegisters *regs)
{
    cs8900a_priv_data_t* cpd = (cs8900a_priv_data_t *)data;
    cyg_drv_interrupt_mask(cpd->interrupt);
    cyg_drv_interrupt_acknowledge(cpd->interrupt);
    return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR);  // Run the DSR
}

static void
cs8900a_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
    // This conditioning out is necessary because of explicit calls to this
    // DSR - which would not ever be called in the case of a polled mode
    // usage ie. in RedBoot.
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
    cs8900a_priv_data_t* cpd = (cs8900a_priv_data_t *)data;
    struct cyg_netdevtab_entry *ndp = (struct cyg_netdevtab_entry *)(cpd->tab);
    struct eth_drv_sc *sc = (struct eth_drv_sc *)(ndp->device_instance);

    DEBUG_FUNCTION();

    // but here, it must be a *sc:
    eth_drv_dsr( vector, count, (cyg_addrword_t)sc );
#else
# ifndef CYGPKG_REDBOOT
#  error Empty CS8900A ethernet DSR is compiled.  Is this what you want?
# endif
#endif
}
#endif

// The deliver function (ex-DSR)  handles the ethernet [logical] processing
static void
cs8900a_deliver(struct eth_drv_sc *sc)
{
    cs8900a_poll(sc);
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
    {
        cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
        // Allow interrupts to happen again
        cyg_drv_interrupt_unmask(cpd->interrupt);
    }
#endif
}

static int
cs8900a_int_vector(struct eth_drv_sc *sc)
{
    cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
    return (cpd->interrupt);
}

static bool 
cs8900a_init(struct cyg_netdevtab_entry *tab)
{
    struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;
    cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
    cyg_addrword_t base = cpd->base;
    cyg_uint16 chip_type, chip_rev, chip_status;
    cyg_uint16 i;
    long timeout = 500000;
    cyg_bool esa_configured = false;
    
    cpd->tab = tab;

    CYGHWR_CL_CS8900A_PLF_INIT(cpd);

#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
    // Initialize environment, setup interrupt handler
    cyg_drv_interrupt_create(cpd->interrupt,
                             cpd->priority,
                             (cyg_addrword_t)cpd, //  Data item passed to interrupt handler
                             (cyg_ISR_t *)cs8900a_isr,
                             (cyg_DSR_t *)cs8900a_dsr,
                             &cpd->interrupt_handle,
                             &cpd->interrupt_object);
    cyg_drv_interrupt_attach(cpd->interrupt_handle);
    cyg_drv_interrupt_acknowledge(cpd->interrupt);
    cyg_drv_interrupt_unmask(cpd->interrupt);

#ifdef CYGSEM_DEVS_ETH_CL_CS8900A_NOINTS
    cyg_thread_create(1,                 // Priority
                      cs8900a_fake_int,   // entry
                      (cyg_addrword_t)sc, // entry parameter
                      "CS8900 int",      // Name
                      &cs8900a_fake_int_stack[0],         // Stack
                      STACK_SIZE,        // Size
                      &cs8900a_fake_int_thread_handle,    // Handle
                      &cs8900a_fake_int_thread_data       // Thread data structure
            );
    cyg_thread_resume(cs8900a_fake_int_thread_handle);  // Start it
#endif
#endif

    // Read controller ID - the first is a dummy read, since (on some
    // platforms) the first access to the controller seems to skip the
    // MSB 8 bits.
    get_reg(base, PP_ChipID);
    chip_type = get_reg(base, PP_ChipID);
    chip_rev = get_reg(base, PP_ChipRev);
#if DEBUG & 8
    diag_printf("CS8900A[%p] - type: 0x%04x, rev: 0x%04x\n", base, chip_type, chip_rev);
#endif
    if (chip_type != PP_ChipID_CL) {
#if DEBUG & 8
        diag_printf("CS8900 - invalid type (0x%04x), must be 0x630e\n", chip_type);
#endif
        return false;
    }

    CYGHWR_CL_CS8900A_PLF_RESET(base);
    put_reg(base, PP_SelfCtl, PP_SelfCtl_Reset);  // Reset chip

    CYGHWR_CL_CS8900A_PLF_POST_RESET(base);
    
    while ((get_reg(base, PP_SelfStat) & PP_SelfStat_InitD) == 0) {
        if (--timeout <= 0) {
#if DEBUG & 8
            diag_printf("CS8900 didn't reset - abort!\n");
#endif
            return false;
        }
    }

    chip_status = get_reg(base, PP_SelfStat);
#if DEBUG & 8
    diag_printf("CS8900 - status: 0x%04x (%sEEPROM present)\n", chip_status,
                chip_status & PP_SelfStat_EEPROM ? "" : "no ");
#endif


    // Disable reception whilst finding the ESA
    put_reg(base, PP_LineCTL, 0 );
    // Find ESA - check possible sources in sequence and stop when
    // one provides the ESA:
    //   RedBoot option (via provide_esa)
    //   Compile-time configuration
    //   EEPROM
    //   <fail configuration of device>
    if (NULL != cpd->provide_esa) {
        esa_configured = cpd->provide_esa(cpd);
# if DEBUG & 8
        if (esa_configured)
            diag_printf("Got ESA from RedBoot option\n");
# endif
    }
    if (!esa_configured && cpd->hardwired_esa) {
        // ESA is already set in cpd->esa[]
#if DEBUG & 8
        diag_printf("Got hardcoded ESA\n");
#endif
        esa_configured = true;
    }
    if (!esa_configured && (chip_status & PP_SelfStat_EEPROM)) {
        // Get ESA from EEPROM - via the PP_IA registers
        cyg_uint16 esa_word;
        for (i = 0;  i < sizeof(cpd->esa);  i += 2) {
#ifndef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
            esa_word = get_reg(base, PP_IA+i);
            cpd->esa[i] = (esa_word & 0xFF);
            cpd->esa[i+1] = (esa_word >> 8) & 0xFF;
#else
            esa_word = get_reg(base, PP_IA+CYG_SWAP16(i));
            cpd->esa[i+1] = (esa_word & 0xFF);
            cpd->esa[i] = (esa_word >> 8) & 0xFF;
#endif
        }
#if DEBUG & 8
        diag_printf("Got EEPROM ESA\n");
#endif
        esa_configured = true;
    }
    if (!esa_configured) {
# if DEBUG & 8
        diag_printf("CS8900 - no EEPROM, static ESA or RedBoot config option.\n");
# endif
        return false;
    }

    // Tell the chip what ESA to use
    for (i = 0;  i < sizeof(cpd->esa);  i += 2) {
#ifndef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
        put_reg(base, PP_IA+i, cpd->esa[i] | (cpd->esa[i+1] << 8));
#else
        put_reg(base, PP_IA+CYG_SWAP16(i), cpd->esa[i+1] | (cpd->esa[i] << 8));
#endif
    }
    // Set logical address mask
    for (i = 0;  i < 8;  i += 2) {
#ifndef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
        put_reg(base, PP_LAF+i, 0xFFFF);
#else
        put_reg(base, PP_LAF+CYG_SWAP16(i), 0xFFFF);
#endif
    }
# if DEBUG & 8
    diag_printf("ESA %02x:%02x:%02x:%02x:%02x:%02x\n",
                cpd->esa[0], cpd->esa[1], cpd->esa[2],
                cpd->esa[3], cpd->esa[4], cpd->esa[5]);
# endif

    // Initialize upper level driver
    (sc->funs->eth_drv->init)(sc, cpd->esa);

    return true;
}

static void
cs8900a_stop(struct eth_drv_sc *sc)
{
    cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
    cyg_addrword_t base = cpd->base;

    put_reg(base, PP_LineCTL, 0);
}

// This function is called to "start up" the interface.  It may be called
// multiple times, even when the hardware is already running.  It will be
// called whenever something "hardware oriented" changes and should leave
// the hardware ready to send/receive packets.
static void
cs8900a_start(struct eth_drv_sc *sc, cyg_uint8 *esa, int flags)
{
    cyg_uint16 stat;
    cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
    cyg_addrword_t base = cpd->base;

    put_reg(base, PP_BusCtl, PP_BusCtl_MemoryE);  // Disable interrupts, memory mode
    put_reg(base, PP_IntReg, PP_IntReg_IRQ0);  // Only possibility
    put_reg(base, PP_RxCFG, PP_RxCFG_RxOK | PP_RxCFG_CRC | 
                      PP_RxCFG_RUNT | PP_RxCFG_EXTRA);
    cpd->rxmode = PP_RxCTL_RxOK | PP_RxCTL_Broadcast | PP_RxCTL_IA;
    put_reg(base, PP_RxCTL, cpd->rxmode);
    put_reg(base, PP_TxCFG, PP_TxCFG_TxOK | PP_TxCFG_Collision | 
                      PP_TxCFG_CRS | PP_TxCFG_SQE | PP_TxCFG_Late | 
                      PP_TxCFG_Jabber | PP_TxCFG_16Collisions);
    put_reg(base, PP_BufCFG, PP_BufCFG_TxRDY | PP_BufCFG_TxUE | PP_BufCFG_RxMiss | 
                       PP_BufCFG_TxCol | PP_BufCFG_Miss | PP_BufCFG_SWI);
    put_reg(base, PP_IntReg, PP_IntReg_IRQ0);  // Only possibility
    put_reg(base, PP_LineCTL, PP_LineCTL_Rx | PP_LineCTL_Tx);
    // Clear Interrupt Status Queue before enabling interrupts
    do {
        HAL_READ_UINT16(cpd->base+CS8900A_ISQ, stat);
    }  while (stat != 0) ;
    cpd->txbusy = false;
    put_reg(base, PP_BusCtl, PP_BusCtl_EnableIRQ);
}

// This routine is called to perform special "control" opertions
static int
cs8900a_control(struct eth_drv_sc *sc, unsigned long key, void *data, int data_length)
{
    cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
    cyg_addrword_t base = cpd->base;
    struct eth_drv_mc_list *mc_list = data;
    unsigned char *esa = (unsigned char *)data;
    int i;

    switch (key) {
    case ETH_DRV_SET_MAC_ADDRESS:
#if 9 & DEBUG
        diag_printf("CS8900A - set ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
                esa[0], esa[1], esa[2],

⌨️ 快捷键说明

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