if_etherc.c

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

C
1,128
字号
//==========================================================================
//
//      dev/if_etherc.c
//
//      Ethernet device driver for SH EtherC CPU module 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####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    jskov
// Contributors: jskov
// Date:         2002-01-30
// Purpose:      
// Description:  Hardware driver for SH EtherC CPU module controller
//
// Notes:        The KEEP_STATISTICS code is not implemented yet. Look
//               for FIXME macro.
//
//####DESCRIPTIONEND####
//
//==========================================================================

#include <pkgconf/system.h>
#include <pkgconf/devs_eth_sh_etherc.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/infra/cyg_ass.h>
#include <cyg/infra/diag.h>
#include <cyg/hal/drv_api.h>
#include <cyg/hal/hal_if.h>             // delays
#include <string.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 */
#include <pkgconf/io_eth_drivers.h>
#endif
#include CYGHWR_MEMORY_LAYOUT_H

#define FIXME 0

// Set size so it is 16-byte aligned
#define _BUF_SIZE (1544+8)

#define IEEE_8023_MIN_FRAME           64    // Smallest possible ethernet frame

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

#include "sh_etherc.h"
#define __WANT_DEVS
#include CYGDAT_DEVS_ETH_SH_ETHERC_INL
#undef  __WANT_DEVS

static void       etherc_poll(struct eth_drv_sc *sc);

static cyg_uint16 etherc_read_MII(struct etherc_priv_data *cpd, int id, int reg);
static void       etherc_write_MII(struct etherc_priv_data *cpd, int id, int reg, cyg_uint16 value);
#define _MII_READ(_priv_, _id_, _reg_) etherc_read_MII(_priv_, _id_, _reg_)
#define _MII_WRITE(_priv_, _id_, _reg_, _val_) etherc_write_MII(_priv_, _id_, _reg_, _val_)
#define _MII_HAS_EXTENDED
#include "phyter.inl"

#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
// This ISR is called when the ethernet interrupt occurs
static cyg_uint32
etherc_isr(cyg_vector_t vector, cyg_addrword_t data)
{
    struct etherc_priv_data *cpd = (struct etherc_priv_data *)data;

    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
}

static void
etherc_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
    struct etherc_priv_data* cpd = (struct etherc_priv_data *)data;
    struct cyg_netdevtab_entry *ndp = (struct cyg_netdevtab_entry *)(cpd->ndp);
    struct eth_drv_sc *sc = (struct eth_drv_sc *)(ndp->device_instance);

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


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

    DEBUG_FUNCTION();

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

static int
etherc_int_vector(struct eth_drv_sc *sc)
{
    struct etherc_priv_data *cpd =
        (struct etherc_priv_data *)sc->driver_private;

    return (cpd->interrupt);
}


static bool 
sh_etherc_init(struct cyg_netdevtab_entry *tab)
{
    struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;
    struct etherc_priv_data *cpd =
        (struct etherc_priv_data *)sc->driver_private;
    cyg_uint8 *p, *d;
    cyg_uint32 reg;
    int i;
    bool esa_configured = true;

    DEBUG_FUNCTION();

    cpd->txbusy = 0;

    // Ensure that addresses of ring descriptors and buffers are properly aligned
    // and in uncached memory.
    cpd->rx_buffers = (cyg_uint8*)CYGARC_UNCACHED_ADDRESS(((cyg_uint32)cpd->rx_buffers+16-1) & ~(16-1));
    cpd->rx_ring = (cyg_uint8*)CYGARC_UNCACHED_ADDRESS(((cyg_uint32)cpd->rx_ring+16-1) & ~(16-1));
    cpd->tx_buffers = (cyg_uint8*)CYGARC_UNCACHED_ADDRESS(((cyg_uint32)cpd->tx_buffers+16-1) & ~(16-1));
    cpd->tx_ring = (cyg_uint8*)CYGARC_UNCACHED_ADDRESS(((cyg_uint32)cpd->tx_ring+16-1) & ~(16-1));

#if DEBUG & 8
    db_printf("Etherc at base 0x%08x\n", cpd->base);
#endif

    // 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[]
        esa_configured = true;
    }
    if (!esa_configured) {
# if DEBUG & 8
        diag_printf("EtherC - no EEPROM, static ESA or RedBoot config option.\n");
# endif
        return false;
    }

