📄 if_8139.c
字号:
//==========================================================================
//
// if_8139.c
//
// RealTek 8139 ethernet driver
//
//==========================================================================
//####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): Eric Doenges
// Contributors: Chris Nimmers, Gary Thomas, Andy Dyer
// Date: 2003-07-09
// Purpose:
// Description: hardware driver for RealTek 8139 ethernet
// Notes: This is a very basic driver that will send and receive
// packets and not much else. A lot of features of the 8139
// are not supported (power management, MII interface,
// access to the serial eeprom, 'twister tuning', etc.).
//
// Many of the configuration options (like media type and
// speed) the 8139 has are taken directly from the serial
// eeprom and are not currently configurable from this driver.
//
// I've tentatively added some code to handle cache coherency
// issues for platforms that do not have a separate address
// space for uncached memory access and do not do cache
// snooping for PCI bus masters. This code can be activated by
// defining CYGPKG_DEVS_ETH_RLTK_8139_SOFTWARE_CACHE_COHERENCY
// in the platform specific .inl file. Note that if
// CYGPKG_DEVS_ETH_RLTK_8139_SOFTWARE_CACHE_COHERENCY is
// defined, the .inl file is also responsible for making sure
// the receive and transmit buffers are located in memory in
// such a way that flushing or invalidating cache lines for
// these buffers will not affect any other buffers. See the
// README in the doc directory for some suggestions on how
// to do this.
//
// Since the official data sheet for this chip is a bit
// vague, I had to look at the Linux and OpenBSD drivers to
// understand the basic workings of the chip; however, I have
// not copied any code from those drivers to avoid tainting
// eCos' license.
//
// FIXME:
//
//####DESCRIPTIONEND####
//
//==========================================================================
#include <pkgconf/system.h>
#ifdef CYGPKG_IO_ETH_DRIVERS
#include <pkgconf/io_eth_drivers.h>
#endif
#include <pkgconf/devs_eth_rltk_8139.h>
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/cyg_ass.h>
#include <cyg/infra/diag.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/drv_api.h>
#include <cyg/hal/hal_cache.h>
#include <cyg/io/eth/netdev.h>
#include <cyg/io/eth/eth_drv.h>
#include <string.h> /* for memset */
#ifdef CYGPKG_IO_PCI
#include <cyg/io/pci.h>
#else
#error "This driver requires the PCI package (CYGPKG_IO_PCI)"
#endif
#include <cyg/io/pci.h>
/* Necessary for memory mapping macros */
#include CYGHWR_MEMORY_LAYOUT_H
/* Check if we should be dumping debug information or not */
#if defined CYGDBG_DEVS_ETH_RLTK_8139_CHATTER \
&& (CYGDBG_DEVS_ETH_RLTK_8139_CHATTER > 0)
#define DEBUG_RLTK8139_DRIVER
#endif
#include "if_8139.h"
/* Which interrupts we will handle */
#define RLTK8139_IRQ (IR_SERR|IR_FOVW|IR_RXOVW|IR_TER|IR_TOK|IR_RER|IR_ROK|IR_FUN)
/* Allow platform-specific configuration of the driver */
#ifndef CYGDAT_DEVS_ETH_RLTK_8139_INL
#error "CYGDAT_DEVS_ETH_RLTK_8139_INL not defined"
#else
#include CYGDAT_DEVS_ETH_RLTK_8139_INL
#endif
#ifndef CYGHWR_RLTK_8139_PLF_INIT
#define CYGHWR_RLTK_8139_PLF_INIT(sc) do {} while(0)
#endif
/*
* If software cache coherency is required, the HAL_DCACHE_INVALIDATE
* hal macro must be defined as well.
*/
#ifdef CYGPKG_DEVS_ETH_RLTK_8139_SOFTWARE_CACHE_COHERENCY
#if !defined HAL_DCACHE_INVALIDATE || !defined HAL_DCACHE_FLUSH
#error "HAL_DCACHE_INVALIDATE/HAL_DCACHE_FLUSH not defined for this platform but CYGPKG_DEVS_ETH_RLTK_8139_CACHE_COHERENCY was defined."
#endif
#endif
/* Local driver function declarations */
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
static cyg_uint32 rltk8139_isr(cyg_vector_t vector, cyg_addrword_t data);
#endif
#ifdef ETH_DRV_SET_MC_LIST
static cyg_uint32 ether_crc(cyg_uint8 *data, int length);
static void rltk8139_set_multicast_list(Rltk8139_t *rltk8139_info,
struct eth_drv_mc_list *mc_list);
#endif
static void rltk8139_reset(struct eth_drv_sc *sc);
static bool rltk8139_init(struct cyg_netdevtab_entry *tab);
static void rltk8139_start(struct eth_drv_sc *sc, unsigned char *enaddr,
int flags);
static void rltk8139_stop(struct eth_drv_sc *sc);
static int rltk8139_control(struct eth_drv_sc *sc, unsigned long key,
void *data, int data_length);
static int rltk8139_can_send(struct eth_drv_sc *sc);
static void rltk8139_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list,
int sg_len, int total_len, unsigned long key);
static void rltk8139_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list,
int sg_len);
static void rltk8139_deliver(struct eth_drv_sc *sc);
static void rltk8139_poll(struct eth_drv_sc *sc);
static int rltk8139_int_vector(struct eth_drv_sc *sc);
#ifdef CYGPKG_DEVS_ETH_RLTK_8139_WRITE_EEPROM
static cyg_uint16 rltk8139_eeprom_read( char *cpErAddr, cyg_uint8 Cmd, cyg_uint8 RomAdr );
static void rltk8139_eeprom_write( char *cpErAddr, cyg_uint8 Cmd, cyg_uint8 RomAdr, cyg_uint16 SrcData );
#endif
#ifdef DEBUG_RLTK8139_DRIVER
void rltk8139_print_state(struct eth_drv_sc *sc);
#endif
#ifdef CYGPKG_DEVS_ETH_RLTK_8139_WRITE_EEPROM
#define EEPROM_CMD_READ 0x06 // eeprom read command
#define EEPROM_CMD_WRITE 0x05 // eeprom write command
#define EEPROM_PG_ON 0x80
#define EEPROM_PG_EECS 0x08
#define EEPROM_PG_EESK 0x04
#define EEPROM_PG_EEDI 0x02
#define EEPROM_PG_EEDO 0x01
#define EEPROM_WR_BUSY_RETRIES 100 // ready wait re-try count
#define EEPROM_MASK(_param0_,_param1_) ((_param0_ & _param1_) ? EEPROM_PG_EEDI : 0 )
#define EEPROM_WR_DATA(_addr_,_txdata_) HAL_WRITE_UINT8(_addr_,_txdata_);
#define EEPROM_WR_DATAPULSE(_addr_,_txdata_) HAL_WRITE_UINT8(_addr_,_txdata_); \
cyg_thread_delay(0); \
HAL_WRITE_UINT8(_addr_,_txdata_ | EEPROM_PG_EESK);
#define EEPROM_RD_DATA(_addr_,_txdata_,_rxdata_) { \
cyg_uint16 read_data; \
HAL_WRITE_UINT8(_addr_,_txdata_); \
cyg_thread_delay(1); \
HAL_READ_UINT8(_addr_, read_data); \
_rxdata_ <<= 1; \
_rxdata_ |= ((read_data & EEPROM_PG_EEDO) ? 0x0001 : 0x0000 ); \
HAL_WRITE_UINT8(_addr_,_txdata_ | EEPROM_PG_EESK); }
#endif
/*
* Define inline functions to access the card. This will also handle
* endianess issues in one place. This code was lifted from the eCos
* i82559 driver.
*/
#if (CYG_BYTEORDER == CYG_MSBFIRST)
#define HAL_CTOLE32(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) & 0xff0000) >> 8) | (((x) >> 24) & 0xff))
#define HAL_LE32TOC(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) & 0xff0000) >> 8) | (((x) >> 24) & 0xff))
#define HAL_CTOLE16(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8))
#define HAL_LE16TOC(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8))
static inline void
OUTB(cyg_uint8 value,
cyg_uint32 io_address)
{
HAL_WRITE_UINT8( io_address, value);
}
static inline void
OUTW(cyg_uint16 value, cyg_uint32 io_address)
{
HAL_WRITE_UINT16(io_address,
(((value & 0xff) << 8) | ((value & 0xff00) >> 8)) );
}
static inline void
OUTL(cyg_uint32 value, cyg_uint32 io_address)
{
HAL_WRITE_UINT32(io_address,
((((value) & 0xff) << 24) | (((value) & 0xff00) << 8)
| (((value) & 0xff0000) >> 8)
| (((value) >> 24) & 0xff)) );
}
static inline cyg_uint8
INB(cyg_uint32 io_address)
{
cyg_uint8 d;
HAL_READ_UINT8(io_address, d);
return d;
}
static inline cyg_uint16
INW(cyg_uint32 io_address)
{
cyg_uint16 d;
HAL_READ_UINT16( io_address, d );
return (((d & 0xff) << 8) | ((d & 0xff00) >> 8));
}
static inline cyg_uint32
INL(cyg_uint32 io_address)
{
cyg_uint32 d;
HAL_READ_UINT32(io_address, d);
return ((((d) & 0xff) << 24) | (((d) & 0xff00) << 8)
| (((d) & 0xff0000) >> 8) | (((d) >> 24) & 0xff));
}
#else
// Maintaining the same styleee as above...
#define HAL_CTOLE32(x) ((((x))))
#define HAL_LE32TOC(x) ((((x))))
#define HAL_CTOLE16(x) ((((x))))
#define HAL_LE16TOC(x) ((((x))))
static inline void OUTB(cyg_uint8 value, cyg_uint32 io_address)
{ HAL_WRITE_UINT8( io_address, value ); }
static inline void OUTW(cyg_uint16 value, cyg_uint32 io_address)
{ HAL_WRITE_UINT16( io_address, value ); }
static inline void OUTL(cyg_uint32 value, cyg_uint32 io_address)
{ HAL_WRITE_UINT32( io_address, value ); }
static inline cyg_uint8 INB(cyg_uint32 io_address)
{ cyg_uint8 _t_; HAL_READ_UINT8( io_address, _t_ ); return _t_; }
static inline cyg_uint16 INW(cyg_uint32 io_address)
{ cyg_uint16 _t_; HAL_READ_UINT16( io_address, _t_ ); return _t_; }
static inline cyg_uint32 INL(cyg_uint32 io_address)
{ cyg_uint32 _t_; HAL_READ_UINT32( io_address, _t_ ); return _t_; }
#endif // byteorder
/*
* Table of all known PCI device/vendor ID combinations for the RealTek 8139.
* Add them as you get to know them.
*/
#define CYGNUM_DEVS_ETH_RLTK_8139_KNOWN_ALIASES 3
static pci_identifier_t
known_8139_aliases[CYGNUM_DEVS_ETH_RLTK_8139_KNOWN_ALIASES] = {
{ 0x10ec, 0x8139, NULL }, /* This is the official RealTek vendor/device code of 8139D(L) */
{ 0x11db, 0x1234, NULL}, /* SEGA DreamCast BroadBandAdapter */
{ 0x10ec, 0x8129, NULL } /* This is the official RealTek vendor/device code of 8139C(L) */
};
/*
* Check if the device description matches a known 8139 alias.
*/
static cyg_bool
rltk8139_find_match_func(cyg_uint16 vendor_id, cyg_uint16 device_id,
cyg_uint32 class_id, void *p)
{
int i;
for (i = 0; i < CYGNUM_DEVS_ETH_RLTK_8139_KNOWN_ALIASES; ++i) {
if (((known_8139_aliases[i].vendor_id == PCI_ANY_ID) ||
(known_8139_aliases[i].vendor_id == vendor_id)) &&
((known_8139_aliases[i].device_id == PCI_ANY_ID) ||
(known_8139_aliases[i].device_id == device_id)))
return true;
}
return false;
}
/*
* Find the Nth 8139 device on all attached PCI busses and do some initial
* PCI-type initialization. Also setup the interrupt for use in eCos.
*/
static bool
rltk8139_find(int n_th, struct eth_drv_sc *sc)
{
Rltk8139_t *rltk8139_info;
cyg_pci_device_id pci_device_id;
cyg_pci_device pci_device_info;
cyg_uint16 command;
int found = -1;
/* Go through all PCI devices until we find the Nth 8139 chip */
pci_device_id = CYG_PCI_NULL_DEVID;
do {
if (!cyg_pci_find_matching(&rltk8139_find_match_func, NULL,
&pci_device_id))
return false;
else
found += 1;
} while (found != n_th);
/* Save device ID in driver private data in case we ever need it again */
rltk8139_info = (Rltk8139_t *)(sc->driver_private);
rltk8139_info->pci_device_id = pci_device_id;
/* Now that we have found the device, we can extract some data about it */
cyg_pci_get_device_info(pci_device_id, &pci_device_info);
/* Get the assigned interrupt and set up ISR and DSR for it. */
if (cyg_pci_translate_interrupt(&pci_device_info, &rltk8139_info->vector)) {
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf(" Wired to HAL interrupt vector %d\n", rltk8139_info->vector);
#endif
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
/*
* Note that we use the generic eth_drv_dsr routine instead of
* our own.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -