if_etherc.c

来自「eCos操作系统源码」· 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_DEVSstatic 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 occursstatic cyg_uint32etherc_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 voidetherc_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 CYGPKG_IO_ETH_DRIVERS_NET    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] processingstatic voidetherc_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 intetherc_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 voidetherc_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 + -
显示快捷键?