if_rhine.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,320 行 · 第 1/3 页
C
1,320 行
//==========================================================================
//
// dev/if_rhine.c
//
// Ethernet device driver for VIA RHINE compatible controllers
//
//==========================================================================
//####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): jskov, based on pcnet driver
// Contributors: gthomas, jskov, hmt
// Date: 2001-05-30
// Purpose:
// Description: hardware driver for VIA Rhine ethernet
//
// FIXME: Make endian safe
// Make use of virtual addressing for memory shared over PCI
// (see _ADDR_MASK).
// Link failure not detected for some reason.
//
//####DESCRIPTIONEND####
//
//==========================================================================
#include <pkgconf/system.h>
#include <pkgconf/devs_eth_via_rhine.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/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
#ifdef CYGPKG_IO_PCI
#include <cyg/io/pci.h>
#else
#error "Need PCI package here"
#endif
#define _BUF_SIZE 1544
#ifdef CYGPKG_INFRA_DEBUG
// Then we log, OOI, the number of times we get a bad packet number
// from the tx done fifo.
int rhine_txfifo_good = 0;
int rhine_txfifo_bad = 0;
#endif
#include "via_rhine.h"
#define __WANT_DEVS
#include CYGDAT_DEVS_ETH_VIA_RHINE_INL
#undef __WANT_DEVS
static void rhine_poll(struct eth_drv_sc *sc);
// This ISR is called when the ethernet interrupt occurs
static cyg_uint32
rhine_isr(cyg_vector_t vector, cyg_addrword_t data)
{
struct rhine_priv_data *cpd = (struct rhine_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
rhine_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 rhine_priv_data* cpd = (struct rhine_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 Rhine ethernet DSR is compiled. Is this what you want?
# endif
#endif
}
// The deliver function (ex-DSR) handles the ethernet [logical] processing
static void
rhine_deliver(struct eth_drv_sc *sc)
{
struct rhine_priv_data *cpd = (struct rhine_priv_data *)sc->driver_private;
DEBUG_FUNCTION();
// Service the interrupt:
rhine_poll(sc);
// Allow interrupts to happen again
cyg_drv_interrupt_unmask(cpd->interrupt);
}
static int
rhine_int_vector(struct eth_drv_sc *sc)
{
struct rhine_priv_data *cpd =
(struct rhine_priv_data *)sc->driver_private;
return (cpd->interrupt);
}
// ------------------------------------------------------------------------
// Physical interface
#if 0 // fix warning since this isn't actually used
static void
rhine_write_MII(struct rhine_priv_data *cpd, int id, int reg, cyg_uint16 value)
{
cyg_uint8 stat;
int i = 1000;
// Wait for a previous access to complete (within reason)
do {
HAL_PCI_IO_READ_UINT8(cpd->base + RHINE_MIICR, stat);
} while ((stat & (RHINE_MIICR_RCMD | RHINE_MIICR_WCMD)) && i-- > 0);
HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_MIICR, 0);
HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_PHYADR, id);
HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_MIIAD, reg);
HAL_PCI_IO_WRITE_UINT16(cpd->base + RHINE_MIIDATA, value);
HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_MIICR, RHINE_MIICR_WCMD);
}
#endif
static int
rhine_read_MII(struct rhine_priv_data *cpd, int id, int reg)
{
int i = 1000;
cyg_uint8 stat;
cyg_uint16 val;
// Wait for a previous access to complete (within reason)
do {
HAL_PCI_IO_READ_UINT8(cpd->base + RHINE_MIICR, stat);
} while ((stat & (RHINE_MIICR_RCMD | RHINE_MIICR_WCMD)) && i-- > 0);
HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_MIICR, 0);
HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_PHYADR, id);
HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_MIIAD, reg);
HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_MIICR, RHINE_MIICR_RCMD);
i = 1000;
do {
HAL_PCI_IO_READ_UINT8(cpd->base + RHINE_MIICR, stat);
} while ((stat & RHINE_MIICR_RCMD) && i-- > 0);
HAL_PCI_IO_READ_UINT16(cpd->base + RHINE_MIIDATA, val);
return val;
}
// ------------------------------------------------------------------------
// Memory management
//
// Simply carve off from the front of the PCI mapped window into real memory
static cyg_uint32 rhine_heap_size;
static cyg_uint8 *rhine_heap_base;
static cyg_uint8 *rhine_heap_free;
static void*
pciwindow_mem_alloc(int size)
{
void *p_memory;
int _size = size;
CYG_ASSERT(
(CYGHWR_VIA_RHINE_PCI_MEM_MAP_BASE <= (int)rhine_heap_free)
&&
((CYGHWR_VIA_RHINE_PCI_MEM_MAP_BASE +
CYGHWR_VIA_RHINE_PCI_MEM_MAP_SIZE) > (int)rhine_heap_free)
&&
(0 < rhine_heap_size)
&&
(CYGHWR_VIA_RHINE_PCI_MEM_MAP_SIZE >= rhine_heap_size)
&&
(CYGHWR_VIA_RHINE_PCI_MEM_MAP_BASE == (int)rhine_heap_base),
"Heap variables corrupted" );
p_memory = (void *)0;
size = (size + 3) & ~3;
if ( (rhine_heap_free+size) < (rhine_heap_base+rhine_heap_size) ) {
cyg_uint32 *p;
p_memory = (void *)rhine_heap_free;
rhine_heap_free += size;
for ( p = (cyg_uint32 *)p_memory; _size > 0; _size -= 4 )
*p++ = 0;
}
#if DEBUG & 9
diag_printf("Allocated %d bytes at %08x\n", size, p_memory);
#endif
return p_memory;
}
static cyg_pci_match_func find_rhine_match_func;
static cyg_bool
find_rhine_match_func( cyg_uint16 v, cyg_uint16 d, cyg_uint32 c, void *p )
{
#if DEBUG & 9
diag_printf("PCI match vendor %04x device %04x\n", v, d);
#endif
return
(0x1106 == v) && // vendor: VIA
((0x3065 == d) || // device: DL10030A
(0x3043 == d)); // device: VT86C100A
}
static int
pci_init_find_rhines( void )
{
cyg_pci_device_id devid;
cyg_pci_device dev_info;
cyg_uint16 cmd;
int device_index;
int found_devices = 0;
DEBUG_FUNCTION();
#ifdef CYGARC_UNCACHED_ADDRESS
CYG_ASSERT( CYGARC_UNCACHED_ADDRESS((CYG_ADDRWORD)CYGMEM_SECTION_pci_window) ==
CYGHWR_VIA_RHINE_PCI_MEM_MAP_BASE,
"PCI window configured does not match PCI memory section base" );
#else
CYG_ASSERT( (CYG_ADDRWORD)CYGMEM_SECTION_pci_window ==
CYGHWR_VIA_RHINE_PCI_MEM_MAP_BASE,
"PCI window configured does not match PCI memory section base" );
#endif
CYG_ASSERT( CYGMEM_SECTION_pci_window_SIZE ==
CYGHWR_VIA_RHINE_PCI_MEM_MAP_SIZE,
"PCI window configured does not match PCI memory section size" );
if (
#ifdef CYGARC_UNCACHED_ADDRESS
CYGARC_UNCACHED_ADDRESS((CYG_ADDRWORD)CYGMEM_SECTION_pci_window) !=
#else
(CYG_ADDRWORD)CYGMEM_SECTION_pci_window !=
#endif
CYGHWR_VIA_RHINE_PCI_MEM_MAP_BASE
||
CYGMEM_SECTION_pci_window_SIZE !=
CYGHWR_VIA_RHINE_PCI_MEM_MAP_SIZE ) {
#if DEBUG & 8
diag_printf("pci_init_find_rhines(): PCI window misconfigured\n");
#endif
return 0;
}
// First initialize the heap in PCI window'd memory
rhine_heap_size = CYGHWR_VIA_RHINE_PCI_MEM_MAP_SIZE;
rhine_heap_base = (cyg_uint8 *)CYGHWR_VIA_RHINE_PCI_MEM_MAP_BASE;
rhine_heap_free = rhine_heap_base;
cyg_pci_init();
#if DEBUG & 8
diag_printf("Finished cyg_pci_init();\n");
#endif
devid = CYG_PCI_NULL_DEVID;
for (device_index = 0;
device_index < CYGNUM_DEVS_ETH_VIA_RHINE_DEV_COUNT;
device_index++) {
struct rhine_priv_data* cpd = rhine_priv_array[device_index];
cpd->index = device_index;
// See above for find_rhine_match_func - it selects any of several
// variants. This is necessary in case we have multiple mixed-type
// devices on one board in arbitrary orders.
if (cyg_pci_find_matching( &find_rhine_match_func, NULL, &devid )) {
#if DEBUG & 8
diag_printf("eth%d = rhine\n", device_index);
#endif
cyg_pci_get_device_info(devid, &dev_info);
cpd->interrupt_handle = 0; // Flag not attached.
if (cyg_pci_translate_interrupt(&dev_info, &cpd->interrupt)) {
#if DEBUG & 8
diag_printf(" Wired to HAL vector %d\n", cpd->interrupt);
#endif
cyg_drv_interrupt_create(
cpd->interrupt,
1, // Priority - unused
(cyg_addrword_t)cpd,// Data item passed to ISR & DSR
rhine_isr, // ISR
rhine_dsr, // DSR
&cpd->interrupt_handle, // handle to intr obj
&cpd->interrupt_object ); // space for int obj
cyg_drv_interrupt_attach(cpd->interrupt_handle);
// Don't unmask the interrupt yet, that could get us into a
// race.
}
else {
cpd->interrupt = 0;
#if DEBUG & 8
diag_printf(" Does not generate interrupts.\n");
#endif
}
if (cyg_pci_configure_device(&dev_info)) {
#if DEBUG & 8
int i;
diag_printf("Found device on bus %d, devfn 0x%02x:\n",
CYG_PCI_DEV_GET_BUS(devid),
CYG_PCI_DEV_GET_DEVFN(devid));
if (dev_info.command & CYG_PCI_CFG_COMMAND_ACTIVE) {
diag_printf(" Note that board is active. Probed"
" sizes and CPU addresses invalid!\n");
}
diag_printf(" Vendor 0x%04x", dev_info.vendor);
diag_printf("\n Device 0x%04x", dev_info.device);
diag_printf("\n Command 0x%04x, Status 0x%04x\n",
dev_info.command, dev_info.status);
diag_printf(" Class/Rev 0x%08x", dev_info.class_rev);
diag_printf("\n Header 0x%02x\n", dev_info.header_type);
diag_printf(" SubVendor 0x%04x, Sub ID 0x%04x\n",
dev_info.header.normal.sub_vendor,
dev_info.header.normal.sub_id);
for(i = 0; i < CYG_PCI_MAX_BAR; i++) {
diag_printf(" BAR[%d] 0x%08x /", i, dev_info.base_address[i]);
diag_printf(" probed size 0x%08x / CPU addr 0x%08x\n",
dev_info.base_size[i], dev_info.base_map[i]);
}
diag_printf(" eth%d configured\n", device_index);
#endif
found_devices++;
cpd->found = 1;
cpd->active = 0;
cpd->devid = devid;
cpd->base = (unsigned char*) dev_info.base_map[0];
#if DEBUG & 8
diag_printf(" I/O address = 0x%08x\n", cpd->base);
#endif
// Don't use cyg_pci_set_device_info since it clears
// some of the fields we want to print out below.
cyg_pci_read_config_uint16(dev_info.devid,
CYG_PCI_CFG_COMMAND, &cmd);
cmd |= (CYG_PCI_CFG_COMMAND_IO // enable I/O space
| CYG_PCI_CFG_COMMAND_MEMORY // enable memory space
| CYG_PCI_CFG_COMMAND_MASTER); // enable bus master
cyg_pci_write_config_uint16(dev_info.devid,
CYG_PCI_CFG_COMMAND, cmd);
// Extra init code needed for D-Link controller. This
// is snuffed from the Linux driver and was provided
// by D-Link. I've been unable to find documentation
// for the part.
if (0x3065 == dev_info.device) {
cyg_uint8 tmp;
#if DEBUG & 8
diag_printf("Pre-reset init code for D-Link.\n");
#endif
HAL_PCI_IO_READ_UINT8(cpd->base+RHINE_STICKYHW, tmp);
tmp &= 0xfc;
HAL_PCI_IO_WRITE_UINT8(cpd->base+RHINE_STICKYHW, tmp);
HAL_PCI_IO_WRITE_UINT8(cpd->base+RHINE_WOL_CG_CLR, 0x80);
HAL_PCI_IO_WRITE_UINT8(cpd->base+RHINE_WOL_CR_CLR, 0xff);
HAL_PCI_IO_WRITE_UINT8(cpd->base+RHINE_PWR_CSR_CLR, 0xff);
}
// Now the PCI part of the device is configured, reset
// it. This should make it safe to enable the
// interrupt
HAL_PCI_IO_WRITE_UINT8(cpd->base+RHINE_CR1, RHINE_CR1_SRST);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?