2005-04-17 02:20:36 +04:00
/* Intel EtherExpress 16 device driver for Linux
*
* Written by John Sullivan , 1995
* based on original code by Donald Becker , with changes by
* Alan Cox and Pauline Middelink .
*
* Support for 8 - bit mode by Zoltan Szilagyi < zoltans @ cs . arizona . edu >
*
* Many modifications , and currently maintained , by
* Philip Blundell < philb @ gnu . org >
* Added the Compaq LTE Alan Cox < alan @ redhat . com >
* Added MCA support Adam Fritzler < mid @ auk . cx >
*
* Note - this driver is experimental still - it has problems on faster
* machines . Someone needs to sit down and go through it line by line with
* a databook . . .
*/
/* The EtherExpress 16 is a fairly simple card, based on a shared-memory
* design using the i82586 Ethernet coprocessor . It bears no relationship ,
* as far as I know , to the similarly - named " EtherExpress Pro " range .
*
* Historically , Linux support for these cards has been very bad . However ,
* things seem to be getting better slowly .
*/
/* If your card is confused about what sort of interface it has (eg it
* persistently reports " 10baseT " when none is fitted ) , running ' SOFTSET / BART '
* or ' SOFTSET / LISA ' from DOS seems to help .
*/
/* Here's the scoop on memory mapping.
*
* There are three ways to access EtherExpress card memory : either using the
* shared - memory mapping , or using PIO through the dataport , or using PIO
* through the " shadow memory " ports .
*
* The shadow memory system works by having the card map some of its memory
* as follows :
*
* ( the low five bits of the SMPTR are ignored )
*
* base + 0x4000 . .400f memory at SMPTR + 0. .15
* base + 0x8000 . .800f memory at SMPTR + 16. .31
* base + 0xc000 . . c007 dubious stuff ( memory at SMPTR + 16. .23 apparently )
* base + 0xc008 . . c00f memory at 0x0008 . .0 x000f
*
* This last set ( the one at c008 ) is particularly handy because the SCB
* lives at 0x0008 . So that set of ports gives us easy random access to data
* in the SCB without having to mess around setting up pointers and the like .
* We always use this method to access the SCB ( via the scb_xx ( ) functions ) .
*
* Dataport access works by aiming the appropriate ( read or write ) pointer
* at the first address you ' re interested in , and then reading or writing from
* the dataport . The pointers auto - increment after each transfer . We use
* this for data transfer .
*
* We don ' t use the shared - memory system because it allegedly doesn ' t work on
* all cards , and because it ' s a bit more prone to go wrong ( it ' s one more
* thing to configure . . . ) .
*/
/* Known bugs:
*
* - The card seems to want to give us two interrupts every time something
* happens , where just one would be better .
*/
/*
*
* Note by Zoltan Szilagyi 10 - 12 - 96 :
*
* I ' ve succeeded in eliminating the " CU wedged " messages , and hence the
* lockups , which were only occurring with cards running in 8 - bit mode ( " force
* 8 - bit operation " in Intel's SoftSet utility). This version of the driver
* sets the 82586 and the ASIC to 8 - bit mode at startup ; it also stops the
* CU before submitting a packet for transmission , and then restarts it as soon
* as the process of handing the packet is complete . This is definitely an
* unnecessary slowdown if the card is running in 16 - bit mode ; therefore one
* should detect 16 - bit vs 8 - bit mode from the EEPROM settings and act
* accordingly . In 8 - bit mode with this bugfix I ' m getting about 150 K / s for
* ftp ' s , which is significantly better than I get in DOS , so the overhead of
* stopping and restarting the CU with each transmit is not prohibitive in
* practice .
*
* Update by David Woodhouse 11 / 5 / 99 :
*
* I ' ve seen " CU wedged " messages in 16 - bit mode , on the Alpha architecture .
* I assume that this is because 16 - bit accesses are actually handled as two
* 8 - bit accesses .
*/
# ifdef __alpha__
# define LOCKUP16 1
# endif
# ifndef LOCKUP16
# define LOCKUP16 0
# endif
# include <linux/config.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/fcntl.h>
# include <linux/interrupt.h>
# include <linux/ioport.h>
# include <linux/string.h>
# include <linux/in.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/skbuff.h>
# include <linux/slab.h>
# include <linux/mca-legacy.h>
# include <linux/spinlock.h>
# include <linux/bitops.h>
# include <asm/system.h>
# include <asm/io.h>
# include <asm/irq.h>
# ifndef NET_DEBUG
# define NET_DEBUG 4
# endif
# include "eexpress.h"
# define EEXP_IO_EXTENT 16
/*
* Private data declarations
*/
struct net_local
{
struct net_device_stats stats ;
unsigned long last_tx ; /* jiffies when last transmit started */
unsigned long init_time ; /* jiffies when eexp_hw_init586 called */
unsigned short rx_first ; /* first rx buf, same as RX_BUF_START */
unsigned short rx_last ; /* last rx buf */
unsigned short rx_ptr ; /* first rx buf to look at */
unsigned short tx_head ; /* next free tx buf */
unsigned short tx_reap ; /* first in-use tx buf */
unsigned short tx_tail ; /* previous tx buf to tx_head */
unsigned short tx_link ; /* last known-executing tx buf */
unsigned short last_tx_restart ; /* set to tx_link when we
restart the CU */
unsigned char started ;
unsigned short rx_buf_start ;
unsigned short rx_buf_end ;
unsigned short num_tx_bufs ;
unsigned short num_rx_bufs ;
unsigned char width ; /* 0 for 16bit, 1 for 8bit */
unsigned char was_promisc ;
unsigned char old_mc_count ;
spinlock_t lock ;
} ;
/* This is the code and data that is downloaded to the EtherExpress card's
* memory at boot time .
*/
static unsigned short start_code [ ] = {
/* 0x0000 */
0x0001 , /* ISCP: busy - cleared after reset */
0x0008 , 0x0000 , 0x0000 , /* offset,address (lo,hi) of SCB */
0x0000 , 0x0000 , /* SCB: status, commands */
0x0000 , 0x0000 , /* links to first command block,
first receive descriptor */
0x0000 , 0x0000 , /* CRC error, alignment error counts */
0x0000 , 0x0000 , /* out of resources, overrun error counts */
0x0000 , 0x0000 , /* pad */
0x0000 , 0x0000 ,
/* 0x20 -- start of 82586 CU program */
# define CONF_LINK 0x20
0x0000 , Cmd_Config ,
0x0032 , /* link to next command */
0x080c , /* 12 bytes follow : fifo threshold=8 */
0x2e40 , /* don't rx bad frames
* SRDY / ARDY = > ext . sync . : preamble len = 8
* take addresses from data buffers
* 6 bytes / address
*/
0x6000 , /* default backoff method & priority
* interframe spacing = 0x60 */
0xf200 , /* slot time=0x200
* max collision retry = 0xf */
# define CONF_PROMISC 0x2e
0x0000 , /* no HDLC : normal CRC : enable broadcast
* disable promiscuous / multicast modes */
0x003c , /* minimum frame length = 60 octets) */
0x0000 , Cmd_SetAddr ,
0x003e , /* link to next command */
# define CONF_HWADDR 0x38
0x0000 , 0x0000 , 0x0000 , /* hardware address placed here */
0x0000 , Cmd_MCast ,
0x0076 , /* link to next command */
# define CONF_NR_MULTICAST 0x44
0x0000 , /* number of multicast addresses */
# define CONF_MULTICAST 0x46
0x0000 , 0x0000 , 0x0000 , /* some addresses */
0x0000 , 0x0000 , 0x0000 ,
0x0000 , 0x0000 , 0x0000 ,
0x0000 , 0x0000 , 0x0000 ,
0x0000 , 0x0000 , 0x0000 ,
0x0000 , 0x0000 , 0x0000 ,
0x0000 , 0x0000 , 0x0000 ,
0x0000 , 0x0000 , 0x0000 ,
# define CONF_DIAG_RESULT 0x76
0x0000 , Cmd_Diag ,
0x007c , /* link to next command */
0x0000 , Cmd_TDR | Cmd_INT ,
0x0084 ,
# define CONF_TDR_RESULT 0x82
0x0000 ,
0x0000 , Cmd_END | Cmd_Nop , /* end of configure sequence */
0x0084 /* dummy link */
} ;
/* maps irq number to EtherExpress magic value */
static char irqrmap [ ] = { 0 , 0 , 1 , 2 , 3 , 4 , 0 , 0 , 0 , 1 , 5 , 6 , 0 , 0 , 0 , 0 } ;
# ifdef CONFIG_MCA_LEGACY
/* mapping of the first four bits of the second POS register */
static unsigned short mca_iomap [ ] = {
0x270 , 0x260 , 0x250 , 0x240 , 0x230 , 0x220 , 0x210 , 0x200 ,
0x370 , 0x360 , 0x350 , 0x340 , 0x330 , 0x320 , 0x310 , 0x300
} ;
/* bits 5-7 of the second POS register */
static char mca_irqmap [ ] = { 12 , 9 , 3 , 4 , 5 , 10 , 11 , 15 } ;
# endif
/*
* Prototypes for Linux interface
*/
static int eexp_open ( struct net_device * dev ) ;
static int eexp_close ( struct net_device * dev ) ;
static void eexp_timeout ( struct net_device * dev ) ;
static struct net_device_stats * eexp_stats ( struct net_device * dev ) ;
static int eexp_xmit ( struct sk_buff * buf , struct net_device * dev ) ;
static irqreturn_t eexp_irq ( int irq , void * dev_addr , struct pt_regs * regs ) ;
static void eexp_set_multicast ( struct net_device * dev ) ;
/*
* Prototypes for hardware access functions
*/
static void eexp_hw_rx_pio ( struct net_device * dev ) ;
static void eexp_hw_tx_pio ( struct net_device * dev , unsigned short * buf ,
unsigned short len ) ;
static int eexp_hw_probe ( struct net_device * dev , unsigned short ioaddr ) ;
static unsigned short eexp_hw_readeeprom ( unsigned short ioaddr ,
unsigned char location ) ;
static unsigned short eexp_hw_lasttxstat ( struct net_device * dev ) ;
static void eexp_hw_txrestart ( struct net_device * dev ) ;
static void eexp_hw_txinit ( struct net_device * dev ) ;
static void eexp_hw_rxinit ( struct net_device * dev ) ;
static void eexp_hw_init586 ( struct net_device * dev ) ;
static void eexp_setup_filter ( struct net_device * dev ) ;
static char * eexp_ifmap [ ] = { " AUI " , " BNC " , " RJ45 " } ;
enum eexp_iftype { AUI = 0 , BNC = 1 , TPE = 2 } ;
# define STARTED_RU 2
# define STARTED_CU 1
/*
* Primitive hardware access functions .
*/
static inline unsigned short scb_status ( struct net_device * dev )
{
return inw ( dev - > base_addr + 0xc008 ) ;
}
static inline unsigned short scb_rdcmd ( struct net_device * dev )
{
return inw ( dev - > base_addr + 0xc00a ) ;
}
static inline void scb_command ( struct net_device * dev , unsigned short cmd )
{
outw ( cmd , dev - > base_addr + 0xc00a ) ;
}
static inline void scb_wrcbl ( struct net_device * dev , unsigned short val )
{
outw ( val , dev - > base_addr + 0xc00c ) ;
}
static inline void scb_wrrfa ( struct net_device * dev , unsigned short val )
{
outw ( val , dev - > base_addr + 0xc00e ) ;
}
static inline void set_loopback ( struct net_device * dev )
{
outb ( inb ( dev - > base_addr + Config ) | 2 , dev - > base_addr + Config ) ;
}
static inline void clear_loopback ( struct net_device * dev )
{
outb ( inb ( dev - > base_addr + Config ) & ~ 2 , dev - > base_addr + Config ) ;
}
static inline unsigned short int SHADOW ( short int addr )
{
addr & = 0x1f ;
if ( addr > 0xf ) addr + = 0x3ff0 ;
return addr + 0x4000 ;
}
/*
* Linux interface
*/
/*
* checks for presence of EtherExpress card
*/
static int __init do_express_probe ( struct net_device * dev )
{
unsigned short * port ;
static unsigned short ports [ ] = { 0x240 , 0x300 , 0x310 , 0x270 , 0x320 , 0x340 , 0 } ;
unsigned short ioaddr = dev - > base_addr ;
int dev_irq = dev - > irq ;
int err ;
SET_MODULE_OWNER ( dev ) ;
dev - > if_port = 0xff ; /* not set */
# ifdef CONFIG_MCA_LEGACY
if ( MCA_bus ) {
int slot = 0 ;
/*
* Only find one card at a time . Subsequent calls
* will find others , however , proper multicard MCA
* probing and setup can ' t be done with the
* old - style Space . c init routines . - - ASF
*/
while ( slot ! = MCA_NOTFOUND ) {
int pos0 , pos1 ;
slot = mca_find_unused_adapter ( 0x628B , slot ) ;
if ( slot = = MCA_NOTFOUND )
break ;
pos0 = mca_read_stored_pos ( slot , 2 ) ;
pos1 = mca_read_stored_pos ( slot , 3 ) ;
ioaddr = mca_iomap [ pos1 & 0xf ] ;
dev - > irq = mca_irqmap [ ( pos1 > > 4 ) & 0x7 ] ;
/*
* XXX : Transciever selection is done
* differently on the MCA version .
* How to get it to select something
* other than external / AUI is currently
* unknown . This code is just for looks . - - ASF
*/
if ( ( pos0 & 0x7 ) = = 0x1 )
dev - > if_port = AUI ;
else if ( ( pos0 & 0x7 ) = = 0x5 ) {
if ( pos1 & 0x80 )
dev - > if_port = BNC ;
else
dev - > if_port = TPE ;
}
mca_set_adapter_name ( slot , " Intel EtherExpress 16 MCA " ) ;
mca_set_adapter_procfn ( slot , NULL , dev ) ;
mca_mark_as_used ( slot ) ;
break ;
}
}
# endif
if ( ioaddr & 0xfe00 ) {
if ( ! request_region ( ioaddr , EEXP_IO_EXTENT , " EtherExpress " ) )
return - EBUSY ;
err = eexp_hw_probe ( dev , ioaddr ) ;
release_region ( ioaddr , EEXP_IO_EXTENT ) ;
return err ;
} else if ( ioaddr )
return - ENXIO ;
for ( port = & ports [ 0 ] ; * port ; port + + )
{
unsigned short sum = 0 ;
int i ;
if ( ! request_region ( * port , EEXP_IO_EXTENT , " EtherExpress " ) )
continue ;
for ( i = 0 ; i < 4 ; i + + )
{
unsigned short t ;
t = inb ( * port + ID_PORT ) ;
sum | = ( t > > 4 ) < < ( ( t & 0x03 ) < < 2 ) ;
}
if ( sum = = 0xbaba & & ! eexp_hw_probe ( dev , * port ) ) {
release_region ( * port , EEXP_IO_EXTENT ) ;
return 0 ;
}
release_region ( * port , EEXP_IO_EXTENT ) ;
dev - > irq = dev_irq ;
}
return - ENODEV ;
}
# ifndef MODULE
struct net_device * __init express_probe ( int unit )
{
struct net_device * dev = alloc_etherdev ( sizeof ( struct net_local ) ) ;
int err ;
if ( ! dev )
return ERR_PTR ( - ENOMEM ) ;
sprintf ( dev - > name , " eth%d " , unit ) ;
netdev_boot_setup_check ( dev ) ;
err = do_express_probe ( dev ) ;
2005-05-13 04:11:55 +04:00
if ( ! err )
return dev ;
2005-04-17 02:20:36 +04:00
free_netdev ( dev ) ;
return ERR_PTR ( err ) ;
}
# endif
/*
* open and initialize the adapter , ready for use
*/
static int eexp_open ( struct net_device * dev )
{
int ret ;
unsigned short ioaddr = dev - > base_addr ;
struct net_local * lp = netdev_priv ( dev ) ;
# if NET_DEBUG > 6
printk ( KERN_DEBUG " %s: eexp_open() \n " , dev - > name ) ;
# endif
if ( ! dev - > irq | | ! irqrmap [ dev - > irq ] )
return - ENXIO ;
ret = request_irq ( dev - > irq , & eexp_irq , 0 , dev - > name , dev ) ;
if ( ret ) return ret ;
if ( ! request_region ( ioaddr , EEXP_IO_EXTENT , " EtherExpress " ) ) {
printk ( KERN_WARNING " EtherExpress io port %x, is busy. \n "
, ioaddr ) ;
goto err_out1 ;
}
if ( ! request_region ( ioaddr + 0x4000 , EEXP_IO_EXTENT , " EtherExpress shadow " ) ) {
printk ( KERN_WARNING " EtherExpress io port %x, is busy. \n "
, ioaddr + 0x4000 ) ;
goto err_out2 ;
}
if ( ! request_region ( ioaddr + 0x8000 , EEXP_IO_EXTENT , " EtherExpress shadow " ) ) {
printk ( KERN_WARNING " EtherExpress io port %x, is busy. \n "
, ioaddr + 0x8000 ) ;
goto err_out3 ;
}
if ( ! request_region ( ioaddr + 0xc000 , EEXP_IO_EXTENT , " EtherExpress shadow " ) ) {
printk ( KERN_WARNING " EtherExpress io port %x, is busy. \n "
, ioaddr + 0xc000 ) ;
goto err_out4 ;
}
if ( lp - > width ) {
printk ( " %s: forcing ASIC to 8-bit mode \n " , dev - > name ) ;
outb ( inb ( dev - > base_addr + Config ) & ~ 4 , dev - > base_addr + Config ) ;
}
eexp_hw_init586 ( dev ) ;
netif_start_queue ( dev ) ;
# if NET_DEBUG > 6
printk ( KERN_DEBUG " %s: leaving eexp_open() \n " , dev - > name ) ;
# endif
return 0 ;
err_out4 :
release_region ( ioaddr + 0x8000 , EEXP_IO_EXTENT ) ;
err_out3 :
release_region ( ioaddr + 0x4000 , EEXP_IO_EXTENT ) ;
err_out2 :
release_region ( ioaddr , EEXP_IO_EXTENT ) ;
err_out1 :
free_irq ( dev - > irq , dev ) ;
return - EBUSY ;
}
/*
* close and disable the interface , leaving the 586 in reset .
*/
static int eexp_close ( struct net_device * dev )
{
unsigned short ioaddr = dev - > base_addr ;
struct net_local * lp = netdev_priv ( dev ) ;
int irq = dev - > irq ;
netif_stop_queue ( dev ) ;
outb ( SIRQ_dis | irqrmap [ irq ] , ioaddr + SET_IRQ ) ;
lp - > started = 0 ;
scb_command ( dev , SCB_CUsuspend | SCB_RUsuspend ) ;
outb ( 0 , ioaddr + SIGNAL_CA ) ;
free_irq ( irq , dev ) ;
outb ( i586_RST , ioaddr + EEPROM_Ctrl ) ;
release_region ( ioaddr , EEXP_IO_EXTENT ) ;
release_region ( ioaddr + 0x4000 , 16 ) ;
release_region ( ioaddr + 0x8000 , 16 ) ;
release_region ( ioaddr + 0xc000 , 16 ) ;
return 0 ;
}
/*
* Return interface stats
*/
static struct net_device_stats * eexp_stats ( struct net_device * dev )
{
struct net_local * lp = netdev_priv ( dev ) ;
return & lp - > stats ;
}
/*
* This gets called when a higher level thinks we are broken . Check that
* nothing has become jammed in the CU .
*/
static void unstick_cu ( struct net_device * dev )
{
struct net_local * lp = netdev_priv ( dev ) ;
unsigned short ioaddr = dev - > base_addr ;
if ( lp - > started )
{
if ( ( jiffies - dev - > trans_start ) > 50 )
{
if ( lp - > tx_link = = lp - > last_tx_restart )
{
unsigned short boguscount = 200 , rsst ;
printk ( KERN_WARNING " %s: Retransmit timed out, status %04x, resetting... \n " ,
dev - > name , scb_status ( dev ) ) ;
eexp_hw_txinit ( dev ) ;
lp - > last_tx_restart = 0 ;
scb_wrcbl ( dev , lp - > tx_link ) ;
scb_command ( dev , SCB_CUstart ) ;
outb ( 0 , ioaddr + SIGNAL_CA ) ;
while ( ! SCB_complete ( rsst = scb_status ( dev ) ) )
{
if ( ! - - boguscount )
{
boguscount = 200 ;
printk ( KERN_WARNING " %s: Reset timed out status %04x, retrying... \n " ,
dev - > name , rsst ) ;
scb_wrcbl ( dev , lp - > tx_link ) ;
scb_command ( dev , SCB_CUstart ) ;
outb ( 0 , ioaddr + SIGNAL_CA ) ;
}
}
netif_wake_queue ( dev ) ;
}
else
{
unsigned short status = scb_status ( dev ) ;
if ( SCB_CUdead ( status ) )
{
unsigned short txstatus = eexp_hw_lasttxstat ( dev ) ;
printk ( KERN_WARNING " %s: Transmit timed out, CU not active status %04x %04x, restarting... \n " ,
dev - > name , status , txstatus ) ;
eexp_hw_txrestart ( dev ) ;
}
else
{
unsigned short txstatus = eexp_hw_lasttxstat ( dev ) ;
if ( netif_queue_stopped ( dev ) & & ! txstatus )
{
printk ( KERN_WARNING " %s: CU wedged, status %04x %04x, resetting... \n " ,
dev - > name , status , txstatus ) ;
eexp_hw_init586 ( dev ) ;
netif_wake_queue ( dev ) ;
}
else
{
printk ( KERN_WARNING " %s: transmit timed out \n " , dev - > name ) ;
}
}
}
}
}
else
{
if ( ( jiffies - lp - > init_time ) > 10 )
{
unsigned short status = scb_status ( dev ) ;
printk ( KERN_WARNING " %s: i82586 startup timed out, status %04x, resetting... \n " ,
dev - > name , status ) ;
eexp_hw_init586 ( dev ) ;
netif_wake_queue ( dev ) ;
}
}
}
static void eexp_timeout ( struct net_device * dev )
{
struct net_local * lp = netdev_priv ( dev ) ;
# ifdef CONFIG_SMP
unsigned long flags ;
# endif
int status ;
disable_irq ( dev - > irq ) ;
/*
* Best would be to use synchronize_irq ( ) ; spin_lock ( ) here
* lets make it work first . .
*/
# ifdef CONFIG_SMP
spin_lock_irqsave ( & lp - > lock , flags ) ;
# endif
status = scb_status ( dev ) ;
unstick_cu ( dev ) ;
printk ( KERN_INFO " %s: transmit timed out, %s? \n " , dev - > name ,
( SCB_complete ( status ) ? " lost interrupt " :
" board on fire " ) ) ;
lp - > stats . tx_errors + + ;
lp - > last_tx = jiffies ;
if ( ! SCB_complete ( status ) ) {
scb_command ( dev , SCB_CUabort ) ;
outb ( 0 , dev - > base_addr + SIGNAL_CA ) ;
}
netif_wake_queue ( dev ) ;
# ifdef CONFIG_SMP
spin_unlock_irqrestore ( & lp - > lock , flags ) ;
# endif
}
/*
* Called to transmit a packet , or to allow us to right ourselves
* if the kernel thinks we ' ve died .
*/
static int eexp_xmit ( struct sk_buff * buf , struct net_device * dev )
{
struct net_local * lp = netdev_priv ( dev ) ;
short length = buf - > len ;
# ifdef CONFIG_SMP
unsigned long flags ;
# endif
# if NET_DEBUG > 6
printk ( KERN_DEBUG " %s: eexp_xmit() \n " , dev - > name ) ;
# endif
if ( buf - > len < ETH_ZLEN ) {
buf = skb_padto ( buf , ETH_ZLEN ) ;
if ( buf = = NULL )
return 0 ;
length = ETH_ZLEN ;
}
disable_irq ( dev - > irq ) ;
/*
* Best would be to use synchronize_irq ( ) ; spin_lock ( ) here
* lets make it work first . .
*/
# ifdef CONFIG_SMP
spin_lock_irqsave ( & lp - > lock , flags ) ;
# endif
{
unsigned short * data = ( unsigned short * ) buf - > data ;
lp - > stats . tx_bytes + = length ;
eexp_hw_tx_pio ( dev , data , length ) ;
}
dev_kfree_skb ( buf ) ;
# ifdef CONFIG_SMP
spin_unlock_irqrestore ( & lp - > lock , flags ) ;
# endif
enable_irq ( dev - > irq ) ;
return 0 ;
}
/*
* Handle an EtherExpress interrupt
* If we ' ve finished initializing , start the RU and CU up .
* If we ' ve already started , reap tx buffers , handle any received packets ,
* check to make sure we ' ve not become wedged .
*/
/*
* Handle an EtherExpress interrupt
* If we ' ve finished initializing , start the RU and CU up .
* If we ' ve already started , reap tx buffers , handle any received packets ,
* check to make sure we ' ve not become wedged .
*/
static unsigned short eexp_start_irq ( struct net_device * dev ,
unsigned short status )
{
unsigned short ack_cmd = SCB_ack ( status ) ;
struct net_local * lp = netdev_priv ( dev ) ;
unsigned short ioaddr = dev - > base_addr ;
if ( ( dev - > flags & IFF_UP ) & & ! ( lp - > started & STARTED_CU ) ) {
short diag_status , tdr_status ;
while ( SCB_CUstat ( status ) = = 2 )
status = scb_status ( dev ) ;
# if NET_DEBUG > 4
printk ( " %s: CU went non-active (status %04x) \n " ,
dev - > name , status ) ;
# endif
outw ( CONF_DIAG_RESULT & ~ 31 , ioaddr + SM_PTR ) ;
diag_status = inw ( ioaddr + SHADOW ( CONF_DIAG_RESULT ) ) ;
if ( diag_status & 1 < < 11 ) {
printk ( KERN_WARNING " %s: 82586 failed self-test \n " ,
dev - > name ) ;
} else if ( ! ( diag_status & 1 < < 13 ) ) {
printk ( KERN_WARNING " %s: 82586 self-test failed to complete \n " , dev - > name ) ;
}
outw ( CONF_TDR_RESULT & ~ 31 , ioaddr + SM_PTR ) ;
tdr_status = inw ( ioaddr + SHADOW ( CONF_TDR_RESULT ) ) ;
if ( tdr_status & ( TDR_SHORT | TDR_OPEN ) ) {
printk ( KERN_WARNING " %s: TDR reports cable %s at %d tick%s \n " , dev - > name , ( tdr_status & TDR_SHORT ) ? " short " : " broken " , tdr_status & TDR_TIME , ( ( tdr_status & TDR_TIME ) ! = 1 ) ? " s " : " " ) ;
}
else if ( tdr_status & TDR_XCVRPROBLEM ) {
printk ( KERN_WARNING " %s: TDR reports transceiver problem \n " , dev - > name ) ;
}
else if ( tdr_status & TDR_LINKOK ) {
# if NET_DEBUG > 4
printk ( KERN_DEBUG " %s: TDR reports link OK \n " , dev - > name ) ;
# endif
} else {
printk ( " %s: TDR is ga-ga (status %04x) \n " , dev - > name ,
tdr_status ) ;
}
lp - > started | = STARTED_CU ;
scb_wrcbl ( dev , lp - > tx_link ) ;
/* if the RU isn't running, start it now */
if ( ! ( lp - > started & STARTED_RU ) ) {
ack_cmd | = SCB_RUstart ;
scb_wrrfa ( dev , lp - > rx_buf_start ) ;
lp - > rx_ptr = lp - > rx_buf_start ;
lp - > started | = STARTED_RU ;
}
ack_cmd | = SCB_CUstart | 0x2000 ;
}
if ( ( dev - > flags & IFF_UP ) & & ! ( lp - > started & STARTED_RU ) & & SCB_RUstat ( status ) = = 4 )
lp - > started | = STARTED_RU ;
return ack_cmd ;
}
static void eexp_cmd_clear ( struct net_device * dev )
{
unsigned long int oldtime = jiffies ;
while ( scb_rdcmd ( dev ) & & ( ( jiffies - oldtime ) < 10 ) ) ;
if ( scb_rdcmd ( dev ) ) {
printk ( " %s: command didn't clear \n " , dev - > name ) ;
}
}
static irqreturn_t eexp_irq ( int irq , void * dev_info , struct pt_regs * regs )
{
struct net_device * dev = dev_info ;
struct net_local * lp ;
unsigned short ioaddr , status , ack_cmd ;
unsigned short old_read_ptr , old_write_ptr ;
if ( dev = = NULL )
{
printk ( KERN_WARNING " eexpress: irq %d for unknown device \n " ,
irq ) ;
return IRQ_NONE ;
}
lp = netdev_priv ( dev ) ;
ioaddr = dev - > base_addr ;
spin_lock ( & lp - > lock ) ;
old_read_ptr = inw ( ioaddr + READ_PTR ) ;
old_write_ptr = inw ( ioaddr + WRITE_PTR ) ;
outb ( SIRQ_dis | irqrmap [ irq ] , ioaddr + SET_IRQ ) ;
status = scb_status ( dev ) ;
# if NET_DEBUG > 4
printk ( KERN_DEBUG " %s: interrupt (status %x) \n " , dev - > name , status ) ;
# endif
if ( lp - > started = = ( STARTED_CU | STARTED_RU ) ) {
do {
eexp_cmd_clear ( dev ) ;
ack_cmd = SCB_ack ( status ) ;
scb_command ( dev , ack_cmd ) ;
outb ( 0 , ioaddr + SIGNAL_CA ) ;
eexp_cmd_clear ( dev ) ;
if ( SCB_complete ( status ) ) {
if ( ! eexp_hw_lasttxstat ( dev ) ) {
printk ( " %s: tx interrupt but no status \n " , dev - > name ) ;
}
}
if ( SCB_rxdframe ( status ) )
eexp_hw_rx_pio ( dev ) ;
status = scb_status ( dev ) ;
} while ( status & 0xc000 ) ;
if ( SCB_RUdead ( status ) )
{
printk ( KERN_WARNING " %s: RU stopped: status %04x \n " ,
dev - > name , status ) ;
#if 0
printk ( KERN_WARNING " %s: cur_rfd=%04x, cur_rbd=%04x \n " , dev - > name , lp - > cur_rfd , lp - > cur_rbd ) ;
outw ( lp - > cur_rfd , ioaddr + READ_PTR ) ;
printk ( KERN_WARNING " %s: [%04x] \n " , dev - > name , inw ( ioaddr + DATAPORT ) ) ;
outw ( lp - > cur_rfd + 6 , ioaddr + READ_PTR ) ;
printk ( KERN_WARNING " %s: rbd is %04x \n " , dev - > name , rbd = inw ( ioaddr + DATAPORT ) ) ;
outw ( rbd , ioaddr + READ_PTR ) ;
printk ( KERN_WARNING " %s: [%04x %04x] " , dev - > name , inw ( ioaddr + DATAPORT ) , inw ( ioaddr + DATAPORT ) ) ;
outw ( rbd + 8 , ioaddr + READ_PTR ) ;
printk ( " [%04x] \n " , inw ( ioaddr + DATAPORT ) ) ;
# endif
lp - > stats . rx_errors + + ;
# if 1
eexp_hw_rxinit ( dev ) ;
# else
lp - > cur_rfd = lp - > first_rfd ;
# endif
scb_wrrfa ( dev , lp - > rx_buf_start ) ;
scb_command ( dev , SCB_RUstart ) ;
outb ( 0 , ioaddr + SIGNAL_CA ) ;
}
} else {
if ( status & 0x8000 )
ack_cmd = eexp_start_irq ( dev , status ) ;
else
ack_cmd = SCB_ack ( status ) ;
scb_command ( dev , ack_cmd ) ;
outb ( 0 , ioaddr + SIGNAL_CA ) ;
}
eexp_cmd_clear ( dev ) ;
outb ( SIRQ_en | irqrmap [ irq ] , ioaddr + SET_IRQ ) ;
# if NET_DEBUG > 6
printk ( " %s: leaving eexp_irq() \n " , dev - > name ) ;
# endif
outw ( old_read_ptr , ioaddr + READ_PTR ) ;
outw ( old_write_ptr , ioaddr + WRITE_PTR ) ;
spin_unlock ( & lp - > lock ) ;
return IRQ_HANDLED ;
}
/*
* Hardware access functions
*/
/*
* Set the cable type to use .
*/
static void eexp_hw_set_interface ( struct net_device * dev )
{
unsigned char oldval = inb ( dev - > base_addr + 0x300e ) ;
oldval & = ~ 0x82 ;
switch ( dev - > if_port ) {
case TPE :
oldval | = 0x2 ;
case BNC :
oldval | = 0x80 ;
break ;
}
outb ( oldval , dev - > base_addr + 0x300e ) ;
mdelay ( 20 ) ;
}
/*
* Check all the receive buffers , and hand any received packets
* to the upper levels . Basic sanity check on each frame
* descriptor , though we don ' t bother trying to fix broken ones .
*/
static void eexp_hw_rx_pio ( struct net_device * dev )
{
struct net_local * lp = netdev_priv ( dev ) ;
unsigned short rx_block = lp - > rx_ptr ;
unsigned short boguscount = lp - > num_rx_bufs ;
unsigned short ioaddr = dev - > base_addr ;
unsigned short status ;
# if NET_DEBUG > 6
printk ( KERN_DEBUG " %s: eexp_hw_rx() \n " , dev - > name ) ;
# endif
do {
unsigned short rfd_cmd , rx_next , pbuf , pkt_len ;
outw ( rx_block , ioaddr + READ_PTR ) ;
status = inw ( ioaddr + DATAPORT ) ;
if ( FD_Done ( status ) )
{
rfd_cmd = inw ( ioaddr + DATAPORT ) ;
rx_next = inw ( ioaddr + DATAPORT ) ;
pbuf = inw ( ioaddr + DATAPORT ) ;
outw ( pbuf , ioaddr + READ_PTR ) ;
pkt_len = inw ( ioaddr + DATAPORT ) ;
if ( rfd_cmd ! = 0x0000 )
{
printk ( KERN_WARNING " %s: rfd_cmd not zero:0x%04x \n " ,
dev - > name , rfd_cmd ) ;
continue ;
}
else if ( pbuf ! = rx_block + 0x16 )
{
printk ( KERN_WARNING " %s: rfd and rbd out of sync 0x%04x 0x%04x \n " ,
dev - > name , rx_block + 0x16 , pbuf ) ;
continue ;
}
else if ( ( pkt_len & 0xc000 ) ! = 0xc000 )
{
printk ( KERN_WARNING " %s: EOF or F not set on received buffer (%04x) \n " ,
dev - > name , pkt_len & 0xc000 ) ;
continue ;
}
else if ( ! FD_OK ( status ) )
{
lp - > stats . rx_errors + + ;
if ( FD_CRC ( status ) )
lp - > stats . rx_crc_errors + + ;
if ( FD_Align ( status ) )
lp - > stats . rx_frame_errors + + ;
if ( FD_Resrc ( status ) )
lp - > stats . rx_fifo_errors + + ;
if ( FD_DMA ( status ) )
lp - > stats . rx_over_errors + + ;
if ( FD_Short ( status ) )
lp - > stats . rx_length_errors + + ;
}
else
{
struct sk_buff * skb ;
pkt_len & = 0x3fff ;
skb = dev_alloc_skb ( pkt_len + 16 ) ;
if ( skb = = NULL )
{
printk ( KERN_WARNING " %s: Memory squeeze, dropping packet \n " , dev - > name ) ;
lp - > stats . rx_dropped + + ;
break ;
}
skb - > dev = dev ;
skb_reserve ( skb , 2 ) ;
outw ( pbuf + 10 , ioaddr + READ_PTR ) ;
insw ( ioaddr + DATAPORT , skb_put ( skb , pkt_len ) , ( pkt_len + 1 ) > > 1 ) ;
skb - > protocol = eth_type_trans ( skb , dev ) ;
netif_rx ( skb ) ;
dev - > last_rx = jiffies ;
lp - > stats . rx_packets + + ;
lp - > stats . rx_bytes + = pkt_len ;
}
outw ( rx_block , ioaddr + WRITE_PTR ) ;
outw ( 0 , ioaddr + DATAPORT ) ;
outw ( 0 , ioaddr + DATAPORT ) ;
rx_block = rx_next ;
}
} while ( FD_Done ( status ) & & boguscount - - ) ;
lp - > rx_ptr = rx_block ;
}
/*
* Hand a packet to the card for transmission
* If we get here , we MUST have already checked
* to make sure there is room in the transmit
* buffer region .
*/
static void eexp_hw_tx_pio ( struct net_device * dev , unsigned short * buf ,
unsigned short len )
{
struct net_local * lp = netdev_priv ( dev ) ;
unsigned short ioaddr = dev - > base_addr ;
if ( LOCKUP16 | | lp - > width ) {
/* Stop the CU so that there is no chance that it
jumps off to a bogus address while we are writing the
pointer to the next transmit packet in 8 - bit mode - -
this eliminates the " CU wedged " errors in 8 - bit mode .
( Zoltan Szilagyi 10 - 12 - 96 ) */
scb_command ( dev , SCB_CUsuspend ) ;
outw ( 0xFFFF , ioaddr + SIGNAL_CA ) ;
}
outw ( lp - > tx_head , ioaddr + WRITE_PTR ) ;
outw ( 0x0000 , ioaddr + DATAPORT ) ;
outw ( Cmd_INT | Cmd_Xmit , ioaddr + DATAPORT ) ;
outw ( lp - > tx_head + 0x08 , ioaddr + DATAPORT ) ;
outw ( lp - > tx_head + 0x0e , ioaddr + DATAPORT ) ;
outw ( 0x0000 , ioaddr + DATAPORT ) ;
outw ( 0x0000 , ioaddr + DATAPORT ) ;
outw ( lp - > tx_head + 0x08 , ioaddr + DATAPORT ) ;
outw ( 0x8000 | len , ioaddr + DATAPORT ) ;
outw ( - 1 , ioaddr + DATAPORT ) ;
outw ( lp - > tx_head + 0x16 , ioaddr + DATAPORT ) ;
outw ( 0 , ioaddr + DATAPORT ) ;
outsw ( ioaddr + DATAPORT , buf , ( len + 1 ) > > 1 ) ;
outw ( lp - > tx_tail + 0xc , ioaddr + WRITE_PTR ) ;
outw ( lp - > tx_head , ioaddr + DATAPORT ) ;
dev - > trans_start = jiffies ;
lp - > tx_tail = lp - > tx_head ;
if ( lp - > tx_head = = TX_BUF_START + ( ( lp - > num_tx_bufs - 1 ) * TX_BUF_SIZE ) )
lp - > tx_head = TX_BUF_START ;
else
lp - > tx_head + = TX_BUF_SIZE ;
if ( lp - > tx_head ! = lp - > tx_reap )
netif_wake_queue ( dev ) ;
if ( LOCKUP16 | | lp - > width ) {
/* Restart the CU so that the packet can actually
be transmitted . ( Zoltan Szilagyi 10 - 12 - 96 ) */
scb_command ( dev , SCB_CUresume ) ;
outw ( 0xFFFF , ioaddr + SIGNAL_CA ) ;
}
lp - > stats . tx_packets + + ;
lp - > last_tx = jiffies ;
}
/*
* Sanity check the suspected EtherExpress card
* Read hardware address , reset card , size memory and initialize buffer
* memory pointers . These are held in dev - > priv , in case someone has more
* than one card in a machine .
*/
static int __init eexp_hw_probe ( struct net_device * dev , unsigned short ioaddr )
{
unsigned short hw_addr [ 3 ] ;
unsigned char buswidth ;
unsigned int memory_size ;
int i ;
unsigned short xsum = 0 ;
struct net_local * lp = netdev_priv ( dev ) ;
printk ( " %s: EtherExpress 16 at %#x " , dev - > name , ioaddr ) ;
outb ( ASIC_RST , ioaddr + EEPROM_Ctrl ) ;
outb ( 0 , ioaddr + EEPROM_Ctrl ) ;
udelay ( 500 ) ;
outb ( i586_RST , ioaddr + EEPROM_Ctrl ) ;
hw_addr [ 0 ] = eexp_hw_readeeprom ( ioaddr , 2 ) ;
hw_addr [ 1 ] = eexp_hw_readeeprom ( ioaddr , 3 ) ;
hw_addr [ 2 ] = eexp_hw_readeeprom ( ioaddr , 4 ) ;
/* Standard Address or Compaq LTE Address */
if ( ! ( ( hw_addr [ 2 ] = = 0x00aa & & ( ( hw_addr [ 1 ] & 0xff00 ) = = 0x0000 ) ) | |
( hw_addr [ 2 ] = = 0x0080 & & ( ( hw_addr [ 1 ] & 0xff00 ) = = 0x5F00 ) ) ) )
{
printk ( " rejected: invalid address %04x%04x%04x \n " ,
hw_addr [ 2 ] , hw_addr [ 1 ] , hw_addr [ 0 ] ) ;
return - ENODEV ;
}
/* Calculate the EEPROM checksum. Carry on anyway if it's bad,
* though .
*/
for ( i = 0 ; i < 64 ; i + + )
xsum + = eexp_hw_readeeprom ( ioaddr , i ) ;
if ( xsum ! = 0xbaba )
printk ( " (bad EEPROM xsum 0x%02x) " , xsum ) ;
dev - > base_addr = ioaddr ;
for ( i = 0 ; i < 6 ; i + + )
dev - > dev_addr [ i ] = ( ( unsigned char * ) hw_addr ) [ 5 - i ] ;
{
static char irqmap [ ] = { 0 , 9 , 3 , 4 , 5 , 10 , 11 , 0 } ;
unsigned short setupval = eexp_hw_readeeprom ( ioaddr , 0 ) ;
/* Use the IRQ from EEPROM if none was given */
if ( ! dev - > irq )
dev - > irq = irqmap [ setupval > > 13 ] ;
if ( dev - > if_port = = 0xff ) {
dev - > if_port = ! ( setupval & 0x1000 ) ? AUI :
eexp_hw_readeeprom ( ioaddr , 5 ) & 0x1 ? TPE : BNC ;
}
buswidth = ! ( ( setupval & 0x400 ) > > 10 ) ;
}
memset ( lp , 0 , sizeof ( struct net_local ) ) ;
spin_lock_init ( & lp - > lock ) ;
printk ( " (IRQ %d, %s connector, %d-bit bus " , dev - > irq ,
eexp_ifmap [ dev - > if_port ] , buswidth ? 8 : 16 ) ;
if ( ! request_region ( dev - > base_addr + 0x300e , 1 , " EtherExpress " ) )
return - EBUSY ;
eexp_hw_set_interface ( dev ) ;
release_region ( dev - > base_addr + 0x300e , 1 ) ;
/* Find out how much RAM we have on the card */
outw ( 0 , dev - > base_addr + WRITE_PTR ) ;
for ( i = 0 ; i < 32768 ; i + + )
outw ( 0 , dev - > base_addr + DATAPORT ) ;
for ( memory_size = 0 ; memory_size < 64 ; memory_size + + )
{
outw ( memory_size < < 10 , dev - > base_addr + READ_PTR ) ;
if ( inw ( dev - > base_addr + DATAPORT ) )
break ;
outw ( memory_size < < 10 , dev - > base_addr + WRITE_PTR ) ;
outw ( memory_size | 0x5000 , dev - > base_addr + DATAPORT ) ;
outw ( memory_size < < 10 , dev - > base_addr + READ_PTR ) ;
if ( inw ( dev - > base_addr + DATAPORT ) ! = ( memory_size | 0x5000 ) )
break ;
}
/* Sort out the number of buffers. We may have 16, 32, 48 or 64k
* of RAM to play with .
*/
lp - > num_tx_bufs = 4 ;
lp - > rx_buf_end = 0x3ff6 ;
switch ( memory_size )
{
case 64 :
lp - > rx_buf_end + = 0x4000 ;
case 48 :
lp - > num_tx_bufs + = 4 ;
lp - > rx_buf_end + = 0x4000 ;
case 32 :
lp - > rx_buf_end + = 0x4000 ;
case 16 :
printk ( " , %dk RAM) \n " , memory_size ) ;
break ;
default :
printk ( " ) bad memory size (%dk). \n " , memory_size ) ;
return - ENODEV ;
break ;
}
lp - > rx_buf_start = TX_BUF_START + ( lp - > num_tx_bufs * TX_BUF_SIZE ) ;
lp - > width = buswidth ;
dev - > open = eexp_open ;
dev - > stop = eexp_close ;
dev - > hard_start_xmit = eexp_xmit ;
dev - > get_stats = eexp_stats ;
dev - > set_multicast_list = & eexp_set_multicast ;
dev - > tx_timeout = eexp_timeout ;
dev - > watchdog_timeo = 2 * HZ ;
2005-05-13 04:11:55 +04:00
return register_netdev ( dev ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Read a word from the EtherExpress on - board serial EEPROM .
* The EEPROM contains 64 words of 16 bits .
*/
static unsigned short __init eexp_hw_readeeprom ( unsigned short ioaddr ,
unsigned char location )
{
unsigned short cmd = 0x180 | ( location & 0x7f ) ;
unsigned short rval = 0 , wval = EC_CS | i586_RST ;
int i ;
outb ( EC_CS | i586_RST , ioaddr + EEPROM_Ctrl ) ;
for ( i = 0x100 ; i ; i > > = 1 )
{
if ( cmd & i )
wval | = EC_Wr ;
else
wval & = ~ EC_Wr ;
outb ( wval , ioaddr + EEPROM_Ctrl ) ;
outb ( wval | EC_Clk , ioaddr + EEPROM_Ctrl ) ;
eeprom_delay ( ) ;
outb ( wval , ioaddr + EEPROM_Ctrl ) ;
eeprom_delay ( ) ;
}
wval & = ~ EC_Wr ;
outb ( wval , ioaddr + EEPROM_Ctrl ) ;
for ( i = 0x8000 ; i ; i > > = 1 )
{
outb ( wval | EC_Clk , ioaddr + EEPROM_Ctrl ) ;
eeprom_delay ( ) ;
if ( inb ( ioaddr + EEPROM_Ctrl ) & EC_Rd )
rval | = i ;
outb ( wval , ioaddr + EEPROM_Ctrl ) ;
eeprom_delay ( ) ;
}
wval & = ~ EC_CS ;
outb ( wval | EC_Clk , ioaddr + EEPROM_Ctrl ) ;
eeprom_delay ( ) ;
outb ( wval , ioaddr + EEPROM_Ctrl ) ;
eeprom_delay ( ) ;
return rval ;
}
/*
* Reap tx buffers and return last transmit status .
* if = = 0 then either :
* a ) we ' re not transmitting anything , so why are we here ?
* b ) we ' ve died .
* otherwise , Stat_Busy ( return ) means we ' ve still got some packets
* to transmit , Stat_Done ( return ) means our buffers should be empty
* again
*/
static unsigned short eexp_hw_lasttxstat ( struct net_device * dev )
{
struct net_local * lp = netdev_priv ( dev ) ;
unsigned short tx_block = lp - > tx_reap ;
unsigned short status ;
if ( ! netif_queue_stopped ( dev ) & & lp - > tx_head = = lp - > tx_reap )
return 0x0000 ;
do
{
outw ( tx_block & ~ 31 , dev - > base_addr + SM_PTR ) ;
status = inw ( dev - > base_addr + SHADOW ( tx_block ) ) ;
if ( ! Stat_Done ( status ) )
{
lp - > tx_link = tx_block ;
return status ;
}
else
{
lp - > last_tx_restart = 0 ;
lp - > stats . collisions + = Stat_NoColl ( status ) ;
if ( ! Stat_OK ( status ) )
{
char * whatsup = NULL ;
lp - > stats . tx_errors + + ;
if ( Stat_Abort ( status ) )
lp - > stats . tx_aborted_errors + + ;
if ( Stat_TNoCar ( status ) ) {
whatsup = " aborted, no carrier " ;
lp - > stats . tx_carrier_errors + + ;
}
if ( Stat_TNoCTS ( status ) ) {
whatsup = " aborted, lost CTS " ;
lp - > stats . tx_carrier_errors + + ;
}
if ( Stat_TNoDMA ( status ) ) {
whatsup = " FIFO underran " ;
lp - > stats . tx_fifo_errors + + ;
}
if ( Stat_TXColl ( status ) ) {
whatsup = " aborted, too many collisions " ;
lp - > stats . tx_aborted_errors + + ;
}
if ( whatsup )
printk ( KERN_INFO " %s: transmit %s \n " ,
dev - > name , whatsup ) ;
}
else
lp - > stats . tx_packets + + ;
}
if ( tx_block = = TX_BUF_START + ( ( lp - > num_tx_bufs - 1 ) * TX_BUF_SIZE ) )
lp - > tx_reap = tx_block = TX_BUF_START ;
else
lp - > tx_reap = tx_block + = TX_BUF_SIZE ;
netif_wake_queue ( dev ) ;
}
while ( lp - > tx_reap ! = lp - > tx_head ) ;
lp - > tx_link = lp - > tx_tail + 0x08 ;
return status ;
}
/*
* This should never happen . It is called when some higher routine detects
* that the CU has stopped , to try to restart it from the last packet we knew
* we were working on , or the idle loop if we had finished for the time .
*/
static void eexp_hw_txrestart ( struct net_device * dev )
{
struct net_local * lp = netdev_priv ( dev ) ;
unsigned short ioaddr = dev - > base_addr ;
lp - > last_tx_restart = lp - > tx_link ;
scb_wrcbl ( dev , lp - > tx_link ) ;
scb_command ( dev , SCB_CUstart ) ;
outb ( 0 , ioaddr + SIGNAL_CA ) ;
{
unsigned short boguscount = 50 , failcount = 5 ;
while ( ! scb_status ( dev ) )
{
if ( ! - - boguscount )
{
if ( - - failcount )
{
printk ( KERN_WARNING " %s: CU start timed out, status %04x, cmd %04x \n " , dev - > name , scb_status ( dev ) , scb_rdcmd ( dev ) ) ;
scb_wrcbl ( dev , lp - > tx_link ) ;
scb_command ( dev , SCB_CUstart ) ;
outb ( 0 , ioaddr + SIGNAL_CA ) ;
boguscount = 100 ;
}
else
{
printk ( KERN_WARNING " %s: Failed to restart CU, resetting board... \n " , dev - > name ) ;
eexp_hw_init586 ( dev ) ;
netif_wake_queue ( dev ) ;
return ;
}
}
}
}
}
/*
* Writes down the list of transmit buffers into card memory . Each
* entry consists of an 82586 transmit command , followed by a jump
* pointing to itself . When we want to transmit a packet , we write
* the data into the appropriate transmit buffer and then modify the
* preceding jump to point at the new transmit command . This means that
* the 586 command unit is continuously active .
*/
static void eexp_hw_txinit ( struct net_device * dev )
{
struct net_local * lp = netdev_priv ( dev ) ;
unsigned short tx_block = TX_BUF_START ;
unsigned short curtbuf ;
unsigned short ioaddr = dev - > base_addr ;
for ( curtbuf = 0 ; curtbuf < lp - > num_tx_bufs ; curtbuf + + )
{
outw ( tx_block , ioaddr + WRITE_PTR ) ;
outw ( 0x0000 , ioaddr + DATAPORT ) ;
outw ( Cmd_INT | Cmd_Xmit , ioaddr + DATAPORT ) ;
outw ( tx_block + 0x08 , ioaddr + DATAPORT ) ;
outw ( tx_block + 0x0e , ioaddr + DATAPORT ) ;
outw ( 0x0000 , ioaddr + DATAPORT ) ;
outw ( 0x0000 , ioaddr + DATAPORT ) ;
outw ( tx_block + 0x08 , ioaddr + DATAPORT ) ;
outw ( 0x8000 , ioaddr + DATAPORT ) ;
outw ( - 1 , ioaddr + DATAPORT ) ;
outw ( tx_block + 0x16 , ioaddr + DATAPORT ) ;
outw ( 0x0000 , ioaddr + DATAPORT ) ;
tx_block + = TX_BUF_SIZE ;
}
lp - > tx_head = TX_BUF_START ;
lp - > tx_reap = TX_BUF_START ;
lp - > tx_tail = tx_block - TX_BUF_SIZE ;
lp - > tx_link = lp - > tx_tail + 0x08 ;
lp - > rx_buf_start = tx_block ;
}
/*
* Write the circular list of receive buffer descriptors to card memory .
* The end of the list isn ' t marked , which means that the 82586 receive
* unit will loop until buffers become available ( this avoids it giving us
* " out of resources " messages ) .
*/
static void eexp_hw_rxinit ( struct net_device * dev )
{
struct net_local * lp = netdev_priv ( dev ) ;
unsigned short rx_block = lp - > rx_buf_start ;
unsigned short ioaddr = dev - > base_addr ;
lp - > num_rx_bufs = 0 ;
lp - > rx_first = lp - > rx_ptr = rx_block ;
do
{
lp - > num_rx_bufs + + ;
outw ( rx_block , ioaddr + WRITE_PTR ) ;
outw ( 0 , ioaddr + DATAPORT ) ; outw ( 0 , ioaddr + DATAPORT ) ;
outw ( rx_block + RX_BUF_SIZE , ioaddr + DATAPORT ) ;
outw ( 0xffff , ioaddr + DATAPORT ) ;
outw ( 0x0000 , ioaddr + DATAPORT ) ;
outw ( 0xdead , ioaddr + DATAPORT ) ;
outw ( 0xdead , ioaddr + DATAPORT ) ;
outw ( 0xdead , ioaddr + DATAPORT ) ;
outw ( 0xdead , ioaddr + DATAPORT ) ;
outw ( 0xdead , ioaddr + DATAPORT ) ;
outw ( 0xdead , ioaddr + DATAPORT ) ;
outw ( 0x0000 , ioaddr + DATAPORT ) ;
outw ( rx_block + RX_BUF_SIZE + 0x16 , ioaddr + DATAPORT ) ;
outw ( rx_block + 0x20 , ioaddr + DATAPORT ) ;
outw ( 0 , ioaddr + DATAPORT ) ;
outw ( RX_BUF_SIZE - 0x20 , ioaddr + DATAPORT ) ;
lp - > rx_last = rx_block ;
rx_block + = RX_BUF_SIZE ;
} while ( rx_block < = lp - > rx_buf_end - RX_BUF_SIZE ) ;
/* Make first Rx frame descriptor point to first Rx buffer
descriptor */
outw ( lp - > rx_first + 6 , ioaddr + WRITE_PTR ) ;
outw ( lp - > rx_first + 0x16 , ioaddr + DATAPORT ) ;
/* Close Rx frame descriptor ring */
outw ( lp - > rx_last + 4 , ioaddr + WRITE_PTR ) ;
outw ( lp - > rx_first , ioaddr + DATAPORT ) ;
/* Close Rx buffer descriptor ring */
outw ( lp - > rx_last + 0x16 + 2 , ioaddr + WRITE_PTR ) ;
outw ( lp - > rx_first + 0x16 , ioaddr + DATAPORT ) ;
}
/*
* Un - reset the 586 , and start the configuration sequence . We don ' t wait for
* this to finish , but allow the interrupt handler to start the CU and RU for
* us . We can ' t start the receive / transmission system up before we know that
* the hardware is configured correctly .
*/
static void eexp_hw_init586 ( struct net_device * dev )
{
struct net_local * lp = netdev_priv ( dev ) ;
unsigned short ioaddr = dev - > base_addr ;
int i ;
# if NET_DEBUG > 6
printk ( " %s: eexp_hw_init586() \n " , dev - > name ) ;
# endif
lp - > started = 0 ;
set_loopback ( dev ) ;
outb ( SIRQ_dis | irqrmap [ dev - > irq ] , ioaddr + SET_IRQ ) ;
/* Download the startup code */
outw ( lp - > rx_buf_end & ~ 31 , ioaddr + SM_PTR ) ;
outw ( lp - > width ? 0x0001 : 0x0000 , ioaddr + 0x8006 ) ;
outw ( 0x0000 , ioaddr + 0x8008 ) ;
outw ( 0x0000 , ioaddr + 0x800a ) ;
outw ( 0x0000 , ioaddr + 0x800c ) ;
outw ( 0x0000 , ioaddr + 0x800e ) ;
for ( i = 0 ; i < ( sizeof ( start_code ) ) ; i + = 32 ) {
int j ;
outw ( i , ioaddr + SM_PTR ) ;
for ( j = 0 ; j < 16 ; j + = 2 )
outw ( start_code [ ( i + j ) / 2 ] ,
ioaddr + 0x4000 + j ) ;
for ( j = 0 ; j < 16 ; j + = 2 )
outw ( start_code [ ( i + j + 16 ) / 2 ] ,
ioaddr + 0x8000 + j ) ;
}
/* Do we want promiscuous mode or multicast? */
outw ( CONF_PROMISC & ~ 31 , ioaddr + SM_PTR ) ;
i = inw ( ioaddr + SHADOW ( CONF_PROMISC ) ) ;
outw ( ( dev - > flags & IFF_PROMISC ) ? ( i | 1 ) : ( i & ~ 1 ) ,
ioaddr + SHADOW ( CONF_PROMISC ) ) ;
lp - > was_promisc = dev - > flags & IFF_PROMISC ;
#if 0
eexp_setup_filter ( dev ) ;
# endif
/* Write our hardware address */
outw ( CONF_HWADDR & ~ 31 , ioaddr + SM_PTR ) ;
outw ( ( ( unsigned short * ) dev - > dev_addr ) [ 0 ] , ioaddr + SHADOW ( CONF_HWADDR ) ) ;
outw ( ( ( unsigned short * ) dev - > dev_addr ) [ 1 ] ,
ioaddr + SHADOW ( CONF_HWADDR + 2 ) ) ;
outw ( ( ( unsigned short * ) dev - > dev_addr ) [ 2 ] ,
ioaddr + SHADOW ( CONF_HWADDR + 4 ) ) ;
eexp_hw_txinit ( dev ) ;
eexp_hw_rxinit ( dev ) ;
outb ( 0 , ioaddr + EEPROM_Ctrl ) ;
mdelay ( 5 ) ;
scb_command ( dev , 0xf000 ) ;
outb ( 0 , ioaddr + SIGNAL_CA ) ;
outw ( 0 , ioaddr + SM_PTR ) ;
{
unsigned short rboguscount = 50 , rfailcount = 5 ;
while ( inw ( ioaddr + 0x4000 ) )
{
if ( ! - - rboguscount )
{
printk ( KERN_WARNING " %s: i82586 reset timed out, kicking... \n " ,
dev - > name ) ;
scb_command ( dev , 0 ) ;
outb ( 0 , ioaddr + SIGNAL_CA ) ;
rboguscount = 100 ;
if ( ! - - rfailcount )
{
printk ( KERN_WARNING " %s: i82586 not responding, giving up. \n " ,
dev - > name ) ;
return ;
}
}
}
}
scb_wrcbl ( dev , CONF_LINK ) ;
scb_command ( dev , 0xf000 | SCB_CUstart ) ;
outb ( 0 , ioaddr + SIGNAL_CA ) ;
{
unsigned short iboguscount = 50 , ifailcount = 5 ;
while ( ! scb_status ( dev ) )
{
if ( ! - - iboguscount )
{
if ( - - ifailcount )
{
printk ( KERN_WARNING " %s: i82586 initialization timed out, status %04x, cmd %04x \n " ,
dev - > name , scb_status ( dev ) , scb_rdcmd ( dev ) ) ;
scb_wrcbl ( dev , CONF_LINK ) ;
scb_command ( dev , 0xf000 | SCB_CUstart ) ;
outb ( 0 , ioaddr + SIGNAL_CA ) ;
iboguscount = 100 ;
}
else
{
printk ( KERN_WARNING " %s: Failed to initialize i82586, giving up. \n " , dev - > name ) ;
return ;
}
}
}
}
clear_loopback ( dev ) ;
outb ( SIRQ_en | irqrmap [ dev - > irq ] , ioaddr + SET_IRQ ) ;
lp - > init_time = jiffies ;
# if NET_DEBUG > 6
printk ( " %s: leaving eexp_hw_init586() \n " , dev - > name ) ;
# endif
return ;
}
static void eexp_setup_filter ( struct net_device * dev )
{
struct dev_mc_list * dmi = dev - > mc_list ;
unsigned short ioaddr = dev - > base_addr ;
int count = dev - > mc_count ;
int i ;
if ( count > 8 ) {
printk ( KERN_INFO " %s: too many multicast addresses (%d) \n " ,
dev - > name , count ) ;
count = 8 ;
}
outw ( CONF_NR_MULTICAST & ~ 31 , ioaddr + SM_PTR ) ;
outw ( count , ioaddr + SHADOW ( CONF_NR_MULTICAST ) ) ;
for ( i = 0 ; i < count ; i + + ) {
unsigned short * data = ( unsigned short * ) dmi - > dmi_addr ;
if ( ! dmi ) {
printk ( KERN_INFO " %s: too few multicast addresses \n " , dev - > name ) ;
break ;
}
if ( dmi - > dmi_addrlen ! = ETH_ALEN ) {
printk ( KERN_INFO " %s: invalid multicast address length given. \n " , dev - > name ) ;
continue ;
}
outw ( ( CONF_MULTICAST + ( 6 * i ) ) & ~ 31 , ioaddr + SM_PTR ) ;
outw ( data [ 0 ] , ioaddr + SHADOW ( CONF_MULTICAST + ( 6 * i ) ) ) ;
outw ( ( CONF_MULTICAST + ( 6 * i ) + 2 ) & ~ 31 , ioaddr + SM_PTR ) ;
outw ( data [ 1 ] , ioaddr + SHADOW ( CONF_MULTICAST + ( 6 * i ) + 2 ) ) ;
outw ( ( CONF_MULTICAST + ( 6 * i ) + 4 ) & ~ 31 , ioaddr + SM_PTR ) ;
outw ( data [ 2 ] , ioaddr + SHADOW ( CONF_MULTICAST + ( 6 * i ) + 4 ) ) ;
}
}
/*
* Set or clear the multicast filter for this adaptor .
*/
static void
eexp_set_multicast ( struct net_device * dev )
{
unsigned short ioaddr = dev - > base_addr ;
struct net_local * lp = netdev_priv ( dev ) ;
int kick = 0 , i ;
if ( ( dev - > flags & IFF_PROMISC ) ! = lp - > was_promisc ) {
outw ( CONF_PROMISC & ~ 31 , ioaddr + SM_PTR ) ;
i = inw ( ioaddr + SHADOW ( CONF_PROMISC ) ) ;
outw ( ( dev - > flags & IFF_PROMISC ) ? ( i | 1 ) : ( i & ~ 1 ) ,
ioaddr + SHADOW ( CONF_PROMISC ) ) ;
lp - > was_promisc = dev - > flags & IFF_PROMISC ;
kick = 1 ;
}
if ( ! ( dev - > flags & IFF_PROMISC ) ) {
eexp_setup_filter ( dev ) ;
if ( lp - > old_mc_count ! = dev - > mc_count ) {
kick = 1 ;
lp - > old_mc_count = dev - > mc_count ;
}
}
if ( kick ) {
unsigned long oj ;
scb_command ( dev , SCB_CUsuspend ) ;
outb ( 0 , ioaddr + SIGNAL_CA ) ;
outb ( 0 , ioaddr + SIGNAL_CA ) ;
#if 0
printk ( " %s: waiting for CU to go suspended \n " , dev - > name ) ;
# endif
oj = jiffies ;
while ( ( SCB_CUstat ( scb_status ( dev ) ) = = 2 ) & &
( ( jiffies - oj ) < 2000 ) ) ;
if ( SCB_CUstat ( scb_status ( dev ) ) = = 2 )
printk ( " %s: warning, CU didn't stop \n " , dev - > name ) ;
lp - > started & = ~ ( STARTED_CU ) ;
scb_wrcbl ( dev , CONF_LINK ) ;
scb_command ( dev , SCB_CUstart ) ;
outb ( 0 , ioaddr + SIGNAL_CA ) ;
}
}
/*
* MODULE stuff
*/
# ifdef MODULE
# define EEXP_MAX_CARDS 4 /* max number of cards to support */
static struct net_device * dev_eexp [ EEXP_MAX_CARDS ] ;
static int irq [ EEXP_MAX_CARDS ] ;
static int io [ EEXP_MAX_CARDS ] ;
module_param_array ( io , int , NULL , 0 ) ;
module_param_array ( irq , int , NULL , 0 ) ;
MODULE_PARM_DESC ( io , " EtherExpress 16 I/O base address(es) " ) ;
MODULE_PARM_DESC ( irq , " EtherExpress 16 IRQ number(s) " ) ;
MODULE_LICENSE ( " GPL " ) ;
/* Ideally the user would give us io=, irq= for every card. If any parameters
* are specified , we verify and then use them . If no parameters are given , we
* autoprobe for one card only .
*/
int init_module ( void )
{
struct net_device * dev ;
int this_dev , found = 0 ;
for ( this_dev = 0 ; this_dev < EEXP_MAX_CARDS ; this_dev + + ) {
dev = alloc_etherdev ( sizeof ( struct net_local ) ) ;
dev - > irq = irq [ this_dev ] ;
dev - > base_addr = io [ this_dev ] ;
if ( io [ this_dev ] = = 0 ) {
if ( this_dev )
break ;
printk ( KERN_NOTICE " eexpress.c: Module autoprobe not recommended, give io=xx. \n " ) ;
}
2005-05-13 04:11:55 +04:00
if ( do_express_probe ( dev ) = = 0 ) {
2005-04-17 02:20:36 +04:00
dev_eexp [ this_dev ] = dev ;
found + + ;
continue ;
}
printk ( KERN_WARNING " eexpress.c: Failed to register card at 0x%x. \n " , io [ this_dev ] ) ;
free_netdev ( dev ) ;
break ;
}
if ( found )
return 0 ;
return - ENXIO ;
}
void cleanup_module ( void )
{
int this_dev ;
for ( this_dev = 0 ; this_dev < EEXP_MAX_CARDS ; this_dev + + ) {
struct net_device * dev = dev_eexp [ this_dev ] ;
if ( dev ) {
unregister_netdev ( dev ) ;
free_netdev ( dev ) ;
}
}
}
# endif
/*
* Local Variables :
* c - file - style : " linux "
* tab - width : 8
* End :
*/