2005-04-17 02:20:36 +04:00
/* drivers/atm/firestream.c - FireStream 155 (MB86697) and
* FireStream 50 ( MB86695 ) device driver
*/
/* Written & (C) 2000 by R.E.Wolff@BitWizard.nl
* Copied snippets from zatm . c by Werner Almesberger , EPFL LRC / ICA
* and ambassador . c Copyright ( C ) 1995 - 1999 Madge Networks Ltd
*/
/*
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
The GNU GPL is contained in / usr / doc / copyright / GPL on a Debian
system and in the file COPYING in the Linux kernel source .
*/
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/pci.h>
2006-06-27 13:53:53 +04:00
# include <linux/poison.h>
2005-04-17 02:20:36 +04:00
# include <linux/errno.h>
# include <linux/atm.h>
# include <linux/atmdev.h>
# include <linux/sonet.h>
# include <linux/skbuff.h>
# include <linux/netdevice.h>
# include <linux/delay.h>
# include <linux/ioport.h> /* for request_region */
# include <linux/uio.h>
# include <linux/init.h>
# include <linux/capability.h>
# include <linux/bitops.h>
# include <asm/byteorder.h>
# include <asm/system.h>
# include <asm/string.h>
# include <asm/io.h>
# include <asm/atomic.h>
# include <asm/uaccess.h>
# include <linux/wait.h>
# include "firestream.h"
static int loopback = 0 ;
static int num = 0x5a ;
/* According to measurements (but they look suspicious to me!) done in
* ' 97 , 37 % of the packets are one cell in size . So it pays to have
* buffers allocated at that size . A large jump in percentage of
* packets occurs at packets around 536 bytes in length . So it also
* pays to have those pre - allocated . Unfortunately , we can ' t fully
* take advantage of this as the majority of the packets is likely to
* be TCP / IP ( As where obviously the measurement comes from ) There the
* link would be opened with say a 1500 byte MTU , and we can ' t handle
* smaller buffers more efficiently than the larger ones . - - REW
*/
/* Due to the way Linux memory management works, specifying "576" as
* an allocation size here isn ' t going to help . They are allocated
* from 1024 - byte regions anyway . With the size of the sk_buffs ( quite
* large ) , it doesn ' t pay to allocate the smallest size ( 64 ) - - REW */
/* This is all guesswork. Hard numbers to back this up or disprove this,
* are appreciated . - - REW */
/* The last entry should be about 64k. However, the "buffer size" is
* passed to the chip in a 16 bit field . I don ' t know how " 65536 "
* would be interpreted . - - REW */
# define NP FS_NR_FREE_POOLS
static int rx_buf_sizes [ NP ] = { 128 , 256 , 512 , 1024 , 2048 , 4096 , 16384 , 65520 } ;
/* log2: 7 8 9 10 11 12 14 16 */
#if 0
static int rx_pool_sizes [ NP ] = { 1024 , 1024 , 512 , 256 , 128 , 64 , 32 , 32 } ;
# else
/* debug */
static int rx_pool_sizes [ NP ] = { 128 , 128 , 128 , 64 , 64 , 64 , 32 , 32 } ;
# endif
/* log2: 10 10 9 8 7 6 5 5 */
/* sumlog2: 17 18 18 18 18 18 19 21 */
/* mem allocated: 128k 256k 256k 256k 256k 256k 512k 2M */
/* tot mem: almost 4M */
/* NP is shorter, so that it fits on a single line. */
# undef NP
/* Small hardware gotcha:
The FS50 CAM ( VP / VC match registers ) always take the lowest channel
number that matches . This is not a problem .
However , they also ignore whether the channel is enabled or
not . This means that if you allocate channel 0 to 1.2 and then
channel 1 to 0.0 , then disabeling channel 0 and writing 0 to the
match channel for channel 0 will " steal " the traffic from channel
1 , even if you correctly disable channel 0.
Workaround :
- When disabling channels , write an invalid VP / VC value to the
match register . ( We use 0xffffffff , which in the worst case
matches VP / VC = < maxVP > / < maxVC > , but I expect it not to match
anything as some " when not in use, program to 0 " bits are now
programmed to 1. . . )
- Don ' t initialize the match registers to 0 , as 0.0 is a valid
channel .
*/
/* Optimization hints and tips.
The FireStream chips are very capable of reducing the amount of
" interrupt-traffic " for the CPU . This driver requests an interrupt on EVERY
action . You could try to minimize this a bit .
Besides that , the userspace - > kernel copy and the PCI bus are the
performance limiting issues for this driver .
You could queue up a bunch of outgoing packets without telling the
FireStream . I ' m not sure that ' s going to win you much though . The
Linux layer won ' t tell us in advance when it ' s not going to give us
any more packets in a while . So this is tricky to implement right without
introducing extra delays .
- - REW
*/
/* The strings that define what the RX queue entry is all about. */
/* Fujitsu: Please tell me which ones can have a pointer to a
freepool descriptor ! */
static char * res_strings [ ] = {
" RX OK: streaming not EOP " ,
" RX OK: streaming EOP " ,
" RX OK: Single buffer packet " ,
" RX OK: packet mode " ,
" RX OK: F4 OAM (end to end) " ,
" RX OK: F4 OAM (Segment) " ,
" RX OK: F5 OAM (end to end) " ,
" RX OK: F5 OAM (Segment) " ,
" RX OK: RM cell " ,
" RX OK: TRANSP cell " ,
" RX OK: TRANSPC cell " ,
" Unmatched cell " ,
" reserved 12 " ,
" reserved 13 " ,
" reserved 14 " ,
" Unrecognized cell " ,
" reserved 16 " ,
" reassemby abort: AAL5 abort " ,
" packet purged " ,
" packet ageing timeout " ,
" channel ageing timeout " ,
" calculated lenght error " ,
" programmed lenght limit error " ,
" aal5 crc32 error " ,
" oam transp or transpc crc10 error " ,
" reserved 25 " ,
" reserved 26 " ,
" reserved 27 " ,
" reserved 28 " ,
" reserved 29 " ,
" reserved 30 " ,
" reassembly abort: no buffers " ,
" receive buffer overflow " ,
" change in GFC " ,
" receive buffer full " ,
" low priority discard - no receive descriptor " ,
" low priority discard - missing end of packet " ,
" reserved 41 " ,
" reserved 42 " ,
" reserved 43 " ,
" reserved 44 " ,
" reserved 45 " ,
" reserved 46 " ,
" reserved 47 " ,
" reserved 48 " ,
" reserved 49 " ,
" reserved 50 " ,
" reserved 51 " ,
" reserved 52 " ,
" reserved 53 " ,
" reserved 54 " ,
" reserved 55 " ,
" reserved 56 " ,
" reserved 57 " ,
" reserved 58 " ,
" reserved 59 " ,
" reserved 60 " ,
" reserved 61 " ,
" reserved 62 " ,
" reserved 63 " ,
} ;
static char * irq_bitname [ ] = {
" LPCO " ,
" DPCO " ,
" RBRQ0_W " ,
" RBRQ1_W " ,
" RBRQ2_W " ,
" RBRQ3_W " ,
" RBRQ0_NF " ,
" RBRQ1_NF " ,
" RBRQ2_NF " ,
" RBRQ3_NF " ,
" BFP_SC " ,
" INIT " ,
" INIT_ERR " ,
" USCEO " ,
" UPEC0 " ,
" VPFCO " ,
" CRCCO " ,
" HECO " ,
" TBRQ_W " ,
" TBRQ_NF " ,
" CTPQ_E " ,
" GFC_C0 " ,
" PCI_FTL " ,
" CSQ_W " ,
" CSQ_NF " ,
" EXT_INT " ,
" RXDMA_S "
} ;
# define PHY_EOF -1
# define PHY_CLEARALL -2
struct reginit_item {
int reg , val ;
} ;
static struct reginit_item PHY_NTC_INIT [ ] __devinitdata = {
{ PHY_CLEARALL , 0x40 } ,
{ 0x12 , 0x0001 } ,
{ 0x13 , 0x7605 } ,
{ 0x1A , 0x0001 } ,
{ 0x1B , 0x0005 } ,
{ 0x38 , 0x0003 } ,
{ 0x39 , 0x0006 } , /* changed here to make loopback */
{ 0x01 , 0x5262 } ,
{ 0x15 , 0x0213 } ,
{ 0x00 , 0x0003 } ,
{ PHY_EOF , 0 } , /* -1 signals end of list */
} ;
/* Safetyfeature: If the card interrupts more than this number of times
in a jiffy ( 1 / 100 th of a second ) then we just disable the interrupt and
print a message . This prevents the system from hanging .
150000 packets per second is close to the limit a PC is going to have
anyway . We therefore have to disable this for production . - - REW */
# undef IRQ_RATE_LIMIT // 100
/* Interrupts work now. Unlike serial cards, ATM cards don't work all
that great without interrupts . - - REW */
# undef FS_POLL_FREQ // 100
/*
This driver can spew a whole lot of debugging output at you . If you
need maximum performance , you should disable the DEBUG define . To
aid in debugging in the field , I ' m leaving the compile - time debug
features enabled , and disable them " runtime " . That allows me to
instruct people with problems to enable debugging without requiring
them to recompile . . . - - REW
*/
# define DEBUG
# ifdef DEBUG
# define fs_dprintk(f, str...) if (fs_debug & f) printk (str)
# else
# define fs_dprintk(f, str...) /* nothing */
# endif
static int fs_keystream = 0 ;
# ifdef DEBUG
/* I didn't forget to set this to zero before shipping. Hit me with a stick
if you get this with the debug default not set to zero again . - - REW */
static int fs_debug = 0 ;
# else
# define fs_debug 0
# endif
# ifdef MODULE
# ifdef DEBUG
module_param ( fs_debug , int , 0644 ) ;
# endif
module_param ( loopback , int , 0 ) ;
module_param ( num , int , 0 ) ;
module_param ( fs_keystream , int , 0 ) ;
/* XXX Add rx_buf_sizes, and rx_pool_sizes As per request Amar. -- REW */
# endif
# define FS_DEBUG_FLOW 0x00000001
# define FS_DEBUG_OPEN 0x00000002
# define FS_DEBUG_QUEUE 0x00000004
# define FS_DEBUG_IRQ 0x00000008
# define FS_DEBUG_INIT 0x00000010
# define FS_DEBUG_SEND 0x00000020
# define FS_DEBUG_PHY 0x00000040
# define FS_DEBUG_CLEANUP 0x00000080
# define FS_DEBUG_QOS 0x00000100
# define FS_DEBUG_TXQ 0x00000200
# define FS_DEBUG_ALLOC 0x00000400
# define FS_DEBUG_TXMEM 0x00000800
# define FS_DEBUG_QSIZE 0x00001000
# define func_enter() fs_dprintk (FS_DEBUG_FLOW, "fs: enter %s\n", __FUNCTION__)
# define func_exit() fs_dprintk (FS_DEBUG_FLOW, "fs: exit %s\n", __FUNCTION__)
static struct fs_dev * fs_boards = NULL ;
# ifdef DEBUG
static void my_hd ( void * addr , int len )
{
int j , ch ;
unsigned char * ptr = addr ;
while ( len > 0 ) {
printk ( " %p " , ptr ) ;
for ( j = 0 ; j < ( ( len < 16 ) ? len : 16 ) ; j + + ) {
printk ( " %02x %s " , ptr [ j ] , ( j = = 7 ) ? " " : " " ) ;
}
for ( ; j < 16 ; j + + ) {
printk ( " %s " , ( j = = 7 ) ? " " : " " ) ;
}
for ( j = 0 ; j < ( ( len < 16 ) ? len : 16 ) ; j + + ) {
ch = ptr [ j ] ;
printk ( " %c " , ( ch < 0x20 ) ? ' . ' : ( ( ch > 0x7f ) ? ' . ' : ch ) ) ;
}
printk ( " \n " ) ;
ptr + = 16 ;
len - = 16 ;
}
}
# else /* DEBUG */
static void my_hd ( void * addr , int len ) { }
# endif /* DEBUG */
/********** free an skb (as per ATM device driver documentation) **********/
/* Hmm. If this is ATM specific, why isn't there an ATM routine for this?
* I copied it over from the ambassador driver . - - REW */
static inline void fs_kfree_skb ( struct sk_buff * skb )
{
if ( ATM_SKB ( skb ) - > vcc - > pop )
ATM_SKB ( skb ) - > vcc - > pop ( ATM_SKB ( skb ) - > vcc , skb ) ;
else
dev_kfree_skb_any ( skb ) ;
}
/* It seems the ATM forum recommends this horribly complicated 16bit
* floating point format . Turns out the Ambassador uses the exact same
* encoding . I just copied it over . If Mitch agrees , I ' ll move it over
* to the atm_misc file or something like that . ( and remove it from
* here and the ambassador driver ) - - REW
*/
/* The good thing about this format is that it is monotonic. So,
a conversion routine need not be very complicated . To be able to
round " nearest " we need to take along a few extra bits . Lets
put these after 16 bits , so that we can just return the top 16
bits of the 32 bit number as the result :
int mr ( unsigned int rate , int r )
{
int e = 16 + 9 ;
static int round [ 4 ] = { 0 , 0 , 0xffff , 0x8000 } ;
if ( ! rate ) return 0 ;
while ( rate & 0xfc000000 ) {
rate > > = 1 ;
e + + ;
}
while ( ! ( rate & 0xfe000000 ) ) {
rate < < = 1 ;
e - - ;
}
// Now the mantissa is in positions bit 16-25. Excepf for the "hidden 1" that's in bit 26.
rate & = ~ 0x02000000 ;
// Next add in the exponent
rate | = e < < ( 16 + 9 ) ;
// And perform the rounding:
return ( rate + round [ r ] ) > > 16 ;
}
14 lines - of - code . Compare that with the 120 that the Ambassador
guys needed . ( would be 8 lines shorter if I ' d try to really reduce
the number of lines :
int mr ( unsigned int rate , int r )
{
int e = 16 + 9 ;
static int round [ 4 ] = { 0 , 0 , 0xffff , 0x8000 } ;
if ( ! rate ) return 0 ;
for ( ; rate & 0xfc000000 ; rate > > = 1 , e + + ) ;
for ( ; ! ( rate & 0xfe000000 ) ; rate < < = 1 , e - - ) ;
return ( ( rate & ~ 0x02000000 ) | ( e < < ( 16 + 9 ) ) + round [ r ] ) > > 16 ;
}
Exercise for the reader : Remove one more line - of - code , without
cheating . ( Just joining two lines is cheating ) . ( I know it ' s
possible , don ' t think you ' ve beat me if you found it . . . If you
manage to lose two lines or more , keep me updated ! ; - )
- - REW */
# define ROUND_UP 1
# define ROUND_DOWN 2
# define ROUND_NEAREST 3
/********** make rate (not quite as much fun as Horizon) **********/
static unsigned int make_rate ( unsigned int rate , int r ,
u16 * bits , unsigned int * actual )
{
unsigned char exp = - 1 ; /* hush gcc */
unsigned int man = - 1 ; /* hush gcc */
fs_dprintk ( FS_DEBUG_QOS , " make_rate %u " , rate ) ;
/* rates in cells per second, ITU format (nasty 16-bit floating-point)
given 5 - bit e and 9 - bit m :
rate = EITHER ( 1 + m / 2 ^ 9 ) * 2 ^ e OR 0
bits = EITHER 1 < < 14 | e < < 9 | m OR 0
( bit 15 is " reserved " , bit 14 " non-zero " )
smallest rate is 0 ( special representation )
largest rate is ( 1 + 511 / 512 ) * 2 ^ 31 = 4290772992 ( < 2 ^ 32 - 1 )
smallest non - zero rate is ( 1 + 0 / 512 ) * 2 ^ 0 = 1 ( > 0 )
simple algorithm :
find position of top bit , this gives e
remove top bit and shift ( rounding if feeling clever ) by 9 - e
*/
/* Ambassador ucode bug: please don't set bit 14! so 0 rate not
representable . // This should move into the ambassador driver
when properly merged . - - REW */
if ( rate > 0xffc00000U ) {
/* larger than largest representable rate */
if ( r = = ROUND_UP ) {
return - EINVAL ;
} else {
exp = 31 ;
man = 511 ;
}
} else if ( rate ) {
/* representable rate */
exp = 31 ;
man = rate ;
/* invariant: rate = man*2^(exp-31) */
while ( ! ( man & ( 1 < < 31 ) ) ) {
exp = exp - 1 ;
man = man < < 1 ;
}
/* man has top bit set
rate = ( 2 ^ 31 + ( man - 2 ^ 31 ) ) * 2 ^ ( exp - 31 )
rate = ( 1 + ( man - 2 ^ 31 ) / 2 ^ 31 ) * 2 ^ exp
*/
man = man < < 1 ;
man & = 0xffffffffU ; /* a nop on 32-bit systems */
/* rate = (1+man/2^32)*2^exp
exp is in the range 0 to 31 , man is in the range 0 to 2 ^ 32 - 1
time to lose significance . . . we want m in the range 0 to 2 ^ 9 - 1
rounding presents a minor problem . . . we first decide which way
we are rounding ( based on given rounding direction and possibly
the bits of the mantissa that are to be discarded ) .
*/
switch ( r ) {
case ROUND_DOWN : {
/* just truncate */
man = man > > ( 32 - 9 ) ;
break ;
}
case ROUND_UP : {
/* check all bits that we are discarding */
if ( man & ( - 1 > > 9 ) ) {
man = ( man > > ( 32 - 9 ) ) + 1 ;
if ( man = = ( 1 < < 9 ) ) {
/* no need to check for round up outside of range */
man = 0 ;
exp + = 1 ;
}
} else {
man = ( man > > ( 32 - 9 ) ) ;
}
break ;
}
case ROUND_NEAREST : {
/* check msb that we are discarding */
if ( man & ( 1 < < ( 32 - 9 - 1 ) ) ) {
man = ( man > > ( 32 - 9 ) ) + 1 ;
if ( man = = ( 1 < < 9 ) ) {
/* no need to check for round up outside of range */
man = 0 ;
exp + = 1 ;
}
} else {
man = ( man > > ( 32 - 9 ) ) ;
}
break ;
}
}
} else {
/* zero rate - not representable */
if ( r = = ROUND_DOWN ) {
return - EINVAL ;
} else {
exp = 0 ;
man = 0 ;
}
}
fs_dprintk ( FS_DEBUG_QOS , " rate: man=%u, exp=%hu " , man , exp ) ;
if ( bits )
* bits = /* (1<<14) | */ ( exp < < 9 ) | man ;
if ( actual )
* actual = ( exp > = 9 )
? ( 1 < < exp ) + ( man < < ( exp - 9 ) )
: ( 1 < < exp ) + ( ( man + ( 1 < < ( 9 - exp - 1 ) ) ) > > ( 9 - exp ) ) ;
return 0 ;
}
/* FireStream access routines */
/* For DEEP-DOWN debugging these can be rigged to intercept accesses to
certain registers or to just log all accesses . */
static inline void write_fs ( struct fs_dev * dev , int offset , u32 val )
{
writel ( val , dev - > base + offset ) ;
}
static inline u32 read_fs ( struct fs_dev * dev , int offset )
{
return readl ( dev - > base + offset ) ;
}
static inline struct FS_QENTRY * get_qentry ( struct fs_dev * dev , struct queue * q )
{
return bus_to_virt ( read_fs ( dev , Q_WP ( q - > offset ) ) & Q_ADDR_MASK ) ;
}
static void submit_qentry ( struct fs_dev * dev , struct queue * q , struct FS_QENTRY * qe )
{
u32 wp ;
struct FS_QENTRY * cqe ;
/* XXX Sanity check: the write pointer can be checked to be
still the same as the value passed as qe . . . - - REW */
/* udelay (5); */
while ( ( wp = read_fs ( dev , Q_WP ( q - > offset ) ) ) & Q_FULL ) {
fs_dprintk ( FS_DEBUG_TXQ , " Found queue at %x full. Waiting. \n " ,
q - > offset ) ;
schedule ( ) ;
}
wp & = ~ 0xf ;
cqe = bus_to_virt ( wp ) ;
if ( qe ! = cqe ) {
fs_dprintk ( FS_DEBUG_TXQ , " q mismatch! %p %p \n " , qe , cqe ) ;
}
write_fs ( dev , Q_WP ( q - > offset ) , Q_INCWRAP ) ;
{
static int c ;
if ( ! ( c + + % 100 ) )
{
int rp , wp ;
rp = read_fs ( dev , Q_RP ( q - > offset ) ) ;
wp = read_fs ( dev , Q_WP ( q - > offset ) ) ;
fs_dprintk ( FS_DEBUG_TXQ , " q at %d: %x-%x: %x entries. \n " ,
q - > offset , rp , wp , wp - rp ) ;
}
}
}
# ifdef DEBUG_EXTRA
static struct FS_QENTRY pq [ 60 ] ;
static int qp ;
static struct FS_BPENTRY dq [ 60 ] ;
static int qd ;
static void * da [ 60 ] ;
# endif
static void submit_queue ( struct fs_dev * dev , struct queue * q ,
u32 cmd , u32 p1 , u32 p2 , u32 p3 )
{
struct FS_QENTRY * qe ;
qe = get_qentry ( dev , q ) ;
qe - > cmd = cmd ;
qe - > p0 = p1 ;
qe - > p1 = p2 ;
qe - > p2 = p3 ;
submit_qentry ( dev , q , qe ) ;
# ifdef DEBUG_EXTRA
pq [ qp ] . cmd = cmd ;
pq [ qp ] . p0 = p1 ;
pq [ qp ] . p1 = p2 ;
pq [ qp ] . p2 = p3 ;
qp + + ;
if ( qp > = 60 ) qp = 0 ;
# endif
}
/* Test the "other" way one day... -- REW */
# if 1
# define submit_command submit_queue
# else
static void submit_command ( struct fs_dev * dev , struct queue * q ,
u32 cmd , u32 p1 , u32 p2 , u32 p3 )
{
write_fs ( dev , CMDR0 , cmd ) ;
write_fs ( dev , CMDR1 , p1 ) ;
write_fs ( dev , CMDR2 , p2 ) ;
write_fs ( dev , CMDR3 , p3 ) ;
}
# endif
static void process_return_queue ( struct fs_dev * dev , struct queue * q )
{
long rq ;
struct FS_QENTRY * qe ;
void * tc ;
while ( ! ( ( rq = read_fs ( dev , Q_RP ( q - > offset ) ) ) & Q_EMPTY ) ) {
fs_dprintk ( FS_DEBUG_QUEUE , " reaping return queue entry at %lx \n " , rq ) ;
qe = bus_to_virt ( rq ) ;
fs_dprintk ( FS_DEBUG_QUEUE , " queue entry: %08x %08x %08x %08x. (%d) \n " ,
qe - > cmd , qe - > p0 , qe - > p1 , qe - > p2 , STATUS_CODE ( qe ) ) ;
switch ( STATUS_CODE ( qe ) ) {
case 5 :
tc = bus_to_virt ( qe - > p0 ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Free tc: %p \n " , tc ) ;
kfree ( tc ) ;
break ;
}
write_fs ( dev , Q_RP ( q - > offset ) , Q_INCWRAP ) ;
}
}
static void process_txdone_queue ( struct fs_dev * dev , struct queue * q )
{
long rq ;
long tmp ;
struct FS_QENTRY * qe ;
struct sk_buff * skb ;
struct FS_BPENTRY * td ;
while ( ! ( ( rq = read_fs ( dev , Q_RP ( q - > offset ) ) ) & Q_EMPTY ) ) {
fs_dprintk ( FS_DEBUG_QUEUE , " reaping txdone entry at %lx \n " , rq ) ;
qe = bus_to_virt ( rq ) ;
fs_dprintk ( FS_DEBUG_QUEUE , " queue entry: %08x %08x %08x %08x: %d \n " ,
qe - > cmd , qe - > p0 , qe - > p1 , qe - > p2 , STATUS_CODE ( qe ) ) ;
if ( STATUS_CODE ( qe ) ! = 2 )
fs_dprintk ( FS_DEBUG_TXMEM , " queue entry: %08x %08x %08x %08x: %d \n " ,
qe - > cmd , qe - > p0 , qe - > p1 , qe - > p2 , STATUS_CODE ( qe ) ) ;
switch ( STATUS_CODE ( qe ) ) {
case 0x01 : /* This is for AAL0 where we put the chip in streaming mode */
/* Fall through */
case 0x02 :
/* Process a real txdone entry. */
tmp = qe - > p0 ;
if ( tmp & 0x0f )
printk ( KERN_WARNING " td not aligned: %ld \n " , tmp ) ;
tmp & = ~ 0x0f ;
td = bus_to_virt ( tmp ) ;
fs_dprintk ( FS_DEBUG_QUEUE , " Pool entry: %08x %08x %08x %08x %p. \n " ,
td - > flags , td - > next , td - > bsa , td - > aal_bufsize , td - > skb ) ;
skb = td - > skb ;
if ( skb = = FS_VCC ( ATM_SKB ( skb ) - > vcc ) - > last_skb ) {
wake_up_interruptible ( & FS_VCC ( ATM_SKB ( skb ) - > vcc ) - > close_wait ) ;
FS_VCC ( ATM_SKB ( skb ) - > vcc ) - > last_skb = NULL ;
}
td - > dev - > ntxpckts - - ;
{
static int c = 0 ;
if ( ! ( c + + % 100 ) ) {
fs_dprintk ( FS_DEBUG_QSIZE , " [%d] " , td - > dev - > ntxpckts ) ;
}
}
atomic_inc ( & ATM_SKB ( skb ) - > vcc - > stats - > tx ) ;
fs_dprintk ( FS_DEBUG_TXMEM , " i " ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Free t-skb: %p \n " , skb ) ;
fs_kfree_skb ( skb ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Free trans-d: %p \n " , td ) ;
2006-06-27 13:53:53 +04:00
memset ( td , ATM_POISON_FREE , sizeof ( struct FS_BPENTRY ) ) ;
2005-04-17 02:20:36 +04:00
kfree ( td ) ;
break ;
default :
/* Here we get the tx purge inhibit command ... */
/* Action, I believe, is "don't do anything". -- REW */
;
}
write_fs ( dev , Q_RP ( q - > offset ) , Q_INCWRAP ) ;
}
}
static void process_incoming ( struct fs_dev * dev , struct queue * q )
{
long rq ;
struct FS_QENTRY * qe ;
struct FS_BPENTRY * pe ;
struct sk_buff * skb ;
unsigned int channo ;
struct atm_vcc * atm_vcc ;
while ( ! ( ( rq = read_fs ( dev , Q_RP ( q - > offset ) ) ) & Q_EMPTY ) ) {
fs_dprintk ( FS_DEBUG_QUEUE , " reaping incoming queue entry at %lx \n " , rq ) ;
qe = bus_to_virt ( rq ) ;
fs_dprintk ( FS_DEBUG_QUEUE , " queue entry: %08x %08x %08x %08x. " ,
qe - > cmd , qe - > p0 , qe - > p1 , qe - > p2 ) ;
fs_dprintk ( FS_DEBUG_QUEUE , " -> %x: %s \n " ,
STATUS_CODE ( qe ) ,
res_strings [ STATUS_CODE ( qe ) ] ) ;
pe = bus_to_virt ( qe - > p0 ) ;
fs_dprintk ( FS_DEBUG_QUEUE , " Pool entry: %08x %08x %08x %08x %p %p. \n " ,
pe - > flags , pe - > next , pe - > bsa , pe - > aal_bufsize ,
pe - > skb , pe - > fp ) ;
channo = qe - > cmd & 0xffff ;
if ( channo < dev - > nchannels )
atm_vcc = dev - > atm_vccs [ channo ] ;
else
atm_vcc = NULL ;
/* Single buffer packet */
switch ( STATUS_CODE ( qe ) ) {
case 0x1 :
/* Fall through for streaming mode */
case 0x2 : /* Packet received OK.... */
if ( atm_vcc ) {
skb = pe - > skb ;
pe - > fp - > n - - ;
#if 0
fs_dprintk ( FS_DEBUG_QUEUE , " Got skb: %p \n " , skb ) ;
if ( FS_DEBUG_QUEUE & fs_debug ) my_hd ( bus_to_virt ( pe - > bsa ) , 0x20 ) ;
# endif
skb_put ( skb , qe - > p1 & 0xffff ) ;
ATM_SKB ( skb ) - > vcc = atm_vcc ;
atomic_inc ( & atm_vcc - > stats - > rx ) ;
2005-08-15 04:24:31 +04:00
__net_timestamp ( skb ) ;
2005-04-17 02:20:36 +04:00
fs_dprintk ( FS_DEBUG_ALLOC , " Free rec-skb: %p (pushed) \n " , skb ) ;
atm_vcc - > push ( atm_vcc , skb ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Free rec-d: %p \n " , pe ) ;
kfree ( pe ) ;
} else {
printk ( KERN_ERR " Got a receive on a non-open channel %d. \n " , channo ) ;
}
break ;
case 0x17 : /* AAL 5 CRC32 error. IFF the length field is nonzero, a buffer
has been consumed and needs to be processed . - - REW */
if ( qe - > p1 & 0xffff ) {
pe = bus_to_virt ( qe - > p0 ) ;
pe - > fp - > n - - ;
fs_dprintk ( FS_DEBUG_ALLOC , " Free rec-skb: %p \n " , pe - > skb ) ;
dev_kfree_skb_any ( pe - > skb ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Free rec-d: %p \n " , pe ) ;
kfree ( pe ) ;
}
if ( atm_vcc )
atomic_inc ( & atm_vcc - > stats - > rx_drop ) ;
break ;
case 0x1f : /* Reassembly abort: no buffers. */
/* Silently increment error counter. */
if ( atm_vcc )
atomic_inc ( & atm_vcc - > stats - > rx_drop ) ;
break ;
default : /* Hmm. Haven't written the code to handle the others yet... -- REW */
printk ( KERN_WARNING " Don't know what to do with RX status %x: %s. \n " ,
STATUS_CODE ( qe ) , res_strings [ STATUS_CODE ( qe ) ] ) ;
}
write_fs ( dev , Q_RP ( q - > offset ) , Q_INCWRAP ) ;
}
}
# define DO_DIRECTION(tp) ((tp)->traffic_class != ATM_NONE)
static int fs_open ( struct atm_vcc * atm_vcc )
{
struct fs_dev * dev ;
struct fs_vcc * vcc ;
struct fs_transmit_config * tc ;
struct atm_trafprm * txtp ;
struct atm_trafprm * rxtp ;
/* struct fs_receive_config *rc;*/
/* struct FS_QENTRY *qe; */
int error ;
int bfp ;
int to ;
unsigned short tmc0 ;
short vpi = atm_vcc - > vpi ;
int vci = atm_vcc - > vci ;
func_enter ( ) ;
dev = FS_DEV ( atm_vcc - > dev ) ;
fs_dprintk ( FS_DEBUG_OPEN , " fs: open on dev: %p, vcc at %p \n " ,
dev , atm_vcc ) ;
if ( vci ! = ATM_VPI_UNSPEC & & vpi ! = ATM_VCI_UNSPEC )
set_bit ( ATM_VF_ADDR , & atm_vcc - > flags ) ;
if ( ( atm_vcc - > qos . aal ! = ATM_AAL5 ) & &
( atm_vcc - > qos . aal ! = ATM_AAL2 ) )
return - EINVAL ; /* XXX AAL0 */
fs_dprintk ( FS_DEBUG_OPEN , " fs: (itf %d): open %d.%d \n " ,
atm_vcc - > dev - > number , atm_vcc - > vpi , atm_vcc - > vci ) ;
/* XXX handle qos parameters (rate limiting) ? */
vcc = kmalloc ( sizeof ( struct fs_vcc ) , GFP_KERNEL ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Alloc VCC: %p(%Zd) \n " , vcc , sizeof ( struct fs_vcc ) ) ;
if ( ! vcc ) {
clear_bit ( ATM_VF_ADDR , & atm_vcc - > flags ) ;
return - ENOMEM ;
}
atm_vcc - > dev_data = vcc ;
vcc - > last_skb = NULL ;
init_waitqueue_head ( & vcc - > close_wait ) ;
txtp = & atm_vcc - > qos . txtp ;
rxtp = & atm_vcc - > qos . rxtp ;
if ( ! test_bit ( ATM_VF_PARTIAL , & atm_vcc - > flags ) ) {
if ( IS_FS50 ( dev ) ) {
/* Increment the channel numer: take a free one next time. */
for ( to = 33 ; to ; to - - , dev - > channo + + ) {
/* We only have 32 channels */
if ( dev - > channo > = 32 )
dev - > channo = 0 ;
/* If we need to do RX, AND the RX is inuse, try the next */
if ( DO_DIRECTION ( rxtp ) & & dev - > atm_vccs [ dev - > channo ] )
continue ;
/* If we need to do TX, AND the TX is inuse, try the next */
if ( DO_DIRECTION ( txtp ) & & test_bit ( dev - > channo , dev - > tx_inuse ) )
continue ;
/* Ok, both are free! (or not needed) */
break ;
}
if ( ! to ) {
printk ( " No more free channels for FS50.. \n " ) ;
return - EBUSY ;
}
vcc - > channo = dev - > channo ;
dev - > channo & = dev - > channel_mask ;
} else {
vcc - > channo = ( vpi < < FS155_VCI_BITS ) | ( vci ) ;
if ( ( ( DO_DIRECTION ( rxtp ) & & dev - > atm_vccs [ vcc - > channo ] ) ) | |
( DO_DIRECTION ( txtp ) & & test_bit ( vcc - > channo , dev - > tx_inuse ) ) ) {
printk ( " Channel is in use for FS155. \n " ) ;
return - EBUSY ;
}
}
fs_dprintk ( FS_DEBUG_OPEN , " OK. Allocated channel %x(%d). \n " ,
vcc - > channo , vcc - > channo ) ;
}
if ( DO_DIRECTION ( txtp ) ) {
tc = kmalloc ( sizeof ( struct fs_transmit_config ) , GFP_KERNEL ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Alloc tc: %p(%Zd) \n " ,
tc , sizeof ( struct fs_transmit_config ) ) ;
if ( ! tc ) {
fs_dprintk ( FS_DEBUG_OPEN , " fs: can't alloc transmit_config. \n " ) ;
return - ENOMEM ;
}
/* Allocate the "open" entry from the high priority txq. This makes
it most likely that the chip will notice it . It also prevents us
from having to wait for completion . On the other hand , we may
need to wait for completion anyway , to see if it completed
2006-06-26 20:35:02 +04:00
successfully . */
2005-04-17 02:20:36 +04:00
switch ( atm_vcc - > qos . aal ) {
case ATM_AAL2 :
case ATM_AAL0 :
tc - > flags = 0
| TC_FLAGS_TRANSPARENT_PAYLOAD
| TC_FLAGS_PACKET
| ( 1 < < 28 )
| TC_FLAGS_TYPE_UBR /* XXX Change to VBR -- PVDL */
| TC_FLAGS_CAL0 ;
break ;
case ATM_AAL5 :
tc - > flags = 0
| TC_FLAGS_AAL5
| TC_FLAGS_PACKET /* ??? */
| TC_FLAGS_TYPE_CBR
| TC_FLAGS_CAL0 ;
break ;
default :
printk ( " Unknown aal: %d \n " , atm_vcc - > qos . aal ) ;
tc - > flags = 0 ;
}
/* Docs are vague about this atm_hdr field. By the way, the FS
* chip makes odd errors if lower bits are set . . . . - - REW */
tc - > atm_hdr = ( vpi < < 20 ) | ( vci < < 4 ) ;
{
int pcr = atm_pcr_goal ( txtp ) ;
fs_dprintk ( FS_DEBUG_OPEN , " pcr = %d. \n " , pcr ) ;
/* XXX Hmm. officially we're only allowed to do this if rounding
is round_down - - REW */
if ( IS_FS50 ( dev ) ) {
if ( pcr > 51840000 / 53 / 8 ) pcr = 51840000 / 53 / 8 ;
} else {
if ( pcr > 155520000 / 53 / 8 ) pcr = 155520000 / 53 / 8 ;
}
if ( ! pcr ) {
/* no rate cap */
tmc0 = IS_FS50 ( dev ) ? 0x61BE : 0x64c9 ; /* Just copied over the bits from Fujitsu -- REW */
} else {
int r ;
if ( pcr < 0 ) {
r = ROUND_DOWN ;
pcr = - pcr ;
} else {
r = ROUND_UP ;
}
error = make_rate ( pcr , r , & tmc0 , NULL ) ;
}
fs_dprintk ( FS_DEBUG_OPEN , " pcr = %d. \n " , pcr ) ;
}
tc - > TMC [ 0 ] = tmc0 | 0x4000 ;
tc - > TMC [ 1 ] = 0 ; /* Unused */
tc - > TMC [ 2 ] = 0 ; /* Unused */
tc - > TMC [ 3 ] = 0 ; /* Unused */
tc - > spec = 0 ; /* UTOPIA address, UDF, HEC: Unused -> 0 */
tc - > rtag [ 0 ] = 0 ; /* What should I do with routing tags???
- - Not used - - AS - - Thanks - - REW */
tc - > rtag [ 1 ] = 0 ;
tc - > rtag [ 2 ] = 0 ;
if ( fs_debug & FS_DEBUG_OPEN ) {
fs_dprintk ( FS_DEBUG_OPEN , " TX config record: \n " ) ;
my_hd ( tc , sizeof ( * tc ) ) ;
}
/* We now use the "submit_command" function to submit commands to
the firestream . There is a define up near the definition of
that routine that switches this routine between immediate write
to the immediate comamnd registers and queuing the commands in
the HPTXQ for execution . This last technique might be more
efficient if we know we ' re going to submit a whole lot of
commands in one go , but this driver is not setup to be able to
use such a construct . So it probably doen ' t matter much right
now . - - REW */
/* The command is IMMediate and INQueue. The parameters are out-of-line.. */
submit_command ( dev , & dev - > hp_txq ,
QE_CMD_CONFIG_TX | QE_CMD_IMM_INQ | vcc - > channo ,
virt_to_bus ( tc ) , 0 , 0 ) ;
submit_command ( dev , & dev - > hp_txq ,
QE_CMD_TX_EN | QE_CMD_IMM_INQ | vcc - > channo ,
0 , 0 , 0 ) ;
set_bit ( vcc - > channo , dev - > tx_inuse ) ;
}
if ( DO_DIRECTION ( rxtp ) ) {
dev - > atm_vccs [ vcc - > channo ] = atm_vcc ;
for ( bfp = 0 ; bfp < FS_NR_FREE_POOLS ; bfp + + )
if ( atm_vcc - > qos . rxtp . max_sdu < = dev - > rx_fp [ bfp ] . bufsize ) break ;
if ( bfp > = FS_NR_FREE_POOLS ) {
fs_dprintk ( FS_DEBUG_OPEN , " No free pool fits sdu: %d. \n " ,
atm_vcc - > qos . rxtp . max_sdu ) ;
/* XXX Cleanup? -- Would just calling fs_close work??? -- REW */
/* XXX clear tx inuse. Close TX part? */
dev - > atm_vccs [ vcc - > channo ] = NULL ;
kfree ( vcc ) ;
return - EINVAL ;
}
switch ( atm_vcc - > qos . aal ) {
case ATM_AAL0 :
case ATM_AAL2 :
submit_command ( dev , & dev - > hp_txq ,
QE_CMD_CONFIG_RX | QE_CMD_IMM_INQ | vcc - > channo ,
RC_FLAGS_TRANSP |
RC_FLAGS_BFPS_BFP * bfp |
RC_FLAGS_RXBM_PSB , 0 , 0 ) ;
break ;
case ATM_AAL5 :
submit_command ( dev , & dev - > hp_txq ,
QE_CMD_CONFIG_RX | QE_CMD_IMM_INQ | vcc - > channo ,
RC_FLAGS_AAL5 |
RC_FLAGS_BFPS_BFP * bfp |
RC_FLAGS_RXBM_PSB , 0 , 0 ) ;
break ;
} ;
if ( IS_FS50 ( dev ) ) {
submit_command ( dev , & dev - > hp_txq ,
QE_CMD_REG_WR | QE_CMD_IMM_INQ ,
0x80 + vcc - > channo ,
( vpi < < 16 ) | vci , 0 ) ; /* XXX -- Use defines. */
}
submit_command ( dev , & dev - > hp_txq ,
QE_CMD_RX_EN | QE_CMD_IMM_INQ | vcc - > channo ,
0 , 0 , 0 ) ;
}
/* Indicate we're done! */
set_bit ( ATM_VF_READY , & atm_vcc - > flags ) ;
func_exit ( ) ;
return 0 ;
}
static void fs_close ( struct atm_vcc * atm_vcc )
{
struct fs_dev * dev = FS_DEV ( atm_vcc - > dev ) ;
struct fs_vcc * vcc = FS_VCC ( atm_vcc ) ;
struct atm_trafprm * txtp ;
struct atm_trafprm * rxtp ;
func_enter ( ) ;
clear_bit ( ATM_VF_READY , & atm_vcc - > flags ) ;
fs_dprintk ( FS_DEBUG_QSIZE , " --==**[%d]**==-- " , dev - > ntxpckts ) ;
if ( vcc - > last_skb ) {
fs_dprintk ( FS_DEBUG_QUEUE , " Waiting for skb %p to be sent. \n " ,
vcc - > last_skb ) ;
/* We're going to wait for the last packet to get sent on this VC. It would
be impolite not to send them don ' t you think ?
XXX
We don ' t know which packets didn ' t get sent . So if we get interrupted in
this sleep_on , we ' ll lose any reference to these packets . Memory leak !
On the other hand , it ' s awfully convenient that we can abort a " close " that
is taking too long . Maybe just use non - interruptible sleep on ? - - REW */
interruptible_sleep_on ( & vcc - > close_wait ) ;
}
txtp = & atm_vcc - > qos . txtp ;
rxtp = & atm_vcc - > qos . rxtp ;
/* See App note XXX (Unpublished as of now) for the reason for the
removal of the " CMD_IMM_INQ " part of the TX_PURGE_INH . . . - - REW */
if ( DO_DIRECTION ( txtp ) ) {
submit_command ( dev , & dev - > hp_txq ,
QE_CMD_TX_PURGE_INH | /*QE_CMD_IMM_INQ|*/ vcc - > channo , 0 , 0 , 0 ) ;
clear_bit ( vcc - > channo , dev - > tx_inuse ) ;
}
if ( DO_DIRECTION ( rxtp ) ) {
submit_command ( dev , & dev - > hp_txq ,
QE_CMD_RX_PURGE_INH | QE_CMD_IMM_INQ | vcc - > channo , 0 , 0 , 0 ) ;
dev - > atm_vccs [ vcc - > channo ] = NULL ;
/* This means that this is configured as a receive channel */
if ( IS_FS50 ( dev ) ) {
/* Disable the receive filter. Is 0/0 indeed an invalid receive
channel ? - - REW . Yes it is . - - Hang . Ok . I ' ll use - 1
( 0xfff . . . ) - - REW */
submit_command ( dev , & dev - > hp_txq ,
QE_CMD_REG_WR | QE_CMD_IMM_INQ ,
0x80 + vcc - > channo , - 1 , 0 ) ;
}
}
fs_dprintk ( FS_DEBUG_ALLOC , " Free vcc: %p \n " , vcc ) ;
kfree ( vcc ) ;
func_exit ( ) ;
}
static int fs_send ( struct atm_vcc * atm_vcc , struct sk_buff * skb )
{
struct fs_dev * dev = FS_DEV ( atm_vcc - > dev ) ;
struct fs_vcc * vcc = FS_VCC ( atm_vcc ) ;
struct FS_BPENTRY * td ;
func_enter ( ) ;
fs_dprintk ( FS_DEBUG_TXMEM , " I " ) ;
fs_dprintk ( FS_DEBUG_SEND , " Send: atm_vcc %p skb %p vcc %p dev %p \n " ,
atm_vcc , skb , vcc , dev ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Alloc t-skb: %p (atm_send) \n " , skb ) ;
ATM_SKB ( skb ) - > vcc = atm_vcc ;
vcc - > last_skb = skb ;
td = kmalloc ( sizeof ( struct FS_BPENTRY ) , GFP_ATOMIC ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Alloc transd: %p(%Zd) \n " , td , sizeof ( struct FS_BPENTRY ) ) ;
if ( ! td ) {
/* Oops out of mem */
return - ENOMEM ;
}
fs_dprintk ( FS_DEBUG_SEND , " first word in buffer: %x \n " ,
* ( int * ) skb - > data ) ;
td - > flags = TD_EPI | TD_DATA | skb - > len ;
td - > next = 0 ;
td - > bsa = virt_to_bus ( skb - > data ) ;
td - > skb = skb ;
td - > dev = dev ;
dev - > ntxpckts + + ;
# ifdef DEBUG_EXTRA
da [ qd ] = td ;
dq [ qd ] . flags = td - > flags ;
dq [ qd ] . next = td - > next ;
dq [ qd ] . bsa = td - > bsa ;
dq [ qd ] . skb = td - > skb ;
dq [ qd ] . dev = td - > dev ;
qd + + ;
if ( qd > = 60 ) qd = 0 ;
# endif
submit_queue ( dev , & dev - > hp_txq ,
QE_TRANSMIT_DE | vcc - > channo ,
virt_to_bus ( td ) , 0 ,
virt_to_bus ( td ) ) ;
fs_dprintk ( FS_DEBUG_QUEUE , " in send: txq %d txrq %d \n " ,
read_fs ( dev , Q_EA ( dev - > hp_txq . offset ) ) -
read_fs ( dev , Q_SA ( dev - > hp_txq . offset ) ) ,
read_fs ( dev , Q_EA ( dev - > tx_relq . offset ) ) -
read_fs ( dev , Q_SA ( dev - > tx_relq . offset ) ) ) ;
func_exit ( ) ;
return 0 ;
}
/* Some function placeholders for functions we don't yet support. */
#if 0
static int fs_ioctl ( struct atm_dev * dev , unsigned int cmd , void __user * arg )
{
func_enter ( ) ;
func_exit ( ) ;
return - ENOIOCTLCMD ;
}
static int fs_getsockopt ( struct atm_vcc * vcc , int level , int optname ,
void __user * optval , int optlen )
{
func_enter ( ) ;
func_exit ( ) ;
return 0 ;
}
static int fs_setsockopt ( struct atm_vcc * vcc , int level , int optname ,
void __user * optval , int optlen )
{
func_enter ( ) ;
func_exit ( ) ;
return 0 ;
}
static void fs_phy_put ( struct atm_dev * dev , unsigned char value ,
unsigned long addr )
{
func_enter ( ) ;
func_exit ( ) ;
}
static unsigned char fs_phy_get ( struct atm_dev * dev , unsigned long addr )
{
func_enter ( ) ;
func_exit ( ) ;
return 0 ;
}
static int fs_change_qos ( struct atm_vcc * vcc , struct atm_qos * qos , int flags )
{
func_enter ( ) ;
func_exit ( ) ;
return 0 ;
} ;
# endif
static const struct atmdev_ops ops = {
. open = fs_open ,
. close = fs_close ,
. send = fs_send ,
. owner = THIS_MODULE ,
/* ioctl: fs_ioctl, */
/* getsockopt: fs_getsockopt, */
/* setsockopt: fs_setsockopt, */
/* change_qos: fs_change_qos, */
/* For now implement these internally here... */
/* phy_put: fs_phy_put, */
/* phy_get: fs_phy_get, */
} ;
static void __devinit undocumented_pci_fix ( struct pci_dev * pdev )
{
int tint ;
/* The Windows driver says: */
/* Switch off FireStream Retry Limit Threshold
*/
/* The register at 0x28 is documented as "reserved", no further
comments . */
pci_read_config_dword ( pdev , 0x28 , & tint ) ;
if ( tint ! = 0x80 ) {
tint = 0x80 ;
pci_write_config_dword ( pdev , 0x28 , tint ) ;
}
}
/**************************************************************************
* PHY routines *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void __devinit write_phy ( struct fs_dev * dev , int regnum , int val )
{
submit_command ( dev , & dev - > hp_txq , QE_CMD_PRP_WR | QE_CMD_IMM_INQ ,
regnum , val , 0 ) ;
}
static int __devinit init_phy ( struct fs_dev * dev , struct reginit_item * reginit )
{
int i ;
func_enter ( ) ;
while ( reginit - > reg ! = PHY_EOF ) {
if ( reginit - > reg = = PHY_CLEARALL ) {
/* "PHY_CLEARALL means clear all registers. Numregisters is in "val". */
for ( i = 0 ; i < reginit - > val ; i + + ) {
write_phy ( dev , i , 0 ) ;
}
} else {
write_phy ( dev , reginit - > reg , reginit - > val ) ;
}
reginit + + ;
}
func_exit ( ) ;
return 0 ;
}
static void reset_chip ( struct fs_dev * dev )
{
int i ;
write_fs ( dev , SARMODE0 , SARMODE0_SRTS0 ) ;
/* Undocumented delay */
udelay ( 128 ) ;
/* The "internal registers are documented to all reset to zero, but
comments & code in the Windows driver indicates that the pools are
NOT reset . */
for ( i = 0 ; i < FS_NR_FREE_POOLS ; i + + ) {
write_fs ( dev , FP_CNF ( RXB_FP ( i ) ) , 0 ) ;
write_fs ( dev , FP_SA ( RXB_FP ( i ) ) , 0 ) ;
write_fs ( dev , FP_EA ( RXB_FP ( i ) ) , 0 ) ;
write_fs ( dev , FP_CNT ( RXB_FP ( i ) ) , 0 ) ;
write_fs ( dev , FP_CTU ( RXB_FP ( i ) ) , 0 ) ;
}
/* The same goes for the match channel registers, although those are
NOT documented that way in the Windows driver . - - REW */
/* The Windows driver DOES write 0 to these registers somewhere in
the init sequence . However , a small hardware - feature , will
prevent reception of data on VPI / VCI = 0 / 0 ( Unless the channel
allocated happens to have no disabled channels that have a lower
number . - - REW */
/* Clear the match channel registers. */
if ( IS_FS50 ( dev ) ) {
for ( i = 0 ; i < FS50_NR_CHANNELS ; i + + ) {
write_fs ( dev , 0x200 + i * 4 , - 1 ) ;
}
}
}
2005-10-07 10:46:04 +04:00
static void __devinit * aligned_kmalloc ( int size , gfp_t flags , int alignment )
2005-04-17 02:20:36 +04:00
{
void * t ;
if ( alignment < = 0x10 ) {
t = kmalloc ( size , flags ) ;
if ( ( unsigned long ) t & ( alignment - 1 ) ) {
printk ( " Kmalloc doesn't align things correctly! %p \n " , t ) ;
kfree ( t ) ;
return aligned_kmalloc ( size , flags , alignment * 4 ) ;
}
return t ;
}
printk ( KERN_ERR " Request for > 0x10 alignment not yet implemented (hard!) \n " ) ;
return NULL ;
}
static int __devinit init_q ( struct fs_dev * dev ,
struct queue * txq , int queue , int nentries , int is_rq )
{
int sz = nentries * sizeof ( struct FS_QENTRY ) ;
struct FS_QENTRY * p ;
func_enter ( ) ;
fs_dprintk ( FS_DEBUG_INIT , " Inititing queue at %x: %d entries: \n " ,
queue , nentries ) ;
p = aligned_kmalloc ( sz , GFP_KERNEL , 0x10 ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Alloc queue: %p(%d) \n " , p , sz ) ;
if ( ! p ) return 0 ;
write_fs ( dev , Q_SA ( queue ) , virt_to_bus ( p ) ) ;
write_fs ( dev , Q_EA ( queue ) , virt_to_bus ( p + nentries - 1 ) ) ;
write_fs ( dev , Q_WP ( queue ) , virt_to_bus ( p ) ) ;
write_fs ( dev , Q_RP ( queue ) , virt_to_bus ( p ) ) ;
if ( is_rq ) {
/* Configuration for the receive queue: 0: interrupt immediately,
no pre - warning to empty queues : We do our best to keep the
queue filled anyway . */
write_fs ( dev , Q_CNF ( queue ) , 0 ) ;
}
txq - > sa = p ;
txq - > ea = p ;
txq - > offset = queue ;
func_exit ( ) ;
return 1 ;
}
static int __devinit init_fp ( struct fs_dev * dev ,
struct freepool * fp , int queue , int bufsize , int nr_buffers )
{
func_enter ( ) ;
fs_dprintk ( FS_DEBUG_INIT , " Inititing free pool at %x: \n " , queue ) ;
write_fs ( dev , FP_CNF ( queue ) , ( bufsize * RBFP_RBS ) | RBFP_RBSVAL | RBFP_CME ) ;
write_fs ( dev , FP_SA ( queue ) , 0 ) ;
write_fs ( dev , FP_EA ( queue ) , 0 ) ;
write_fs ( dev , FP_CTU ( queue ) , 0 ) ;
write_fs ( dev , FP_CNT ( queue ) , 0 ) ;
fp - > offset = queue ;
fp - > bufsize = bufsize ;
fp - > nr_buffers = nr_buffers ;
func_exit ( ) ;
return 1 ;
}
static inline int nr_buffers_in_freepool ( struct fs_dev * dev , struct freepool * fp )
{
#if 0
/* This seems to be unreliable.... */
return read_fs ( dev , FP_CNT ( fp - > offset ) ) ;
# else
return fp - > n ;
# endif
}
/* Check if this gets going again if a pool ever runs out. -- Yes, it
does . I ' ve seen " receive abort: no buffers " and things started
working again after that . . . - - REW */
2005-07-20 00:56:01 +04:00
static void top_off_fp ( struct fs_dev * dev , struct freepool * fp ,
2005-10-07 10:46:04 +04:00
gfp_t gfp_flags )
2005-04-17 02:20:36 +04:00
{
struct FS_BPENTRY * qe , * ne ;
struct sk_buff * skb ;
int n = 0 ;
fs_dprintk ( FS_DEBUG_QUEUE , " Topping off queue at %x (%d-%d/%d) \n " ,
fp - > offset , read_fs ( dev , FP_CNT ( fp - > offset ) ) , fp - > n ,
fp - > nr_buffers ) ;
while ( nr_buffers_in_freepool ( dev , fp ) < fp - > nr_buffers ) {
skb = alloc_skb ( fp - > bufsize , gfp_flags ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Alloc rec-skb: %p(%d) \n " , skb , fp - > bufsize ) ;
if ( ! skb ) break ;
ne = kmalloc ( sizeof ( struct FS_BPENTRY ) , gfp_flags ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Alloc rec-d: %p(%Zd) \n " , ne , sizeof ( struct FS_BPENTRY ) ) ;
if ( ! ne ) {
fs_dprintk ( FS_DEBUG_ALLOC , " Free rec-skb: %p \n " , skb ) ;
dev_kfree_skb_any ( skb ) ;
break ;
}
fs_dprintk ( FS_DEBUG_QUEUE , " Adding skb %p desc %p -> %p(%p) " ,
skb , ne , skb - > data , skb - > head ) ;
n + + ;
ne - > flags = FP_FLAGS_EPI | fp - > bufsize ;
ne - > next = virt_to_bus ( NULL ) ;
ne - > bsa = virt_to_bus ( skb - > data ) ;
ne - > aal_bufsize = fp - > bufsize ;
ne - > skb = skb ;
ne - > fp = fp ;
qe = ( struct FS_BPENTRY * ) ( read_fs ( dev , FP_EA ( fp - > offset ) ) ) ;
fs_dprintk ( FS_DEBUG_QUEUE , " link at %p \n " , qe ) ;
if ( qe ) {
qe = bus_to_virt ( ( long ) qe ) ;
qe - > next = virt_to_bus ( ne ) ;
qe - > flags & = ~ FP_FLAGS_EPI ;
} else
write_fs ( dev , FP_SA ( fp - > offset ) , virt_to_bus ( ne ) ) ;
write_fs ( dev , FP_EA ( fp - > offset ) , virt_to_bus ( ne ) ) ;
fp - > n + + ; /* XXX Atomic_inc? */
write_fs ( dev , FP_CTU ( fp - > offset ) , 1 ) ;
}
fs_dprintk ( FS_DEBUG_QUEUE , " Added %d entries. \n " , n ) ;
}
static void __devexit free_queue ( struct fs_dev * dev , struct queue * txq )
{
func_enter ( ) ;
write_fs ( dev , Q_SA ( txq - > offset ) , 0 ) ;
write_fs ( dev , Q_EA ( txq - > offset ) , 0 ) ;
write_fs ( dev , Q_RP ( txq - > offset ) , 0 ) ;
write_fs ( dev , Q_WP ( txq - > offset ) , 0 ) ;
/* Configuration ? */
fs_dprintk ( FS_DEBUG_ALLOC , " Free queue: %p \n " , txq - > sa ) ;
kfree ( txq - > sa ) ;
func_exit ( ) ;
}
static void __devexit free_freepool ( struct fs_dev * dev , struct freepool * fp )
{
func_enter ( ) ;
write_fs ( dev , FP_CNF ( fp - > offset ) , 0 ) ;
write_fs ( dev , FP_SA ( fp - > offset ) , 0 ) ;
write_fs ( dev , FP_EA ( fp - > offset ) , 0 ) ;
write_fs ( dev , FP_CNT ( fp - > offset ) , 0 ) ;
write_fs ( dev , FP_CTU ( fp - > offset ) , 0 ) ;
func_exit ( ) ;
}
static irqreturn_t fs_irq ( int irq , void * dev_id , struct pt_regs * pt_regs )
{
int i ;
u32 status ;
struct fs_dev * dev = dev_id ;
status = read_fs ( dev , ISR ) ;
if ( ! status )
return IRQ_NONE ;
func_enter ( ) ;
# ifdef IRQ_RATE_LIMIT
/* Aaargh! I'm ashamed. This costs more lines-of-code than the actual
interrupt routine ! . ( Well , used to when I wrote that comment ) - - REW */
{
static int lastjif ;
static int nintr = 0 ;
if ( lastjif = = jiffies ) {
if ( + + nintr > IRQ_RATE_LIMIT ) {
free_irq ( dev - > irq , dev_id ) ;
printk ( KERN_ERR " fs: Too many interrupts. Turning off interrupt %d. \n " ,
dev - > irq ) ;
}
} else {
lastjif = jiffies ;
nintr = 0 ;
}
}
# endif
fs_dprintk ( FS_DEBUG_QUEUE , " in intr: txq %d txrq %d \n " ,
read_fs ( dev , Q_EA ( dev - > hp_txq . offset ) ) -
read_fs ( dev , Q_SA ( dev - > hp_txq . offset ) ) ,
read_fs ( dev , Q_EA ( dev - > tx_relq . offset ) ) -
read_fs ( dev , Q_SA ( dev - > tx_relq . offset ) ) ) ;
/* print the bits in the ISR register. */
if ( fs_debug & FS_DEBUG_IRQ ) {
/* The FS_DEBUG things are unneccesary here. But this way it is
clear for grep that these are debug prints . */
fs_dprintk ( FS_DEBUG_IRQ , " IRQ status: " ) ;
for ( i = 0 ; i < 27 ; i + + )
if ( status & ( 1 < < i ) )
fs_dprintk ( FS_DEBUG_IRQ , " %s " , irq_bitname [ i ] ) ;
fs_dprintk ( FS_DEBUG_IRQ , " \n " ) ;
}
if ( status & ISR_RBRQ0_W ) {
fs_dprintk ( FS_DEBUG_IRQ , " Iiiin-coming (0)!!!! \n " ) ;
process_incoming ( dev , & dev - > rx_rq [ 0 ] ) ;
/* items mentioned on RBRQ0 are from FP 0 or 1. */
top_off_fp ( dev , & dev - > rx_fp [ 0 ] , GFP_ATOMIC ) ;
top_off_fp ( dev , & dev - > rx_fp [ 1 ] , GFP_ATOMIC ) ;
}
if ( status & ISR_RBRQ1_W ) {
fs_dprintk ( FS_DEBUG_IRQ , " Iiiin-coming (1)!!!! \n " ) ;
process_incoming ( dev , & dev - > rx_rq [ 1 ] ) ;
top_off_fp ( dev , & dev - > rx_fp [ 2 ] , GFP_ATOMIC ) ;
top_off_fp ( dev , & dev - > rx_fp [ 3 ] , GFP_ATOMIC ) ;
}
if ( status & ISR_RBRQ2_W ) {
fs_dprintk ( FS_DEBUG_IRQ , " Iiiin-coming (2)!!!! \n " ) ;
process_incoming ( dev , & dev - > rx_rq [ 2 ] ) ;
top_off_fp ( dev , & dev - > rx_fp [ 4 ] , GFP_ATOMIC ) ;
top_off_fp ( dev , & dev - > rx_fp [ 5 ] , GFP_ATOMIC ) ;
}
if ( status & ISR_RBRQ3_W ) {
fs_dprintk ( FS_DEBUG_IRQ , " Iiiin-coming (3)!!!! \n " ) ;
process_incoming ( dev , & dev - > rx_rq [ 3 ] ) ;
top_off_fp ( dev , & dev - > rx_fp [ 6 ] , GFP_ATOMIC ) ;
top_off_fp ( dev , & dev - > rx_fp [ 7 ] , GFP_ATOMIC ) ;
}
if ( status & ISR_CSQ_W ) {
fs_dprintk ( FS_DEBUG_IRQ , " Command executed ok! \n " ) ;
process_return_queue ( dev , & dev - > st_q ) ;
}
if ( status & ISR_TBRQ_W ) {
fs_dprintk ( FS_DEBUG_IRQ , " Data tramsitted! \n " ) ;
process_txdone_queue ( dev , & dev - > tx_relq ) ;
}
func_exit ( ) ;
return IRQ_HANDLED ;
}
# ifdef FS_POLL_FREQ
static void fs_poll ( unsigned long data )
{
struct fs_dev * dev = ( struct fs_dev * ) data ;
fs_irq ( 0 , dev , NULL ) ;
dev - > timer . expires = jiffies + FS_POLL_FREQ ;
add_timer ( & dev - > timer ) ;
}
# endif
static int __devinit fs_init ( struct fs_dev * dev )
{
struct pci_dev * pci_dev ;
int isr , to ;
int i ;
func_enter ( ) ;
pci_dev = dev - > pci_dev ;
2006-06-13 02:20:16 +04:00
printk ( KERN_INFO " found a FireStream %d card, base %16llx, irq%d. \n " ,
2005-04-17 02:20:36 +04:00
IS_FS50 ( dev ) ? 50 : 155 ,
2006-06-13 02:20:16 +04:00
( unsigned long long ) pci_resource_start ( pci_dev , 0 ) ,
dev - > pci_dev - > irq ) ;
2005-04-17 02:20:36 +04:00
if ( fs_debug & FS_DEBUG_INIT )
my_hd ( ( unsigned char * ) dev , sizeof ( * dev ) ) ;
undocumented_pci_fix ( pci_dev ) ;
dev - > hw_base = pci_resource_start ( pci_dev , 0 ) ;
dev - > base = ioremap ( dev - > hw_base , 0x1000 ) ;
reset_chip ( dev ) ;
write_fs ( dev , SARMODE0 , 0
| ( 0 * SARMODE0_SHADEN ) /* We don't use shadow registers. */
| ( 1 * SARMODE0_INTMODE_READCLEAR )
| ( 1 * SARMODE0_CWRE )
| IS_FS50 ( dev ) ? SARMODE0_PRPWT_FS50_5 :
SARMODE0_PRPWT_FS155_3
| ( 1 * SARMODE0_CALSUP_1 )
| IS_FS50 ( dev ) ? ( 0
| SARMODE0_RXVCS_32
| SARMODE0_ABRVCS_32
| SARMODE0_TXVCS_32 ) :
( 0
| SARMODE0_RXVCS_1k
| SARMODE0_ABRVCS_1k
| SARMODE0_TXVCS_1k ) ) ;
/* 10ms * 100 is 1 second. That should be enough, as AN3:9 says it takes
1 ms . */
to = 100 ;
while ( - - to ) {
isr = read_fs ( dev , ISR ) ;
/* This bit is documented as "RESERVED" */
if ( isr & ISR_INIT_ERR ) {
printk ( KERN_ERR " Error initializing the FS... \n " ) ;
return 1 ;
}
if ( isr & ISR_INIT ) {
fs_dprintk ( FS_DEBUG_INIT , " Ha! Initialized OK! \n " ) ;
break ;
}
/* Try again after 10ms. */
msleep ( 10 ) ;
}
if ( ! to ) {
printk ( KERN_ERR " timeout initializing the FS... \n " ) ;
return 1 ;
}
/* XXX fix for fs155 */
dev - > channel_mask = 0x1f ;
dev - > channo = 0 ;
/* AN3: 10 */
write_fs ( dev , SARMODE1 , 0
| ( fs_keystream * SARMODE1_DEFHEC ) /* XXX PHY */
| ( ( loopback = = 1 ) * SARMODE1_TSTLP ) /* XXX Loopback mode enable... */
| ( 1 * SARMODE1_DCRM )
| ( 1 * SARMODE1_DCOAM )
| ( 0 * SARMODE1_OAMCRC )
| ( 0 * SARMODE1_DUMPE )
| ( 0 * SARMODE1_GPLEN )
| ( 0 * SARMODE1_GNAM )
| ( 0 * SARMODE1_GVAS )
| ( 0 * SARMODE1_GPAS )
| ( 1 * SARMODE1_GPRI )
| ( 0 * SARMODE1_PMS )
| ( 0 * SARMODE1_GFCR )
| ( 1 * SARMODE1_HECM2 )
| ( 1 * SARMODE1_HECM1 )
| ( 1 * SARMODE1_HECM0 )
| ( 1 < < 12 ) /* That's what hang's driver does. Program to 0 */
| ( 0 * 0xff ) /* XXX FS155 */ ) ;
/* Cal prescale etc */
/* AN3: 11 */
write_fs ( dev , TMCONF , 0x0000000f ) ;
write_fs ( dev , CALPRESCALE , 0x01010101 * num ) ;
write_fs ( dev , 0x80 , 0x000F00E4 ) ;
/* AN3: 12 */
write_fs ( dev , CELLOSCONF , 0
| ( 0 * CELLOSCONF_CEN )
| ( CELLOSCONF_SC1 )
| ( 0x80 * CELLOSCONF_COBS )
| ( num * CELLOSCONF_COPK ) /* Changed from 0xff to 0x5a */
| ( num * CELLOSCONF_COST ) ) ; /* after a hint from Hang.
* performance jumped 50 - > 70. . . */
/* Magic value by Hang */
write_fs ( dev , CELLOSCONF_COST , 0x0B809191 ) ;
if ( IS_FS50 ( dev ) ) {
write_fs ( dev , RAS0 , RAS0_DCD_XHLT ) ;
dev - > atm_dev - > ci_range . vpi_bits = 12 ;
dev - > atm_dev - > ci_range . vci_bits = 16 ;
dev - > nchannels = FS50_NR_CHANNELS ;
} else {
write_fs ( dev , RAS0 , RAS0_DCD_XHLT
| ( ( ( 1 < < FS155_VPI_BITS ) - 1 ) * RAS0_VPSEL )
| ( ( ( 1 < < FS155_VCI_BITS ) - 1 ) * RAS0_VCSEL ) ) ;
/* We can chose the split arbitarily. We might be able to
support more . Whatever . This should do for now . */
dev - > atm_dev - > ci_range . vpi_bits = FS155_VPI_BITS ;
dev - > atm_dev - > ci_range . vci_bits = FS155_VCI_BITS ;
/* Address bits we can't use should be compared to 0. */
write_fs ( dev , RAC , 0 ) ;
/* Manual (AN9, page 6) says ASF1=0 means compare Utopia address
* too . I can ' t find ASF1 anywhere . Anyway , we AND with just the
* other bits , then compare with 0 , which is exactly what we
* want . */
write_fs ( dev , RAM , ( 1 < < ( 28 - FS155_VPI_BITS - FS155_VCI_BITS ) ) - 1 ) ;
dev - > nchannels = FS155_NR_CHANNELS ;
}
dev - > atm_vccs = kmalloc ( dev - > nchannels * sizeof ( struct atm_vcc * ) ,
GFP_KERNEL ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Alloc atmvccs: %p(%Zd) \n " ,
dev - > atm_vccs , dev - > nchannels * sizeof ( struct atm_vcc * ) ) ;
if ( ! dev - > atm_vccs ) {
printk ( KERN_WARNING " Couldn't allocate memory for VCC buffers. Woops! \n " ) ;
/* XXX Clean up..... */
return 1 ;
}
memset ( dev - > atm_vccs , 0 , dev - > nchannels * sizeof ( struct atm_vcc * ) ) ;
dev - > tx_inuse = kmalloc ( dev - > nchannels / 8 /* bits/byte */ , GFP_KERNEL ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Alloc tx_inuse: %p(%d) \n " ,
dev - > atm_vccs , dev - > nchannels / 8 ) ;
if ( ! dev - > tx_inuse ) {
printk ( KERN_WARNING " Couldn't allocate memory for tx_inuse bits! \n " ) ;
/* XXX Clean up..... */
return 1 ;
}
memset ( dev - > tx_inuse , 0 , dev - > nchannels / 8 ) ;
/* -- RAS1 : FS155 and 50 differ. Default (0) should be OK for both */
/* -- RAS2 : FS50 only: Default is OK. */
/* DMAMODE, default should be OK. -- REW */
write_fs ( dev , DMAMR , DMAMR_TX_MODE_FULL ) ;
init_q ( dev , & dev - > hp_txq , TX_PQ ( TXQ_HP ) , TXQ_NENTRIES , 0 ) ;
init_q ( dev , & dev - > lp_txq , TX_PQ ( TXQ_LP ) , TXQ_NENTRIES , 0 ) ;
init_q ( dev , & dev - > tx_relq , TXB_RQ , TXQ_NENTRIES , 1 ) ;
init_q ( dev , & dev - > st_q , ST_Q , TXQ_NENTRIES , 1 ) ;
for ( i = 0 ; i < FS_NR_FREE_POOLS ; i + + ) {
init_fp ( dev , & dev - > rx_fp [ i ] , RXB_FP ( i ) ,
rx_buf_sizes [ i ] , rx_pool_sizes [ i ] ) ;
top_off_fp ( dev , & dev - > rx_fp [ i ] , GFP_KERNEL ) ;
}
for ( i = 0 ; i < FS_NR_RX_QUEUES ; i + + )
init_q ( dev , & dev - > rx_rq [ i ] , RXB_RQ ( i ) , RXRQ_NENTRIES , 1 ) ;
dev - > irq = pci_dev - > irq ;
2006-07-02 06:29:38 +04:00
if ( request_irq ( dev - > irq , fs_irq , IRQF_SHARED , " firestream " , dev ) ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_WARNING " couldn't get irq %d for firestream. \n " , pci_dev - > irq ) ;
/* XXX undo all previous stuff... */
return 1 ;
}
fs_dprintk ( FS_DEBUG_INIT , " Grabbed irq %d for dev at %p. \n " , dev - > irq , dev ) ;
/* We want to be notified of most things. Just the statistics count
overflows are not interesting */
write_fs ( dev , IMR , 0
| ISR_RBRQ0_W
| ISR_RBRQ1_W
| ISR_RBRQ2_W
| ISR_RBRQ3_W
| ISR_TBRQ_W
| ISR_CSQ_W ) ;
write_fs ( dev , SARMODE0 , 0
| ( 0 * SARMODE0_SHADEN ) /* We don't use shadow registers. */
| ( 1 * SARMODE0_GINT )
| ( 1 * SARMODE0_INTMODE_READCLEAR )
| ( 0 * SARMODE0_CWRE )
| ( IS_FS50 ( dev ) ? SARMODE0_PRPWT_FS50_5 :
SARMODE0_PRPWT_FS155_3 )
| ( 1 * SARMODE0_CALSUP_1 )
| ( IS_FS50 ( dev ) ? ( 0
| SARMODE0_RXVCS_32
| SARMODE0_ABRVCS_32
| SARMODE0_TXVCS_32 ) :
( 0
| SARMODE0_RXVCS_1k
| SARMODE0_ABRVCS_1k
| SARMODE0_TXVCS_1k ) )
| ( 1 * SARMODE0_RUN ) ) ;
init_phy ( dev , PHY_NTC_INIT ) ;
if ( loopback = = 2 ) {
write_phy ( dev , 0x39 , 0x000e ) ;
}
# ifdef FS_POLL_FREQ
init_timer ( & dev - > timer ) ;
dev - > timer . data = ( unsigned long ) dev ;
dev - > timer . function = fs_poll ;
dev - > timer . expires = jiffies + FS_POLL_FREQ ;
add_timer ( & dev - > timer ) ;
# endif
dev - > atm_dev - > dev_data = dev ;
func_exit ( ) ;
return 0 ;
}
static int __devinit firestream_init_one ( struct pci_dev * pci_dev ,
const struct pci_device_id * ent )
{
struct atm_dev * atm_dev ;
struct fs_dev * fs_dev ;
if ( pci_enable_device ( pci_dev ) )
goto err_out ;
fs_dev = kmalloc ( sizeof ( struct fs_dev ) , GFP_KERNEL ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Alloc fs-dev: %p(%Zd) \n " ,
fs_dev , sizeof ( struct fs_dev ) ) ;
if ( ! fs_dev )
goto err_out ;
memset ( fs_dev , 0 , sizeof ( struct fs_dev ) ) ;
atm_dev = atm_dev_register ( " fs " , & ops , - 1 , NULL ) ;
if ( ! atm_dev )
goto err_out_free_fs_dev ;
fs_dev - > pci_dev = pci_dev ;
fs_dev - > atm_dev = atm_dev ;
fs_dev - > flags = ent - > driver_data ;
if ( fs_init ( fs_dev ) )
goto err_out_free_atm_dev ;
fs_dev - > next = fs_boards ;
fs_boards = fs_dev ;
return 0 ;
err_out_free_atm_dev :
atm_dev_deregister ( atm_dev ) ;
err_out_free_fs_dev :
kfree ( fs_dev ) ;
err_out :
return - ENODEV ;
}
static void __devexit firestream_remove_one ( struct pci_dev * pdev )
{
int i ;
struct fs_dev * dev , * nxtdev ;
struct fs_vcc * vcc ;
struct FS_BPENTRY * fp , * nxt ;
func_enter ( ) ;
#if 0
printk ( " hptxq: \n " ) ;
for ( i = 0 ; i < 60 ; i + + ) {
printk ( " %d: %08x %08x %08x %08x \n " ,
i , pq [ qp ] . cmd , pq [ qp ] . p0 , pq [ qp ] . p1 , pq [ qp ] . p2 ) ;
qp + + ;
if ( qp > = 60 ) qp = 0 ;
}
printk ( " descriptors: \n " ) ;
for ( i = 0 ; i < 60 ; i + + ) {
printk ( " %d: %p: %08x %08x %p %p \n " ,
i , da [ qd ] , dq [ qd ] . flags , dq [ qd ] . bsa , dq [ qd ] . skb , dq [ qd ] . dev ) ;
qd + + ;
if ( qd > = 60 ) qd = 0 ;
}
# endif
for ( dev = fs_boards ; dev ! = NULL ; dev = nxtdev ) {
fs_dprintk ( FS_DEBUG_CLEANUP , " Releasing resources for dev at %p. \n " , dev ) ;
/* XXX Hit all the tx channels too! */
for ( i = 0 ; i < dev - > nchannels ; i + + ) {
if ( dev - > atm_vccs [ i ] ) {
vcc = FS_VCC ( dev - > atm_vccs [ i ] ) ;
submit_command ( dev , & dev - > hp_txq ,
QE_CMD_TX_PURGE_INH | QE_CMD_IMM_INQ | vcc - > channo , 0 , 0 , 0 ) ;
submit_command ( dev , & dev - > hp_txq ,
QE_CMD_RX_PURGE_INH | QE_CMD_IMM_INQ | vcc - > channo , 0 , 0 , 0 ) ;
}
}
/* XXX Wait a while for the chip to release all buffers. */
for ( i = 0 ; i < FS_NR_FREE_POOLS ; i + + ) {
for ( fp = bus_to_virt ( read_fs ( dev , FP_SA ( dev - > rx_fp [ i ] . offset ) ) ) ;
! ( fp - > flags & FP_FLAGS_EPI ) ; fp = nxt ) {
fs_dprintk ( FS_DEBUG_ALLOC , " Free rec-skb: %p \n " , fp - > skb ) ;
dev_kfree_skb_any ( fp - > skb ) ;
nxt = bus_to_virt ( fp - > next ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Free rec-d: %p \n " , fp ) ;
kfree ( fp ) ;
}
fs_dprintk ( FS_DEBUG_ALLOC , " Free rec-skb: %p \n " , fp - > skb ) ;
dev_kfree_skb_any ( fp - > skb ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Free rec-d: %p \n " , fp ) ;
kfree ( fp ) ;
}
/* Hang the chip in "reset", prevent it clobbering memory that is
no longer ours . */
reset_chip ( dev ) ;
fs_dprintk ( FS_DEBUG_CLEANUP , " Freeing irq%d. \n " , dev - > irq ) ;
free_irq ( dev - > irq , dev ) ;
del_timer ( & dev - > timer ) ;
atm_dev_deregister ( dev - > atm_dev ) ;
free_queue ( dev , & dev - > hp_txq ) ;
free_queue ( dev , & dev - > lp_txq ) ;
free_queue ( dev , & dev - > tx_relq ) ;
free_queue ( dev , & dev - > st_q ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Free atmvccs: %p \n " , dev - > atm_vccs ) ;
kfree ( dev - > atm_vccs ) ;
for ( i = 0 ; i < FS_NR_FREE_POOLS ; i + + )
free_freepool ( dev , & dev - > rx_fp [ i ] ) ;
for ( i = 0 ; i < FS_NR_RX_QUEUES ; i + + )
free_queue ( dev , & dev - > rx_rq [ i ] ) ;
fs_dprintk ( FS_DEBUG_ALLOC , " Free fs-dev: %p \n " , dev ) ;
nxtdev = dev - > next ;
kfree ( dev ) ;
}
func_exit ( ) ;
}
static struct pci_device_id firestream_pci_tbl [ ] = {
{ PCI_VENDOR_ID_FUJITSU_ME , PCI_DEVICE_ID_FUJITSU_FS50 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FS_IS50 } ,
{ PCI_VENDOR_ID_FUJITSU_ME , PCI_DEVICE_ID_FUJITSU_FS155 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FS_IS155 } ,
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , firestream_pci_tbl ) ;
static struct pci_driver firestream_driver = {
. name = " firestream " ,
. id_table = firestream_pci_tbl ,
. probe = firestream_init_one ,
. remove = __devexit_p ( firestream_remove_one ) ,
} ;
static int __init firestream_init_module ( void )
{
int error ;
func_enter ( ) ;
error = pci_register_driver ( & firestream_driver ) ;
func_exit ( ) ;
return error ;
}
static void __exit firestream_cleanup_module ( void )
{
pci_unregister_driver ( & firestream_driver ) ;
}
module_init ( firestream_init_module ) ;
module_exit ( firestream_cleanup_module ) ;
MODULE_LICENSE ( " GPL " ) ;