2005-04-17 02:20:36 +04:00
/* drivers/atm/eni.c - Efficient Networks ENI155P device driver */
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
# include <linux/module.h>
# include <linux/config.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/pci.h>
# include <linux/errno.h>
# include <linux/atm.h>
# include <linux/atmdev.h>
# include <linux/sonet.h>
# include <linux/skbuff.h>
# include <linux/time.h>
# include <linux/delay.h>
# include <linux/uio.h>
# include <linux/init.h>
# include <linux/atm_eni.h>
# include <linux/bitops.h>
# include <asm/system.h>
# include <asm/io.h>
# include <asm/atomic.h>
# include <asm/uaccess.h>
# include <asm/string.h>
# include <asm/byteorder.h>
# include "tonga.h"
# include "midway.h"
# include "suni.h"
# include "eni.h"
# if !defined(__i386__) && !defined(__x86_64__)
# ifndef ioremap_nocache
# define ioremap_nocache(X,Y) ioremap(X,Y)
# endif
# endif
/*
* TODO :
*
* Show stoppers
* none
*
* Minor
* - OAM support
* - fix bugs listed below
*/
/*
* KNOWN BUGS :
*
* - may run into JK - JK bug and deadlock
* - should allocate UBR channel first
* - buffer space allocation algorithm is stupid
* ( RX : should be maxSDU + maxdelay * rate
* TX : should be maxSDU + min ( maxSDU , maxdelay * rate ) )
* - doesn ' t support OAM cells
* - eni_put_free may hang if not putting memory fragments that _complete_
* 2 ^ n block ( never happens in real life , though )
*/
#if 0
# define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
# else
# define DPRINTK(format,args...)
# endif
# ifndef CONFIG_ATM_ENI_TUNE_BURST
# define CONFIG_ATM_ENI_BURST_TX_8W
# define CONFIG_ATM_ENI_BURST_RX_4W
# endif
# ifndef CONFIG_ATM_ENI_DEBUG
# define NULLCHECK(x)
# define EVENT(s,a,b)
static void event_dump ( void )
{
}
# else
/*
* NULL pointer checking
*/
# define NULLCHECK(x) \
if ( ( unsigned long ) ( x ) < 0x30 ) \
printk ( KERN_CRIT # x " ==0x%lx \n " , ( unsigned long ) ( x ) )
/*
* Very extensive activity logging . Greatly improves bug detection speed but
* costs a few Mbps if enabled .
*/
# define EV 64
static const char * ev [ EV ] ;
static unsigned long ev_a [ EV ] , ev_b [ EV ] ;
static int ec = 0 ;
static void EVENT ( const char * s , unsigned long a , unsigned long b )
{
ev [ ec ] = s ;
ev_a [ ec ] = a ;
ev_b [ ec ] = b ;
ec = ( ec + 1 ) % EV ;
}
static void event_dump ( void )
{
int n , i ;
for ( n = 0 ; n < EV ; n + + ) {
i = ( ec + n ) % EV ;
printk ( KERN_NOTICE ) ;
printk ( ev [ i ] ? ev [ i ] : " (null) " , ev_a [ i ] , ev_b [ i ] ) ;
}
}
# endif /* CONFIG_ATM_ENI_DEBUG */
/*
* NExx must not be equal at end
* EExx may be equal at end
* xxPJOK verify validity of pointer jumps
* xxPMOK operating on a circular buffer of " c " words
*/
# define NEPJOK(a0,a1,b) \
( ( a0 ) < ( a1 ) ? ( b ) < = ( a0 ) | | ( b ) > ( a1 ) : ( b ) < = ( a0 ) & & ( b ) > ( a1 ) )
# define EEPJOK(a0,a1,b) \
( ( a0 ) < ( a1 ) ? ( b ) < ( a0 ) | | ( b ) > = ( a1 ) : ( b ) < ( a0 ) & & ( b ) > = ( a1 ) )
# define NEPMOK(a0,d,b,c) NEPJOK(a0,(a0+d) & (c-1),b)
# define EEPMOK(a0,d,b,c) EEPJOK(a0,(a0+d) & (c-1),b)
static int tx_complete = 0 , dma_complete = 0 , queued = 0 , requeued = 0 ,
backlogged = 0 , rx_enqueued = 0 , rx_dequeued = 0 , pushed = 0 , submitted = 0 ,
putting = 0 ;
static struct atm_dev * eni_boards = NULL ;
static u32 * cpu_zeroes = NULL ; /* aligned "magic" zeroes */
static dma_addr_t zeroes ;
/* Read/write registers on card */
# define eni_in(r) readl(eni_dev->reg+(r)*4)
# define eni_out(v,r) writel((v),eni_dev->reg+(r)*4)
/*-------------------------------- utilities --------------------------------*/
static void dump_mem ( struct eni_dev * eni_dev )
{
int i ;
for ( i = 0 ; i < eni_dev - > free_len ; i + + )
printk ( KERN_DEBUG " %d: %p %d \n " , i ,
eni_dev - > free_list [ i ] . start ,
1 < < eni_dev - > free_list [ i ] . order ) ;
}
static void dump ( struct atm_dev * dev )
{
struct eni_dev * eni_dev ;
int i ;
eni_dev = ENI_DEV ( dev ) ;
printk ( KERN_NOTICE " Free memory \n " ) ;
dump_mem ( eni_dev ) ;
printk ( KERN_NOTICE " TX buffers \n " ) ;
for ( i = 0 ; i < NR_CHAN ; i + + )
if ( eni_dev - > tx [ i ] . send )
printk ( KERN_NOTICE " TX %d @ %p: %ld \n " , i ,
eni_dev - > tx [ i ] . send , eni_dev - > tx [ i ] . words * 4 ) ;
printk ( KERN_NOTICE " RX buffers \n " ) ;
for ( i = 0 ; i < 1024 ; i + + )
if ( eni_dev - > rx_map [ i ] & & ENI_VCC ( eni_dev - > rx_map [ i ] ) - > rx )
printk ( KERN_NOTICE " RX %d @ %p: %ld \n " , i ,
ENI_VCC ( eni_dev - > rx_map [ i ] ) - > recv ,
ENI_VCC ( eni_dev - > rx_map [ i ] ) - > words * 4 ) ;
printk ( KERN_NOTICE " ---- \n " ) ;
}
static void eni_put_free ( struct eni_dev * eni_dev , void __iomem * start ,
unsigned long size )
{
struct eni_free * list ;
int len , order ;
DPRINTK ( " init 0x%lx+%ld(0x%lx) \n " , start , size , size ) ;
start + = eni_dev - > base_diff ;
list = eni_dev - > free_list ;
len = eni_dev - > free_len ;
while ( size ) {
if ( len > = eni_dev - > free_list_size ) {
printk ( KERN_CRIT " eni_put_free overflow (%p,%ld) \n " ,
start , size ) ;
break ;
}
for ( order = 0 ; ! ( ( ( unsigned long ) start | size ) & ( 1 < < order ) ) ; order + + ) ;
if ( MID_MIN_BUF_SIZE > ( 1 < < order ) ) {
printk ( KERN_CRIT " eni_put_free: order %d too small \n " ,
order ) ;
break ;
}
list [ len ] . start = ( void __iomem * ) start ;
list [ len ] . order = order ;
len + + ;
start + = 1 < < order ;
size - = 1 < < order ;
}
eni_dev - > free_len = len ;
/*dump_mem(eni_dev);*/
}
static void __iomem * eni_alloc_mem ( struct eni_dev * eni_dev , unsigned long * size )
{
struct eni_free * list ;
void __iomem * start ;
int len , i , order , best_order , index ;
list = eni_dev - > free_list ;
len = eni_dev - > free_len ;
if ( * size < MID_MIN_BUF_SIZE ) * size = MID_MIN_BUF_SIZE ;
if ( * size > MID_MAX_BUF_SIZE ) return NULL ;
for ( order = 0 ; ( 1 < < order ) < * size ; order + + ) ;
DPRINTK ( " trying: %ld->%d \n " , * size , order ) ;
best_order = 65 ; /* we don't have more than 2^64 of anything ... */
index = 0 ; /* silence GCC */
for ( i = 0 ; i < len ; i + + )
if ( list [ i ] . order = = order ) {
best_order = order ;
index = i ;
break ;
}
else if ( best_order > list [ i ] . order & & list [ i ] . order > order ) {
best_order = list [ i ] . order ;
index = i ;
}
if ( best_order = = 65 ) return NULL ;
start = list [ index ] . start - eni_dev - > base_diff ;
list [ index ] = list [ - - len ] ;
eni_dev - > free_len = len ;
* size = 1 < < order ;
eni_put_free ( eni_dev , start + * size , ( 1 < < best_order ) - * size ) ;
DPRINTK ( " %ld bytes (order %d) at 0x%lx \n " , * size , order , start ) ;
memset_io ( start , 0 , * size ) ; /* never leak data */
/*dump_mem(eni_dev);*/
return start ;
}
static void eni_free_mem ( struct eni_dev * eni_dev , void __iomem * start ,
unsigned long size )
{
struct eni_free * list ;
int len , i , order ;
start + = eni_dev - > base_diff ;
list = eni_dev - > free_list ;
len = eni_dev - > free_len ;
for ( order = - 1 ; size ; order + + ) size > > = 1 ;
DPRINTK ( " eni_free_mem: %p+0x%lx (order %d) \n " , start , size , order ) ;
for ( i = 0 ; i < len ; i + + )
if ( ( ( unsigned long ) list [ i ] . start ) = = ( ( unsigned long ) start ^ ( 1 < < order ) ) & &
list [ i ] . order = = order ) {
DPRINTK ( " match[%d]: 0x%lx/0x%lx(0x%x), %d/%d \n " , i ,
list [ i ] . start , start , 1 < < order , list [ i ] . order , order ) ;
list [ i ] = list [ - - len ] ;
start = ( void __iomem * ) ( ( unsigned long ) start & ~ ( unsigned long ) ( 1 < < order ) ) ;
order + + ;
i = - 1 ;
continue ;
}
if ( len > = eni_dev - > free_list_size ) {
printk ( KERN_ALERT " eni_free_mem overflow (%p,%d) \n " , start ,
order ) ;
return ;
}
list [ len ] . start = start ;
list [ len ] . order = order ;
eni_dev - > free_len = len + 1 ;
/*dump_mem(eni_dev);*/
}
/*----------------------------------- RX ------------------------------------*/
# define ENI_VCC_NOS ((struct atm_vcc *) 1)
static void rx_ident_err ( struct atm_vcc * vcc )
{
struct atm_dev * dev ;
struct eni_dev * eni_dev ;
struct eni_vcc * eni_vcc ;
dev = vcc - > dev ;
eni_dev = ENI_DEV ( dev ) ;
/* immediately halt adapter */
eni_out ( eni_in ( MID_MC_S ) &
~ ( MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE ) , MID_MC_S ) ;
/* dump useful information */
eni_vcc = ENI_VCC ( vcc ) ;
printk ( KERN_ALERT DEV_LABEL " (itf %d): driver error - RX ident "
" mismatch \n " , dev - > number ) ;
printk ( KERN_ALERT " VCI %d, rxing %d, words %ld \n " , vcc - > vci ,
eni_vcc - > rxing , eni_vcc - > words ) ;
printk ( KERN_ALERT " host descr 0x%lx, rx pos 0x%lx, descr value "
" 0x%x \n " , eni_vcc - > descr , eni_vcc - > rx_pos ,
( unsigned ) readl ( eni_vcc - > recv + eni_vcc - > descr * 4 ) ) ;
printk ( KERN_ALERT " last %p, servicing %d \n " , eni_vcc - > last ,
eni_vcc - > servicing ) ;
EVENT ( " ---dump ends here--- \n " , 0 , 0 ) ;
printk ( KERN_NOTICE " ---recent events--- \n " ) ;
event_dump ( ) ;
ENI_DEV ( dev ) - > fast = NULL ; /* really stop it */
ENI_DEV ( dev ) - > slow = NULL ;
skb_queue_head_init ( & ENI_DEV ( dev ) - > rx_queue ) ;
}
static int do_rx_dma ( struct atm_vcc * vcc , struct sk_buff * skb ,
unsigned long skip , unsigned long size , unsigned long eff )
{
struct eni_dev * eni_dev ;
struct eni_vcc * eni_vcc ;
u32 dma_rd , dma_wr ;
u32 dma [ RX_DMA_BUF * 2 ] ;
dma_addr_t paddr ;
unsigned long here ;
int i , j ;
eni_dev = ENI_DEV ( vcc - > dev ) ;
eni_vcc = ENI_VCC ( vcc ) ;
paddr = 0 ; /* GCC, shut up */
if ( skb ) {
paddr = pci_map_single ( eni_dev - > pci_dev , skb - > data , skb - > len ,
PCI_DMA_FROMDEVICE ) ;
ENI_PRV_PADDR ( skb ) = paddr ;
if ( paddr & 3 )
printk ( KERN_CRIT DEV_LABEL " (itf %d): VCI %d has "
" mis-aligned RX data (0x%lx) \n " , vcc - > dev - > number ,
vcc - > vci , ( unsigned long ) paddr ) ;
ENI_PRV_SIZE ( skb ) = size + skip ;
/* PDU plus descriptor */
ATM_SKB ( skb ) - > vcc = vcc ;
}
j = 0 ;
if ( ( eff & & skip ) | | 1 ) { /* @@@ actually, skip is always == 1 ... */
here = ( eni_vcc - > descr + skip ) & ( eni_vcc - > words - 1 ) ;
dma [ j + + ] = ( here < < MID_DMA_COUNT_SHIFT ) | ( vcc - > vci
< < MID_DMA_VCI_SHIFT ) | MID_DT_JK ;
j + + ;
}
here = ( eni_vcc - > descr + size + skip ) & ( eni_vcc - > words - 1 ) ;
if ( ! eff ) size + = skip ;
else {
unsigned long words ;
if ( ! size ) {
DPRINTK ( " strange things happen ... \n " ) ;
EVENT ( " strange things happen ... (skip=%ld,eff=%ld) \n " ,
size , eff ) ;
}
words = eff ;
if ( paddr & 15 ) {
unsigned long init ;
init = 4 - ( ( paddr & 15 ) > > 2 ) ;
if ( init > words ) init = words ;
dma [ j + + ] = MID_DT_WORD | ( init < < MID_DMA_COUNT_SHIFT ) |
( vcc - > vci < < MID_DMA_VCI_SHIFT ) ;
dma [ j + + ] = paddr ;
paddr + = init < < 2 ;
words - = init ;
}
# ifdef CONFIG_ATM_ENI_BURST_RX_16W /* may work with some PCI chipsets ... */
if ( words & ~ 15 ) {
dma [ j + + ] = MID_DT_16W | ( ( words > > 4 ) < <
MID_DMA_COUNT_SHIFT ) | ( vcc - > vci < <
MID_DMA_VCI_SHIFT ) ;
dma [ j + + ] = paddr ;
paddr + = ( words & ~ 15 ) < < 2 ;
words & = 15 ;
}
# endif
# ifdef CONFIG_ATM_ENI_BURST_RX_8W /* works only with *some* PCI chipsets ... */
if ( words & ~ 7 ) {
dma [ j + + ] = MID_DT_8W | ( ( words > > 3 ) < <
MID_DMA_COUNT_SHIFT ) | ( vcc - > vci < <
MID_DMA_VCI_SHIFT ) ;
dma [ j + + ] = paddr ;
paddr + = ( words & ~ 7 ) < < 2 ;
words & = 7 ;
}
# endif
# ifdef CONFIG_ATM_ENI_BURST_RX_4W /* recommended */
if ( words & ~ 3 ) {
dma [ j + + ] = MID_DT_4W | ( ( words > > 2 ) < <
MID_DMA_COUNT_SHIFT ) | ( vcc - > vci < <
MID_DMA_VCI_SHIFT ) ;
dma [ j + + ] = paddr ;
paddr + = ( words & ~ 3 ) < < 2 ;
words & = 3 ;
}
# endif
# ifdef CONFIG_ATM_ENI_BURST_RX_2W /* probably useless if RX_4W, RX_8W, ... */
if ( words & ~ 1 ) {
dma [ j + + ] = MID_DT_2W | ( ( words > > 1 ) < <
MID_DMA_COUNT_SHIFT ) | ( vcc - > vci < <
MID_DMA_VCI_SHIFT ) ;
dma [ j + + ] = paddr ;
paddr + = ( words & ~ 1 ) < < 2 ;
words & = 1 ;
}
# endif
if ( words ) {
dma [ j + + ] = MID_DT_WORD | ( words < < MID_DMA_COUNT_SHIFT )
| ( vcc - > vci < < MID_DMA_VCI_SHIFT ) ;
dma [ j + + ] = paddr ;
}
}
if ( size ! = eff ) {
dma [ j + + ] = ( here < < MID_DMA_COUNT_SHIFT ) |
( vcc - > vci < < MID_DMA_VCI_SHIFT ) | MID_DT_JK ;
j + + ;
}
if ( ! j | | j > 2 * RX_DMA_BUF ) {
printk ( KERN_CRIT DEV_LABEL " !j or j too big!!! \n " ) ;
goto trouble ;
}
dma [ j - 2 ] | = MID_DMA_END ;
j = j > > 1 ;
dma_wr = eni_in ( MID_DMA_WR_RX ) ;
dma_rd = eni_in ( MID_DMA_RD_RX ) ;
/*
* Can I move the dma_wr pointer by 2 j + 1 positions without overwriting
* data that hasn ' t been read ( position of dma_rd ) yet ?
*/
if ( ! NEPMOK ( dma_wr , j + j + 1 , dma_rd , NR_DMA_RX ) ) { /* @@@ +1 is ugly */
printk ( KERN_WARNING DEV_LABEL " (itf %d): RX DMA full \n " ,
vcc - > dev - > number ) ;
goto trouble ;
}
for ( i = 0 ; i < j ; i + + ) {
writel ( dma [ i * 2 ] , eni_dev - > rx_dma + dma_wr * 8 ) ;
writel ( dma [ i * 2 + 1 ] , eni_dev - > rx_dma + dma_wr * 8 + 4 ) ;
dma_wr = ( dma_wr + 1 ) & ( NR_DMA_RX - 1 ) ;
}
if ( skb ) {
ENI_PRV_POS ( skb ) = eni_vcc - > descr + size + 1 ;
skb_queue_tail ( & eni_dev - > rx_queue , skb ) ;
eni_vcc - > last = skb ;
rx_enqueued + + ;
}
eni_vcc - > descr = here ;
eni_out ( dma_wr , MID_DMA_WR_RX ) ;
return 0 ;
trouble :
if ( paddr )
pci_unmap_single ( eni_dev - > pci_dev , paddr , skb - > len ,
PCI_DMA_FROMDEVICE ) ;
if ( skb ) dev_kfree_skb_irq ( skb ) ;
return - 1 ;
}
static void discard ( struct atm_vcc * vcc , unsigned long size )
{
struct eni_vcc * eni_vcc ;
eni_vcc = ENI_VCC ( vcc ) ;
EVENT ( " discard (size=%ld) \n " , size , 0 ) ;
while ( do_rx_dma ( vcc , NULL , 1 , size , 0 ) ) EVENT ( " BUSY LOOP " , 0 , 0 ) ;
/* could do a full fallback, but that might be more expensive */
if ( eni_vcc - > rxing ) ENI_PRV_POS ( eni_vcc - > last ) + = size + 1 ;
else eni_vcc - > rx_pos = ( eni_vcc - > rx_pos + size + 1 ) & ( eni_vcc - > words - 1 ) ;
}
/*
* TODO : should check whether direct copies ( without DMA setup , dequeuing on
* interrupt , etc . ) aren ' t much faster for AAL0
*/
static int rx_aal0 ( struct atm_vcc * vcc )
{
struct eni_vcc * eni_vcc ;
unsigned long descr ;
unsigned long length ;
struct sk_buff * skb ;
DPRINTK ( " >rx_aal0 \n " ) ;
eni_vcc = ENI_VCC ( vcc ) ;
descr = readl ( eni_vcc - > recv + eni_vcc - > descr * 4 ) ;
if ( ( descr & MID_RED_IDEN ) ! = ( MID_RED_RX_ID < < MID_RED_SHIFT ) ) {
rx_ident_err ( vcc ) ;
return 1 ;
}
if ( descr & MID_RED_T ) {
DPRINTK ( DEV_LABEL " (itf %d): trashing empty cell \n " ,
vcc - > dev - > number ) ;
length = 0 ;
atomic_inc ( & vcc - > stats - > rx_err ) ;
}
else {
length = ATM_CELL_SIZE - 1 ; /* no HEC */
}
skb = length ? atm_alloc_charge ( vcc , length , GFP_ATOMIC ) : NULL ;
if ( ! skb ) {
discard ( vcc , length > > 2 ) ;
return 0 ;
}
skb_put ( skb , length ) ;
2005-08-15 04:24:31 +04:00
skb_set_timestamp ( skb , & eni_vcc - > timestamp ) ;
2005-04-17 02:20:36 +04:00
DPRINTK ( " got len %ld \n " , length ) ;
if ( do_rx_dma ( vcc , skb , 1 , length > > 2 , length > > 2 ) ) return 1 ;
eni_vcc - > rxing + + ;
return 0 ;
}
static int rx_aal5 ( struct atm_vcc * vcc )
{
struct eni_vcc * eni_vcc ;
unsigned long descr ;
unsigned long size , eff , length ;
struct sk_buff * skb ;
EVENT ( " rx_aal5 \n " , 0 , 0 ) ;
DPRINTK ( " >rx_aal5 \n " ) ;
eni_vcc = ENI_VCC ( vcc ) ;
descr = readl ( eni_vcc - > recv + eni_vcc - > descr * 4 ) ;
if ( ( descr & MID_RED_IDEN ) ! = ( MID_RED_RX_ID < < MID_RED_SHIFT ) ) {
rx_ident_err ( vcc ) ;
return 1 ;
}
if ( descr & ( MID_RED_T | MID_RED_CRC_ERR ) ) {
if ( descr & MID_RED_T ) {
EVENT ( " empty cell (descr=0x%lx) \n " , descr , 0 ) ;
DPRINTK ( DEV_LABEL " (itf %d): trashing empty cell \n " ,
vcc - > dev - > number ) ;
size = 0 ;
}
else {
static unsigned long silence = 0 ;
if ( time_after ( jiffies , silence ) | | silence = = 0 ) {
printk ( KERN_WARNING DEV_LABEL " (itf %d): "
" discarding PDU(s) with CRC error \n " ,
vcc - > dev - > number ) ;
silence = ( jiffies + 2 * HZ ) | 1 ;
}
size = ( descr & MID_RED_COUNT ) * ( ATM_CELL_PAYLOAD > > 2 ) ;
EVENT ( " CRC error (descr=0x%lx,size=%ld) \n " , descr ,
size ) ;
}
eff = length = 0 ;
atomic_inc ( & vcc - > stats - > rx_err ) ;
}
else {
size = ( descr & MID_RED_COUNT ) * ( ATM_CELL_PAYLOAD > > 2 ) ;
DPRINTK ( " size=%ld \n " , size ) ;
length = readl ( eni_vcc - > recv + ( ( ( eni_vcc - > descr + size - 1 ) &
( eni_vcc - > words - 1 ) ) ) * 4 ) & 0xffff ;
/* -trailer(2)+header(1) */
if ( length & & length < = ( size < < 2 ) - 8 & & length < =
ATM_MAX_AAL5_PDU ) eff = ( length + 3 ) > > 2 ;
else { /* ^ trailer length (8) */
EVENT ( " bad PDU (descr=0x08%lx,length=%ld) \n " , descr ,
length ) ;
printk ( KERN_ERR DEV_LABEL " (itf %d): bad AAL5 PDU "
" (VCI=%d,length=%ld,size=%ld (descr 0x%lx)) \n " ,
vcc - > dev - > number , vcc - > vci , length , size < < 2 , descr ) ;
length = eff = 0 ;
atomic_inc ( & vcc - > stats - > rx_err ) ;
}
}
skb = eff ? atm_alloc_charge ( vcc , eff < < 2 , GFP_ATOMIC ) : NULL ;
if ( ! skb ) {
discard ( vcc , size ) ;
return 0 ;
}
skb_put ( skb , length ) ;
DPRINTK ( " got len %ld \n " , length ) ;
if ( do_rx_dma ( vcc , skb , 1 , size , eff ) ) return 1 ;
eni_vcc - > rxing + + ;
return 0 ;
}
static inline int rx_vcc ( struct atm_vcc * vcc )
{
void __iomem * vci_dsc ;
unsigned long tmp ;
struct eni_vcc * eni_vcc ;
eni_vcc = ENI_VCC ( vcc ) ;
vci_dsc = ENI_DEV ( vcc - > dev ) - > vci + vcc - > vci * 16 ;
EVENT ( " rx_vcc(1) \n " , 0 , 0 ) ;
while ( eni_vcc - > descr ! = ( tmp = ( readl ( vci_dsc + 4 ) & MID_VCI_DESCR ) > >
MID_VCI_DESCR_SHIFT ) ) {
EVENT ( " rx_vcc(2: host dsc=0x%lx, nic dsc=0x%lx) \n " ,
eni_vcc - > descr , tmp ) ;
DPRINTK ( " CB_DESCR %ld REG_DESCR %d \n " , ENI_VCC ( vcc ) - > descr ,
( ( ( unsigned ) readl ( vci_dsc + 4 ) & MID_VCI_DESCR ) > >
MID_VCI_DESCR_SHIFT ) ) ;
if ( ENI_VCC ( vcc ) - > rx ( vcc ) ) return 1 ;
}
/* clear IN_SERVICE flag */
writel ( readl ( vci_dsc ) & ~ MID_VCI_IN_SERVICE , vci_dsc ) ;
/*
* If new data has arrived between evaluating the while condition and
* clearing IN_SERVICE , we wouldn ' t be notified until additional data
* follows . So we have to loop again to be sure .
*/
EVENT ( " rx_vcc(3) \n " , 0 , 0 ) ;
while ( ENI_VCC ( vcc ) - > descr ! = ( tmp = ( readl ( vci_dsc + 4 ) & MID_VCI_DESCR )
> > MID_VCI_DESCR_SHIFT ) ) {
EVENT ( " rx_vcc(4: host dsc=0x%lx, nic dsc=0x%lx) \n " ,
eni_vcc - > descr , tmp ) ;
DPRINTK ( " CB_DESCR %ld REG_DESCR %d \n " , ENI_VCC ( vcc ) - > descr ,
( ( ( unsigned ) readl ( vci_dsc + 4 ) & MID_VCI_DESCR ) > >
MID_VCI_DESCR_SHIFT ) ) ;
if ( ENI_VCC ( vcc ) - > rx ( vcc ) ) return 1 ;
}
return 0 ;
}
static void poll_rx ( struct atm_dev * dev )
{
struct eni_dev * eni_dev ;
struct atm_vcc * curr ;
eni_dev = ENI_DEV ( dev ) ;
while ( ( curr = eni_dev - > fast ) ) {
EVENT ( " poll_rx.fast \n " , 0 , 0 ) ;
if ( rx_vcc ( curr ) ) return ;
eni_dev - > fast = ENI_VCC ( curr ) - > next ;
ENI_VCC ( curr ) - > next = ENI_VCC_NOS ;
barrier ( ) ;
ENI_VCC ( curr ) - > servicing - - ;
}
while ( ( curr = eni_dev - > slow ) ) {
EVENT ( " poll_rx.slow \n " , 0 , 0 ) ;
if ( rx_vcc ( curr ) ) return ;
eni_dev - > slow = ENI_VCC ( curr ) - > next ;
ENI_VCC ( curr ) - > next = ENI_VCC_NOS ;
barrier ( ) ;
ENI_VCC ( curr ) - > servicing - - ;
}
}
static void get_service ( struct atm_dev * dev )
{
struct eni_dev * eni_dev ;
struct atm_vcc * vcc ;
unsigned long vci ;
DPRINTK ( " >get_service \n " ) ;
eni_dev = ENI_DEV ( dev ) ;
while ( eni_in ( MID_SERV_WRITE ) ! = eni_dev - > serv_read ) {
vci = readl ( eni_dev - > service + eni_dev - > serv_read * 4 ) ;
eni_dev - > serv_read = ( eni_dev - > serv_read + 1 ) & ( NR_SERVICE - 1 ) ;
vcc = eni_dev - > rx_map [ vci & 1023 ] ;
if ( ! vcc ) {
printk ( KERN_CRIT DEV_LABEL " (itf %d): VCI %ld not "
" found \n " , dev - > number , vci ) ;
continue ; /* nasty but we try to go on anyway */
/* @@@ nope, doesn't work */
}
EVENT ( " getting from service \n " , 0 , 0 ) ;
if ( ENI_VCC ( vcc ) - > next ! = ENI_VCC_NOS ) {
EVENT ( " double service \n " , 0 , 0 ) ;
DPRINTK ( " Grr, servicing VCC %ld twice \n " , vci ) ;
continue ;
}
do_gettimeofday ( & ENI_VCC ( vcc ) - > timestamp ) ;
ENI_VCC ( vcc ) - > next = NULL ;
if ( vcc - > qos . rxtp . traffic_class = = ATM_CBR ) {
if ( eni_dev - > fast )
ENI_VCC ( eni_dev - > last_fast ) - > next = vcc ;
else eni_dev - > fast = vcc ;
eni_dev - > last_fast = vcc ;
}
else {
if ( eni_dev - > slow )
ENI_VCC ( eni_dev - > last_slow ) - > next = vcc ;
else eni_dev - > slow = vcc ;
eni_dev - > last_slow = vcc ;
}
putting + + ;
ENI_VCC ( vcc ) - > servicing + + ;
}
}
static void dequeue_rx ( struct atm_dev * dev )
{
struct eni_dev * eni_dev ;
struct eni_vcc * eni_vcc ;
struct atm_vcc * vcc ;
struct sk_buff * skb ;
void __iomem * vci_dsc ;
int first ;
eni_dev = ENI_DEV ( dev ) ;
first = 1 ;
while ( 1 ) {
skb = skb_dequeue ( & eni_dev - > rx_queue ) ;
if ( ! skb ) {
if ( first ) {
DPRINTK ( DEV_LABEL " (itf %d): RX but not "
" rxing \n " , dev - > number ) ;
EVENT ( " nothing to dequeue \n " , 0 , 0 ) ;
}
break ;
}
EVENT ( " dequeued (size=%ld,pos=0x%lx) \n " , ENI_PRV_SIZE ( skb ) ,
ENI_PRV_POS ( skb ) ) ;
rx_dequeued + + ;
vcc = ATM_SKB ( skb ) - > vcc ;
eni_vcc = ENI_VCC ( vcc ) ;
first = 0 ;
vci_dsc = eni_dev - > vci + vcc - > vci * 16 ;
if ( ! EEPMOK ( eni_vcc - > rx_pos , ENI_PRV_SIZE ( skb ) ,
( readl ( vci_dsc + 4 ) & MID_VCI_READ ) > > MID_VCI_READ_SHIFT ,
eni_vcc - > words ) ) {
EVENT ( " requeuing \n " , 0 , 0 ) ;
skb_queue_head ( & eni_dev - > rx_queue , skb ) ;
break ;
}
eni_vcc - > rxing - - ;
eni_vcc - > rx_pos = ENI_PRV_POS ( skb ) & ( eni_vcc - > words - 1 ) ;
pci_unmap_single ( eni_dev - > pci_dev , ENI_PRV_PADDR ( skb ) , skb - > len ,
PCI_DMA_TODEVICE ) ;
if ( ! skb - > len ) dev_kfree_skb_irq ( skb ) ;
else {
EVENT ( " pushing (len=%ld) \n " , skb - > len , 0 ) ;
if ( vcc - > qos . aal = = ATM_AAL0 )
* ( unsigned long * ) skb - > data =
ntohl ( * ( unsigned long * ) skb - > data ) ;
memset ( skb - > cb , 0 , sizeof ( struct eni_skb_prv ) ) ;
vcc - > push ( vcc , skb ) ;
pushed + + ;
}
atomic_inc ( & vcc - > stats - > rx ) ;
}
wake_up ( & eni_dev - > rx_wait ) ;
}
static int open_rx_first ( struct atm_vcc * vcc )
{
struct eni_dev * eni_dev ;
struct eni_vcc * eni_vcc ;
unsigned long size ;
DPRINTK ( " open_rx_first \n " ) ;
eni_dev = ENI_DEV ( vcc - > dev ) ;
eni_vcc = ENI_VCC ( vcc ) ;
eni_vcc - > rx = NULL ;
if ( vcc - > qos . rxtp . traffic_class = = ATM_NONE ) return 0 ;
size = vcc - > qos . rxtp . max_sdu * eni_dev - > rx_mult / 100 ;
if ( size > MID_MAX_BUF_SIZE & & vcc - > qos . rxtp . max_sdu < =
MID_MAX_BUF_SIZE )
size = MID_MAX_BUF_SIZE ;
eni_vcc - > recv = eni_alloc_mem ( eni_dev , & size ) ;
DPRINTK ( " rx at 0x%lx \n " , eni_vcc - > recv ) ;
eni_vcc - > words = size > > 2 ;
if ( ! eni_vcc - > recv ) return - ENOBUFS ;
eni_vcc - > rx = vcc - > qos . aal = = ATM_AAL5 ? rx_aal5 : rx_aal0 ;
eni_vcc - > descr = 0 ;
eni_vcc - > rx_pos = 0 ;
eni_vcc - > rxing = 0 ;
eni_vcc - > servicing = 0 ;
eni_vcc - > next = ENI_VCC_NOS ;
return 0 ;
}
static int open_rx_second ( struct atm_vcc * vcc )
{
void __iomem * here ;
struct eni_dev * eni_dev ;
struct eni_vcc * eni_vcc ;
unsigned long size ;
int order ;
DPRINTK ( " open_rx_second \n " ) ;
eni_dev = ENI_DEV ( vcc - > dev ) ;
eni_vcc = ENI_VCC ( vcc ) ;
if ( ! eni_vcc - > rx ) return 0 ;
/* set up VCI descriptor */
here = eni_dev - > vci + vcc - > vci * 16 ;
DPRINTK ( " loc 0x%x \n " , ( unsigned ) ( eni_vcc - > recv - eni_dev - > ram ) / 4 ) ;
size = eni_vcc - > words > > 8 ;
for ( order = - 1 ; size ; order + + ) size > > = 1 ;
writel ( 0 , here + 4 ) ; /* descr, read = 0 */
writel ( 0 , here + 8 ) ; /* write, state, count = 0 */
if ( eni_dev - > rx_map [ vcc - > vci ] )
printk ( KERN_CRIT DEV_LABEL " (itf %d): BUG - VCI %d already "
" in use \n " , vcc - > dev - > number , vcc - > vci ) ;
eni_dev - > rx_map [ vcc - > vci ] = vcc ; /* now it counts */
writel ( ( ( vcc - > qos . aal ! = ATM_AAL5 ? MID_MODE_RAW : MID_MODE_AAL5 ) < <
MID_VCI_MODE_SHIFT ) | MID_VCI_PTI_MODE |
( ( ( eni_vcc - > recv - eni_dev - > ram ) > > ( MID_LOC_SKIP + 2 ) ) < <
MID_VCI_LOCATION_SHIFT ) | ( order < < MID_VCI_SIZE_SHIFT ) , here ) ;
return 0 ;
}
static void close_rx ( struct atm_vcc * vcc )
{
DECLARE_WAITQUEUE ( wait , current ) ;
void __iomem * here ;
struct eni_dev * eni_dev ;
struct eni_vcc * eni_vcc ;
eni_vcc = ENI_VCC ( vcc ) ;
if ( ! eni_vcc - > rx ) return ;
eni_dev = ENI_DEV ( vcc - > dev ) ;
if ( vcc - > vpi ! = ATM_VPI_UNSPEC & & vcc - > vci ! = ATM_VCI_UNSPEC ) {
here = eni_dev - > vci + vcc - > vci * 16 ;
/* block receiver */
writel ( ( readl ( here ) & ~ MID_VCI_MODE ) | ( MID_MODE_TRASH < <
MID_VCI_MODE_SHIFT ) , here ) ;
/* wait for receiver to become idle */
udelay ( 27 ) ;
/* discard pending cell */
writel ( readl ( here ) & ~ MID_VCI_IN_SERVICE , here ) ;
/* don't accept any new ones */
eni_dev - > rx_map [ vcc - > vci ] = NULL ;
/* wait for RX queue to drain */
DPRINTK ( " eni_close: waiting for RX ... \n " ) ;
EVENT ( " RX closing \n " , 0 , 0 ) ;
add_wait_queue ( & eni_dev - > rx_wait , & wait ) ;
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
barrier ( ) ;
for ( ; ; ) {
/* transition service->rx: rxing++, servicing-- */
if ( ! eni_vcc - > servicing ) {
barrier ( ) ;
if ( ! eni_vcc - > rxing ) break ;
}
EVENT ( " drain PDUs (rx %ld, serv %ld) \n " , eni_vcc - > rxing ,
eni_vcc - > servicing ) ;
printk ( KERN_INFO " %d+%d RX left \n " , eni_vcc - > servicing ,
eni_vcc - > rxing ) ;
schedule ( ) ;
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
}
for ( ; ; ) {
int at_end ;
u32 tmp ;
tasklet_disable ( & eni_dev - > task ) ;
tmp = readl ( eni_dev - > vci + vcc - > vci * 16 + 4 ) & MID_VCI_READ ;
at_end = eni_vcc - > rx_pos = = tmp > > MID_VCI_READ_SHIFT ;
tasklet_enable ( & eni_dev - > task ) ;
if ( at_end ) break ;
EVENT ( " drain discard (host 0x%lx, nic 0x%lx) \n " ,
eni_vcc - > rx_pos , tmp ) ;
printk ( KERN_INFO " draining RX: host 0x%lx, nic 0x%x \n " ,
eni_vcc - > rx_pos , tmp ) ;
schedule ( ) ;
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
}
set_current_state ( TASK_RUNNING ) ;
remove_wait_queue ( & eni_dev - > rx_wait , & wait ) ;
}
eni_free_mem ( eni_dev , eni_vcc - > recv , eni_vcc - > words < < 2 ) ;
eni_vcc - > rx = NULL ;
}
static int start_rx ( struct atm_dev * dev )
{
struct eni_dev * eni_dev ;
eni_dev = ENI_DEV ( dev ) ;
eni_dev - > rx_map = ( struct atm_vcc * * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! eni_dev - > rx_map ) {
printk ( KERN_ERR DEV_LABEL " (itf %d): couldn't get free page \n " ,
dev - > number ) ;
free_page ( ( unsigned long ) eni_dev - > free_list ) ;
return - ENOMEM ;
}
memset ( eni_dev - > rx_map , 0 , PAGE_SIZE ) ;
eni_dev - > rx_mult = DEFAULT_RX_MULT ;
eni_dev - > fast = eni_dev - > last_fast = NULL ;
eni_dev - > slow = eni_dev - > last_slow = NULL ;
init_waitqueue_head ( & eni_dev - > rx_wait ) ;
skb_queue_head_init ( & eni_dev - > rx_queue ) ;
eni_dev - > serv_read = eni_in ( MID_SERV_WRITE ) ;
eni_out ( 0 , MID_DMA_WR_RX ) ;
return 0 ;
}
/*----------------------------------- TX ------------------------------------*/
enum enq_res { enq_ok , enq_next , enq_jam } ;
static inline void put_dma ( int chan , u32 * dma , int * j , dma_addr_t paddr ,
u32 size )
{
u32 init , words ;
DPRINTK ( " put_dma: 0x%lx+0x%x \n " , ( unsigned long ) paddr , size ) ;
EVENT ( " put_dma: 0x%lx+0x%lx \n " , ( unsigned long ) paddr , size ) ;
#if 0 /* don't complain anymore */
if ( paddr & 3 )
printk ( KERN_ERR " put_dma: unaligned addr (0x%lx) \n " , paddr ) ;
if ( size & 3 )
printk ( KERN_ERR " put_dma: unaligned size (0x%lx) \n " , size ) ;
# endif
if ( paddr & 3 ) {
init = 4 - ( paddr & 3 ) ;
if ( init > size | | size < 7 ) init = size ;
DPRINTK ( " put_dma: %lx DMA: %d/%d bytes \n " ,
( unsigned long ) paddr , init , size ) ;
dma [ ( * j ) + + ] = MID_DT_BYTE | ( init < < MID_DMA_COUNT_SHIFT ) |
( chan < < MID_DMA_CHAN_SHIFT ) ;
dma [ ( * j ) + + ] = paddr ;
paddr + = init ;
size - = init ;
}
words = size > > 2 ;
size & = 3 ;
if ( words & & ( paddr & 31 ) ) {
init = 8 - ( ( paddr & 31 ) > > 2 ) ;
if ( init > words ) init = words ;
DPRINTK ( " put_dma: %lx DMA: %d/%d words \n " ,
( unsigned long ) paddr , init , words ) ;
dma [ ( * j ) + + ] = MID_DT_WORD | ( init < < MID_DMA_COUNT_SHIFT ) |
( chan < < MID_DMA_CHAN_SHIFT ) ;
dma [ ( * j ) + + ] = paddr ;
paddr + = init < < 2 ;
words - = init ;
}
# ifdef CONFIG_ATM_ENI_BURST_TX_16W /* may work with some PCI chipsets ... */
if ( words & ~ 15 ) {
DPRINTK ( " put_dma: %lx DMA: %d*16/%d words \n " ,
( unsigned long ) paddr , words > > 4 , words ) ;
dma [ ( * j ) + + ] = MID_DT_16W | ( ( words > > 4 ) < < MID_DMA_COUNT_SHIFT )
| ( chan < < MID_DMA_CHAN_SHIFT ) ;
dma [ ( * j ) + + ] = paddr ;
paddr + = ( words & ~ 15 ) < < 2 ;
words & = 15 ;
}
# endif
# ifdef CONFIG_ATM_ENI_BURST_TX_8W /* recommended */
if ( words & ~ 7 ) {
DPRINTK ( " put_dma: %lx DMA: %d*8/%d words \n " ,
( unsigned long ) paddr , words > > 3 , words ) ;
dma [ ( * j ) + + ] = MID_DT_8W | ( ( words > > 3 ) < < MID_DMA_COUNT_SHIFT )
| ( chan < < MID_DMA_CHAN_SHIFT ) ;
dma [ ( * j ) + + ] = paddr ;
paddr + = ( words & ~ 7 ) < < 2 ;
words & = 7 ;
}
# endif
# ifdef CONFIG_ATM_ENI_BURST_TX_4W /* probably useless if TX_8W or TX_16W */
if ( words & ~ 3 ) {
DPRINTK ( " put_dma: %lx DMA: %d*4/%d words \n " ,
( unsigned long ) paddr , words > > 2 , words ) ;
dma [ ( * j ) + + ] = MID_DT_4W | ( ( words > > 2 ) < < MID_DMA_COUNT_SHIFT )
| ( chan < < MID_DMA_CHAN_SHIFT ) ;
dma [ ( * j ) + + ] = paddr ;
paddr + = ( words & ~ 3 ) < < 2 ;
words & = 3 ;
}
# endif
# ifdef CONFIG_ATM_ENI_BURST_TX_2W /* probably useless if TX_4W, TX_8W, ... */
if ( words & ~ 1 ) {
DPRINTK ( " put_dma: %lx DMA: %d*2/%d words \n " ,
( unsigned long ) paddr , words > > 1 , words ) ;
dma [ ( * j ) + + ] = MID_DT_2W | ( ( words > > 1 ) < < MID_DMA_COUNT_SHIFT )
| ( chan < < MID_DMA_CHAN_SHIFT ) ;
dma [ ( * j ) + + ] = paddr ;
paddr + = ( words & ~ 1 ) < < 2 ;
words & = 1 ;
}
# endif
if ( words ) {
DPRINTK ( " put_dma: %lx DMA: %d words \n " , ( unsigned long ) paddr ,
words ) ;
dma [ ( * j ) + + ] = MID_DT_WORD | ( words < < MID_DMA_COUNT_SHIFT ) |
( chan < < MID_DMA_CHAN_SHIFT ) ;
dma [ ( * j ) + + ] = paddr ;
paddr + = words < < 2 ;
}
if ( size ) {
DPRINTK ( " put_dma: %lx DMA: %d bytes \n " , ( unsigned long ) paddr ,
size ) ;
dma [ ( * j ) + + ] = MID_DT_BYTE | ( size < < MID_DMA_COUNT_SHIFT ) |
( chan < < MID_DMA_CHAN_SHIFT ) ;
dma [ ( * j ) + + ] = paddr ;
}
}
static enum enq_res do_tx ( struct sk_buff * skb )
{
struct atm_vcc * vcc ;
struct eni_dev * eni_dev ;
struct eni_vcc * eni_vcc ;
struct eni_tx * tx ;
dma_addr_t paddr ;
u32 dma_rd , dma_wr ;
u32 size ; /* in words */
int aal5 , dma_size , i , j ;
DPRINTK ( " >do_tx \n " ) ;
NULLCHECK ( skb ) ;
EVENT ( " do_tx: skb=0x%lx, %ld bytes \n " , ( unsigned long ) skb , skb - > len ) ;
vcc = ATM_SKB ( skb ) - > vcc ;
NULLCHECK ( vcc ) ;
eni_dev = ENI_DEV ( vcc - > dev ) ;
NULLCHECK ( eni_dev ) ;
eni_vcc = ENI_VCC ( vcc ) ;
tx = eni_vcc - > tx ;
NULLCHECK ( tx ) ;
#if 0 /* Enable this for testing with the "align" program */
{
unsigned int hack = * ( ( char * ) skb - > data ) - ' 0 ' ;
if ( hack < 8 ) {
skb - > data + = hack ;
skb - > len - = hack ;
}
}
# endif
#if 0 /* should work now */
if ( ( unsigned long ) skb - > data & 3 )
printk ( KERN_ERR DEV_LABEL " (itf %d): VCI %d has mis-aligned "
" TX data \n " , vcc - > dev - > number , vcc - > vci ) ;
# endif
/*
* Potential future IP speedup : make hard_header big enough to put
* segmentation descriptor directly into PDU . Saves : 4 slave writes ,
* 1 DMA xfer & 2 DMA ' ed bytes ( protocol layering is for wimps : - )
*/
aal5 = vcc - > qos . aal = = ATM_AAL5 ;
/* check space in buffer */
if ( ! aal5 )
size = ( ATM_CELL_PAYLOAD > > 2 ) + TX_DESCR_SIZE ;
/* cell without HEC plus segmentation header (includes
four - byte cell header ) */
else {
size = skb - > len + 4 * AAL5_TRAILER + ATM_CELL_PAYLOAD - 1 ;
/* add AAL5 trailer */
size = ( ( size - ( size % ATM_CELL_PAYLOAD ) ) > > 2 ) + TX_DESCR_SIZE ;
/* add segmentation header */
}
/*
* Can I move tx_pos by size bytes without getting closer than TX_GAP
* to the read pointer ? TX_GAP means to leave some space for what
* the manual calls " too close " .
*/
if ( ! NEPMOK ( tx - > tx_pos , size + TX_GAP ,
eni_in ( MID_TX_RDPTR ( tx - > index ) ) , tx - > words ) ) {
DPRINTK ( DEV_LABEL " (itf %d): TX full (size %d) \n " ,
vcc - > dev - > number , size ) ;
return enq_next ;
}
/* check DMA */
dma_wr = eni_in ( MID_DMA_WR_TX ) ;
dma_rd = eni_in ( MID_DMA_RD_TX ) ;
dma_size = 3 ; /* JK for descriptor and final fill, plus final size
mis - alignment fix */
DPRINTK ( " iovcnt = %d \n " , skb_shinfo ( skb ) - > nr_frags ) ;
if ( ! skb_shinfo ( skb ) - > nr_frags ) dma_size + = 5 ;
else dma_size + = 5 * ( skb_shinfo ( skb ) - > nr_frags + 1 ) ;
if ( dma_size > TX_DMA_BUF ) {
printk ( KERN_CRIT DEV_LABEL " (itf %d): needs %d DMA entries "
" (got only %d) \n " , vcc - > dev - > number , dma_size , TX_DMA_BUF ) ;
}
DPRINTK ( " dma_wr is %d, tx_pos is %ld \n " , dma_wr , tx - > tx_pos ) ;
if ( dma_wr ! = dma_rd & & ( ( dma_rd + NR_DMA_TX - dma_wr ) & ( NR_DMA_TX - 1 ) ) <
dma_size ) {
printk ( KERN_WARNING DEV_LABEL " (itf %d): TX DMA full \n " ,
vcc - > dev - > number ) ;
return enq_jam ;
}
paddr = pci_map_single ( eni_dev - > pci_dev , skb - > data , skb - > len ,
PCI_DMA_TODEVICE ) ;
ENI_PRV_PADDR ( skb ) = paddr ;
/* prepare DMA queue entries */
j = 0 ;
eni_dev - > dma [ j + + ] = ( ( ( tx - > tx_pos + TX_DESCR_SIZE ) & ( tx - > words - 1 ) ) < <
MID_DMA_COUNT_SHIFT ) | ( tx - > index < < MID_DMA_CHAN_SHIFT ) |
MID_DT_JK ;
j + + ;
if ( ! skb_shinfo ( skb ) - > nr_frags )
if ( aal5 ) put_dma ( tx - > index , eni_dev - > dma , & j , paddr , skb - > len ) ;
else put_dma ( tx - > index , eni_dev - > dma , & j , paddr + 4 , skb - > len - 4 ) ;
else {
DPRINTK ( " doing direct send \n " ) ; /* @@@ well, this doesn't work anyway */
for ( i = - 1 ; i < skb_shinfo ( skb ) - > nr_frags ; i + + )
if ( i = = - 1 )
put_dma ( tx - > index , eni_dev - > dma , & j , ( unsigned long )
skb - > data ,
skb - > len - skb - > data_len ) ;
else
put_dma ( tx - > index , eni_dev - > dma , & j , ( unsigned long )
skb_shinfo ( skb ) - > frags [ i ] . page + skb_shinfo ( skb ) - > frags [ i ] . page_offset ,
skb_shinfo ( skb ) - > frags [ i ] . size ) ;
}
if ( skb - > len & 3 )
put_dma ( tx - > index , eni_dev - > dma , & j , zeroes , 4 - ( skb - > len & 3 ) ) ;
/* JK for AAL5 trailer - AAL0 doesn't need it, but who cares ... */
eni_dev - > dma [ j + + ] = ( ( ( tx - > tx_pos + size ) & ( tx - > words - 1 ) ) < <
MID_DMA_COUNT_SHIFT ) | ( tx - > index < < MID_DMA_CHAN_SHIFT ) |
MID_DMA_END | MID_DT_JK ;
j + + ;
DPRINTK ( " DMA at end: %d \n " , j ) ;
/* store frame */
writel ( ( MID_SEG_TX_ID < < MID_SEG_ID_SHIFT ) |
( aal5 ? MID_SEG_AAL5 : 0 ) | ( tx - > prescaler < < MID_SEG_PR_SHIFT ) |
( tx - > resolution < < MID_SEG_RATE_SHIFT ) |
( size / ( ATM_CELL_PAYLOAD / 4 ) ) , tx - > send + tx - > tx_pos * 4 ) ;
/*printk("dsc = 0x%08lx\n",(unsigned long) readl(tx->send+tx->tx_pos*4));*/
writel ( ( vcc - > vci < < MID_SEG_VCI_SHIFT ) |
( aal5 ? 0 : ( skb - > data [ 3 ] & 0xf ) ) |
( ATM_SKB ( skb ) - > atm_options & ATM_ATMOPT_CLP ? MID_SEG_CLP : 0 ) ,
tx - > send + ( ( tx - > tx_pos + 1 ) & ( tx - > words - 1 ) ) * 4 ) ;
DPRINTK ( " size: %d, len:%d \n " , size , skb - > len ) ;
if ( aal5 )
writel ( skb - > len , tx - > send +
( ( tx - > tx_pos + size - AAL5_TRAILER ) & ( tx - > words - 1 ) ) * 4 ) ;
j = j > > 1 ;
for ( i = 0 ; i < j ; i + + ) {
writel ( eni_dev - > dma [ i * 2 ] , eni_dev - > tx_dma + dma_wr * 8 ) ;
writel ( eni_dev - > dma [ i * 2 + 1 ] , eni_dev - > tx_dma + dma_wr * 8 + 4 ) ;
dma_wr = ( dma_wr + 1 ) & ( NR_DMA_TX - 1 ) ;
}
ENI_PRV_POS ( skb ) = tx - > tx_pos ;
ENI_PRV_SIZE ( skb ) = size ;
ENI_VCC ( vcc ) - > txing + = size ;
tx - > tx_pos = ( tx - > tx_pos + size ) & ( tx - > words - 1 ) ;
DPRINTK ( " dma_wr set to %d, tx_pos is now %ld \n " , dma_wr , tx - > tx_pos ) ;
eni_out ( dma_wr , MID_DMA_WR_TX ) ;
skb_queue_tail ( & eni_dev - > tx_queue , skb ) ;
queued + + ;
return enq_ok ;
}
static void poll_tx ( struct atm_dev * dev )
{
struct eni_tx * tx ;
struct sk_buff * skb ;
enum enq_res res ;
int i ;
DPRINTK ( " >poll_tx \n " ) ;
for ( i = NR_CHAN - 1 ; i > = 0 ; i - - ) {
tx = & ENI_DEV ( dev ) - > tx [ i ] ;
if ( tx - > send )
while ( ( skb = skb_dequeue ( & tx - > backlog ) ) ) {
res = do_tx ( skb ) ;
if ( res = = enq_ok ) continue ;
DPRINTK ( " re-queuing TX PDU \n " ) ;
skb_queue_head ( & tx - > backlog , skb ) ;
requeued + + ;
if ( res = = enq_jam ) return ;
break ;
}
}
}
static void dequeue_tx ( struct atm_dev * dev )
{
struct eni_dev * eni_dev ;
struct atm_vcc * vcc ;
struct sk_buff * skb ;
struct eni_tx * tx ;
NULLCHECK ( dev ) ;
eni_dev = ENI_DEV ( dev ) ;
NULLCHECK ( eni_dev ) ;
while ( ( skb = skb_dequeue ( & eni_dev - > tx_queue ) ) ) {
vcc = ATM_SKB ( skb ) - > vcc ;
NULLCHECK ( vcc ) ;
tx = ENI_VCC ( vcc ) - > tx ;
NULLCHECK ( ENI_VCC ( vcc ) - > tx ) ;
DPRINTK ( " dequeue_tx: next 0x%lx curr 0x%x \n " , ENI_PRV_POS ( skb ) ,
( unsigned ) eni_in ( MID_TX_DESCRSTART ( tx - > index ) ) ) ;
if ( ENI_VCC ( vcc ) - > txing < tx - > words & & ENI_PRV_POS ( skb ) = =
eni_in ( MID_TX_DESCRSTART ( tx - > index ) ) ) {
skb_queue_head ( & eni_dev - > tx_queue , skb ) ;
break ;
}
ENI_VCC ( vcc ) - > txing - = ENI_PRV_SIZE ( skb ) ;
pci_unmap_single ( eni_dev - > pci_dev , ENI_PRV_PADDR ( skb ) , skb - > len ,
PCI_DMA_TODEVICE ) ;
if ( vcc - > pop ) vcc - > pop ( vcc , skb ) ;
else dev_kfree_skb_irq ( skb ) ;
atomic_inc ( & vcc - > stats - > tx ) ;
wake_up ( & eni_dev - > tx_wait ) ;
dma_complete + + ;
}
}
static struct eni_tx * alloc_tx ( struct eni_dev * eni_dev , int ubr )
{
int i ;
for ( i = ! ubr ; i < NR_CHAN ; i + + )
if ( ! eni_dev - > tx [ i ] . send ) return eni_dev - > tx + i ;
return NULL ;
}
static int comp_tx ( struct eni_dev * eni_dev , int * pcr , int reserved , int * pre ,
int * res , int unlimited )
{
static const int pre_div [ ] = { 4 , 16 , 128 , 2048 } ;
/* 2^(((x+2)^2-(x+2))/2+1) */
if ( unlimited ) * pre = * res = 0 ;
else {
if ( * pcr > 0 ) {
int div ;
for ( * pre = 0 ; * pre < 3 ; ( * pre ) + + )
if ( TS_CLOCK / pre_div [ * pre ] / 64 < = * pcr ) break ;
div = pre_div [ * pre ] * * pcr ;
DPRINTK ( " min div %d \n " , div ) ;
* res = TS_CLOCK / div - 1 ;
}
else {
int div ;
if ( ! * pcr ) * pcr = eni_dev - > tx_bw + reserved ;
for ( * pre = 3 ; * pre > = 0 ; ( * pre ) - - )
if ( TS_CLOCK / pre_div [ * pre ] / 64 > - * pcr ) break ;
if ( * pre < 3 ) ( * pre ) + + ; /* else fail later */
div = pre_div [ * pre ] * - * pcr ;
DPRINTK ( " max div %d \n " , div ) ;
* res = ( TS_CLOCK + div - 1 ) / div - 1 ;
}
if ( * res < 0 ) * res = 0 ;
if ( * res > MID_SEG_MAX_RATE ) * res = MID_SEG_MAX_RATE ;
}
* pcr = TS_CLOCK / pre_div [ * pre ] / ( * res + 1 ) ;
DPRINTK ( " out pcr: %d (%d:%d) \n " , * pcr , * pre , * res ) ;
return 0 ;
}
static int reserve_or_set_tx ( struct atm_vcc * vcc , struct atm_trafprm * txtp ,
int set_rsv , int set_shp )
{
struct eni_dev * eni_dev = ENI_DEV ( vcc - > dev ) ;
struct eni_vcc * eni_vcc = ENI_VCC ( vcc ) ;
struct eni_tx * tx ;
unsigned long size ;
void __iomem * mem ;
int rate , ubr , unlimited , new_tx ;
int pre , res , order ;
int error ;
rate = atm_pcr_goal ( txtp ) ;
ubr = txtp - > traffic_class = = ATM_UBR ;
unlimited = ubr & & ( ! rate | | rate < = - ATM_OC3_PCR | |
rate > = ATM_OC3_PCR ) ;
if ( ! unlimited ) {
size = txtp - > max_sdu * eni_dev - > tx_mult / 100 ;
if ( size > MID_MAX_BUF_SIZE & & txtp - > max_sdu < =
MID_MAX_BUF_SIZE )
size = MID_MAX_BUF_SIZE ;
}
else {
if ( eni_dev - > ubr ) {
eni_vcc - > tx = eni_dev - > ubr ;
txtp - > pcr = ATM_OC3_PCR ;
return 0 ;
}
size = UBR_BUFFER ;
}
new_tx = ! eni_vcc - > tx ;
mem = NULL ; /* for gcc */
if ( ! new_tx ) tx = eni_vcc - > tx ;
else {
mem = eni_alloc_mem ( eni_dev , & size ) ;
if ( ! mem ) return - ENOBUFS ;
tx = alloc_tx ( eni_dev , unlimited ) ;
if ( ! tx ) {
eni_free_mem ( eni_dev , mem , size ) ;
return - EBUSY ;
}
DPRINTK ( " got chan %d \n " , tx - > index ) ;
tx - > reserved = tx - > shaping = 0 ;
tx - > send = mem ;
tx - > words = size > > 2 ;
skb_queue_head_init ( & tx - > backlog ) ;
for ( order = 0 ; size > ( 1 < < ( order + 10 ) ) ; order + + ) ;
eni_out ( ( order < < MID_SIZE_SHIFT ) |
( ( tx - > send - eni_dev - > ram ) > > ( MID_LOC_SKIP + 2 ) ) ,
MID_TX_PLACE ( tx - > index ) ) ;
tx - > tx_pos = eni_in ( MID_TX_DESCRSTART ( tx - > index ) ) &
MID_DESCR_START ;
}
error = comp_tx ( eni_dev , & rate , tx - > reserved , & pre , & res , unlimited ) ;
if ( ! error & & txtp - > min_pcr > rate ) error = - EINVAL ;
if ( ! error & & txtp - > max_pcr & & txtp - > max_pcr ! = ATM_MAX_PCR & &
txtp - > max_pcr < rate ) error = - EINVAL ;
if ( ! error & & ! ubr & & rate > eni_dev - > tx_bw + tx - > reserved )
error = - EINVAL ;
if ( ! error & & set_rsv & & ! set_shp & & rate < tx - > shaping )
error = - EINVAL ;
if ( ! error & & ! set_rsv & & rate > tx - > reserved & & ! ubr )
error = - EINVAL ;
if ( error ) {
if ( new_tx ) {
tx - > send = NULL ;
eni_free_mem ( eni_dev , mem , size ) ;
}
return error ;
}
txtp - > pcr = rate ;
if ( set_rsv & & ! ubr ) {
eni_dev - > tx_bw + = tx - > reserved ;
tx - > reserved = rate ;
eni_dev - > tx_bw - = rate ;
}
if ( set_shp | | ( unlimited & & new_tx ) ) {
if ( unlimited & & new_tx ) eni_dev - > ubr = tx ;
tx - > prescaler = pre ;
tx - > resolution = res ;
tx - > shaping = rate ;
}
if ( set_shp ) eni_vcc - > tx = tx ;
DPRINTK ( " rsv %d shp %d \n " , tx - > reserved , tx - > shaping ) ;
return 0 ;
}
static int open_tx_first ( struct atm_vcc * vcc )
{
ENI_VCC ( vcc ) - > tx = NULL ;
if ( vcc - > qos . txtp . traffic_class = = ATM_NONE ) return 0 ;
ENI_VCC ( vcc ) - > txing = 0 ;
return reserve_or_set_tx ( vcc , & vcc - > qos . txtp , 1 , 1 ) ;
}
static int open_tx_second ( struct atm_vcc * vcc )
{
return 0 ; /* nothing to do */
}
static void close_tx ( struct atm_vcc * vcc )
{
DECLARE_WAITQUEUE ( wait , current ) ;
struct eni_dev * eni_dev ;
struct eni_vcc * eni_vcc ;
eni_vcc = ENI_VCC ( vcc ) ;
if ( ! eni_vcc - > tx ) return ;
eni_dev = ENI_DEV ( vcc - > dev ) ;
/* wait for TX queue to drain */
DPRINTK ( " eni_close: waiting for TX ... \n " ) ;
add_wait_queue ( & eni_dev - > tx_wait , & wait ) ;
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
for ( ; ; ) {
int txing ;
tasklet_disable ( & eni_dev - > task ) ;
txing = skb_peek ( & eni_vcc - > tx - > backlog ) | | eni_vcc - > txing ;
tasklet_enable ( & eni_dev - > task ) ;
if ( ! txing ) break ;
DPRINTK ( " %d TX left \n " , eni_vcc - > txing ) ;
schedule ( ) ;
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
}
set_current_state ( TASK_RUNNING ) ;
remove_wait_queue ( & eni_dev - > tx_wait , & wait ) ;
if ( eni_vcc - > tx ! = eni_dev - > ubr ) {
/*
* Looping a few times in here is probably far cheaper than
* keeping track of TX completions all the time , so let ' s poll
* a bit . . .
*/
while ( eni_in ( MID_TX_RDPTR ( eni_vcc - > tx - > index ) ) ! =
eni_in ( MID_TX_DESCRSTART ( eni_vcc - > tx - > index ) ) )
schedule ( ) ;
eni_free_mem ( eni_dev , eni_vcc - > tx - > send , eni_vcc - > tx - > words < < 2 ) ;
eni_vcc - > tx - > send = NULL ;
eni_dev - > tx_bw + = eni_vcc - > tx - > reserved ;
}
eni_vcc - > tx = NULL ;
}
static int start_tx ( struct atm_dev * dev )
{
struct eni_dev * eni_dev ;
int i ;
eni_dev = ENI_DEV ( dev ) ;
eni_dev - > lost = 0 ;
eni_dev - > tx_bw = ATM_OC3_PCR ;
eni_dev - > tx_mult = DEFAULT_TX_MULT ;
init_waitqueue_head ( & eni_dev - > tx_wait ) ;
eni_dev - > ubr = NULL ;
skb_queue_head_init ( & eni_dev - > tx_queue ) ;
eni_out ( 0 , MID_DMA_WR_TX ) ;
for ( i = 0 ; i < NR_CHAN ; i + + ) {
eni_dev - > tx [ i ] . send = NULL ;
eni_dev - > tx [ i ] . index = i ;
}
return 0 ;
}
/*--------------------------------- common ----------------------------------*/
#if 0 /* may become useful again when tuning things */
static void foo ( void )
{
printk ( KERN_INFO
" tx_complete=%d,dma_complete=%d,queued=%d,requeued=%d,sub=%d, \n "
" backlogged=%d,rx_enqueued=%d,rx_dequeued=%d,putting=%d,pushed=%d \n " ,
tx_complete , dma_complete , queued , requeued , submitted , backlogged ,
rx_enqueued , rx_dequeued , putting , pushed ) ;
if ( eni_boards ) printk ( KERN_INFO " loss: %ld \n " , ENI_DEV ( eni_boards ) - > lost ) ;
}
# endif
static void bug_int ( struct atm_dev * dev , unsigned long reason )
{
struct eni_dev * eni_dev ;
DPRINTK ( " >bug_int \n " ) ;
eni_dev = ENI_DEV ( dev ) ;
if ( reason & MID_DMA_ERR_ACK )
printk ( KERN_CRIT DEV_LABEL " (itf %d): driver error - DMA "
" error \n " , dev - > number ) ;
if ( reason & MID_TX_IDENT_MISM )
printk ( KERN_CRIT DEV_LABEL " (itf %d): driver error - ident "
" mismatch \n " , dev - > number ) ;
if ( reason & MID_TX_DMA_OVFL )
printk ( KERN_CRIT DEV_LABEL " (itf %d): driver error - DMA "
" overflow \n " , dev - > number ) ;
EVENT ( " ---dump ends here--- \n " , 0 , 0 ) ;
printk ( KERN_NOTICE " ---recent events--- \n " ) ;
event_dump ( ) ;
}
static irqreturn_t eni_int ( int irq , void * dev_id , struct pt_regs * regs )
{
struct atm_dev * dev ;
struct eni_dev * eni_dev ;
u32 reason ;
DPRINTK ( " >eni_int \n " ) ;
dev = dev_id ;
eni_dev = ENI_DEV ( dev ) ;
reason = eni_in ( MID_ISA ) ;
DPRINTK ( DEV_LABEL " : int 0x%lx \n " , ( unsigned long ) reason ) ;
/*
* Must handle these two right now , because reading ISA doesn ' t clear
* them , so they re - occur and we never make it to the tasklet . Since
* they ' re rare , we don ' t mind the occasional invocation of eni_tasklet
* with eni_dev - > events = = 0.
*/
if ( reason & MID_STAT_OVFL ) {
EVENT ( " stat overflow \n " , 0 , 0 ) ;
eni_dev - > lost + = eni_in ( MID_STAT ) & MID_OVFL_TRASH ;
}
if ( reason & MID_SUNI_INT ) {
EVENT ( " SUNI int \n " , 0 , 0 ) ;
dev - > phy - > interrupt ( dev ) ;
#if 0
foo ( ) ;
# endif
}
spin_lock ( & eni_dev - > lock ) ;
eni_dev - > events | = reason ;
spin_unlock ( & eni_dev - > lock ) ;
tasklet_schedule ( & eni_dev - > task ) ;
return IRQ_HANDLED ;
}
static void eni_tasklet ( unsigned long data )
{
struct atm_dev * dev = ( struct atm_dev * ) data ;
struct eni_dev * eni_dev = ENI_DEV ( dev ) ;
unsigned long flags ;
u32 events ;
DPRINTK ( " eni_tasklet (dev %p) \n " , dev ) ;
spin_lock_irqsave ( & eni_dev - > lock , flags ) ;
events = xchg ( & eni_dev - > events , 0 ) ;
spin_unlock_irqrestore ( & eni_dev - > lock , flags ) ;
if ( events & MID_RX_DMA_COMPLETE ) {
EVENT ( " INT: RX DMA complete, starting dequeue_rx \n " , 0 , 0 ) ;
dequeue_rx ( dev ) ;
EVENT ( " dequeue_rx done, starting poll_rx \n " , 0 , 0 ) ;
poll_rx ( dev ) ;
EVENT ( " poll_rx done \n " , 0 , 0 ) ;
/* poll_tx ? */
}
if ( events & MID_SERVICE ) {
EVENT ( " INT: service, starting get_service \n " , 0 , 0 ) ;
get_service ( dev ) ;
EVENT ( " get_service done, starting poll_rx \n " , 0 , 0 ) ;
poll_rx ( dev ) ;
EVENT ( " poll_rx done \n " , 0 , 0 ) ;
}
if ( events & MID_TX_DMA_COMPLETE ) {
EVENT ( " INT: TX DMA COMPLETE \n " , 0 , 0 ) ;
dequeue_tx ( dev ) ;
}
if ( events & MID_TX_COMPLETE ) {
EVENT ( " INT: TX COMPLETE \n " , 0 , 0 ) ;
tx_complete + + ;
wake_up ( & eni_dev - > tx_wait ) ;
/* poll_rx ? */
}
if ( events & ( MID_DMA_ERR_ACK | MID_TX_IDENT_MISM | MID_TX_DMA_OVFL ) ) {
EVENT ( " bug interrupt \n " , 0 , 0 ) ;
bug_int ( dev , events ) ;
}
poll_tx ( dev ) ;
}
/*--------------------------------- entries ---------------------------------*/
static const char * media_name [ ] __devinitdata = {
" MMF " , " SMF " , " MMF " , " 03? " , /* 0- 3 */
" UTP " , " 05? " , " 06? " , " 07? " , /* 4- 7 */
" TAXI " , " 09? " , " 10? " , " 11? " , /* 8-11 */
" 12? " , " 13? " , " 14? " , " 15? " , /* 12-15 */
" MMF " , " SMF " , " 18? " , " 19? " , /* 16-19 */
" UTP " , " 21? " , " 22? " , " 23? " , /* 20-23 */
" 24? " , " 25? " , " 26? " , " 27? " , /* 24-27 */
" 28? " , " 29? " , " 30? " , " 31? " /* 28-31 */
} ;
# define SET_SEPROM \
( { if ( ! error & & ! pci_error ) { \
pci_error = pci_write_config_byte ( eni_dev - > pci_dev , PCI_TONGA_CTRL , tonga ) ; \
udelay ( 10 ) ; /* 10 usecs */ \
} } )
# define GET_SEPROM \
( { if ( ! error & & ! pci_error ) { \
pci_error = pci_read_config_byte ( eni_dev - > pci_dev , PCI_TONGA_CTRL , & tonga ) ; \
udelay ( 10 ) ; /* 10 usecs */ \
} } )
static int __devinit get_esi_asic ( struct atm_dev * dev )
{
struct eni_dev * eni_dev ;
unsigned char tonga ;
int error , failed , pci_error ;
int address , i , j ;
eni_dev = ENI_DEV ( dev ) ;
error = pci_error = 0 ;
tonga = SEPROM_MAGIC | SEPROM_DATA | SEPROM_CLK ;
SET_SEPROM ;
for ( i = 0 ; i < ESI_LEN & & ! error & & ! pci_error ; i + + ) {
/* start operation */
tonga | = SEPROM_DATA ;
SET_SEPROM ;
tonga | = SEPROM_CLK ;
SET_SEPROM ;
tonga & = ~ SEPROM_DATA ;
SET_SEPROM ;
tonga & = ~ SEPROM_CLK ;
SET_SEPROM ;
/* send address */
address = ( ( i + SEPROM_ESI_BASE ) < < 1 ) + 1 ;
for ( j = 7 ; j > = 0 ; j - - ) {
tonga = ( address > > j ) & 1 ? tonga | SEPROM_DATA :
tonga & ~ SEPROM_DATA ;
SET_SEPROM ;
tonga | = SEPROM_CLK ;
SET_SEPROM ;
tonga & = ~ SEPROM_CLK ;
SET_SEPROM ;
}
/* get ack */
tonga | = SEPROM_DATA ;
SET_SEPROM ;
tonga | = SEPROM_CLK ;
SET_SEPROM ;
GET_SEPROM ;
failed = tonga & SEPROM_DATA ;
tonga & = ~ SEPROM_CLK ;
SET_SEPROM ;
tonga | = SEPROM_DATA ;
SET_SEPROM ;
if ( failed ) error = - EIO ;
else {
dev - > esi [ i ] = 0 ;
for ( j = 7 ; j > = 0 ; j - - ) {
dev - > esi [ i ] < < = 1 ;
tonga | = SEPROM_DATA ;
SET_SEPROM ;
tonga | = SEPROM_CLK ;
SET_SEPROM ;
GET_SEPROM ;
if ( tonga & SEPROM_DATA ) dev - > esi [ i ] | = 1 ;
tonga & = ~ SEPROM_CLK ;
SET_SEPROM ;
tonga | = SEPROM_DATA ;
SET_SEPROM ;
}
/* get ack */
tonga | = SEPROM_DATA ;
SET_SEPROM ;
tonga | = SEPROM_CLK ;
SET_SEPROM ;
GET_SEPROM ;
if ( ! ( tonga & SEPROM_DATA ) ) error = - EIO ;
tonga & = ~ SEPROM_CLK ;
SET_SEPROM ;
tonga | = SEPROM_DATA ;
SET_SEPROM ;
}
/* stop operation */
tonga & = ~ SEPROM_DATA ;
SET_SEPROM ;
tonga | = SEPROM_CLK ;
SET_SEPROM ;
tonga | = SEPROM_DATA ;
SET_SEPROM ;
}
if ( pci_error ) {
printk ( KERN_ERR DEV_LABEL " (itf %d): error reading ESI "
" (0x%02x) \n " , dev - > number , pci_error ) ;
error = - EIO ;
}
return error ;
}
# undef SET_SEPROM
# undef GET_SEPROM
static int __devinit get_esi_fpga ( struct atm_dev * dev , void __iomem * base )
{
void __iomem * mac_base ;
int i ;
mac_base = base + EPROM_SIZE - sizeof ( struct midway_eprom ) ;
for ( i = 0 ; i < ESI_LEN ; i + + ) dev - > esi [ i ] = readb ( mac_base + ( i ^ 3 ) ) ;
return 0 ;
}
static int __devinit eni_do_init ( struct atm_dev * dev )
{
struct midway_eprom __iomem * eprom ;
struct eni_dev * eni_dev ;
struct pci_dev * pci_dev ;
unsigned long real_base ;
void __iomem * base ;
unsigned char revision ;
int error , i , last ;
DPRINTK ( " >eni_init \n " ) ;
dev - > ci_range . vpi_bits = 0 ;
dev - > ci_range . vci_bits = NR_VCI_LD ;
dev - > link_rate = ATM_OC3_PCR ;
eni_dev = ENI_DEV ( dev ) ;
pci_dev = eni_dev - > pci_dev ;
real_base = pci_resource_start ( pci_dev , 0 ) ;
eni_dev - > irq = pci_dev - > irq ;
error = pci_read_config_byte ( pci_dev , PCI_REVISION_ID , & revision ) ;
if ( error ) {
printk ( KERN_ERR DEV_LABEL " (itf %d): init error 0x%02x \n " ,
dev - > number , error ) ;
return - EINVAL ;
}
if ( ( error = pci_write_config_word ( pci_dev , PCI_COMMAND ,
PCI_COMMAND_MEMORY |
( eni_dev - > asic ? PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0 ) ) ) ) {
printk ( KERN_ERR DEV_LABEL " (itf %d): can't enable memory "
" (0x%02x) \n " , dev - > number , error ) ;
return - EIO ;
}
printk ( KERN_NOTICE DEV_LABEL " (itf %d): rev.%d,base=0x%lx,irq=%d, " ,
dev - > number , revision , real_base , eni_dev - > irq ) ;
if ( ! ( base = ioremap_nocache ( real_base , MAP_MAX_SIZE ) ) ) {
printk ( " \n " ) ;
printk ( KERN_ERR DEV_LABEL " (itf %d): can't set up page "
" mapping \n " , dev - > number ) ;
return error ;
}
eni_dev - > base_diff = real_base - ( unsigned long ) base ;
/* id may not be present in ASIC Tonga boards - check this @@@ */
if ( ! eni_dev - > asic ) {
eprom = ( base + EPROM_SIZE - sizeof ( struct midway_eprom ) ) ;
if ( readl ( & eprom - > magic ) ! = ENI155_MAGIC ) {
printk ( " \n " ) ;
printk ( KERN_ERR KERN_ERR DEV_LABEL " (itf %d): bad "
" magic - expected 0x%x, got 0x%x \n " , dev - > number ,
ENI155_MAGIC , ( unsigned ) readl ( & eprom - > magic ) ) ;
return - EINVAL ;
}
}
eni_dev - > phy = base + PHY_BASE ;
eni_dev - > reg = base + REG_BASE ;
eni_dev - > ram = base + RAM_BASE ;
last = MAP_MAX_SIZE - RAM_BASE ;
for ( i = last - RAM_INCREMENT ; i > = 0 ; i - = RAM_INCREMENT ) {
writel ( 0x55555555 , eni_dev - > ram + i ) ;
if ( readl ( eni_dev - > ram + i ) ! = 0x55555555 ) last = i ;
else {
writel ( 0xAAAAAAAA , eni_dev - > ram + i ) ;
if ( readl ( eni_dev - > ram + i ) ! = 0xAAAAAAAA ) last = i ;
else writel ( i , eni_dev - > ram + i ) ;
}
}
for ( i = 0 ; i < last ; i + = RAM_INCREMENT )
if ( readl ( eni_dev - > ram + i ) ! = i ) break ;
eni_dev - > mem = i ;
memset_io ( eni_dev - > ram , 0 , eni_dev - > mem ) ;
/* TODO: should shrink allocation now */
printk ( " mem=%dkB ( " , eni_dev - > mem > > 10 ) ;
/* TODO: check for non-SUNI, check for TAXI ? */
if ( ! ( eni_in ( MID_RES_ID_MCON ) & 0x200 ) ! = ! eni_dev - > asic ) {
printk ( " ) \n " ) ;
printk ( KERN_ERR DEV_LABEL " (itf %d): ERROR - wrong id 0x%x \n " ,
dev - > number , ( unsigned ) eni_in ( MID_RES_ID_MCON ) ) ;
return - EINVAL ;
}
error = eni_dev - > asic ? get_esi_asic ( dev ) : get_esi_fpga ( dev , base ) ;
if ( error ) return error ;
for ( i = 0 ; i < ESI_LEN ; i + + )
printk ( " %s%02X " , i ? " - " : " " , dev - > esi [ i ] ) ;
printk ( " ) \n " ) ;
printk ( KERN_NOTICE DEV_LABEL " (itf %d): %s,%s \n " , dev - > number ,
eni_in ( MID_RES_ID_MCON ) & 0x200 ? " ASIC " : " FPGA " ,
media_name [ eni_in ( MID_RES_ID_MCON ) & DAUGTHER_ID ] ) ;
return suni_init ( dev ) ;
}
static int __devinit eni_start ( struct atm_dev * dev )
{
struct eni_dev * eni_dev ;
void __iomem * buf ;
unsigned long buffer_mem ;
int error ;
DPRINTK ( " >eni_start \n " ) ;
eni_dev = ENI_DEV ( dev ) ;
if ( request_irq ( eni_dev - > irq , & eni_int , SA_SHIRQ , DEV_LABEL , dev ) ) {
printk ( KERN_ERR DEV_LABEL " (itf %d): IRQ%d is already in use \n " ,
dev - > number , eni_dev - > irq ) ;
2005-04-25 06:14:36 +04:00
error = - EAGAIN ;
goto out ;
2005-04-17 02:20:36 +04:00
}
pci_set_master ( eni_dev - > pci_dev ) ;
if ( ( error = pci_write_config_word ( eni_dev - > pci_dev , PCI_COMMAND ,
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
( eni_dev - > asic ? PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0 ) ) ) ) {
printk ( KERN_ERR DEV_LABEL " (itf %d): can't enable memory+ "
" master (0x%02x) \n " , dev - > number , error ) ;
2005-04-25 06:14:36 +04:00
goto free_irq ;
2005-04-17 02:20:36 +04:00
}
if ( ( error = pci_write_config_byte ( eni_dev - > pci_dev , PCI_TONGA_CTRL ,
END_SWAP_DMA ) ) ) {
printk ( KERN_ERR DEV_LABEL " (itf %d): can't set endian swap "
" (0x%02x) \n " , dev - > number , error ) ;
2005-04-25 06:14:36 +04:00
goto free_irq ;
2005-04-17 02:20:36 +04:00
}
/* determine addresses of internal tables */
eni_dev - > vci = eni_dev - > ram ;
eni_dev - > rx_dma = eni_dev - > ram + NR_VCI * 16 ;
eni_dev - > tx_dma = eni_dev - > rx_dma + NR_DMA_RX * 8 ;
eni_dev - > service = eni_dev - > tx_dma + NR_DMA_TX * 8 ;
buf = eni_dev - > service + NR_SERVICE * 4 ;
DPRINTK ( " vci 0x%lx,rx 0x%lx, tx 0x%lx,srv 0x%lx,buf 0x%lx \n " ,
eni_dev - > vci , eni_dev - > rx_dma , eni_dev - > tx_dma ,
eni_dev - > service , buf ) ;
spin_lock_init ( & eni_dev - > lock ) ;
tasklet_init ( & eni_dev - > task , eni_tasklet , ( unsigned long ) dev ) ;
eni_dev - > events = 0 ;
/* initialize memory management */
buffer_mem = eni_dev - > mem - ( buf - eni_dev - > ram ) ;
eni_dev - > free_list_size = buffer_mem / MID_MIN_BUF_SIZE / 2 ;
eni_dev - > free_list = ( struct eni_free * ) kmalloc (
sizeof ( struct eni_free ) * ( eni_dev - > free_list_size + 1 ) , GFP_KERNEL ) ;
if ( ! eni_dev - > free_list ) {
printk ( KERN_ERR DEV_LABEL " (itf %d): couldn't get free page \n " ,
dev - > number ) ;
2005-04-25 06:14:36 +04:00
error = - ENOMEM ;
goto free_irq ;
2005-04-17 02:20:36 +04:00
}
eni_dev - > free_len = 0 ;
eni_put_free ( eni_dev , buf , buffer_mem ) ;
memset_io ( eni_dev - > vci , 0 , 16 * NR_VCI ) ; /* clear VCI table */
/*
* byte_addr free ( k )
* 0x00000000 512 VCI table
* 0x00004000 496 RX DMA
* 0x00005000 492 TX DMA
* 0x00006000 488 service list
* 0x00007000 484 buffers
* 0x00080000 0 end ( 512 kB )
*/
eni_out ( 0xffffffff , MID_IE ) ;
error = start_tx ( dev ) ;
2005-04-25 06:14:36 +04:00
if ( error ) goto free_list ;
2005-04-17 02:20:36 +04:00
error = start_rx ( dev ) ;
2005-04-25 06:14:36 +04:00
if ( error ) goto free_list ;
2005-04-17 02:20:36 +04:00
error = dev - > phy - > start ( dev ) ;
2005-04-25 06:14:36 +04:00
if ( error ) goto free_list ;
2005-04-17 02:20:36 +04:00
eni_out ( eni_in ( MID_MC_S ) | ( 1 < < MID_INT_SEL_SHIFT ) |
MID_TX_LOCK_MODE | MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE ,
MID_MC_S ) ;
/* Tonga uses SBus INTReq1 */
( void ) eni_in ( MID_ISA ) ; /* clear Midway interrupts */
return 0 ;
2005-04-25 06:14:36 +04:00
free_list :
kfree ( eni_dev - > free_list ) ;
free_irq :
free_irq ( eni_dev - > irq , eni_dev ) ;
out :
return error ;
2005-04-17 02:20:36 +04:00
}
static void eni_close ( struct atm_vcc * vcc )
{
DPRINTK ( " >eni_close \n " ) ;
if ( ! ENI_VCC ( vcc ) ) return ;
clear_bit ( ATM_VF_READY , & vcc - > flags ) ;
close_rx ( vcc ) ;
close_tx ( vcc ) ;
DPRINTK ( " eni_close: done waiting \n " ) ;
/* deallocate memory */
kfree ( ENI_VCC ( vcc ) ) ;
vcc - > dev_data = NULL ;
clear_bit ( ATM_VF_ADDR , & vcc - > flags ) ;
/*foo();*/
}
static int eni_open ( struct atm_vcc * vcc )
{
struct eni_dev * eni_dev ;
struct eni_vcc * eni_vcc ;
int error ;
short vpi = vcc - > vpi ;
int vci = vcc - > vci ;
DPRINTK ( " >eni_open \n " ) ;
EVENT ( " eni_open \n " , 0 , 0 ) ;
if ( ! test_bit ( ATM_VF_PARTIAL , & vcc - > flags ) )
vcc - > dev_data = NULL ;
eni_dev = ENI_DEV ( vcc - > dev ) ;
if ( vci ! = ATM_VPI_UNSPEC & & vpi ! = ATM_VCI_UNSPEC )
set_bit ( ATM_VF_ADDR , & vcc - > flags ) ;
if ( vcc - > qos . aal ! = ATM_AAL0 & & vcc - > qos . aal ! = ATM_AAL5 )
return - EINVAL ;
DPRINTK ( DEV_LABEL " (itf %d): open %d.%d \n " , vcc - > dev - > number , vcc - > vpi ,
vcc - > vci ) ;
if ( ! test_bit ( ATM_VF_PARTIAL , & vcc - > flags ) ) {
eni_vcc = kmalloc ( sizeof ( struct eni_vcc ) , GFP_KERNEL ) ;
if ( ! eni_vcc ) return - ENOMEM ;
vcc - > dev_data = eni_vcc ;
eni_vcc - > tx = NULL ; /* for eni_close after open_rx */
if ( ( error = open_rx_first ( vcc ) ) ) {
eni_close ( vcc ) ;
return error ;
}
if ( ( error = open_tx_first ( vcc ) ) ) {
eni_close ( vcc ) ;
return error ;
}
}
if ( vci = = ATM_VPI_UNSPEC | | vpi = = ATM_VCI_UNSPEC ) return 0 ;
if ( ( error = open_rx_second ( vcc ) ) ) {
eni_close ( vcc ) ;
return error ;
}
if ( ( error = open_tx_second ( vcc ) ) ) {
eni_close ( vcc ) ;
return error ;
}
set_bit ( ATM_VF_READY , & vcc - > flags ) ;
/* should power down SUNI while !ref_count @@@ */
return 0 ;
}
static int eni_change_qos ( struct atm_vcc * vcc , struct atm_qos * qos , int flgs )
{
struct eni_dev * eni_dev = ENI_DEV ( vcc - > dev ) ;
struct eni_tx * tx = ENI_VCC ( vcc ) - > tx ;
struct sk_buff * skb ;
int error , rate , rsv , shp ;
if ( qos - > txtp . traffic_class = = ATM_NONE ) return 0 ;
if ( tx = = eni_dev - > ubr ) return - EBADFD ;
rate = atm_pcr_goal ( & qos - > txtp ) ;
if ( rate < 0 ) rate = - rate ;
rsv = shp = 0 ;
if ( ( flgs & ATM_MF_DEC_RSV ) & & rate & & rate < tx - > reserved ) rsv = 1 ;
if ( ( flgs & ATM_MF_INC_RSV ) & & ( ! rate | | rate > tx - > reserved ) ) rsv = 1 ;
if ( ( flgs & ATM_MF_DEC_SHP ) & & rate & & rate < tx - > shaping ) shp = 1 ;
if ( ( flgs & ATM_MF_INC_SHP ) & & ( ! rate | | rate > tx - > shaping ) ) shp = 1 ;
if ( ! rsv & & ! shp ) return 0 ;
error = reserve_or_set_tx ( vcc , & qos - > txtp , rsv , shp ) ;
if ( error ) return error ;
if ( shp & & ! ( flgs & ATM_MF_IMMED ) ) return 0 ;
/*
* Walk through the send buffer and patch the rate information in all
* segmentation buffer descriptors of this VCC .
*/
tasklet_disable ( & eni_dev - > task ) ;
skb_queue_walk ( & eni_dev - > tx_queue , skb ) {
void __iomem * dsc ;
if ( ATM_SKB ( skb ) - > vcc ! = vcc ) continue ;
dsc = tx - > send + ENI_PRV_POS ( skb ) * 4 ;
writel ( ( readl ( dsc ) & ~ ( MID_SEG_RATE | MID_SEG_PR ) ) |
( tx - > prescaler < < MID_SEG_PR_SHIFT ) |
( tx - > resolution < < MID_SEG_RATE_SHIFT ) , dsc ) ;
}
tasklet_enable ( & eni_dev - > task ) ;
return 0 ;
}
static int eni_ioctl ( struct atm_dev * dev , unsigned int cmd , void __user * arg )
{
struct eni_dev * eni_dev = ENI_DEV ( dev ) ;
if ( cmd = = ENI_MEMDUMP ) {
if ( ! capable ( CAP_NET_ADMIN ) ) return - EPERM ;
printk ( KERN_WARNING " Please use /proc/atm/ " DEV_LABEL " :%d "
" instead of obsolete ioctl ENI_MEMDUMP \n " , dev - > number ) ;
dump ( dev ) ;
return 0 ;
}
if ( cmd = = ENI_SETMULT ) {
struct eni_multipliers mult ;
if ( ! capable ( CAP_NET_ADMIN ) ) return - EPERM ;
if ( copy_from_user ( & mult , arg ,
sizeof ( struct eni_multipliers ) ) )
return - EFAULT ;
if ( ( mult . tx & & mult . tx < = 100 ) | | ( mult . rx & & mult . rx < = 100 ) | |
mult . tx > 65536 | | mult . rx > 65536 )
return - EINVAL ;
if ( mult . tx ) eni_dev - > tx_mult = mult . tx ;
if ( mult . rx ) eni_dev - > rx_mult = mult . rx ;
return 0 ;
}
if ( cmd = = ATM_SETCIRANGE ) {
struct atm_cirange ci ;
if ( copy_from_user ( & ci , arg , sizeof ( struct atm_cirange ) ) )
return - EFAULT ;
if ( ( ci . vpi_bits = = 0 | | ci . vpi_bits = = ATM_CI_MAX ) & &
( ci . vci_bits = = NR_VCI_LD | | ci . vpi_bits = = ATM_CI_MAX ) )
return 0 ;
return - EINVAL ;
}
if ( ! dev - > phy - > ioctl ) return - ENOIOCTLCMD ;
return dev - > phy - > ioctl ( dev , cmd , arg ) ;
}
static int eni_getsockopt ( struct atm_vcc * vcc , int level , int optname ,
void __user * optval , int optlen )
{
return - EINVAL ;
}
static int eni_setsockopt ( struct atm_vcc * vcc , int level , int optname ,
void __user * optval , int optlen )
{
return - EINVAL ;
}
static int eni_send ( struct atm_vcc * vcc , struct sk_buff * skb )
{
enum enq_res res ;
DPRINTK ( " >eni_send \n " ) ;
if ( ! ENI_VCC ( vcc ) - > tx ) {
if ( vcc - > pop ) vcc - > pop ( vcc , skb ) ;
else dev_kfree_skb ( skb ) ;
return - EINVAL ;
}
if ( ! skb ) {
printk ( KERN_CRIT " !skb in eni_send ? \n " ) ;
if ( vcc - > pop ) vcc - > pop ( vcc , skb ) ;
return - EINVAL ;
}
if ( vcc - > qos . aal = = ATM_AAL0 ) {
if ( skb - > len ! = ATM_CELL_SIZE - 1 ) {
if ( vcc - > pop ) vcc - > pop ( vcc , skb ) ;
else dev_kfree_skb ( skb ) ;
return - EINVAL ;
}
* ( u32 * ) skb - > data = htonl ( * ( u32 * ) skb - > data ) ;
}
submitted + + ;
ATM_SKB ( skb ) - > vcc = vcc ;
tasklet_disable ( & ENI_DEV ( vcc - > dev ) - > task ) ;
res = do_tx ( skb ) ;
tasklet_enable ( & ENI_DEV ( vcc - > dev ) - > task ) ;
if ( res = = enq_ok ) return 0 ;
skb_queue_tail ( & ENI_VCC ( vcc ) - > tx - > backlog , skb ) ;
backlogged + + ;
tasklet_schedule ( & ENI_DEV ( vcc - > dev ) - > task ) ;
return 0 ;
}
static void eni_phy_put ( struct atm_dev * dev , unsigned char value ,
unsigned long addr )
{
writel ( value , ENI_DEV ( dev ) - > phy + addr * 4 ) ;
}
static unsigned char eni_phy_get ( struct atm_dev * dev , unsigned long addr )
{
return readl ( ENI_DEV ( dev ) - > phy + addr * 4 ) ;
}
static int eni_proc_read ( struct atm_dev * dev , loff_t * pos , char * page )
{
struct hlist_node * node ;
struct sock * s ;
static const char * signal [ ] = { " LOST " , " unknown " , " okay " } ;
struct eni_dev * eni_dev = ENI_DEV ( dev ) ;
struct atm_vcc * vcc ;
int left , i ;
left = * pos ;
if ( ! left )
return sprintf ( page , DEV_LABEL " (itf %d) signal %s, %dkB, "
" %d cps remaining \n " , dev - > number , signal [ ( int ) dev - > signal ] ,
eni_dev - > mem > > 10 , eni_dev - > tx_bw ) ;
if ( ! - - left )
return sprintf ( page , " %4sBursts: TX "
# if !defined(CONFIG_ATM_ENI_BURST_TX_16W) && \
! defined ( CONFIG_ATM_ENI_BURST_TX_8W ) & & \
! defined ( CONFIG_ATM_ENI_BURST_TX_4W ) & & \
! defined ( CONFIG_ATM_ENI_BURST_TX_2W )
" none "
# endif
# ifdef CONFIG_ATM_ENI_BURST_TX_16W
" 16W "
# endif
# ifdef CONFIG_ATM_ENI_BURST_TX_8W
" 8W "
# endif
# ifdef CONFIG_ATM_ENI_BURST_TX_4W
" 4W "
# endif
# ifdef CONFIG_ATM_ENI_BURST_TX_2W
" 2W "
# endif
" , RX "
# if !defined(CONFIG_ATM_ENI_BURST_RX_16W) && \
! defined ( CONFIG_ATM_ENI_BURST_RX_8W ) & & \
! defined ( CONFIG_ATM_ENI_BURST_RX_4W ) & & \
! defined ( CONFIG_ATM_ENI_BURST_RX_2W )
" none "
# endif
# ifdef CONFIG_ATM_ENI_BURST_RX_16W
" 16W "
# endif
# ifdef CONFIG_ATM_ENI_BURST_RX_8W
" 8W "
# endif
# ifdef CONFIG_ATM_ENI_BURST_RX_4W
" 4W "
# endif
# ifdef CONFIG_ATM_ENI_BURST_RX_2W
" 2W "
# endif
# ifndef CONFIG_ATM_ENI_TUNE_BURST
" (default) "
# endif
" \n " , " " ) ;
if ( ! - - left )
return sprintf ( page , " %4sBuffer multipliers: tx %d%%, rx %d%% \n " ,
" " , eni_dev - > tx_mult , eni_dev - > rx_mult ) ;
for ( i = 0 ; i < NR_CHAN ; i + + ) {
struct eni_tx * tx = eni_dev - > tx + i ;
if ( ! tx - > send ) continue ;
if ( ! - - left ) {
return sprintf ( page , " tx[%d]: 0x%ld-0x%ld "
" (%6ld bytes), rsv %d cps, shp %d cps%s \n " , i ,
( unsigned long ) ( tx - > send - eni_dev - > ram ) ,
tx - > send - eni_dev - > ram + tx - > words * 4 - 1 , tx - > words * 4 ,
tx - > reserved , tx - > shaping ,
tx = = eni_dev - > ubr ? " (UBR) " : " " ) ;
}
if ( - - left ) continue ;
return sprintf ( page , " %10sbacklog %u packets \n " , " " ,
skb_queue_len ( & tx - > backlog ) ) ;
}
read_lock ( & vcc_sklist_lock ) ;
for ( i = 0 ; i < VCC_HTABLE_SIZE ; + + i ) {
struct hlist_head * head = & vcc_hash [ i ] ;
sk_for_each ( s , node , head ) {
struct eni_vcc * eni_vcc ;
int length ;
vcc = atm_sk ( s ) ;
if ( vcc - > dev ! = dev )
continue ;
eni_vcc = ENI_VCC ( vcc ) ;
if ( - - left ) continue ;
length = sprintf ( page , " vcc %4d: " , vcc - > vci ) ;
if ( eni_vcc - > rx ) {
length + = sprintf ( page + length , " 0x%ld-0x%ld "
" (%6ld bytes) " ,
( unsigned long ) ( eni_vcc - > recv - eni_dev - > ram ) ,
eni_vcc - > recv - eni_dev - > ram + eni_vcc - > words * 4 - 1 ,
eni_vcc - > words * 4 ) ;
if ( eni_vcc - > tx ) length + = sprintf ( page + length , " , " ) ;
}
if ( eni_vcc - > tx )
length + = sprintf ( page + length , " tx[%d], txing %d bytes " ,
eni_vcc - > tx - > index , eni_vcc - > txing ) ;
page [ length ] = ' \n ' ;
read_unlock ( & vcc_sklist_lock ) ;
return length + 1 ;
}
}
read_unlock ( & vcc_sklist_lock ) ;
for ( i = 0 ; i < eni_dev - > free_len ; i + + ) {
struct eni_free * fe = eni_dev - > free_list + i ;
unsigned long offset ;
if ( - - left ) continue ;
offset = ( unsigned long ) eni_dev - > ram + eni_dev - > base_diff ;
return sprintf ( page , " free %p-%p (%6d bytes) \n " ,
fe - > start - offset , fe - > start - offset + ( 1 < < fe - > order ) - 1 ,
1 < < fe - > order ) ;
}
return 0 ;
}
static const struct atmdev_ops ops = {
. open = eni_open ,
. close = eni_close ,
. ioctl = eni_ioctl ,
. getsockopt = eni_getsockopt ,
. setsockopt = eni_setsockopt ,
. send = eni_send ,
. phy_put = eni_phy_put ,
. phy_get = eni_phy_get ,
. change_qos = eni_change_qos ,
. proc_read = eni_proc_read
} ;
static int __devinit eni_init_one ( struct pci_dev * pci_dev ,
const struct pci_device_id * ent )
{
struct atm_dev * dev ;
struct eni_dev * eni_dev ;
int error = - ENOMEM ;
DPRINTK ( " eni_init_one \n " ) ;
if ( pci_enable_device ( pci_dev ) ) {
error = - EIO ;
goto out0 ;
}
eni_dev = ( struct eni_dev * ) kmalloc ( sizeof ( struct eni_dev ) , GFP_KERNEL ) ;
if ( ! eni_dev ) goto out0 ;
if ( ! cpu_zeroes ) {
cpu_zeroes = pci_alloc_consistent ( pci_dev , ENI_ZEROES_SIZE ,
& zeroes ) ;
if ( ! cpu_zeroes ) goto out1 ;
}
dev = atm_dev_register ( DEV_LABEL , & ops , - 1 , NULL ) ;
if ( ! dev ) goto out2 ;
pci_set_drvdata ( pci_dev , dev ) ;
eni_dev - > pci_dev = pci_dev ;
dev - > dev_data = eni_dev ;
eni_dev - > asic = ent - > driver_data ;
error = eni_do_init ( dev ) ;
if ( error ) goto out3 ;
error = eni_start ( dev ) ;
if ( error ) goto out3 ;
eni_dev - > more = eni_boards ;
eni_boards = dev ;
return 0 ;
out3 :
atm_dev_deregister ( dev ) ;
out2 :
pci_free_consistent ( eni_dev - > pci_dev , ENI_ZEROES_SIZE , cpu_zeroes , zeroes ) ;
cpu_zeroes = NULL ;
out1 :
kfree ( eni_dev ) ;
out0 :
return error ;
}
static struct pci_device_id eni_pci_tbl [ ] = {
{ PCI_VENDOR_ID_EF , PCI_DEVICE_ID_EF_ATM_FPGA , PCI_ANY_ID , PCI_ANY_ID ,
0 , 0 , 0 /* FPGA */ } ,
{ PCI_VENDOR_ID_EF , PCI_DEVICE_ID_EF_ATM_ASIC , PCI_ANY_ID , PCI_ANY_ID ,
0 , 0 , 1 /* ASIC */ } ,
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , eni_pci_tbl ) ;
static void __devexit eni_remove_one ( struct pci_dev * pci_dev )
{
/* grrr */
}
static struct pci_driver eni_driver = {
. name = DEV_LABEL ,
. id_table = eni_pci_tbl ,
. probe = eni_init_one ,
. remove = __devexit_p ( eni_remove_one ) ,
} ;
static int __init eni_init ( void )
{
struct sk_buff * skb ; /* dummy for sizeof */
if ( sizeof ( skb - > cb ) < sizeof ( struct eni_skb_prv ) ) {
printk ( KERN_ERR " eni_detect: skb->cb is too small (%Zd < %Zd) \n " ,
sizeof ( skb - > cb ) , sizeof ( struct eni_skb_prv ) ) ;
return - EIO ;
}
return pci_register_driver ( & eni_driver ) ;
}
module_init ( eni_init ) ;
/* @@@ since exit routine not defined, this module can not be unloaded */
MODULE_LICENSE ( " GPL " ) ;