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 + -
显示快捷键?