#if DEBUG & 9
    db_printf("ETHERC 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

    // Prepare RX and TX rings
    p = cpd->rx_ring;
    d = cpd->rx_buffers;
    for (i = 0; i < cpd->rx_ring_cnt; i++) {
        _SU32(p, ETHERC_RD_STAT) = ETHERC_RD_STAT_RACT | ETHERC_RD_STAT_RFP_OTO;
        _SU16(p, ETHERC_RD_RBL) = _BUF_SIZE;
        _SU16(p, ETHERC_RD_RDL) = 0;
        _SU32(p, ETHERC_RD_RBA) = (cyg_uint32)d;
        _SU32(p, ETHERC_RD_PAD) = 0;
        p += ETHERC_RD_SIZE;
        d += _BUF_SIZE;
    }
    // Set ring-end marker
    p -= ETHERC_RD_SIZE;
    _SU32(p, ETHERC_RD_STAT) |= ETHERC_RD_STAT_RDLE;
    cpd->rx_ring_next = 0;

    p = cpd->tx_ring;
    d = cpd->tx_buffers;
    for (i = 0; i < cpd->tx_ring_cnt; i++) {
        _SU32(p, ETHERC_TD_STAT) = ETHERC_TD_STAT_TFP_OTO;
        _SU16(p, ETHERC_TD_TDL) = 0;
        _SU16(p, ETHERC_TD_PAD0) = 0;
        _SU32(p, ETHERC_TD_TBA) = (cyg_uint32)d;
        _SU32(p, ETHERC_TD_PAD1) = 0;
        p += ETHERC_TD_SIZE;
        d += _BUF_SIZE;
    }
    // Set ring-end marker
    p -= ETHERC_RD_SIZE;
    _SU32(p, ETHERC_TD_STAT) |= ETHERC_TD_STAT_TDLE;
    cpd->tx_ring_free = cpd->tx_ring_alloc = cpd->tx_ring_owned = 0;

    // Reset ethernet module, then wait for (at least) 16 clocks.
    put_reg(cpd, _REG_EDMR, CYGARC_REG_EDMR_SWR | CYGARC_REG_EDMR_DL16);
    for (i = 0; i < 16; i++);
    put_reg(cpd, _REG_EDMR, CYGARC_REG_EDMR_DL16);

    // Program ring data into controller
    put_reg(cpd, _REG_RDLAR, (cyg_uint32)cpd->rx_ring);
    put_reg(cpd, _REG_TDLAR, (cyg_uint32)cpd->tx_ring);

    // Set ESA
    put_reg(cpd, _REG_MAHR, (cpd->esa[0] << 24) | (cpd->esa[1] << 16) | (cpd->esa[2] << 8) | cpd->esa[3]);
    put_reg(cpd, _REG_MALR, (cpd->esa[4] << 8) | cpd->esa[5]);

    // Set receive mode: receive continuously
    put_reg(cpd, _REG_RCR, CYGARC_REG_RCR_RNC);

    // Stop controller, set duplex mode
    put_reg(cpd, _REG_ECMR, CYGARC_REG_ECMR_DM);
    cpd->active = 0;

    // Clear pending interrupt flags
    reg = get_reg(cpd, _REG_EESR);
    put_reg(cpd, _REG_EESR, reg);

#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
    // Attach ISR/DSRs.
    cpd->interrupt = CYGNUM_HAL_INTERRUPT_EDMAC_EINT;
    cyg_drv_interrupt_create(cpd->interrupt,
                             1,                   // Priority
                             (cyg_addrword_t)cpd, // Data item passed to ISR & DSR
                             etherc_isr,          // ISR
                             etherc_dsr,          // DSR
                             &cpd->interrupt_handle, // handle to intr obj
                             &cpd->interrupt_object ); // space for int obj

    cyg_drv_interrupt_attach(cpd->interrupt_handle);
    cyg_drv_interrupt_unmask(cpd->interrupt);
    put_reg(cpd, _REG_EESIPR, CYGARC_REG_EESIPR_TCIP|CYGARC_REG_EESIPR_FRIP);
#if DEBUG & 8
    db_printf("Attached interrupt on vector %d\n", cpd->interrupt);
#endif
#endif

    // Record the net dev pointer
    cpd->ndp = (void *)tab;


    // Initialize the PHY
#ifdef CYGSEM_DEVS_ETH_SH_ETHERC_FORCE_10MBPS
#if DEBUG & 8
    db_printf("Forcing 10Mb link\n");
#endif
    _MII_SPEED_FORCE_10MB(cpd, 1);
    _MII_RENEGOTIATE(cpd, 1);
#else
    // Wait for automatic negotiation to complete
    _MII_RENEGOTIATION_WAIT(cpd, 1);
#endif

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

#if DEBUG & 9
    db_printf("Done\n");
#endif
    return true;
}

static void
etherc_suspend(struct etherc_priv_data *cpd)
{
    cyg_uint32 reg;
#if 0
    bool still_active;
#endif

    reg = get_reg(cpd, _REG_ECMR);
    reg &= ~(CYGARC_REG_ECMR_TE | CYGARC_REG_ECMR_RE);
    put_reg(cpd, _REG_ECMR, reg);

#if 0 // 
    // Try to find out if controller stopped. Supposedly, it should
    // communicate this by clearing the active signal of the active
    // RX/TX descriptors.

    // Check RX
    do {
        still_active = false;
        reg = get_reg(cpd, _REG_RDFAR);
#if 1
        {
            int i;
            cyg_uint8* p;
            p = cpd->rx_ring;
            for (i = 0; i < cpd->rx_ring_cnt; i++) {
                if ((cyg_uint32)p == reg) {
                    if (_SU32(reg, ETHERC_RD_STAT) & ETHERC_RD_STAT_RACT) {
                        still_active = true;
                        break;
                    }
                }
            }
        }
#else
        if (_SU32(reg, ETHERC_RD_STAT) & ETHERC_RD_STAT_RACT)
            still_active = true;
#endif
    } while (still_active);

    // Check TX
    do {
        still_active = false;
        reg = get_reg(cpd, _REG_TDFAR);
#if 1
        {
            int i;
            cyg_uint8* p;
            p = cpd->tx_ring;
            for (i = 0; i < cpd->tx_ring_cnt; i++) {

⌨️ 快捷键说明

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