2005-04-16 15:20:36 -07:00
/*
* linux / drivers / net / am79c961 . c
*
* by Russell King < rmk @ arm . linux . org . uk > 1995 - 2001.
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* Derived from various things including skeleton . c
*
* This is a special driver for the am79c961A Lance chip used in the
* Intel ( formally Digital Equipment Corp ) EBSA110 platform . Please
* note that this can not be built as a module ( it doesn ' t make sense ) .
*/
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/interrupt.h>
# include <linux/ioport.h>
# include <linux/slab.h>
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/crc32.h>
# include <linux/bitops.h>
2005-10-31 17:14:57 +00:00
# include <linux/platform_device.h>
2005-04-16 15:20:36 -07:00
2005-10-28 10:29:21 +01:00
# include <asm/hardware.h>
2005-04-16 15:20:36 -07:00
# include <asm/io.h>
2005-10-31 17:14:57 +00:00
# include <asm/system.h>
2005-04-16 15:20:36 -07:00
# define TX_BUFFERS 15
# define RX_BUFFERS 25
# include "am79c961a.h"
static irqreturn_t
am79c961_interrupt ( int irq , void * dev_id , struct pt_regs * regs ) ;
static unsigned int net_debug = NET_DEBUG ;
static const char version [ ] =
" am79c961 ethernet driver (C) 1995-2001 Russell King v0.04 \n " ;
/* --------------------------------------------------------------------------- */
# ifdef __arm__
static void write_rreg ( u_long base , u_int reg , u_int val )
{
__asm__ (
" str%?h %1, [%2] @ NET_RAP \n \t "
" str%?h %0, [%2, #-4] @ NET_RDP "
:
: " r " ( val ) , " r " ( reg ) , " r " ( ISAIO_BASE + 0x0464 ) ) ;
}
static inline unsigned short read_rreg ( u_long base_addr , u_int reg )
{
unsigned short v ;
__asm__ (
" str%?h %1, [%2] @ NET_RAP \n \t "
" ldr%?h %0, [%2, #-4] @ NET_RDP "
: " =r " ( v )
: " r " ( reg ) , " r " ( ISAIO_BASE + 0x0464 ) ) ;
return v ;
}
static inline void write_ireg ( u_long base , u_int reg , u_int val )
{
__asm__ (
" str%?h %1, [%2] @ NET_RAP \n \t "
" str%?h %0, [%2, #8] @ NET_IDP "
:
: " r " ( val ) , " r " ( reg ) , " r " ( ISAIO_BASE + 0x0464 ) ) ;
}
static inline unsigned short read_ireg ( u_long base_addr , u_int reg )
{
u_short v ;
__asm__ (
" str%?h %1, [%2] @ NAT_RAP \n \t "
2005-10-02 18:02:25 +01:00
" ldr%?h %0, [%2, #8] @ NET_IDP \n \t "
2005-04-16 15:20:36 -07:00
: " =r " ( v )
: " r " ( reg ) , " r " ( ISAIO_BASE + 0x0464 ) ) ;
return v ;
}
# define am_writeword(dev,off,val) __raw_writew(val, ISAMEM_BASE + ((off) << 1))
# define am_readword(dev,off) __raw_readw(ISAMEM_BASE + ((off) << 1))
static inline void
am_writebuffer ( struct net_device * dev , u_int offset , unsigned char * buf , unsigned int length )
{
offset = ISAMEM_BASE + ( offset < < 1 ) ;
length = ( length + 1 ) & ~ 1 ;
if ( ( int ) buf & 2 ) {
__asm__ __volatile__ ( " str%?h %2, [%0], #4 "
: " =&r " ( offset ) : " 0 " ( offset ) , " r " ( buf [ 0 ] | ( buf [ 1 ] < < 8 ) ) ) ;
buf + = 2 ;
length - = 2 ;
}
while ( length > 8 ) {
unsigned int tmp , tmp2 ;
__asm__ __volatile__ (
" ldm%?ia %1!, {%2, %3} \n \t "
" str%?h %2, [%0], #4 \n \t "
" mov%? %2, %2, lsr #16 \n \t "
" str%?h %2, [%0], #4 \n \t "
" str%?h %3, [%0], #4 \n \t "
" mov%? %3, %3, lsr #16 \n \t "
" str%?h %3, [%0], #4 "
: " =&r " ( offset ) , " =&r " ( buf ) , " =r " ( tmp ) , " =r " ( tmp2 )
: " 0 " ( offset ) , " 1 " ( buf ) ) ;
length - = 8 ;
}
while ( length > 0 ) {
__asm__ __volatile__ ( " str%?h %2, [%0], #4 "
: " =&r " ( offset ) : " 0 " ( offset ) , " r " ( buf [ 0 ] | ( buf [ 1 ] < < 8 ) ) ) ;
buf + = 2 ;
length - = 2 ;
}
}
static inline void
am_readbuffer ( struct net_device * dev , u_int offset , unsigned char * buf , unsigned int length )
{
offset = ISAMEM_BASE + ( offset < < 1 ) ;
length = ( length + 1 ) & ~ 1 ;
if ( ( int ) buf & 2 ) {
unsigned int tmp ;
__asm__ __volatile__ (
" ldr%?h %2, [%0], #4 \n \t "
" str%?b %2, [%1], #1 \n \t "
" mov%? %2, %2, lsr #8 \n \t "
" str%?b %2, [%1], #1 "
: " =&r " ( offset ) , " =&r " ( buf ) , " =r " ( tmp ) : " 0 " ( offset ) , " 1 " ( buf ) ) ;
length - = 2 ;
}
while ( length > 8 ) {
unsigned int tmp , tmp2 , tmp3 ;
__asm__ __volatile__ (
" ldr%?h %2, [%0], #4 \n \t "
" ldr%?h %3, [%0], #4 \n \t "
" orr%? %2, %2, %3, lsl #16 \n \t "
" ldr%?h %3, [%0], #4 \n \t "
" ldr%?h %4, [%0], #4 \n \t "
" orr%? %3, %3, %4, lsl #16 \n \t "
" stm%?ia %1!, {%2, %3} "
: " =&r " ( offset ) , " =&r " ( buf ) , " =r " ( tmp ) , " =r " ( tmp2 ) , " =r " ( tmp3 )
: " 0 " ( offset ) , " 1 " ( buf ) ) ;
length - = 8 ;
}
while ( length > 0 ) {
unsigned int tmp ;
__asm__ __volatile__ (
" ldr%?h %2, [%0], #4 \n \t "
" str%?b %2, [%1], #1 \n \t "
" mov%? %2, %2, lsr #8 \n \t "
" str%?b %2, [%1], #1 "
: " =&r " ( offset ) , " =&r " ( buf ) , " =r " ( tmp ) : " 0 " ( offset ) , " 1 " ( buf ) ) ;
length - = 2 ;
}
}
# else
# error Not compatible
# endif
static int
am79c961_ramtest ( struct net_device * dev , unsigned int val )
{
unsigned char * buffer = kmalloc ( 65536 , GFP_KERNEL ) ;
int i , error = 0 , errorcount = 0 ;
if ( ! buffer )
return 0 ;
memset ( buffer , val , 65536 ) ;
am_writebuffer ( dev , 0 , buffer , 65536 ) ;
memset ( buffer , val ^ 255 , 65536 ) ;
am_readbuffer ( dev , 0 , buffer , 65536 ) ;
for ( i = 0 ; i < 65536 ; i + + ) {
if ( buffer [ i ] ! = val & & ! error ) {
printk ( " %s: buffer error (%02X %02X) %05X - " , dev - > name , val , buffer [ i ] , i ) ;
error = 1 ;
errorcount + + ;
} else if ( error & & buffer [ i ] = = val ) {
printk ( " %05X \n " , i ) ;
error = 0 ;
}
}
if ( error )
printk ( " 10000 \n " ) ;
kfree ( buffer ) ;
return errorcount ;
}
static void
am79c961_init_for_open ( struct net_device * dev )
{
struct dev_priv * priv = netdev_priv ( dev ) ;
unsigned long flags ;
unsigned char * p ;
u_int hdr_addr , first_free_addr ;
int i ;
/*
* Stop the chip .
*/
spin_lock_irqsave ( priv - > chip_lock , flags ) ;
write_rreg ( dev - > base_addr , CSR0 , CSR0_BABL | CSR0_CERR | CSR0_MISS | CSR0_MERR | CSR0_TINT | CSR0_RINT | CSR0_STOP ) ;
spin_unlock_irqrestore ( priv - > chip_lock , flags ) ;
write_ireg ( dev - > base_addr , 5 , 0x00a0 ) ; /* Receive address LED */
write_ireg ( dev - > base_addr , 6 , 0x0081 ) ; /* Collision LED */
write_ireg ( dev - > base_addr , 7 , 0x0090 ) ; /* XMIT LED */
write_ireg ( dev - > base_addr , 2 , 0x0000 ) ; /* MODE register selects media */
for ( i = LADRL ; i < = LADRH ; i + + )
write_rreg ( dev - > base_addr , i , 0 ) ;
for ( i = PADRL , p = dev - > dev_addr ; i < = PADRH ; i + + , p + = 2 )
write_rreg ( dev - > base_addr , i , p [ 0 ] | ( p [ 1 ] < < 8 ) ) ;
i = MODE_PORT_10BT ;
if ( dev - > flags & IFF_PROMISC )
i | = MODE_PROMISC ;
write_rreg ( dev - > base_addr , MODE , i ) ;
write_rreg ( dev - > base_addr , POLLINT , 0 ) ;
write_rreg ( dev - > base_addr , SIZERXR , - RX_BUFFERS ) ;
write_rreg ( dev - > base_addr , SIZETXR , - TX_BUFFERS ) ;
first_free_addr = RX_BUFFERS * 8 + TX_BUFFERS * 8 + 16 ;
hdr_addr = 0 ;
priv - > rxhead = 0 ;
priv - > rxtail = 0 ;
priv - > rxhdr = hdr_addr ;
for ( i = 0 ; i < RX_BUFFERS ; i + + ) {
priv - > rxbuffer [ i ] = first_free_addr ;
am_writeword ( dev , hdr_addr , first_free_addr ) ;
am_writeword ( dev , hdr_addr + 2 , RMD_OWN ) ;
am_writeword ( dev , hdr_addr + 4 , ( - 1600 ) ) ;
am_writeword ( dev , hdr_addr + 6 , 0 ) ;
first_free_addr + = 1600 ;
hdr_addr + = 8 ;
}
priv - > txhead = 0 ;
priv - > txtail = 0 ;
priv - > txhdr = hdr_addr ;
for ( i = 0 ; i < TX_BUFFERS ; i + + ) {
priv - > txbuffer [ i ] = first_free_addr ;
am_writeword ( dev , hdr_addr , first_free_addr ) ;
am_writeword ( dev , hdr_addr + 2 , TMD_STP | TMD_ENP ) ;
am_writeword ( dev , hdr_addr + 4 , 0xf000 ) ;
am_writeword ( dev , hdr_addr + 6 , 0 ) ;
first_free_addr + = 1600 ;
hdr_addr + = 8 ;
}
write_rreg ( dev - > base_addr , BASERXL , priv - > rxhdr ) ;
write_rreg ( dev - > base_addr , BASERXH , 0 ) ;
write_rreg ( dev - > base_addr , BASETXL , priv - > txhdr ) ;
write_rreg ( dev - > base_addr , BASERXH , 0 ) ;
write_rreg ( dev - > base_addr , CSR0 , CSR0_STOP ) ;
write_rreg ( dev - > base_addr , CSR3 , CSR3_IDONM | CSR3_BABLM | CSR3_DXSUFLO ) ;
write_rreg ( dev - > base_addr , CSR4 , CSR4_APAD_XMIT | CSR4_MFCOM | CSR4_RCVCCOM | CSR4_TXSTRTM | CSR4_JABM ) ;
write_rreg ( dev - > base_addr , CSR0 , CSR0_IENA | CSR0_STRT ) ;
}
static void am79c961_timer ( unsigned long data )
{
struct net_device * dev = ( struct net_device * ) data ;
struct dev_priv * priv = netdev_priv ( dev ) ;
unsigned int lnkstat , carrier ;
lnkstat = read_ireg ( dev - > base_addr , ISALED0 ) & ISALED0_LNKST ;
carrier = netif_carrier_ok ( dev ) ;
2005-10-31 17:14:57 +00:00
if ( lnkstat & & ! carrier ) {
2005-04-16 15:20:36 -07:00
netif_carrier_on ( dev ) ;
2005-10-31 17:14:57 +00:00
printk ( " %s: link up \n " , dev - > name ) ;
} else if ( ! lnkstat & & carrier ) {
2005-04-16 15:20:36 -07:00
netif_carrier_off ( dev ) ;
2005-10-31 17:14:57 +00:00
printk ( " %s: link down \n " , dev - > name ) ;
}
2005-04-16 15:20:36 -07:00
2005-10-02 18:12:03 +01:00
mod_timer ( & priv - > timer , jiffies + msecs_to_jiffies ( 500 ) ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Open / initialize the board .
*/
static int
am79c961_open ( struct net_device * dev )
{
struct dev_priv * priv = netdev_priv ( dev ) ;
int ret ;
memset ( & priv - > stats , 0 , sizeof ( priv - > stats ) ) ;
ret = request_irq ( dev - > irq , am79c961_interrupt , 0 , dev - > name , dev ) ;
if ( ret )
return ret ;
am79c961_init_for_open ( dev ) ;
netif_carrier_off ( dev ) ;
priv - > timer . expires = jiffies ;
add_timer ( & priv - > timer ) ;
netif_start_queue ( dev ) ;
return 0 ;
}
/*
* The inverse routine to am79c961_open ( ) .
*/
static int
am79c961_close ( struct net_device * dev )
{
struct dev_priv * priv = netdev_priv ( dev ) ;
unsigned long flags ;
del_timer_sync ( & priv - > timer ) ;
netif_stop_queue ( dev ) ;
netif_carrier_off ( dev ) ;
spin_lock_irqsave ( priv - > chip_lock , flags ) ;
write_rreg ( dev - > base_addr , CSR0 , CSR0_STOP ) ;
write_rreg ( dev - > base_addr , CSR3 , CSR3_MASKALL ) ;
spin_unlock_irqrestore ( priv - > chip_lock , flags ) ;
free_irq ( dev - > irq , dev ) ;
return 0 ;
}
/*
* Get the current statistics .
*/
static struct net_device_stats * am79c961_getstats ( struct net_device * dev )
{
struct dev_priv * priv = netdev_priv ( dev ) ;
return & priv - > stats ;
}
static void am79c961_mc_hash ( struct dev_mc_list * dmi , unsigned short * hash )
{
if ( dmi - > dmi_addrlen = = ETH_ALEN & & dmi - > dmi_addr [ 0 ] & 0x01 ) {
int idx , bit ;
u32 crc ;
crc = ether_crc_le ( ETH_ALEN , dmi - > dmi_addr ) ;
idx = crc > > 30 ;
bit = ( crc > > 26 ) & 15 ;
hash [ idx ] | = 1 < < bit ;
}
}
/*
* Set or clear promiscuous / multicast mode filter for this adapter .
*/
static void am79c961_setmulticastlist ( struct net_device * dev )
{
struct dev_priv * priv = netdev_priv ( dev ) ;
unsigned long flags ;
unsigned short multi_hash [ 4 ] , mode ;
int i , stopped ;
mode = MODE_PORT_10BT ;
if ( dev - > flags & IFF_PROMISC ) {
mode | = MODE_PROMISC ;
} else if ( dev - > flags & IFF_ALLMULTI ) {
memset ( multi_hash , 0xff , sizeof ( multi_hash ) ) ;
} else {
struct dev_mc_list * dmi ;
memset ( multi_hash , 0x00 , sizeof ( multi_hash ) ) ;
for ( dmi = dev - > mc_list ; dmi ; dmi = dmi - > next )
am79c961_mc_hash ( dmi , multi_hash ) ;
}
spin_lock_irqsave ( priv - > chip_lock , flags ) ;
stopped = read_rreg ( dev - > base_addr , CSR0 ) & CSR0_STOP ;
if ( ! stopped ) {
/*
* Put the chip into suspend mode
*/
write_rreg ( dev - > base_addr , CTRL1 , CTRL1_SPND ) ;
/*
* Spin waiting for chip to report suspend mode
*/
while ( ( read_rreg ( dev - > base_addr , CTRL1 ) & CTRL1_SPND ) = = 0 ) {
spin_unlock_irqrestore ( priv - > chip_lock , flags ) ;
nop ( ) ;
spin_lock_irqsave ( priv - > chip_lock , flags ) ;
}
}
/*
* Update the multicast hash table
*/
for ( i = 0 ; i < sizeof ( multi_hash ) / sizeof ( multi_hash [ 0 ] ) ; i + + )
write_rreg ( dev - > base_addr , i + LADRL , multi_hash [ i ] ) ;
/*
* Write the mode register
*/
write_rreg ( dev - > base_addr , MODE , mode ) ;
if ( ! stopped ) {
/*
* Put the chip back into running mode
*/
write_rreg ( dev - > base_addr , CTRL1 , 0 ) ;
}
spin_unlock_irqrestore ( priv - > chip_lock , flags ) ;
}
static void am79c961_timeout ( struct net_device * dev )
{
printk ( KERN_WARNING " %s: transmit timed out, network cable problem? \n " ,
dev - > name ) ;
/*
* ought to do some setup of the tx side here
*/
netif_wake_queue ( dev ) ;
}
/*
* Transmit a packet
*/
static int
am79c961_sendpacket ( struct sk_buff * skb , struct net_device * dev )
{
struct dev_priv * priv = netdev_priv ( dev ) ;
unsigned int hdraddr , bufaddr ;
unsigned int head ;
unsigned long flags ;
head = priv - > txhead ;
hdraddr = priv - > txhdr + ( head < < 3 ) ;
bufaddr = priv - > txbuffer [ head ] ;
head + = 1 ;
if ( head > = TX_BUFFERS )
head = 0 ;
am_writebuffer ( dev , bufaddr , skb - > data , skb - > len ) ;
am_writeword ( dev , hdraddr + 4 , - skb - > len ) ;
am_writeword ( dev , hdraddr + 2 , TMD_OWN | TMD_STP | TMD_ENP ) ;
priv - > txhead = head ;
spin_lock_irqsave ( priv - > chip_lock , flags ) ;
write_rreg ( dev - > base_addr , CSR0 , CSR0_TDMD | CSR0_IENA ) ;
dev - > trans_start = jiffies ;
spin_unlock_irqrestore ( priv - > chip_lock , flags ) ;
/*
* If the next packet is owned by the ethernet device ,
* then the tx ring is full and we can ' t add another
* packet .
*/
if ( am_readword ( dev , priv - > txhdr + ( priv - > txhead < < 3 ) + 2 ) & TMD_OWN )
netif_stop_queue ( dev ) ;
dev_kfree_skb ( skb ) ;
return 0 ;
}
/*
* If we have a good packet ( s ) , get it / them out of the buffers .
*/
static void
am79c961_rx ( struct net_device * dev , struct dev_priv * priv )
{
do {
struct sk_buff * skb ;
u_int hdraddr ;
u_int pktaddr ;
u_int status ;
int len ;
hdraddr = priv - > rxhdr + ( priv - > rxtail < < 3 ) ;
pktaddr = priv - > rxbuffer [ priv - > rxtail ] ;
status = am_readword ( dev , hdraddr + 2 ) ;
if ( status & RMD_OWN ) /* do we own it? */
break ;
priv - > rxtail + + ;
if ( priv - > rxtail > = RX_BUFFERS )
priv - > rxtail = 0 ;
if ( ( status & ( RMD_ERR | RMD_STP | RMD_ENP ) ) ! = ( RMD_STP | RMD_ENP ) ) {
am_writeword ( dev , hdraddr + 2 , RMD_OWN ) ;
priv - > stats . rx_errors + + ;
if ( status & RMD_ERR ) {
if ( status & RMD_FRAM )
priv - > stats . rx_frame_errors + + ;
if ( status & RMD_CRC )
priv - > stats . rx_crc_errors + + ;
} else if ( status & RMD_STP )
priv - > stats . rx_length_errors + + ;
continue ;
}
len = am_readword ( dev , hdraddr + 6 ) ;
skb = dev_alloc_skb ( len + 2 ) ;
if ( skb ) {
skb - > dev = dev ;
skb_reserve ( skb , 2 ) ;
am_readbuffer ( dev , pktaddr , skb_put ( skb , len ) , len ) ;
am_writeword ( dev , hdraddr + 2 , RMD_OWN ) ;
skb - > protocol = eth_type_trans ( skb , dev ) ;
netif_rx ( skb ) ;
dev - > last_rx = jiffies ;
priv - > stats . rx_bytes + = len ;
priv - > stats . rx_packets + + ;
} else {
am_writeword ( dev , hdraddr + 2 , RMD_OWN ) ;
printk ( KERN_WARNING " %s: memory squeeze, dropping packet. \n " , dev - > name ) ;
priv - > stats . rx_dropped + + ;
break ;
}
} while ( 1 ) ;
}
/*
* Update stats for the transmitted packet
*/
static void
am79c961_tx ( struct net_device * dev , struct dev_priv * priv )
{
do {
short len ;
u_int hdraddr ;
u_int status ;
hdraddr = priv - > txhdr + ( priv - > txtail < < 3 ) ;
status = am_readword ( dev , hdraddr + 2 ) ;
if ( status & TMD_OWN )
break ;
priv - > txtail + + ;
if ( priv - > txtail > = TX_BUFFERS )
priv - > txtail = 0 ;
if ( status & TMD_ERR ) {
u_int status2 ;
priv - > stats . tx_errors + + ;
status2 = am_readword ( dev , hdraddr + 6 ) ;
/*
* Clear the error byte
*/
am_writeword ( dev , hdraddr + 6 , 0 ) ;
if ( status2 & TST_RTRY )
priv - > stats . collisions + = 16 ;
if ( status2 & TST_LCOL )
priv - > stats . tx_window_errors + + ;
if ( status2 & TST_LCAR )
priv - > stats . tx_carrier_errors + + ;
if ( status2 & TST_UFLO )
priv - > stats . tx_fifo_errors + + ;
continue ;
}
priv - > stats . tx_packets + + ;
len = am_readword ( dev , hdraddr + 4 ) ;
priv - > stats . tx_bytes + = - len ;
} while ( priv - > txtail ! = priv - > txhead ) ;
netif_wake_queue ( dev ) ;
}
static irqreturn_t
am79c961_interrupt ( int irq , void * dev_id , struct pt_regs * regs )
{
struct net_device * dev = ( struct net_device * ) dev_id ;
struct dev_priv * priv = netdev_priv ( dev ) ;
u_int status , n = 100 ;
int handled = 0 ;
do {
status = read_rreg ( dev - > base_addr , CSR0 ) ;
write_rreg ( dev - > base_addr , CSR0 , status &
( CSR0_IENA | CSR0_TINT | CSR0_RINT |
CSR0_MERR | CSR0_MISS | CSR0_CERR | CSR0_BABL ) ) ;
if ( status & CSR0_RINT ) {
handled = 1 ;
am79c961_rx ( dev , priv ) ;
}
if ( status & CSR0_TINT ) {
handled = 1 ;
am79c961_tx ( dev , priv ) ;
}
if ( status & CSR0_MISS ) {
handled = 1 ;
priv - > stats . rx_dropped + + ;
}
if ( status & CSR0_CERR ) {
handled = 1 ;
mod_timer ( & priv - > timer , jiffies ) ;
}
} while ( - - n & & status & ( CSR0_RINT | CSR0_TINT ) ) ;
return IRQ_RETVAL ( handled ) ;
}
# ifdef CONFIG_NET_POLL_CONTROLLER
static void am79c961_poll_controller ( struct net_device * dev )
{
unsigned long flags ;
local_irq_save ( flags ) ;
am79c961_interrupt ( dev - > irq , dev , NULL ) ;
local_irq_restore ( flags ) ;
}
# endif
/*
* Initialise the chip . Note that we always expect
* to be entered with interrupts enabled .
*/
static int
am79c961_hw_init ( struct net_device * dev )
{
struct dev_priv * priv = netdev_priv ( dev ) ;
spin_lock_irq ( & priv - > chip_lock ) ;
write_rreg ( dev - > base_addr , CSR0 , CSR0_STOP ) ;
write_rreg ( dev - > base_addr , CSR3 , CSR3_MASKALL ) ;
spin_unlock_irq ( & priv - > chip_lock ) ;
am79c961_ramtest ( dev , 0x66 ) ;
am79c961_ramtest ( dev , 0x99 ) ;
return 0 ;
}
static void __init am79c961_banner ( void )
{
static unsigned version_printed ;
if ( net_debug & & version_printed + + = = 0 )
printk ( KERN_INFO " %s " , version ) ;
}
2006-01-09 13:59:36 +00:00
static int __init am79c961_probe ( struct platform_device * pdev )
2005-04-16 15:20:36 -07:00
{
2005-10-31 17:14:57 +00:00
struct resource * res ;
2005-04-16 15:20:36 -07:00
struct net_device * dev ;
struct dev_priv * priv ;
int i , ret ;
2005-10-31 17:14:57 +00:00
res = platform_get_resource ( pdev , IORESOURCE_IO , 0 ) ;
if ( ! res )
return - ENODEV ;
2005-04-16 15:20:36 -07:00
dev = alloc_etherdev ( sizeof ( struct dev_priv ) ) ;
ret = - ENOMEM ;
if ( ! dev )
goto out ;
2005-10-31 17:14:57 +00:00
SET_NETDEV_DEV ( dev , & pdev - > dev ) ;
2005-04-16 15:20:36 -07:00
priv = netdev_priv ( dev ) ;
/*
* Fixed address and IRQ lines here .
* The PNP initialisation should have been
* done by the ether bootp loader .
*/
2005-10-31 17:14:57 +00:00
dev - > base_addr = res - > start ;
dev - > irq = platform_get_irq ( pdev , 0 ) ;
2005-04-16 15:20:36 -07:00
2006-01-19 17:56:29 +00:00
ret = - ENODEV ;
if ( dev - > irq < 0 )
goto nodev ;
2005-04-16 15:20:36 -07:00
if ( ! request_region ( dev - > base_addr , 0x18 , dev - > name ) )
goto nodev ;
/*
* Reset the device .
*/
inb ( dev - > base_addr + NET_RESET ) ;
udelay ( 5 ) ;
/*
* Check the manufacturer part of the
* ether address .
*/
if ( inb ( dev - > base_addr ) ! = 0x08 | |
inb ( dev - > base_addr + 2 ) ! = 0x00 | |
inb ( dev - > base_addr + 4 ) ! = 0x2b )
goto release ;
2005-10-02 18:12:03 +01:00
for ( i = 0 ; i < 6 ; i + + )
2005-04-16 15:20:36 -07:00
dev - > dev_addr [ i ] = inb ( dev - > base_addr + i * 2 ) & 0xff ;
2005-10-31 17:14:57 +00:00
am79c961_banner ( ) ;
2005-04-16 15:20:36 -07:00
spin_lock_init ( & priv - > chip_lock ) ;
init_timer ( & priv - > timer ) ;
priv - > timer . data = ( unsigned long ) dev ;
priv - > timer . function = am79c961_timer ;
if ( am79c961_hw_init ( dev ) )
goto release ;
dev - > open = am79c961_open ;
dev - > stop = am79c961_close ;
dev - > hard_start_xmit = am79c961_sendpacket ;
dev - > get_stats = am79c961_getstats ;
dev - > set_multicast_list = am79c961_setmulticastlist ;
dev - > tx_timeout = am79c961_timeout ;
# ifdef CONFIG_NET_POLL_CONTROLLER
dev - > poll_controller = am79c961_poll_controller ;
# endif
ret = register_netdev ( dev ) ;
2005-10-02 18:12:03 +01:00
if ( ret = = 0 ) {
printk ( KERN_INFO " %s: ether address " , dev - > name ) ;
2005-10-31 17:14:57 +00:00
/* Retrive and print the ethernet address. */
2005-10-02 18:12:03 +01:00
for ( i = 0 ; i < 6 ; i + + )
printk ( i = = 5 ? " %02x \n " : " %02x: " , dev - > dev_addr [ i ] ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
2005-10-02 18:12:03 +01:00
}
2005-04-16 15:20:36 -07:00
release :
release_region ( dev - > base_addr , 0x18 ) ;
nodev :
free_netdev ( dev ) ;
out :
return ret ;
}
2006-01-09 13:59:36 +00:00
static struct platform_driver am79c961_driver = {
2005-10-31 17:14:57 +00:00
. probe = am79c961_probe ,
2006-01-09 13:59:36 +00:00
. driver = {
. name = " am79c961 " ,
} ,
2005-10-31 17:14:57 +00:00
} ;
static int __init am79c961_init ( void )
{
2006-01-09 13:59:36 +00:00
return platform_driver_register ( & am79c961_driver ) ;
2005-10-31 17:14:57 +00:00
}
2005-04-16 15:20:36 -07:00
__initcall ( am79c961_init ) ;