2005-04-17 02:20:36 +04:00
/*
* linux / drivers / acorn / scsi / fas216 . c
*
* Copyright ( C ) 1997 - 2003 Russell King
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* Based on information in qlogicfas . c by Tom Zerucha , Michael Griffith , and
* other sources , including :
* the AMD Am53CF94 data sheet
* the AMD Am53C94 data sheet
*
* This is a generic driver . To use it , have a look at cumana_2 . c . You
* should define your own structure that overlays FAS216_Info , eg :
* struct my_host_data {
* FAS216_Info info ;
* . . . my host specific data . . .
* } ;
*
* Changelog :
* 30 - 08 - 1997 RMK Created
* 14 - 09 - 1997 RMK Started disconnect support
* 08 - 02 - 1998 RMK Corrected real DMA support
* 15 - 02 - 1998 RMK Started sync xfer support
* 06 - 04 - 1998 RMK Tightened conditions for printing incomplete
* transfers
* 02 - 05 - 1998 RMK Added extra checks in fas216_reset
* 24 - 05 - 1998 RMK Fixed synchronous transfers with period > = 200 ns
* 27 - 06 - 1998 RMK Changed asm / delay . h to linux / delay . h
* 26 - 08 - 1998 RMK Improved message support wrt MESSAGE_REJECT
* 02 - 04 - 2000 RMK Converted to use the new error handling , and
* automatically request sense data upon check
* condition status from targets .
*/
# include <linux/module.h>
# include <linux/blkdev.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/ioport.h>
# include <linux/proc_fs.h>
# include <linux/delay.h>
# include <linux/bitops.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <asm/dma.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/ecard.h>
# include "../scsi.h"
2005-04-03 23:53:59 +04:00
# include <scsi/scsi_dbg.h>
2005-04-17 02:20:36 +04:00
# include <scsi/scsi_host.h>
# include "fas216.h"
# include "scsi.h"
/* NOTE: SCSI2 Synchronous transfers *require* DMA according to
* the data sheet . This restriction is crazy , especially when
* you only want to send 16 bytes ! What were the guys who
* designed this chip on at that time ? Did they read the SCSI2
* spec at all ? The following sections are taken from the SCSI2
* standard ( s2r10 ) concerning this :
*
* > IMPLEMENTORS NOTES :
* > ( 1 ) Re - negotiation at every selection is not recommended , since a
* > significant performance impact is likely .
*
* > The implied synchronous agreement shall remain in effect until a BUS DEVICE
* > RESET message is received , until a hard reset condition occurs , or until one
* > of the two SCSI devices elects to modify the agreement . The default data
* > transfer mode is asynchronous data transfer mode . The default data transfer
* > mode is entered at power on , after a BUS DEVICE RESET message , or after a hard
* > reset condition .
*
* In total , this means that once you have elected to use synchronous
* transfers , you must always use DMA .
*
* I was thinking that this was a good chip until I found this restriction ; (
*/
# define SCSI2_SYNC
# undef SCSI2_TAG
# undef DEBUG_CONNECT
# undef DEBUG_MESSAGES
# undef CHECK_STRUCTURE
# define LOG_CONNECT (1 << 0)
# define LOG_BUSSERVICE (1 << 1)
# define LOG_FUNCTIONDONE (1 << 2)
# define LOG_MESSAGES (1 << 3)
# define LOG_BUFFER (1 << 4)
# define LOG_ERROR (1 << 8)
static int level_mask = LOG_ERROR ;
module_param ( level_mask , int , 0644 ) ;
static int __init fas216_log_setup ( char * str )
{
char * s ;
level_mask = 0 ;
while ( ( s = strsep ( & str , " , " ) ) ! = NULL ) {
switch ( s [ 0 ] ) {
case ' a ' :
if ( strcmp ( s , " all " ) = = 0 )
level_mask | = - 1 ;
break ;
case ' b ' :
if ( strncmp ( s , " bus " , 3 ) = = 0 )
level_mask | = LOG_BUSSERVICE ;
if ( strncmp ( s , " buf " , 3 ) = = 0 )
level_mask | = LOG_BUFFER ;
break ;
case ' c ' :
level_mask | = LOG_CONNECT ;
break ;
case ' e ' :
level_mask | = LOG_ERROR ;
break ;
case ' m ' :
level_mask | = LOG_MESSAGES ;
break ;
case ' n ' :
if ( strcmp ( s , " none " ) = = 0 )
level_mask = 0 ;
break ;
case ' s ' :
level_mask | = LOG_FUNCTIONDONE ;
break ;
}
}
return 1 ;
}
__setup ( " fas216_logging= " , fas216_log_setup ) ;
static inline unsigned char fas216_readb ( FAS216_Info * info , unsigned int reg )
{
unsigned int off = reg < < info - > scsi . io_shift ;
return readb ( info - > scsi . io_base + off ) ;
}
static inline void fas216_writeb ( FAS216_Info * info , unsigned int reg , unsigned int val )
{
unsigned int off = reg < < info - > scsi . io_shift ;
writeb ( val , info - > scsi . io_base + off ) ;
}
static void fas216_dumpstate ( FAS216_Info * info )
{
unsigned char is , stat , inst ;
is = fas216_readb ( info , REG_IS ) ;
stat = fas216_readb ( info , REG_STAT ) ;
inst = fas216_readb ( info , REG_INST ) ;
printk ( " FAS216: CTCL=%02X CTCM=%02X CMD=%02X STAT=%02X "
" INST=%02X IS=%02X CFIS=%02X " ,
fas216_readb ( info , REG_CTCL ) ,
fas216_readb ( info , REG_CTCM ) ,
fas216_readb ( info , REG_CMD ) , stat , inst , is ,
fas216_readb ( info , REG_CFIS ) ) ;
printk ( " CNTL1=%02X CNTL2=%02X CNTL3=%02X CTCH=%02X \n " ,
fas216_readb ( info , REG_CNTL1 ) ,
fas216_readb ( info , REG_CNTL2 ) ,
fas216_readb ( info , REG_CNTL3 ) ,
fas216_readb ( info , REG_CTCH ) ) ;
}
2005-10-31 20:31:56 +03:00
static void print_SCp ( struct scsi_pointer * SCp , const char * prefix , const char * suffix )
2005-04-17 02:20:36 +04:00
{
printk ( " %sptr %p this_residual 0x%x buffer %p buffers_residual 0x%x%s " ,
prefix , SCp - > ptr , SCp - > this_residual , SCp - > buffer ,
SCp - > buffers_residual , suffix ) ;
}
static void fas216_dumpinfo ( FAS216_Info * info )
{
static int used = 0 ;
int i ;
if ( used + + )
return ;
printk ( " FAS216_Info= \n " ) ;
printk ( " { magic_start=%lX host=%p SCpnt=%p origSCpnt=%p \n " ,
info - > magic_start , info - > host , info - > SCpnt ,
info - > origSCpnt ) ;
printk ( " scsi={ io_shift=%X irq=%X cfg={ %X %X %X %X } \n " ,
info - > scsi . io_shift , info - > scsi . irq ,
info - > scsi . cfg [ 0 ] , info - > scsi . cfg [ 1 ] , info - > scsi . cfg [ 2 ] ,
info - > scsi . cfg [ 3 ] ) ;
printk ( " type=%p phase=%X \n " ,
info - > scsi . type , info - > scsi . phase ) ;
print_SCp ( & info - > scsi . SCp , " SCp={ " , " } \n " ) ;
printk ( " msgs async_stp=%X disconnectable=%d aborting=%d } \n " ,
info - > scsi . async_stp ,
info - > scsi . disconnectable , info - > scsi . aborting ) ;
printk ( " stats={ queues=%X removes=%X fins=%X reads=%X writes=%X miscs=%X \n "
" disconnects=%X aborts=%X bus_resets=%X host_resets=%X} \n " ,
info - > stats . queues , info - > stats . removes , info - > stats . fins ,
info - > stats . reads , info - > stats . writes , info - > stats . miscs ,
info - > stats . disconnects , info - > stats . aborts , info - > stats . bus_resets ,
info - > stats . host_resets ) ;
printk ( " ifcfg={ clockrate=%X select_timeout=%X asyncperiod=%X sync_max_depth=%X } \n " ,
info - > ifcfg . clockrate , info - > ifcfg . select_timeout ,
info - > ifcfg . asyncperiod , info - > ifcfg . sync_max_depth ) ;
for ( i = 0 ; i < 8 ; i + + ) {
printk ( " busyluns[%d]=%08lx dev[%d]={ disconnect_ok=%d stp=%X sof=%X sync_state=%X } \n " ,
i , info - > busyluns [ i ] , i ,
info - > device [ i ] . disconnect_ok , info - > device [ i ] . stp ,
info - > device [ i ] . sof , info - > device [ i ] . sync_state ) ;
}
printk ( " dma={ transfer_type=%X setup=%p pseudo=%p stop=%p } \n " ,
info - > dma . transfer_type , info - > dma . setup ,
info - > dma . pseudo , info - > dma . stop ) ;
printk ( " internal_done=%X magic_end=%lX } \n " ,
info - > internal_done , info - > magic_end ) ;
}
# ifdef CHECK_STRUCTURE
static void __fas216_checkmagic ( FAS216_Info * info , const char * func )
{
int corruption = 0 ;
if ( info - > magic_start ! = MAGIC ) {
printk ( KERN_CRIT " FAS216 Error: magic at start corrupted \n " ) ;
corruption + + ;
}
if ( info - > magic_end ! = MAGIC ) {
printk ( KERN_CRIT " FAS216 Error: magic at end corrupted \n " ) ;
corruption + + ;
}
if ( corruption ) {
fas216_dumpinfo ( info ) ;
panic ( " scsi memory space corrupted in %s " , func ) ;
}
}
2008-07-04 10:47:27 +04:00
# define fas216_checkmagic(info) __fas216_checkmagic((info), __func__)
2005-04-17 02:20:36 +04:00
# else
# define fas216_checkmagic(info)
# endif
static const char * fas216_bus_phase ( int stat )
{
static const char * phases [ ] = {
" DATA OUT " , " DATA IN " ,
" COMMAND " , " STATUS " ,
" MISC OUT " , " MISC IN " ,
" MESG OUT " , " MESG IN "
} ;
return phases [ stat & STAT_BUSMASK ] ;
}
static const char * fas216_drv_phase ( FAS216_Info * info )
{
static const char * phases [ ] = {
[ PHASE_IDLE ] = " idle " ,
[ PHASE_SELECTION ] = " selection " ,
[ PHASE_COMMAND ] = " command " ,
[ PHASE_DATAOUT ] = " data out " ,
[ PHASE_DATAIN ] = " data in " ,
[ PHASE_MSGIN ] = " message in " ,
[ PHASE_MSGIN_DISCONNECT ] = " disconnect " ,
[ PHASE_MSGOUT_EXPECT ] = " expect message out " ,
[ PHASE_MSGOUT ] = " message out " ,
[ PHASE_STATUS ] = " status " ,
[ PHASE_DONE ] = " done " ,
} ;
if ( info - > scsi . phase < ARRAY_SIZE ( phases ) & &
phases [ info - > scsi . phase ] )
return phases [ info - > scsi . phase ] ;
return " ??? " ;
}
static char fas216_target ( FAS216_Info * info )
{
if ( info - > SCpnt )
return ' 0 ' + info - > SCpnt - > device - > id ;
else
return ' H ' ;
}
static void
fas216_do_log ( FAS216_Info * info , char target , char * fmt , va_list ap )
{
static char buf [ 1024 ] ;
vsnprintf ( buf , sizeof ( buf ) , fmt , ap ) ;
printk ( " scsi%d.%c: %s " , info - > host - > host_no , target , buf ) ;
}
2006-10-01 15:18:37 +04:00
static void fas216_log_command ( FAS216_Info * info , int level ,
struct scsi_cmnd * SCpnt , char * fmt , . . . )
2005-04-17 02:20:36 +04:00
{
va_list args ;
if ( level ! = 0 & & ! ( level & level_mask ) )
return ;
va_start ( args , fmt ) ;
fas216_do_log ( info , ' 0 ' + SCpnt - > device - > id , fmt , args ) ;
va_end ( args ) ;
printk ( " CDB: " ) ;
2005-04-03 23:53:59 +04:00
__scsi_print_command ( SCpnt - > cmnd ) ;
2005-04-17 02:20:36 +04:00
}
static void
fas216_log_target ( FAS216_Info * info , int level , int target , char * fmt , . . . )
{
va_list args ;
if ( level ! = 0 & & ! ( level & level_mask ) )
return ;
if ( target < 0 )
target = ' H ' ;
else
target + = ' 0 ' ;
va_start ( args , fmt ) ;
fas216_do_log ( info , target , fmt , args ) ;
va_end ( args ) ;
printk ( " \n " ) ;
}
static void fas216_log ( FAS216_Info * info , int level , char * fmt , . . . )
{
va_list args ;
if ( level ! = 0 & & ! ( level & level_mask ) )
return ;
va_start ( args , fmt ) ;
fas216_do_log ( info , fas216_target ( info ) , fmt , args ) ;
va_end ( args ) ;
printk ( " \n " ) ;
}
# define PH_SIZE 32
static struct { int stat , ssr , isr , ph ; } ph_list [ PH_SIZE ] ;
static int ph_ptr ;
static void add_debug_list ( int stat , int ssr , int isr , int ph )
{
ph_list [ ph_ptr ] . stat = stat ;
ph_list [ ph_ptr ] . ssr = ssr ;
ph_list [ ph_ptr ] . isr = isr ;
ph_list [ ph_ptr ] . ph = ph ;
ph_ptr = ( ph_ptr + 1 ) & ( PH_SIZE - 1 ) ;
}
static struct { int command ; void * from ; } cmd_list [ 8 ] ;
static int cmd_ptr ;
static void fas216_cmd ( FAS216_Info * info , unsigned int command )
{
cmd_list [ cmd_ptr ] . command = command ;
cmd_list [ cmd_ptr ] . from = __builtin_return_address ( 0 ) ;
cmd_ptr = ( cmd_ptr + 1 ) & 7 ;
fas216_writeb ( info , REG_CMD , command ) ;
}
static void print_debug_list ( void )
{
int i ;
i = ph_ptr ;
printk ( KERN_ERR " SCSI IRQ trail \n " ) ;
do {
printk ( " %02x:%02x:%02x:%1x " ,
ph_list [ i ] . stat , ph_list [ i ] . ssr ,
ph_list [ i ] . isr , ph_list [ i ] . ph ) ;
i = ( i + 1 ) & ( PH_SIZE - 1 ) ;
if ( ( ( i ^ ph_ptr ) & 7 ) = = 0 )
printk ( " \n " ) ;
} while ( i ! = ph_ptr ) ;
if ( ( i ^ ph_ptr ) & 7 )
printk ( " \n " ) ;
i = cmd_ptr ;
printk ( KERN_ERR " FAS216 commands: " ) ;
do {
printk ( " %02x:%p " , cmd_list [ i ] . command , cmd_list [ i ] . from ) ;
i = ( i + 1 ) & 7 ;
} while ( i ! = cmd_ptr ) ;
printk ( " \n " ) ;
}
static void fas216_done ( FAS216_Info * info , unsigned int result ) ;
/**
* fas216_get_last_msg - retrive last message from the list
* @ info : interface to search
* @ pos : current fifo position
*
* Retrieve a last message from the list , using position in fifo .
*/
static inline unsigned short
fas216_get_last_msg ( FAS216_Info * info , int pos )
{
unsigned short packed_msg = NOP ;
struct message * msg ;
int msgnr = 0 ;
while ( ( msg = msgqueue_getmsg ( & info - > scsi . msgs , msgnr + + ) ) ! = NULL ) {
if ( pos > = msg - > fifo )
break ;
}
if ( msg ) {
if ( msg - > msg [ 0 ] = = EXTENDED_MESSAGE )
packed_msg = EXTENDED_MESSAGE | msg - > msg [ 2 ] < < 8 ;
else
packed_msg = msg - > msg [ 0 ] ;
}
fas216_log ( info , LOG_MESSAGES ,
" Message: %04x found at position %02x \n " , packed_msg , pos ) ;
return packed_msg ;
}
/**
* fas216_syncperiod - calculate STP register value
* @ info : state structure for interface connected to device
* @ ns : period in ns ( between subsequent bytes )
*
* Calculate value to be loaded into the STP register for a given period
* in ns . Returns a value suitable for REG_STP .
*/
static int fas216_syncperiod ( FAS216_Info * info , int ns )
{
int value = ( info - > ifcfg . clockrate * ns ) / 1000 ;
fas216_checkmagic ( info ) ;
if ( value < 4 )
value = 4 ;
else if ( value > 35 )
value = 35 ;
return value & 31 ;
}
/**
* fas216_set_sync - setup FAS216 chip for specified transfer period .
* @ info : state structure for interface connected to device
* @ target : target
*
* Correctly setup FAS216 chip for specified transfer period .
* Notes : we need to switch the chip out of FASTSCSI mode if we have
* a transfer period > = 200 ns - otherwise the chip will violate
* the SCSI timings .
*/
static void fas216_set_sync ( FAS216_Info * info , int target )
{
unsigned int cntl3 ;
fas216_writeb ( info , REG_SOF , info - > device [ target ] . sof ) ;
fas216_writeb ( info , REG_STP , info - > device [ target ] . stp ) ;
cntl3 = info - > scsi . cfg [ 2 ] ;
if ( info - > device [ target ] . period > = ( 200 / 4 ) )
cntl3 = cntl3 & ~ CNTL3_FASTSCSI ;
fas216_writeb ( info , REG_CNTL3 , cntl3 ) ;
}
/* Synchronous transfer support
*
* Note : The SCSI II r10 spec says ( 5.6 .12 ) :
*
* ( 2 ) Due to historical problems with early host adapters that could
* not accept an SDTR message , some targets may not initiate synchronous
* negotiation after a power cycle as required by this standard . Host
* adapters that support synchronous mode may avoid the ensuing failure
* modes when the target is independently power cycled by initiating a
* synchronous negotiation on each REQUEST SENSE and INQUIRY command .
* This approach increases the SCSI bus overhead and is not recommended
* for new implementations . The correct method is to respond to an
* SDTR message with a MESSAGE REJECT message if the either the
* initiator or target devices does not support synchronous transfers
* or does not want to negotiate for synchronous transfers at the time .
* Using the correct method assures compatibility with wide data
* transfers and future enhancements .
*
* We will always initiate a synchronous transfer negotiation request on
* every INQUIRY or REQUEST SENSE message , unless the target itself has
* at some point performed a synchronous transfer negotiation request , or
* we have synchronous transfers disabled for this device .
*/
/**
* fas216_handlesync - Handle a synchronous transfer message
* @ info : state structure for interface
* @ msg : message from target
*
* Handle a synchronous transfer message from the target
*/
static void fas216_handlesync ( FAS216_Info * info , char * msg )
{
struct fas216_device * dev = & info - > device [ info - > SCpnt - > device - > id ] ;
enum { sync , async , none , reject } res = none ;
# ifdef SCSI2_SYNC
switch ( msg [ 0 ] ) {
case MESSAGE_REJECT :
/* Synchronous transfer request failed.
* Note : SCSI II r10 :
*
* SCSI devices that are capable of synchronous
* data transfers shall not respond to an SDTR
* message with a MESSAGE REJECT message .
*
* Hence , if we get this condition , we disable
* negotiation for this device .
*/
if ( dev - > sync_state = = neg_inprogress ) {
dev - > sync_state = neg_invalid ;
res = async ;
}
break ;
case EXTENDED_MESSAGE :
switch ( dev - > sync_state ) {
/* We don't accept synchronous transfer requests.
* Respond with a MESSAGE_REJECT to prevent a
* synchronous transfer agreement from being reached .
*/
case neg_invalid :
res = reject ;
break ;
/* We were not negotiating a synchronous transfer,
* but the device sent us a negotiation request .
* Honour the request by sending back a SDTR
* message containing our capability , limited by
* the targets capability .
*/
default :
fas216_cmd ( info , CMD_SETATN ) ;
if ( msg [ 4 ] > info - > ifcfg . sync_max_depth )
msg [ 4 ] = info - > ifcfg . sync_max_depth ;
if ( msg [ 3 ] < 1000 / info - > ifcfg . clockrate )
msg [ 3 ] = 1000 / info - > ifcfg . clockrate ;
msgqueue_flush ( & info - > scsi . msgs ) ;
msgqueue_addmsg ( & info - > scsi . msgs , 5 ,
EXTENDED_MESSAGE , 3 , EXTENDED_SDTR ,
msg [ 3 ] , msg [ 4 ] ) ;
info - > scsi . phase = PHASE_MSGOUT_EXPECT ;
/* This is wrong. The agreement is not in effect
* until this message is accepted by the device
*/
dev - > sync_state = neg_targcomplete ;
res = sync ;
break ;
/* We initiated the synchronous transfer negotiation,
* and have successfully received a response from the
* target . The synchronous transfer agreement has been
* reached . Note : if the values returned are out of our
* bounds , we must reject the message .
*/
case neg_inprogress :
res = reject ;
if ( msg [ 4 ] < = info - > ifcfg . sync_max_depth & &
msg [ 3 ] > = 1000 / info - > ifcfg . clockrate ) {
dev - > sync_state = neg_complete ;
res = sync ;
}
break ;
}
}
# else
res = reject ;
# endif
switch ( res ) {
case sync :
dev - > period = msg [ 3 ] ;
dev - > sof = msg [ 4 ] ;
dev - > stp = fas216_syncperiod ( info , msg [ 3 ] * 4 ) ;
fas216_set_sync ( info , info - > SCpnt - > device - > id ) ;
break ;
case reject :
fas216_cmd ( info , CMD_SETATN ) ;
msgqueue_flush ( & info - > scsi . msgs ) ;
msgqueue_addmsg ( & info - > scsi . msgs , 1 , MESSAGE_REJECT ) ;
info - > scsi . phase = PHASE_MSGOUT_EXPECT ;
case async :
dev - > period = info - > ifcfg . asyncperiod / 4 ;
dev - > sof = 0 ;
dev - > stp = info - > scsi . async_stp ;
fas216_set_sync ( info , info - > SCpnt - > device - > id ) ;
break ;
case none :
break ;
}
}
/**
* fas216_updateptrs - update data pointers after transfer suspended / paused
* @ info : interface ' s local pointer to update
* @ bytes_transferred : number of bytes transferred
*
* Update data pointers after transfer suspended / paused
*/
static void fas216_updateptrs ( FAS216_Info * info , int bytes_transferred )
{
2005-10-31 20:31:56 +03:00
struct scsi_pointer * SCp = & info - > scsi . SCp ;
2005-04-17 02:20:36 +04:00
fas216_checkmagic ( info ) ;
BUG_ON ( bytes_transferred < 0 ) ;
2007-03-04 23:19:07 +03:00
SCp - > phase - = bytes_transferred ;
2005-04-17 02:20:36 +04:00
while ( bytes_transferred ! = 0 ) {
if ( SCp - > this_residual > bytes_transferred )
break ;
/*
* We have used up this buffer . Move on to the
* next buffer .
*/
bytes_transferred - = SCp - > this_residual ;
if ( ! next_SCp ( SCp ) & & bytes_transferred ) {
printk ( KERN_WARNING " scsi%d.%c: out of buffers \n " ,
info - > host - > host_no , ' 0 ' + info - > SCpnt - > device - > id ) ;
return ;
}
}
SCp - > this_residual - = bytes_transferred ;
if ( SCp - > this_residual )
SCp - > ptr + = bytes_transferred ;
else
SCp - > ptr = NULL ;
}
/**
* fas216_pio - transfer data off of / on to card using programmed IO
* @ info : interface to transfer data to / from
* @ direction : direction to transfer data ( DMA_OUT / DMA_IN )
*
* Transfer data off of / on to card using programmed IO .
* Notes : this is incredibly slow .
*/
static void fas216_pio ( FAS216_Info * info , fasdmadir_t direction )
{
2005-10-31 20:31:56 +03:00
struct scsi_pointer * SCp = & info - > scsi . SCp ;
2005-04-17 02:20:36 +04:00
fas216_checkmagic ( info ) ;
if ( direction = = DMA_OUT )
fas216_writeb ( info , REG_FF , get_next_SCp_byte ( SCp ) ) ;
else
put_next_SCp_byte ( SCp , fas216_readb ( info , REG_FF ) ) ;
if ( SCp - > this_residual = = 0 )
next_SCp ( SCp ) ;
}
static void fas216_set_stc ( FAS216_Info * info , unsigned int length )
{
fas216_writeb ( info , REG_STCL , length ) ;
fas216_writeb ( info , REG_STCM , length > > 8 ) ;
fas216_writeb ( info , REG_STCH , length > > 16 ) ;
}
static unsigned int fas216_get_ctc ( FAS216_Info * info )
{
return fas216_readb ( info , REG_CTCL ) +
( fas216_readb ( info , REG_CTCM ) < < 8 ) +
( fas216_readb ( info , REG_CTCH ) < < 16 ) ;
}
/**
* fas216_cleanuptransfer - clean up after a transfer has completed .
* @ info : interface to clean up
*
* Update the data pointers according to the number of bytes transferred
* on the SCSI bus .
*/
static void fas216_cleanuptransfer ( FAS216_Info * info )
{
unsigned long total , residual , fifo ;
fasdmatype_t dmatype = info - > dma . transfer_type ;
info - > dma . transfer_type = fasdma_none ;
/*
* PIO transfers do not need to be cleaned up .
*/
if ( dmatype = = fasdma_pio | | dmatype = = fasdma_none )
return ;
if ( dmatype = = fasdma_real_all )
2007-03-04 23:19:07 +03:00
total = info - > scsi . SCp . phase ;
2005-04-17 02:20:36 +04:00
else
total = info - > scsi . SCp . this_residual ;
residual = fas216_get_ctc ( info ) ;
fifo = fas216_readb ( info , REG_CFIS ) & CFIS_CF ;
fas216_log ( info , LOG_BUFFER , " cleaning up from previous "
" transfer: length 0x%06x, residual 0x%x, fifo %d " ,
total , residual , fifo ) ;
/*
* If we were performing Data - Out , the transfer counter
* counts down each time a byte is transferred by the
* host to the FIFO . This means we must include the
* bytes left in the FIFO from the transfer counter .
*/
if ( info - > scsi . phase = = PHASE_DATAOUT )
residual + = fifo ;
fas216_updateptrs ( info , total - residual ) ;
}
/**
* fas216_transfer - Perform a DMA / PIO transfer off of / on to card
* @ info : interface from which device disconnected from
*
* Start a DMA / PIO transfer off of / on to card
*/
static void fas216_transfer ( FAS216_Info * info )
{
fasdmadir_t direction ;
fasdmatype_t dmatype ;
fas216_log ( info , LOG_BUFFER ,
" starttransfer: buffer %p length 0x%06x reqlen 0x%06x " ,
info - > scsi . SCp . ptr , info - > scsi . SCp . this_residual ,
2007-03-04 23:19:07 +03:00
info - > scsi . SCp . phase ) ;
2005-04-17 02:20:36 +04:00
if ( ! info - > scsi . SCp . ptr ) {
fas216_log ( info , LOG_ERROR , " null buffer passed to "
" fas216_starttransfer " ) ;
print_SCp ( & info - > scsi . SCp , " SCp: " , " \n " ) ;
print_SCp ( & info - > SCpnt - > SCp , " Cmnd SCp: " , " \n " ) ;
return ;
}
/*
* If we have a synchronous transfer agreement in effect , we must
* use DMA mode . If we are using asynchronous transfers , we may
* use DMA mode or PIO mode .
*/
if ( info - > device [ info - > SCpnt - > device - > id ] . sof )
dmatype = fasdma_real_all ;
else
dmatype = fasdma_pio ;
if ( info - > scsi . phase = = PHASE_DATAOUT )
direction = DMA_OUT ;
else
direction = DMA_IN ;
if ( info - > dma . setup )
dmatype = info - > dma . setup ( info - > host , & info - > scsi . SCp ,
direction , dmatype ) ;
info - > dma . transfer_type = dmatype ;
if ( dmatype = = fasdma_real_all )
2007-03-04 23:19:07 +03:00
fas216_set_stc ( info , info - > scsi . SCp . phase ) ;
2005-04-17 02:20:36 +04:00
else
fas216_set_stc ( info , info - > scsi . SCp . this_residual ) ;
switch ( dmatype ) {
case fasdma_pio :
fas216_log ( info , LOG_BUFFER , " PIO transfer " ) ;
fas216_writeb ( info , REG_SOF , 0 ) ;
fas216_writeb ( info , REG_STP , info - > scsi . async_stp ) ;
fas216_cmd ( info , CMD_TRANSFERINFO ) ;
fas216_pio ( info , direction ) ;
break ;
case fasdma_pseudo :
fas216_log ( info , LOG_BUFFER , " pseudo transfer " ) ;
fas216_cmd ( info , CMD_TRANSFERINFO | CMD_WITHDMA ) ;
info - > dma . pseudo ( info - > host , & info - > scsi . SCp ,
direction , info - > SCpnt - > transfersize ) ;
break ;
case fasdma_real_block :
fas216_log ( info , LOG_BUFFER , " block dma transfer " ) ;
fas216_cmd ( info , CMD_TRANSFERINFO | CMD_WITHDMA ) ;
break ;
case fasdma_real_all :
fas216_log ( info , LOG_BUFFER , " total dma transfer " ) ;
fas216_cmd ( info , CMD_TRANSFERINFO | CMD_WITHDMA ) ;
break ;
default :
fas216_log ( info , LOG_BUFFER | LOG_ERROR ,
" invalid FAS216 DMA type " ) ;
break ;
}
}
/**
* fas216_stoptransfer - Stop a DMA transfer onto / off of the card
* @ info : interface from which device disconnected from
*
* Called when we switch away from DATA IN or DATA OUT phases .
*/
static void fas216_stoptransfer ( FAS216_Info * info )
{
fas216_checkmagic ( info ) ;
if ( info - > dma . transfer_type = = fasdma_real_all | |
info - > dma . transfer_type = = fasdma_real_block )
info - > dma . stop ( info - > host , & info - > scsi . SCp ) ;
fas216_cleanuptransfer ( info ) ;
if ( info - > scsi . phase = = PHASE_DATAIN ) {
unsigned int fifo ;
/*
* If we were performing Data - In , then the FIFO counter
* contains the number of bytes not transferred via DMA
* from the on - board FIFO . Read them manually .
*/
fifo = fas216_readb ( info , REG_CFIS ) & CFIS_CF ;
while ( fifo & & info - > scsi . SCp . ptr ) {
* info - > scsi . SCp . ptr = fas216_readb ( info , REG_FF ) ;
fas216_updateptrs ( info , 1 ) ;
fifo - - ;
}
} else {
/*
* After a Data - Out phase , there may be unsent
* bytes left in the FIFO . Flush them out .
*/
fas216_cmd ( info , CMD_FLUSHFIFO ) ;
}
}
static void fas216_aborttransfer ( FAS216_Info * info )
{
fas216_checkmagic ( info ) ;
if ( info - > dma . transfer_type = = fasdma_real_all | |
info - > dma . transfer_type = = fasdma_real_block )
info - > dma . stop ( info - > host , & info - > scsi . SCp ) ;
info - > dma . transfer_type = fasdma_none ;
fas216_cmd ( info , CMD_FLUSHFIFO ) ;
}
static void fas216_kick ( FAS216_Info * info ) ;
/**
* fas216_disconnected_intr - handle device disconnection
* @ info : interface from which device disconnected from
*
* Handle device disconnection
*/
static void fas216_disconnect_intr ( FAS216_Info * info )
{
unsigned long flags ;
fas216_checkmagic ( info ) ;
fas216_log ( info , LOG_CONNECT , " disconnect phase=%02x " ,
info - > scsi . phase ) ;
msgqueue_flush ( & info - > scsi . msgs ) ;
switch ( info - > scsi . phase ) {
case PHASE_SELECTION : /* while selecting - no target */
case PHASE_SELSTEPS :
fas216_done ( info , DID_NO_CONNECT ) ;
break ;
case PHASE_MSGIN_DISCONNECT : /* message in - disconnecting */
info - > scsi . disconnectable = 1 ;
info - > scsi . phase = PHASE_IDLE ;
info - > stats . disconnects + = 1 ;
spin_lock_irqsave ( & info - > host_lock , flags ) ;
if ( info - > scsi . phase = = PHASE_IDLE )
fas216_kick ( info ) ;
spin_unlock_irqrestore ( & info - > host_lock , flags ) ;
break ;
case PHASE_DONE : /* at end of command - complete */
fas216_done ( info , DID_OK ) ;
break ;
case PHASE_MSGOUT : /* message out - possible ABORT message */
if ( fas216_get_last_msg ( info , info - > scsi . msgin_fifo ) = = ABORT ) {
info - > scsi . aborting = 0 ;
fas216_done ( info , DID_ABORT ) ;
break ;
}
default : /* huh? */
printk ( KERN_ERR " scsi%d.%c: unexpected disconnect in phase %s \n " ,
info - > host - > host_no , fas216_target ( info ) , fas216_drv_phase ( info ) ) ;
print_debug_list ( ) ;
fas216_stoptransfer ( info ) ;
fas216_done ( info , DID_ERROR ) ;
break ;
}
}
/**
* fas216_reselected_intr - start reconnection of a device
* @ info : interface which was reselected
*
* Start reconnection of a device
*/
static void
fas216_reselected_intr ( FAS216_Info * info )
{
unsigned int cfis , i ;
unsigned char msg [ 4 ] ;
unsigned char target , lun , tag ;
fas216_checkmagic ( info ) ;
WARN_ON ( info - > scsi . phase = = PHASE_SELECTION | |
info - > scsi . phase = = PHASE_SELSTEPS ) ;
cfis = fas216_readb ( info , REG_CFIS ) ;
fas216_log ( info , LOG_CONNECT , " reconnect phase=%02x cfis=%02x " ,
info - > scsi . phase , cfis ) ;
cfis & = CFIS_CF ;
if ( cfis < 2 | | cfis > 4 ) {
printk ( KERN_ERR " scsi%d.H: incorrect number of bytes after reselect \n " ,
info - > host - > host_no ) ;
goto bad_message ;
}
for ( i = 0 ; i < cfis ; i + + )
msg [ i ] = fas216_readb ( info , REG_FF ) ;
if ( ! ( msg [ 0 ] & ( 1 < < info - > host - > this_id ) ) | |
! ( msg [ 1 ] & 0x80 ) )
goto initiator_error ;
target = msg [ 0 ] & ~ ( 1 < < info - > host - > this_id ) ;
target = ffs ( target ) - 1 ;
lun = msg [ 1 ] & 7 ;
tag = 0 ;
if ( cfis > = 3 ) {
if ( msg [ 2 ] ! = SIMPLE_QUEUE_TAG )
goto initiator_error ;
tag = msg [ 3 ] ;
}
/* set up for synchronous transfers */
fas216_writeb ( info , REG_SDID , target ) ;
fas216_set_sync ( info , target ) ;
msgqueue_flush ( & info - > scsi . msgs ) ;
fas216_log ( info , LOG_CONNECT , " Reconnected: target %1x lun %1x tag %02x " ,
target , lun , tag ) ;
if ( info - > scsi . disconnectable & & info - > SCpnt ) {
info - > scsi . disconnectable = 0 ;
if ( info - > SCpnt - > device - > id = = target & &
info - > SCpnt - > device - > lun = = lun & &
info - > SCpnt - > tag = = tag ) {
fas216_log ( info , LOG_CONNECT , " reconnected previously executing command " ) ;
} else {
queue_add_cmd_tail ( & info - > queues . disconnected , info - > SCpnt ) ;
fas216_log ( info , LOG_CONNECT , " had to move command to disconnected queue " ) ;
info - > SCpnt = NULL ;
}
}
if ( ! info - > SCpnt ) {
info - > SCpnt = queue_remove_tgtluntag ( & info - > queues . disconnected ,
target , lun , tag ) ;
fas216_log ( info , LOG_CONNECT , " had to get command " ) ;
}
if ( info - > SCpnt ) {
/*
* Restore data pointer from SAVED data pointer
*/
info - > scsi . SCp = info - > SCpnt - > SCp ;
fas216_log ( info , LOG_CONNECT , " data pointers: [%p, %X] " ,
info - > scsi . SCp . ptr , info - > scsi . SCp . this_residual ) ;
info - > scsi . phase = PHASE_MSGIN ;
} else {
/*
* Our command structure not found - abort the
* command on the target . Since we have no
* record of this command , we can ' t send
* an INITIATOR DETECTED ERROR message .
*/
fas216_cmd ( info , CMD_SETATN ) ;
#if 0
if ( tag )
msgqueue_addmsg ( & info - > scsi . msgs , 2 , ABORT_TAG , tag ) ;
else
# endif
msgqueue_addmsg ( & info - > scsi . msgs , 1 , ABORT ) ;
info - > scsi . phase = PHASE_MSGOUT_EXPECT ;
info - > scsi . aborting = 1 ;
}
fas216_cmd ( info , CMD_MSGACCEPTED ) ;
return ;
initiator_error :
printk ( KERN_ERR " scsi%d.H: error during reselection: bytes " ,
info - > host - > host_no ) ;
for ( i = 0 ; i < cfis ; i + + )
printk ( " %02x " , msg [ i ] ) ;
printk ( " \n " ) ;
bad_message :
fas216_cmd ( info , CMD_SETATN ) ;
msgqueue_flush ( & info - > scsi . msgs ) ;
msgqueue_addmsg ( & info - > scsi . msgs , 1 , INITIATOR_ERROR ) ;
info - > scsi . phase = PHASE_MSGOUT_EXPECT ;
fas216_cmd ( info , CMD_MSGACCEPTED ) ;
}
static void fas216_parse_message ( FAS216_Info * info , unsigned char * message , int msglen )
{
int i ;
switch ( message [ 0 ] ) {
case COMMAND_COMPLETE :
if ( msglen ! = 1 )
goto unrecognised ;
printk ( KERN_ERR " scsi%d.%c: command complete with no "
" status in MESSAGE_IN? \n " ,
info - > host - > host_no , fas216_target ( info ) ) ;
break ;
case SAVE_POINTERS :
if ( msglen ! = 1 )
goto unrecognised ;
/*
* Save current data pointer to SAVED data pointer
* SCSI II standard says that we must not acknowledge
* this until we have really saved pointers .
* NOTE : we DO NOT save the command nor status pointers
* as required by the SCSI II standard . These always
* point to the start of their respective areas .
*/
info - > SCpnt - > SCp = info - > scsi . SCp ;
info - > SCpnt - > SCp . sent_command = 0 ;
fas216_log ( info , LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER ,
" save data pointers: [%p, %X] " ,
info - > scsi . SCp . ptr , info - > scsi . SCp . this_residual ) ;
break ;
case RESTORE_POINTERS :
if ( msglen ! = 1 )
goto unrecognised ;
/*
* Restore current data pointer from SAVED data pointer
*/
info - > scsi . SCp = info - > SCpnt - > SCp ;
fas216_log ( info , LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER ,
" restore data pointers: [%p, 0x%x] " ,
info - > scsi . SCp . ptr , info - > scsi . SCp . this_residual ) ;
break ;
case DISCONNECT :
if ( msglen ! = 1 )
goto unrecognised ;
info - > scsi . phase = PHASE_MSGIN_DISCONNECT ;
break ;
case MESSAGE_REJECT :
if ( msglen ! = 1 )
goto unrecognised ;
switch ( fas216_get_last_msg ( info , info - > scsi . msgin_fifo ) ) {
case EXTENDED_MESSAGE | EXTENDED_SDTR < < 8 :
fas216_handlesync ( info , message ) ;
break ;
default :
fas216_log ( info , 0 , " reject, last message 0x%04x " ,
fas216_get_last_msg ( info , info - > scsi . msgin_fifo ) ) ;
}
break ;
case NOP :
break ;
case EXTENDED_MESSAGE :
if ( msglen < 3 )
goto unrecognised ;
switch ( message [ 2 ] ) {
case EXTENDED_SDTR : /* Sync transfer negotiation request/reply */
fas216_handlesync ( info , message ) ;
break ;
default :
goto unrecognised ;
}
break ;
default :
goto unrecognised ;
}
return ;
unrecognised :
fas216_log ( info , 0 , " unrecognised message, rejecting " ) ;
printk ( " scsi%d.%c: message was " , info - > host - > host_no , fas216_target ( info ) ) ;
for ( i = 0 ; i < msglen ; i + + )
printk ( " %s%02X " , i & 31 ? " " : " \n " , message [ i ] ) ;
printk ( " \n " ) ;
/*
* Something strange seems to be happening here -
* I can ' t use SETATN since the chip gives me an
* invalid command interrupt when I do . Weird .
*/
fas216_cmd ( info , CMD_NOP ) ;
fas216_dumpstate ( info ) ;
fas216_cmd ( info , CMD_SETATN ) ;
msgqueue_flush ( & info - > scsi . msgs ) ;
msgqueue_addmsg ( & info - > scsi . msgs , 1 , MESSAGE_REJECT ) ;
info - > scsi . phase = PHASE_MSGOUT_EXPECT ;
fas216_dumpstate ( info ) ;
}
static int fas216_wait_cmd ( FAS216_Info * info , int cmd )
{
int tout ;
int stat ;
fas216_cmd ( info , cmd ) ;
for ( tout = 1000 ; tout ; tout - = 1 ) {
stat = fas216_readb ( info , REG_STAT ) ;
if ( stat & ( STAT_INT | STAT_PARITYERROR ) )
break ;
udelay ( 1 ) ;
}
return stat ;
}
static int fas216_get_msg_byte ( FAS216_Info * info )
{
unsigned int stat = fas216_wait_cmd ( info , CMD_MSGACCEPTED ) ;
if ( ( stat & STAT_INT ) = = 0 )
goto timedout ;
if ( ( stat & STAT_BUSMASK ) ! = STAT_MESGIN )
goto unexpected_phase_change ;
fas216_readb ( info , REG_INST ) ;
stat = fas216_wait_cmd ( info , CMD_TRANSFERINFO ) ;
if ( ( stat & STAT_INT ) = = 0 )
goto timedout ;
if ( stat & STAT_PARITYERROR )
goto parity_error ;
if ( ( stat & STAT_BUSMASK ) ! = STAT_MESGIN )
goto unexpected_phase_change ;
fas216_readb ( info , REG_INST ) ;
return fas216_readb ( info , REG_FF ) ;
timedout :
fas216_log ( info , LOG_ERROR , " timed out waiting for message byte " ) ;
return - 1 ;
unexpected_phase_change :
fas216_log ( info , LOG_ERROR , " unexpected phase change: status = %02x " , stat ) ;
return - 2 ;
parity_error :
fas216_log ( info , LOG_ERROR , " parity error during message in phase " ) ;
return - 3 ;
}
/**
* fas216_message - handle a function done interrupt from FAS216 chip
* @ info : interface which caused function done interrupt
*
* Handle a function done interrupt from FAS216 chip
*/
static void fas216_message ( FAS216_Info * info )
{
unsigned char * message = info - > scsi . message ;
unsigned int msglen = 1 ;
int msgbyte = 0 ;
fas216_checkmagic ( info ) ;
message [ 0 ] = fas216_readb ( info , REG_FF ) ;
if ( message [ 0 ] = = EXTENDED_MESSAGE ) {
msgbyte = fas216_get_msg_byte ( info ) ;
if ( msgbyte > = 0 ) {
message [ 1 ] = msgbyte ;
for ( msglen = 2 ; msglen < message [ 1 ] + 2 ; msglen + + ) {
msgbyte = fas216_get_msg_byte ( info ) ;
if ( msgbyte > = 0 )
message [ msglen ] = msgbyte ;
else
break ;
}
}
}
if ( msgbyte = = - 3 )
goto parity_error ;
# ifdef DEBUG_MESSAGES
{
int i ;
printk ( " scsi%d.%c: message in: " ,
info - > host - > host_no , fas216_target ( info ) ) ;
for ( i = 0 ; i < msglen ; i + + )
printk ( " %02X " , message [ i ] ) ;
printk ( " \n " ) ;
}
# endif
fas216_parse_message ( info , message , msglen ) ;
fas216_cmd ( info , CMD_MSGACCEPTED ) ;
return ;
parity_error :
fas216_cmd ( info , CMD_SETATN ) ;
msgqueue_flush ( & info - > scsi . msgs ) ;
msgqueue_addmsg ( & info - > scsi . msgs , 1 , MSG_PARITY_ERROR ) ;
info - > scsi . phase = PHASE_MSGOUT_EXPECT ;
fas216_cmd ( info , CMD_MSGACCEPTED ) ;
return ;
}
/**
* fas216_send_command - send command after all message bytes have been sent
* @ info : interface which caused bus service
*
* Send a command to a target after all message bytes have been sent
*/
static void fas216_send_command ( FAS216_Info * info )
{
int i ;
fas216_checkmagic ( info ) ;
fas216_cmd ( info , CMD_NOP | CMD_WITHDMA ) ;
fas216_cmd ( info , CMD_FLUSHFIFO ) ;
/* load command */
for ( i = info - > scsi . SCp . sent_command ; i < info - > SCpnt - > cmd_len ; i + + )
fas216_writeb ( info , REG_FF , info - > SCpnt - > cmnd [ i ] ) ;
fas216_cmd ( info , CMD_TRANSFERINFO ) ;
info - > scsi . phase = PHASE_COMMAND ;
}
/**
* fas216_send_messageout - handle bus service to send a message
* @ info : interface which caused bus service
*
* Handle bus service to send a message .
* Note : We do not allow the device to change the data direction !
*/
static void fas216_send_messageout ( FAS216_Info * info , int start )
{
unsigned int tot_msglen = msgqueue_msglength ( & info - > scsi . msgs ) ;
fas216_checkmagic ( info ) ;
fas216_cmd ( info , CMD_FLUSHFIFO ) ;
if ( tot_msglen ) {
struct message * msg ;
int msgnr = 0 ;
while ( ( msg = msgqueue_getmsg ( & info - > scsi . msgs , msgnr + + ) ) ! = NULL ) {
int i ;
for ( i = start ; i < msg - > length ; i + + )
fas216_writeb ( info , REG_FF , msg - > msg [ i ] ) ;
msg - > fifo = tot_msglen - ( fas216_readb ( info , REG_CFIS ) & CFIS_CF ) ;
start = 0 ;
}
} else
fas216_writeb ( info , REG_FF , NOP ) ;
fas216_cmd ( info , CMD_TRANSFERINFO ) ;
info - > scsi . phase = PHASE_MSGOUT ;
}
/**
* fas216_busservice_intr - handle bus service interrupt from FAS216 chip
* @ info : interface which caused bus service interrupt
* @ stat : Status register contents
* @ is : SCSI Status register contents
*
* Handle a bus service interrupt from FAS216 chip
*/
static void fas216_busservice_intr ( FAS216_Info * info , unsigned int stat , unsigned int is )
{
fas216_checkmagic ( info ) ;
fas216_log ( info , LOG_BUSSERVICE ,
" bus service: stat=%02x is=%02x phase=%02x " ,
stat , is , info - > scsi . phase ) ;
switch ( info - > scsi . phase ) {
case PHASE_SELECTION :
if ( ( is & IS_BITS ) ! = IS_MSGBYTESENT )
goto bad_is ;
break ;
case PHASE_SELSTEPS :
switch ( is & IS_BITS ) {
case IS_SELARB :
case IS_MSGBYTESENT :
goto bad_is ;
case IS_NOTCOMMAND :
case IS_EARLYPHASE :
if ( ( stat & STAT_BUSMASK ) = = STAT_MESGIN )
break ;
goto bad_is ;
case IS_COMPLETE :
break ;
}
default :
break ;
}
fas216_cmd ( info , CMD_NOP ) ;
# define STATE(st,ph) ((ph) << 3 | (st))
/* This table describes the legal SCSI state transitions,
* as described by the SCSI II spec .
*/
switch ( STATE ( stat & STAT_BUSMASK , info - > scsi . phase ) ) {
case STATE ( STAT_DATAIN , PHASE_SELSTEPS ) : /* Sel w/ steps -> Data In */
case STATE ( STAT_DATAIN , PHASE_MSGOUT ) : /* Message Out -> Data In */
case STATE ( STAT_DATAIN , PHASE_COMMAND ) : /* Command -> Data In */
case STATE ( STAT_DATAIN , PHASE_MSGIN ) : /* Message In -> Data In */
info - > scsi . phase = PHASE_DATAIN ;
fas216_transfer ( info ) ;
return ;
case STATE ( STAT_DATAIN , PHASE_DATAIN ) : /* Data In -> Data In */
case STATE ( STAT_DATAOUT , PHASE_DATAOUT ) : /* Data Out -> Data Out */
fas216_cleanuptransfer ( info ) ;
fas216_transfer ( info ) ;
return ;
case STATE ( STAT_DATAOUT , PHASE_SELSTEPS ) : /* Sel w/ steps-> Data Out */
case STATE ( STAT_DATAOUT , PHASE_MSGOUT ) : /* Message Out -> Data Out */
case STATE ( STAT_DATAOUT , PHASE_COMMAND ) : /* Command -> Data Out */
case STATE ( STAT_DATAOUT , PHASE_MSGIN ) : /* Message In -> Data Out */
fas216_cmd ( info , CMD_FLUSHFIFO ) ;
info - > scsi . phase = PHASE_DATAOUT ;
fas216_transfer ( info ) ;
return ;
case STATE ( STAT_STATUS , PHASE_DATAOUT ) : /* Data Out -> Status */
case STATE ( STAT_STATUS , PHASE_DATAIN ) : /* Data In -> Status */
fas216_stoptransfer ( info ) ;
case STATE ( STAT_STATUS , PHASE_SELSTEPS ) : /* Sel w/ steps -> Status */
case STATE ( STAT_STATUS , PHASE_MSGOUT ) : /* Message Out -> Status */
case STATE ( STAT_STATUS , PHASE_COMMAND ) : /* Command -> Status */
case STATE ( STAT_STATUS , PHASE_MSGIN ) : /* Message In -> Status */
fas216_cmd ( info , CMD_INITCMDCOMPLETE ) ;
info - > scsi . phase = PHASE_STATUS ;
return ;
case STATE ( STAT_MESGIN , PHASE_DATAOUT ) : /* Data Out -> Message In */
case STATE ( STAT_MESGIN , PHASE_DATAIN ) : /* Data In -> Message In */
fas216_stoptransfer ( info ) ;
case STATE ( STAT_MESGIN , PHASE_COMMAND ) : /* Command -> Message In */
case STATE ( STAT_MESGIN , PHASE_SELSTEPS ) : /* Sel w/ steps -> Message In */
case STATE ( STAT_MESGIN , PHASE_MSGOUT ) : /* Message Out -> Message In */
info - > scsi . msgin_fifo = fas216_readb ( info , REG_CFIS ) & CFIS_CF ;
fas216_cmd ( info , CMD_FLUSHFIFO ) ;
fas216_cmd ( info , CMD_TRANSFERINFO ) ;
info - > scsi . phase = PHASE_MSGIN ;
return ;
case STATE ( STAT_MESGIN , PHASE_MSGIN ) :
info - > scsi . msgin_fifo = fas216_readb ( info , REG_CFIS ) & CFIS_CF ;
fas216_cmd ( info , CMD_TRANSFERINFO ) ;
return ;
case STATE ( STAT_COMMAND , PHASE_MSGOUT ) : /* Message Out -> Command */
case STATE ( STAT_COMMAND , PHASE_MSGIN ) : /* Message In -> Command */
fas216_send_command ( info ) ;
info - > scsi . phase = PHASE_COMMAND ;
return ;
/*
* Selection - > Message Out
*/
case STATE ( STAT_MESGOUT , PHASE_SELECTION ) :
fas216_send_messageout ( info , 1 ) ;
return ;
/*
* Message Out - > Message Out
*/
case STATE ( STAT_MESGOUT , PHASE_SELSTEPS ) :
case STATE ( STAT_MESGOUT , PHASE_MSGOUT ) :
/*
* If we get another message out phase , this usually
* means some parity error occurred . Resend complete
* set of messages . If we have more than one byte to
* send , we need to assert ATN again .
*/
if ( info - > device [ info - > SCpnt - > device - > id ] . parity_check ) {
/*
* We were testing . . . good , the device
* supports parity checking .
*/
info - > device [ info - > SCpnt - > device - > id ] . parity_check = 0 ;
info - > device [ info - > SCpnt - > device - > id ] . parity_enabled = 1 ;
fas216_writeb ( info , REG_CNTL1 , info - > scsi . cfg [ 0 ] ) ;
}
if ( msgqueue_msglength ( & info - > scsi . msgs ) > 1 )
fas216_cmd ( info , CMD_SETATN ) ;
/*FALLTHROUGH*/
/*
* Any - > Message Out
*/
case STATE ( STAT_MESGOUT , PHASE_MSGOUT_EXPECT ) :
fas216_send_messageout ( info , 0 ) ;
return ;
/* Error recovery rules.
* These either attempt to abort or retry the operation .
* TODO : we need more of these
*/
case STATE ( STAT_COMMAND , PHASE_COMMAND ) : /* Command -> Command */
/* error - we've sent out all the command bytes
* we have .
* NOTE : we need SAVE DATA POINTERS / RESTORE DATA POINTERS
* to include the command bytes sent for this to work
* correctly .
*/
printk ( KERN_ERR " scsi%d.%c: "
" target trying to receive more command bytes \n " ,
info - > host - > host_no , fas216_target ( info ) ) ;
fas216_cmd ( info , CMD_SETATN ) ;
fas216_set_stc ( info , 15 ) ;
fas216_cmd ( info , CMD_PADBYTES | CMD_WITHDMA ) ;
msgqueue_flush ( & info - > scsi . msgs ) ;
msgqueue_addmsg ( & info - > scsi . msgs , 1 , INITIATOR_ERROR ) ;
info - > scsi . phase = PHASE_MSGOUT_EXPECT ;
return ;
}
if ( info - > scsi . phase = = PHASE_MSGIN_DISCONNECT ) {
printk ( KERN_ERR " scsi%d.%c: disconnect message received, but bus service %s? \n " ,
info - > host - > host_no , fas216_target ( info ) ,
fas216_bus_phase ( stat ) ) ;
msgqueue_flush ( & info - > scsi . msgs ) ;
fas216_cmd ( info , CMD_SETATN ) ;
msgqueue_addmsg ( & info - > scsi . msgs , 1 , INITIATOR_ERROR ) ;
info - > scsi . phase = PHASE_MSGOUT_EXPECT ;
info - > scsi . aborting = 1 ;
fas216_cmd ( info , CMD_TRANSFERINFO ) ;
return ;
}
printk ( KERN_ERR " scsi%d.%c: bus phase %s after %s? \n " ,
info - > host - > host_no , fas216_target ( info ) ,
fas216_bus_phase ( stat ) ,
fas216_drv_phase ( info ) ) ;
print_debug_list ( ) ;
return ;
bad_is :
fas216_log ( info , 0 , " bus service at step %d? " , is & IS_BITS ) ;
fas216_dumpstate ( info ) ;
print_debug_list ( ) ;
fas216_done ( info , DID_ERROR ) ;
}
/**
* fas216_funcdone_intr - handle a function done interrupt from FAS216 chip
* @ info : interface which caused function done interrupt
* @ stat : Status register contents
* @ is : SCSI Status register contents
*
* Handle a function done interrupt from FAS216 chip
*/
static void fas216_funcdone_intr ( FAS216_Info * info , unsigned int stat , unsigned int is )
{
unsigned int fifo_len = fas216_readb ( info , REG_CFIS ) & CFIS_CF ;
fas216_checkmagic ( info ) ;
fas216_log ( info , LOG_FUNCTIONDONE ,
" function done: stat=%02x is=%02x phase=%02x " ,
stat , is , info - > scsi . phase ) ;
switch ( info - > scsi . phase ) {
case PHASE_STATUS : /* status phase - read status and msg */
if ( fifo_len ! = 2 ) {
fas216_log ( info , 0 , " odd number of bytes in FIFO: %d " , fifo_len ) ;
}
/*
* Read status then message byte .
*/
info - > scsi . SCp . Status = fas216_readb ( info , REG_FF ) ;
info - > scsi . SCp . Message = fas216_readb ( info , REG_FF ) ;
info - > scsi . phase = PHASE_DONE ;
fas216_cmd ( info , CMD_MSGACCEPTED ) ;
break ;
case PHASE_IDLE :
case PHASE_SELECTION :
case PHASE_SELSTEPS :
break ;
case PHASE_MSGIN : /* message in phase */
if ( ( stat & STAT_BUSMASK ) = = STAT_MESGIN ) {
info - > scsi . msgin_fifo = fifo_len ;
fas216_message ( info ) ;
break ;
}
default :
fas216_log ( info , 0 , " internal phase %s for function done? "
" What do I do with this? " ,
fas216_target ( info ) , fas216_drv_phase ( info ) ) ;
}
}
static void fas216_bus_reset ( FAS216_Info * info )
{
neg_t sync_state ;
int i ;
msgqueue_flush ( & info - > scsi . msgs ) ;
sync_state = neg_invalid ;
# ifdef SCSI2_SYNC
if ( info - > ifcfg . capabilities & ( FASCAP_DMA | FASCAP_PSEUDODMA ) )
sync_state = neg_wait ;
# endif
info - > scsi . phase = PHASE_IDLE ;
info - > SCpnt = NULL ; /* bug! */
memset ( & info - > scsi . SCp , 0 , sizeof ( info - > scsi . SCp ) ) ;
for ( i = 0 ; i < 8 ; i + + ) {
info - > device [ i ] . disconnect_ok = info - > ifcfg . disconnect_ok ;
info - > device [ i ] . sync_state = sync_state ;
info - > device [ i ] . period = info - > ifcfg . asyncperiod / 4 ;
info - > device [ i ] . stp = info - > scsi . async_stp ;
info - > device [ i ] . sof = 0 ;
info - > device [ i ] . wide_xfer = 0 ;
}
info - > rst_bus_status = 1 ;
wake_up ( & info - > eh_wait ) ;
}
/**
* fas216_intr - handle interrupts to progress a command
* @ info : interface to service
*
* Handle interrupts from the interface to progress a command
*/
irqreturn_t fas216_intr ( FAS216_Info * info )
{
unsigned char inst , is , stat ;
int handled = IRQ_NONE ;
fas216_checkmagic ( info ) ;
stat = fas216_readb ( info , REG_STAT ) ;
is = fas216_readb ( info , REG_IS ) ;
inst = fas216_readb ( info , REG_INST ) ;
add_debug_list ( stat , is , inst , info - > scsi . phase ) ;
if ( stat & STAT_INT ) {
if ( inst & INST_BUSRESET ) {
fas216_log ( info , 0 , " bus reset detected " ) ;
fas216_bus_reset ( info ) ;
scsi_report_bus_reset ( info - > host , 0 ) ;
} else if ( inst & INST_ILLEGALCMD ) {
fas216_log ( info , LOG_ERROR , " illegal command given \n " ) ;
fas216_dumpstate ( info ) ;
print_debug_list ( ) ;
} else if ( inst & INST_DISCONNECT )
fas216_disconnect_intr ( info ) ;
else if ( inst & INST_RESELECTED ) /* reselected */
fas216_reselected_intr ( info ) ;
else if ( inst & INST_BUSSERVICE ) /* bus service request */
fas216_busservice_intr ( info , stat , is ) ;
else if ( inst & INST_FUNCDONE ) /* function done */
fas216_funcdone_intr ( info , stat , is ) ;
else
fas216_log ( info , 0 , " unknown interrupt received: "
" phase %s inst %02X is %02X stat %02X " ,
fas216_drv_phase ( info ) , inst , is , stat ) ;
handled = IRQ_HANDLED ;
}
return handled ;
}
2006-10-01 15:18:37 +04:00
static void __fas216_start_command ( FAS216_Info * info , struct scsi_cmnd * SCpnt )
2005-04-17 02:20:36 +04:00
{
int tot_msglen ;
/* following what the ESP driver says */
fas216_set_stc ( info , 0 ) ;
fas216_cmd ( info , CMD_NOP | CMD_WITHDMA ) ;
/* flush FIFO */
fas216_cmd ( info , CMD_FLUSHFIFO ) ;
/* load bus-id and timeout */
fas216_writeb ( info , REG_SDID , BUSID ( SCpnt - > device - > id ) ) ;
fas216_writeb ( info , REG_STIM , info - > ifcfg . select_timeout ) ;
/* synchronous transfers */
fas216_set_sync ( info , SCpnt - > device - > id ) ;
tot_msglen = msgqueue_msglength ( & info - > scsi . msgs ) ;
# ifdef DEBUG_MESSAGES
{
struct message * msg ;
int msgnr = 0 , i ;
printk ( " scsi%d.%c: message out: " ,
info - > host - > host_no , ' 0 ' + SCpnt - > device - > id ) ;
while ( ( msg = msgqueue_getmsg ( & info - > scsi . msgs , msgnr + + ) ) ! = NULL ) {
printk ( " { " ) ;
for ( i = 0 ; i < msg - > length ; i + + )
printk ( " %02x " , msg - > msg [ i ] ) ;
printk ( " } " ) ;
}
printk ( " \n " ) ;
}
# endif
if ( tot_msglen = = 1 | | tot_msglen = = 3 ) {
/*
* We have an easy message length to send . . .
*/
struct message * msg ;
int msgnr = 0 , i ;
info - > scsi . phase = PHASE_SELSTEPS ;
/* load message bytes */
while ( ( msg = msgqueue_getmsg ( & info - > scsi . msgs , msgnr + + ) ) ! = NULL ) {
for ( i = 0 ; i < msg - > length ; i + + )
fas216_writeb ( info , REG_FF , msg - > msg [ i ] ) ;
msg - > fifo = tot_msglen - ( fas216_readb ( info , REG_CFIS ) & CFIS_CF ) ;
}
/* load command */
for ( i = 0 ; i < SCpnt - > cmd_len ; i + + )
fas216_writeb ( info , REG_FF , SCpnt - > cmnd [ i ] ) ;
if ( tot_msglen = = 1 )
fas216_cmd ( info , CMD_SELECTATN ) ;
else
fas216_cmd ( info , CMD_SELECTATN3 ) ;
} else {
/*
* We have an unusual number of message bytes to send .
* Load first byte into fifo , and issue SELECT with ATN and
* stop steps .
*/
struct message * msg = msgqueue_getmsg ( & info - > scsi . msgs , 0 ) ;
fas216_writeb ( info , REG_FF , msg - > msg [ 0 ] ) ;
msg - > fifo = 1 ;
fas216_cmd ( info , CMD_SELECTATNSTOP ) ;
}
}
/*
* Decide whether we need to perform a parity test on this device .
* Can also be used to force parity error conditions during initial
* information transfer phase ( message out ) for test purposes .
*/
static int parity_test ( FAS216_Info * info , int target )
{
#if 0
if ( target = = 3 ) {
info - > device [ target ] . parity_check = 0 ;
return 1 ;
}
# endif
return info - > device [ target ] . parity_check ;
}
2006-10-01 15:18:37 +04:00
static void fas216_start_command ( FAS216_Info * info , struct scsi_cmnd * SCpnt )
2005-04-17 02:20:36 +04:00
{
int disconnect_ok ;
/*
* claim host busy
*/
info - > scsi . phase = PHASE_SELECTION ;
info - > scsi . SCp = SCpnt - > SCp ;
info - > SCpnt = SCpnt ;
info - > dma . transfer_type = fasdma_none ;
if ( parity_test ( info , SCpnt - > device - > id ) )
fas216_writeb ( info , REG_CNTL1 , info - > scsi . cfg [ 0 ] | CNTL1_PTE ) ;
else
fas216_writeb ( info , REG_CNTL1 , info - > scsi . cfg [ 0 ] ) ;
/*
* Don ' t allow request sense commands to disconnect .
*/
disconnect_ok = SCpnt - > cmnd [ 0 ] ! = REQUEST_SENSE & &
info - > device [ SCpnt - > device - > id ] . disconnect_ok ;
/*
* build outgoing message bytes
*/
msgqueue_flush ( & info - > scsi . msgs ) ;
msgqueue_addmsg ( & info - > scsi . msgs , 1 , IDENTIFY ( disconnect_ok , SCpnt - > device - > lun ) ) ;
/*
* add tag message if required
*/
if ( SCpnt - > tag )
msgqueue_addmsg ( & info - > scsi . msgs , 2 , SIMPLE_QUEUE_TAG , SCpnt - > tag ) ;
do {
# ifdef SCSI2_SYNC
if ( ( info - > device [ SCpnt - > device - > id ] . sync_state = = neg_wait | |
info - > device [ SCpnt - > device - > id ] . sync_state = = neg_complete ) & &
( SCpnt - > cmnd [ 0 ] = = REQUEST_SENSE | |
SCpnt - > cmnd [ 0 ] = = INQUIRY ) ) {
info - > device [ SCpnt - > device - > id ] . sync_state = neg_inprogress ;
msgqueue_addmsg ( & info - > scsi . msgs , 5 ,
EXTENDED_MESSAGE , 3 , EXTENDED_SDTR ,
1000 / info - > ifcfg . clockrate ,
info - > ifcfg . sync_max_depth ) ;
break ;
}
# endif
} while ( 0 ) ;
__fas216_start_command ( info , SCpnt ) ;
}
2006-10-01 15:18:37 +04:00
static void fas216_allocate_tag ( FAS216_Info * info , struct scsi_cmnd * SCpnt )
2005-04-17 02:20:36 +04:00
{
# ifdef SCSI2_TAG
/*
* tagged queuing - allocate a new tag to this command
*/
if ( SCpnt - > device - > simple_tags & & SCpnt - > cmnd [ 0 ] ! = REQUEST_SENSE & &
SCpnt - > cmnd [ 0 ] ! = INQUIRY ) {
SCpnt - > device - > current_tag + = 1 ;
if ( SCpnt - > device - > current_tag = = 0 )
SCpnt - > device - > current_tag = 1 ;
SCpnt - > tag = SCpnt - > device - > current_tag ;
} else
# endif
set_bit ( SCpnt - > device - > id * 8 + SCpnt - > device - > lun , info - > busyluns ) ;
info - > stats . removes + = 1 ;
switch ( SCpnt - > cmnd [ 0 ] ) {
case WRITE_6 :
case WRITE_10 :
case WRITE_12 :
info - > stats . writes + = 1 ;
break ;
case READ_6 :
case READ_10 :
case READ_12 :
info - > stats . reads + = 1 ;
break ;
default :
info - > stats . miscs + = 1 ;
break ;
}
}
2006-10-01 15:18:37 +04:00
static void fas216_do_bus_device_reset ( FAS216_Info * info ,
struct scsi_cmnd * SCpnt )
2005-04-17 02:20:36 +04:00
{
struct message * msg ;
/*
* claim host busy
*/
info - > scsi . phase = PHASE_SELECTION ;
info - > scsi . SCp = SCpnt - > SCp ;
info - > SCpnt = SCpnt ;
info - > dma . transfer_type = fasdma_none ;
fas216_log ( info , LOG_ERROR , " sending bus device reset " ) ;
msgqueue_flush ( & info - > scsi . msgs ) ;
msgqueue_addmsg ( & info - > scsi . msgs , 1 , BUS_DEVICE_RESET ) ;
/* following what the ESP driver says */
fas216_set_stc ( info , 0 ) ;
fas216_cmd ( info , CMD_NOP | CMD_WITHDMA ) ;
/* flush FIFO */
fas216_cmd ( info , CMD_FLUSHFIFO ) ;
/* load bus-id and timeout */
fas216_writeb ( info , REG_SDID , BUSID ( SCpnt - > device - > id ) ) ;
fas216_writeb ( info , REG_STIM , info - > ifcfg . select_timeout ) ;
/* synchronous transfers */
fas216_set_sync ( info , SCpnt - > device - > id ) ;
msg = msgqueue_getmsg ( & info - > scsi . msgs , 0 ) ;
fas216_writeb ( info , REG_FF , BUS_DEVICE_RESET ) ;
msg - > fifo = 1 ;
fas216_cmd ( info , CMD_SELECTATNSTOP ) ;
}
/**
* fas216_kick - kick a command to the interface
* @ info : our host interface to kick
*
* Kick a command to the interface , interface should be idle .
* Notes : Interrupts are always disabled !
*/
static void fas216_kick ( FAS216_Info * info )
{
2006-10-01 15:18:37 +04:00
struct scsi_cmnd * SCpnt = NULL ;
2005-04-17 02:20:36 +04:00
# define TYPE_OTHER 0
# define TYPE_RESET 1
# define TYPE_QUEUE 2
int where_from = TYPE_OTHER ;
fas216_checkmagic ( info ) ;
/*
* Obtain the next command to process .
*/
do {
if ( info - > rstSCpnt ) {
SCpnt = info - > rstSCpnt ;
/* don't remove it */
where_from = TYPE_RESET ;
break ;
}
if ( info - > reqSCpnt ) {
SCpnt = info - > reqSCpnt ;
info - > reqSCpnt = NULL ;
break ;
}
if ( info - > origSCpnt ) {
SCpnt = info - > origSCpnt ;
info - > origSCpnt = NULL ;
break ;
}
/* retrieve next command */
if ( ! SCpnt ) {
SCpnt = queue_remove_exclude ( & info - > queues . issue ,
info - > busyluns ) ;
where_from = TYPE_QUEUE ;
break ;
}
} while ( 0 ) ;
if ( ! SCpnt ) {
/*
* no command pending , so enable reselection .
*/
fas216_cmd ( info , CMD_ENABLESEL ) ;
return ;
}
/*
* We ' re going to start a command , so disable reselection
*/
fas216_cmd ( info , CMD_DISABLESEL ) ;
if ( info - > scsi . disconnectable & & info - > SCpnt ) {
fas216_log ( info , LOG_CONNECT ,
" moved command for %d to disconnected queue " ,
info - > SCpnt - > device - > id ) ;
queue_add_cmd_tail ( & info - > queues . disconnected , info - > SCpnt ) ;
info - > scsi . disconnectable = 0 ;
info - > SCpnt = NULL ;
}
fas216_log_command ( info , LOG_CONNECT | LOG_MESSAGES , SCpnt ,
" starting " ) ;
switch ( where_from ) {
case TYPE_QUEUE :
fas216_allocate_tag ( info , SCpnt ) ;
case TYPE_OTHER :
fas216_start_command ( info , SCpnt ) ;
break ;
case TYPE_RESET :
fas216_do_bus_device_reset ( info , SCpnt ) ;
break ;
}
fas216_log ( info , LOG_CONNECT , " select: data pointers [%p, %X] " ,
info - > scsi . SCp . ptr , info - > scsi . SCp . this_residual ) ;
/*
* should now get either DISCONNECT or
* ( FUNCTION DONE with BUS SERVICE ) interrupt
*/
}
/*
* Clean up from issuing a BUS DEVICE RESET message to a device .
*/
2006-10-01 15:18:37 +04:00
static void fas216_devicereset_done ( FAS216_Info * info , struct scsi_cmnd * SCpnt ,
unsigned int result )
2005-04-17 02:20:36 +04:00
{
fas216_log ( info , LOG_ERROR , " fas216 device reset complete " ) ;
info - > rstSCpnt = NULL ;
info - > rst_dev_status = 1 ;
wake_up ( & info - > eh_wait ) ;
}
/**
* fas216_rq_sns_done - Finish processing automatic request sense command
* @ info : interface that completed
* @ SCpnt : command that completed
* @ result : driver byte of result
*
* Finish processing automatic request sense command
*/
2006-10-01 15:18:37 +04:00
static void fas216_rq_sns_done ( FAS216_Info * info , struct scsi_cmnd * SCpnt ,
unsigned int result )
2005-04-17 02:20:36 +04:00
{
fas216_log_target ( info , LOG_CONNECT , SCpnt - > device - > id ,
" request sense complete, result=0x%04x%02x%02x " ,
result , SCpnt - > SCp . Message , SCpnt - > SCp . Status ) ;
if ( result ! = DID_OK | | SCpnt - > SCp . Status ! = GOOD )
/*
* Something went wrong . Make sure that we don ' t
* have valid data in the sense buffer that could
* confuse the higher levels .
*/
memset ( SCpnt - > sense_buffer , 0 , sizeof ( SCpnt - > sense_buffer ) ) ;
//printk("scsi%d.%c: sense buffer: ", info->host->host_no, '0' + SCpnt->device->id);
//{ int i; for (i = 0; i < 32; i++) printk("%02x ", SCpnt->sense_buffer[i]); printk("\n"); }
/*
* Note that we don ' t set SCpnt - > result , since that should
* reflect the status of the command that we were asked by
* the upper layers to process . This would have been set
* correctly by fas216_std_done .
*/
2007-09-10 23:39:11 +04:00
scsi_eh_restore_cmnd ( SCpnt , & info - > ses ) ;
2005-04-17 02:20:36 +04:00
SCpnt - > scsi_done ( SCpnt ) ;
}
/**
* fas216_std_done - finish processing of standard command
* @ info : interface that completed
* @ SCpnt : command that completed
* @ result : driver byte of result
*
* Finish processing of standard command
*/
static void
2006-10-01 15:18:37 +04:00
fas216_std_done ( FAS216_Info * info , struct scsi_cmnd * SCpnt , unsigned int result )
2005-04-17 02:20:36 +04:00
{
info - > stats . fins + = 1 ;
SCpnt - > result = result < < 16 | info - > scsi . SCp . Message < < 8 |
info - > scsi . SCp . Status ;
fas216_log_command ( info , LOG_CONNECT , SCpnt ,
" command complete, result=0x%08x " , SCpnt - > result ) ;
/*
* If the driver detected an error , we ' re all done .
*/
if ( host_byte ( SCpnt - > result ) ! = DID_OK | |
msg_byte ( SCpnt - > result ) ! = COMMAND_COMPLETE )
goto done ;
/*
* If the command returned CHECK_CONDITION or COMMAND_TERMINATED
* status , request the sense information .
*/
if ( status_byte ( SCpnt - > result ) = = CHECK_CONDITION | |
status_byte ( SCpnt - > result ) = = COMMAND_TERMINATED )
goto request_sense ;
/*
* If the command did not complete with GOOD status ,
* we are all done here .
*/
if ( status_byte ( SCpnt - > result ) ! = GOOD )
goto done ;
/*
* We have successfully completed a command . Make sure that
* we do not have any buffers left to transfer . The world
* is not perfect , and we seem to occasionally hit this .
* It can be indicative of a buggy driver , target or the upper
* levels of the SCSI code .
*/
if ( info - > scsi . SCp . ptr ) {
switch ( SCpnt - > cmnd [ 0 ] ) {
case INQUIRY :
case START_STOP :
case MODE_SENSE :
break ;
default :
printk ( KERN_ERR " scsi%d.%c: incomplete data transfer "
" detected: res=%08X ptr=%p len=%X CDB: " ,
info - > host - > host_no , ' 0 ' + SCpnt - > device - > id ,
SCpnt - > result , info - > scsi . SCp . ptr ,
info - > scsi . SCp . this_residual ) ;
2005-04-03 23:53:59 +04:00
__scsi_print_command ( SCpnt - > cmnd ) ;
2005-04-17 02:20:36 +04:00
SCpnt - > result & = ~ ( 255 < < 16 ) ;
SCpnt - > result | = DID_BAD_TARGET < < 16 ;
goto request_sense ;
}
}
done :
if ( SCpnt - > scsi_done ) {
SCpnt - > scsi_done ( SCpnt ) ;
return ;
}
panic ( " scsi%d.H: null scsi_done function in fas216_done " ,
info - > host - > host_no ) ;
request_sense :
if ( SCpnt - > cmnd [ 0 ] = = REQUEST_SENSE )
goto done ;
2007-09-10 23:39:11 +04:00
scsi_eh_prep_cmnd ( SCpnt , & info - > ses , NULL , 0 , ~ 0 ) ;
2005-04-17 02:20:36 +04:00
fas216_log_target ( info , LOG_CONNECT , SCpnt - > device - > id ,
" requesting sense " ) ;
2007-09-10 23:39:11 +04:00
init_SCp ( SCpnt ) ;
2005-04-17 02:20:36 +04:00
SCpnt - > SCp . Message = 0 ;
SCpnt - > SCp . Status = 0 ;
SCpnt - > tag = 0 ;
SCpnt - > host_scribble = ( void * ) fas216_rq_sns_done ;
/*
* Place this command into the high priority " request
* sense " slot. This will be the very next command
* executed , unless a target connects to us .
*/
if ( info - > reqSCpnt )
printk ( KERN_WARNING " scsi%d.%c: loosing request command \n " ,
info - > host - > host_no , ' 0 ' + SCpnt - > device - > id ) ;
info - > reqSCpnt = SCpnt ;
}
/**
* fas216_done - complete processing for current command
* @ info : interface that completed
* @ result : driver byte of result
*
* Complete processing for current command
*/
static void fas216_done ( FAS216_Info * info , unsigned int result )
{
2006-10-01 15:18:37 +04:00
void ( * fn ) ( FAS216_Info * , struct scsi_cmnd * , unsigned int ) ;
struct scsi_cmnd * SCpnt ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
fas216_checkmagic ( info ) ;
if ( ! info - > SCpnt )
goto no_command ;
SCpnt = info - > SCpnt ;
info - > SCpnt = NULL ;
info - > scsi . phase = PHASE_IDLE ;
if ( info - > scsi . aborting ) {
fas216_log ( info , 0 , " uncaught abort - returning DID_ABORT " ) ;
result = DID_ABORT ;
info - > scsi . aborting = 0 ;
}
/*
* Sanity check the completion - if we have zero bytes left
* to transfer , we should not have a valid pointer .
*/
if ( info - > scsi . SCp . ptr & & info - > scsi . SCp . this_residual = = 0 ) {
printk ( " scsi%d.%c: zero bytes left to transfer, but "
" buffer pointer still valid: ptr=%p len=%08x CDB: " ,
info - > host - > host_no , ' 0 ' + SCpnt - > device - > id ,
info - > scsi . SCp . ptr , info - > scsi . SCp . this_residual ) ;
info - > scsi . SCp . ptr = NULL ;
2005-04-03 23:53:59 +04:00
__scsi_print_command ( SCpnt - > cmnd ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Clear down this command as completed . If we need to request
* the sense information , fas216_kick will re - assert the busy
* status .
*/
info - > device [ SCpnt - > device - > id ] . parity_check = 0 ;
clear_bit ( SCpnt - > device - > id * 8 + SCpnt - > device - > lun , info - > busyluns ) ;
2006-10-01 15:18:37 +04:00
fn = ( void ( * ) ( FAS216_Info * , struct scsi_cmnd * , unsigned int ) ) SCpnt - > host_scribble ;
2005-04-17 02:20:36 +04:00
fn ( info , SCpnt , result ) ;
if ( info - > scsi . irq ! = NO_IRQ ) {
spin_lock_irqsave ( & info - > host_lock , flags ) ;
if ( info - > scsi . phase = = PHASE_IDLE )
fas216_kick ( info ) ;
spin_unlock_irqrestore ( & info - > host_lock , flags ) ;
}
return ;
no_command :
panic ( " scsi%d.H: null command in fas216_done " ,
info - > host - > host_no ) ;
}
/**
* fas216_queue_command - queue a command for adapter to process .
* @ SCpnt : Command to queue
* @ done : done function to call once command is complete
*
* Queue a command for adapter to process .
* Returns : 0 on success , else error .
* Notes : io_request_lock is held , interrupts are disabled .
*/
2006-10-01 15:18:37 +04:00
int fas216_queue_command ( struct scsi_cmnd * SCpnt ,
void ( * done ) ( struct scsi_cmnd * ) )
2005-04-17 02:20:36 +04:00
{
FAS216_Info * info = ( FAS216_Info * ) SCpnt - > device - > host - > hostdata ;
int result ;
fas216_checkmagic ( info ) ;
fas216_log_command ( info , LOG_CONNECT , SCpnt ,
" received command (%p) " , SCpnt ) ;
SCpnt - > scsi_done = done ;
SCpnt - > host_scribble = ( void * ) fas216_std_done ;
SCpnt - > result = 0 ;
init_SCp ( SCpnt ) ;
info - > stats . queues + = 1 ;
SCpnt - > tag = 0 ;
spin_lock ( & info - > host_lock ) ;
/*
* Add command into execute queue and let it complete under
* whatever scheme we ' re using .
*/
result = ! queue_add_cmd_ordered ( & info - > queues . issue , SCpnt ) ;
/*
* If we successfully added the command ,
* kick the interface to get it moving .
*/
if ( result = = 0 & & info - > scsi . phase = = PHASE_IDLE )
fas216_kick ( info ) ;
spin_unlock ( & info - > host_lock ) ;
fas216_log_target ( info , LOG_CONNECT , - 1 , " queue %s " ,
result ? " failure " : " success " ) ;
return result ;
}
/**
* fas216_internal_done - trigger restart of a waiting thread in fas216_noqueue_command
* @ SCpnt : Command to wake
*
* Trigger restart of a waiting thread in fas216_command
*/
2006-10-01 15:18:37 +04:00
static void fas216_internal_done ( struct scsi_cmnd * SCpnt )
2005-04-17 02:20:36 +04:00
{
FAS216_Info * info = ( FAS216_Info * ) SCpnt - > device - > host - > hostdata ;
fas216_checkmagic ( info ) ;
info - > internal_done = 1 ;
}
/**
* fas216_noqueue_command - process a command for the adapter .
* @ SCpnt : Command to queue
*
* Queue a command for adapter to process .
* Returns : scsi result code .
* Notes : io_request_lock is held , interrupts are disabled .
*/
2006-10-01 15:18:37 +04:00
int fas216_noqueue_command ( struct scsi_cmnd * SCpnt ,
void ( * done ) ( struct scsi_cmnd * ) )
2005-04-17 02:20:36 +04:00
{
FAS216_Info * info = ( FAS216_Info * ) SCpnt - > device - > host - > hostdata ;
fas216_checkmagic ( info ) ;
/*
* We should only be using this if we don ' t have an interrupt .
* Provide some " incentive " to use the queueing code .
*/
BUG_ON ( info - > scsi . irq ! = NO_IRQ ) ;
info - > internal_done = 0 ;
fas216_queue_command ( SCpnt , fas216_internal_done ) ;
/*
* This wastes time , since we can ' t return until the command is
* complete . We can ' t sleep either since we may get re - entered !
* However , we must re - enable interrupts , or else we ' ll be
* waiting forever .
*/
spin_unlock_irq ( info - > host - > host_lock ) ;
while ( ! info - > internal_done ) {
/*
* If we don ' t have an IRQ , then we must poll the card for
* it ' s interrupt , and use that to call this driver ' s
* interrupt routine . That way , we keep the command
* progressing . Maybe we can add some inteligence here
* and go to sleep if we know that the device is going
* to be some time ( eg , disconnected ) .
*/
if ( fas216_readb ( info , REG_STAT ) & STAT_INT ) {
spin_lock_irq ( info - > host - > host_lock ) ;
fas216_intr ( info ) ;
spin_unlock_irq ( info - > host - > host_lock ) ;
}
}
spin_lock_irq ( info - > host - > host_lock ) ;
done ( SCpnt ) ;
return 0 ;
}
/*
* Error handler timeout function . Indicate that we timed out ,
* and wake up any error handler process so it can continue .
*/
static void fas216_eh_timer ( unsigned long data )
{
FAS216_Info * info = ( FAS216_Info * ) data ;
fas216_log ( info , LOG_ERROR , " error handling timed out \n " ) ;
del_timer ( & info - > eh_timer ) ;
if ( info - > rst_bus_status = = 0 )
info - > rst_bus_status = - 1 ;
if ( info - > rst_dev_status = = 0 )
info - > rst_dev_status = - 1 ;
wake_up ( & info - > eh_wait ) ;
}
enum res_find {
res_failed , /* not found */
res_success , /* command on issue queue */
res_hw_abort /* command on disconnected dev */
} ;
/**
* fas216_do_abort - decide how to abort a command
* @ SCpnt : command to abort
*
* Decide how to abort a command .
* Returns : abort status
*/
2006-10-01 15:18:37 +04:00
static enum res_find fas216_find_command ( FAS216_Info * info ,
struct scsi_cmnd * SCpnt )
2005-04-17 02:20:36 +04:00
{
enum res_find res = res_failed ;
if ( queue_remove_cmd ( & info - > queues . issue , SCpnt ) ) {
/*
* The command was on the issue queue , and has not been
* issued yet . We can remove the command from the queue ,
* and acknowledge the abort . Neither the device nor the
* interface know about the command .
*/
printk ( " on issue queue " ) ;
res = res_success ;
} else if ( queue_remove_cmd ( & info - > queues . disconnected , SCpnt ) ) {
/*
* The command was on the disconnected queue . We must
* reconnect with the device if possible , and send it
* an abort message .
*/
printk ( " on disconnected queue " ) ;
res = res_hw_abort ;
} else if ( info - > SCpnt = = SCpnt ) {
printk ( " executing " ) ;
switch ( info - > scsi . phase ) {
/*
* If the interface is idle , and the command is ' disconnectable ' ,
* then it is the same as on the disconnected queue .
*/
case PHASE_IDLE :
if ( info - > scsi . disconnectable ) {
info - > scsi . disconnectable = 0 ;
info - > SCpnt = NULL ;
res = res_hw_abort ;
}
break ;
default :
break ;
}
} else if ( info - > origSCpnt = = SCpnt ) {
/*
* The command will be executed next , but a command
* is currently using the interface . This is similar to
* being on the issue queue , except the busylun bit has
* been set .
*/
info - > origSCpnt = NULL ;
clear_bit ( SCpnt - > device - > id * 8 + SCpnt - > device - > lun , info - > busyluns ) ;
printk ( " waiting for execution " ) ;
res = res_success ;
} else
printk ( " unknown " ) ;
return res ;
}
/**
* fas216_eh_abort - abort this command
* @ SCpnt : command to abort
*
* Abort this command .
* Returns : FAILED if unable to abort
* Notes : io_request_lock is taken , and irqs are disabled
*/
2006-10-01 15:18:37 +04:00
int fas216_eh_abort ( struct scsi_cmnd * SCpnt )
2005-04-17 02:20:36 +04:00
{
FAS216_Info * info = ( FAS216_Info * ) SCpnt - > device - > host - > hostdata ;
int result = FAILED ;
fas216_checkmagic ( info ) ;
info - > stats . aborts + = 1 ;
printk ( KERN_WARNING " scsi%d: abort command " , info - > host - > host_no ) ;
2006-07-26 11:53:23 +04:00
__scsi_print_command ( SCpnt - > cmnd ) ;
2005-04-17 02:20:36 +04:00
print_debug_list ( ) ;
fas216_dumpstate ( info ) ;
printk ( KERN_WARNING " scsi%d: abort %p " , info - > host - > host_no , SCpnt ) ;
switch ( fas216_find_command ( info , SCpnt ) ) {
/*
* We found the command , and cleared it out . Either
* the command is still known to be executing on the
* target , or the busylun bit is not set .
*/
case res_success :
printk ( " success \n " ) ;
result = SUCCESS ;
break ;
/*
* We need to reconnect to the target and send it an
* ABORT or ABORT_TAG message . We can only do this
* if the bus is free .
*/
case res_hw_abort :
/*
* We are unable to abort the command for some reason .
*/
default :
case res_failed :
printk ( " failed \n " ) ;
break ;
}
return result ;
}
/**
* fas216_eh_device_reset - Reset the device associated with this command
* @ SCpnt : command specifing device to reset
*
* Reset the device associated with this command .
* Returns : FAILED if unable to reset .
* Notes : We won ' t be re - entered , so we ' ll only have one device
* reset on the go at one time .
*/
2006-10-01 15:18:37 +04:00
int fas216_eh_device_reset ( struct scsi_cmnd * SCpnt )
2005-04-17 02:20:36 +04:00
{
FAS216_Info * info = ( FAS216_Info * ) SCpnt - > device - > host - > hostdata ;
unsigned long flags ;
int i , res = FAILED , target = SCpnt - > device - > id ;
fas216_log ( info , LOG_ERROR , " device reset for target %d " , target ) ;
spin_lock_irqsave ( & info - > host_lock , flags ) ;
do {
/*
* If we are currently connected to a device , and
* it is the device we want to reset , there is
* nothing we can do here . Chances are it is stuck ,
* and we need a bus reset .
*/
if ( info - > SCpnt & & ! info - > scsi . disconnectable & &
info - > SCpnt - > device - > id = = SCpnt - > device - > id )
break ;
/*
* We ' re going to be resetting this device . Remove
* all pending commands from the driver . By doing
* so , we guarantee that we won ' t touch the command
* structures except to process the reset request .
*/
queue_remove_all_target ( & info - > queues . issue , target ) ;
queue_remove_all_target ( & info - > queues . disconnected , target ) ;
if ( info - > origSCpnt & & info - > origSCpnt - > device - > id = = target )
info - > origSCpnt = NULL ;
if ( info - > reqSCpnt & & info - > reqSCpnt - > device - > id = = target )
info - > reqSCpnt = NULL ;
for ( i = 0 ; i < 8 ; i + + )
clear_bit ( target * 8 + i , info - > busyluns ) ;
/*
* Hijack this SCSI command structure to send
* a bus device reset message to this device .
*/
SCpnt - > host_scribble = ( void * ) fas216_devicereset_done ;
info - > rst_dev_status = 0 ;
info - > rstSCpnt = SCpnt ;
if ( info - > scsi . phase = = PHASE_IDLE )
fas216_kick ( info ) ;
mod_timer ( & info - > eh_timer , 30 * HZ ) ;
spin_unlock_irqrestore ( & info - > host_lock , flags ) ;
/*
* Wait up to 30 seconds for the reset to complete .
*/
wait_event ( info - > eh_wait , info - > rst_dev_status ) ;
del_timer_sync ( & info - > eh_timer ) ;
spin_lock_irqsave ( & info - > host_lock , flags ) ;
info - > rstSCpnt = NULL ;
if ( info - > rst_dev_status = = 1 )
res = SUCCESS ;
} while ( 0 ) ;
SCpnt - > host_scribble = NULL ;
spin_unlock_irqrestore ( & info - > host_lock , flags ) ;
fas216_log ( info , LOG_ERROR , " device reset complete: %s \n " ,
res = = SUCCESS ? " success " : " failed " ) ;
return res ;
}
/**
* fas216_eh_bus_reset - Reset the bus associated with the command
* @ SCpnt : command specifing bus to reset
*
* Reset the bus associated with the command .
* Returns : FAILED if unable to reset .
* Notes : Further commands are blocked .
*/
2006-10-01 15:18:37 +04:00
int fas216_eh_bus_reset ( struct scsi_cmnd * SCpnt )
2005-04-17 02:20:36 +04:00
{
FAS216_Info * info = ( FAS216_Info * ) SCpnt - > device - > host - > hostdata ;
unsigned long flags ;
2005-10-31 20:32:08 +03:00
struct scsi_device * SDpnt ;
2005-04-17 02:20:36 +04:00
fas216_checkmagic ( info ) ;
fas216_log ( info , LOG_ERROR , " resetting bus " ) ;
info - > stats . bus_resets + = 1 ;
spin_lock_irqsave ( & info - > host_lock , flags ) ;
/*
* Stop all activity on this interface .
*/
fas216_aborttransfer ( info ) ;
fas216_writeb ( info , REG_CNTL3 , info - > scsi . cfg [ 2 ] ) ;
/*
* Clear any pending interrupts .
*/
while ( fas216_readb ( info , REG_STAT ) & STAT_INT )
fas216_readb ( info , REG_INST ) ;
info - > rst_bus_status = 0 ;
/*
* For each attached hard - reset device , clear out
* all command structures . Leave the running
* command in place .
*/
shost_for_each_device ( SDpnt , info - > host ) {
int i ;
if ( SDpnt - > soft_reset )
continue ;
queue_remove_all_target ( & info - > queues . issue , SDpnt - > id ) ;
queue_remove_all_target ( & info - > queues . disconnected , SDpnt - > id ) ;
if ( info - > origSCpnt & & info - > origSCpnt - > device - > id = = SDpnt - > id )
info - > origSCpnt = NULL ;
if ( info - > reqSCpnt & & info - > reqSCpnt - > device - > id = = SDpnt - > id )
info - > reqSCpnt = NULL ;
info - > SCpnt = NULL ;
for ( i = 0 ; i < 8 ; i + + )
clear_bit ( SDpnt - > id * 8 + i , info - > busyluns ) ;
}
info - > scsi . phase = PHASE_IDLE ;
/*
* Reset the SCSI bus . Device cleanup happens in
* the interrupt handler .
*/
fas216_cmd ( info , CMD_RESETSCSI ) ;
mod_timer ( & info - > eh_timer , jiffies + HZ ) ;
spin_unlock_irqrestore ( & info - > host_lock , flags ) ;
/*
* Wait one second for the interrupt .
*/
wait_event ( info - > eh_wait , info - > rst_bus_status ) ;
del_timer_sync ( & info - > eh_timer ) ;
fas216_log ( info , LOG_ERROR , " bus reset complete: %s \n " ,
info - > rst_bus_status = = 1 ? " success " : " failed " ) ;
return info - > rst_bus_status = = 1 ? SUCCESS : FAILED ;
}
/**
* fas216_init_chip - Initialise FAS216 state after reset
* @ info : state structure for interface
*
* Initialise FAS216 state after reset
*/
static void fas216_init_chip ( FAS216_Info * info )
{
unsigned int clock = ( ( info - > ifcfg . clockrate - 1 ) / 5 + 1 ) & 7 ;
fas216_writeb ( info , REG_CLKF , clock ) ;
fas216_writeb ( info , REG_CNTL1 , info - > scsi . cfg [ 0 ] ) ;
fas216_writeb ( info , REG_CNTL2 , info - > scsi . cfg [ 1 ] ) ;
fas216_writeb ( info , REG_CNTL3 , info - > scsi . cfg [ 2 ] ) ;
fas216_writeb ( info , REG_STIM , info - > ifcfg . select_timeout ) ;
fas216_writeb ( info , REG_SOF , 0 ) ;
fas216_writeb ( info , REG_STP , info - > scsi . async_stp ) ;
fas216_writeb ( info , REG_CNTL1 , info - > scsi . cfg [ 0 ] ) ;
}
/**
* fas216_eh_host_reset - Reset the host associated with this command
* @ SCpnt : command specifing host to reset
*
* Reset the host associated with this command .
* Returns : FAILED if unable to reset .
* Notes : io_request_lock is taken , and irqs are disabled
*/
2006-10-01 15:18:37 +04:00
int fas216_eh_host_reset ( struct scsi_cmnd * SCpnt )
2005-04-17 02:20:36 +04:00
{
FAS216_Info * info = ( FAS216_Info * ) SCpnt - > device - > host - > hostdata ;
2005-05-28 15:57:14 +04:00
spin_lock_irq ( info - > host - > host_lock ) ;
2005-04-17 02:20:36 +04:00
fas216_checkmagic ( info ) ;
printk ( " scsi%d.%c: %s: resetting host \n " ,
2008-07-04 10:47:27 +04:00
info - > host - > host_no , ' 0 ' + SCpnt - > device - > id , __func__ ) ;
2005-04-17 02:20:36 +04:00
/*
* Reset the SCSI chip .
*/
fas216_cmd ( info , CMD_RESETCHIP ) ;
/*
* Ugly ugly ugly !
* We need to release the host_lock and enable
* IRQs if we sleep , but we must relock and disable
* IRQs after the sleep .
*/
spin_unlock_irq ( info - > host - > host_lock ) ;
msleep ( 50 * 1000 / 100 ) ;
spin_lock_irq ( info - > host - > host_lock ) ;
/*
* Release the SCSI reset .
*/
fas216_cmd ( info , CMD_NOP ) ;
fas216_init_chip ( info ) ;
2005-05-28 15:57:14 +04:00
spin_unlock_irq ( info - > host - > host_lock ) ;
2005-04-17 02:20:36 +04:00
return SUCCESS ;
}
# define TYPE_UNKNOWN 0
# define TYPE_NCR53C90 1
# define TYPE_NCR53C90A 2
# define TYPE_NCR53C9x 3
# define TYPE_Am53CF94 4
# define TYPE_EmFAS216 5
# define TYPE_QLFAS216 6
static char * chip_types [ ] = {
" unknown " ,
" NS NCR53C90 " ,
" NS NCR53C90A " ,
" NS NCR53C9x " ,
" AMD Am53CF94 " ,
" Emulex FAS216 " ,
" QLogic FAS216 "
} ;
static int fas216_detect_type ( FAS216_Info * info )
{
int family , rev ;
/*
* Reset the chip .
*/
fas216_writeb ( info , REG_CMD , CMD_RESETCHIP ) ;
udelay ( 50 ) ;
fas216_writeb ( info , REG_CMD , CMD_NOP ) ;
/*
* Check to see if control reg 2 is present .
*/
fas216_writeb ( info , REG_CNTL3 , 0 ) ;
fas216_writeb ( info , REG_CNTL2 , CNTL2_S2FE ) ;
/*
* If we are unable to read back control reg 2
* correctly , it is not present , and we have a
* NCR53C90 .
*/
if ( ( fas216_readb ( info , REG_CNTL2 ) & ( ~ 0xe0 ) ) ! = CNTL2_S2FE )
return TYPE_NCR53C90 ;
/*
* Now , check control register 3
*/
fas216_writeb ( info , REG_CNTL2 , 0 ) ;
fas216_writeb ( info , REG_CNTL3 , 0 ) ;
fas216_writeb ( info , REG_CNTL3 , 5 ) ;
/*
* If we are unable to read the register back
* correctly , we have a NCR53C90A
*/
if ( fas216_readb ( info , REG_CNTL3 ) ! = 5 )
return TYPE_NCR53C90A ;
/*
* Now read the ID from the chip .
*/
fas216_writeb ( info , REG_CNTL3 , 0 ) ;
fas216_writeb ( info , REG_CNTL3 , CNTL3_ADIDCHK ) ;
fas216_writeb ( info , REG_CNTL3 , 0 ) ;
fas216_writeb ( info , REG_CMD , CMD_RESETCHIP ) ;
udelay ( 50 ) ;
fas216_writeb ( info , REG_CMD , CMD_WITHDMA | CMD_NOP ) ;
fas216_writeb ( info , REG_CNTL2 , CNTL2_ENF ) ;
fas216_writeb ( info , REG_CMD , CMD_RESETCHIP ) ;
udelay ( 50 ) ;
fas216_writeb ( info , REG_CMD , CMD_NOP ) ;
rev = fas216_readb ( info , REG_ID ) ;
family = rev > > 3 ;
rev & = 7 ;
switch ( family ) {
case 0x01 :
if ( rev = = 4 )
return TYPE_Am53CF94 ;
break ;
case 0x02 :
switch ( rev ) {
case 2 :
return TYPE_EmFAS216 ;
case 3 :
return TYPE_QLFAS216 ;
}
break ;
default :
break ;
}
printk ( " family %x rev %x \n " , family , rev ) ;
return TYPE_NCR53C9x ;
}
/**
* fas216_reset_state - Initialise driver internal state
* @ info : state to initialise
*
* Initialise driver internal state
*/
static void fas216_reset_state ( FAS216_Info * info )
{
int i ;
fas216_checkmagic ( info ) ;
fas216_bus_reset ( info ) ;
/*
* Clear out all stale info in our state structure
*/
memset ( info - > busyluns , 0 , sizeof ( info - > busyluns ) ) ;
info - > scsi . disconnectable = 0 ;
info - > scsi . aborting = 0 ;
for ( i = 0 ; i < 8 ; i + + ) {
info - > device [ i ] . parity_enabled = 0 ;
info - > device [ i ] . parity_check = 1 ;
}
/*
* Drain all commands on disconnected queue
*/
while ( queue_remove ( & info - > queues . disconnected ) ! = NULL ) ;
/*
* Remove executing commands .
*/
info - > SCpnt = NULL ;
info - > reqSCpnt = NULL ;
info - > rstSCpnt = NULL ;
info - > origSCpnt = NULL ;
}
/**
* fas216_init - initialise FAS / NCR / AMD SCSI structures .
* @ host : a driver - specific filled - out structure
*
* Initialise FAS / NCR / AMD SCSI structures .
* Returns : 0 on success
*/
int fas216_init ( struct Scsi_Host * host )
{
FAS216_Info * info = ( FAS216_Info * ) host - > hostdata ;
info - > magic_start = MAGIC ;
info - > magic_end = MAGIC ;
info - > host = host ;
info - > scsi . cfg [ 0 ] = host - > this_id | CNTL1_PERE ;
info - > scsi . cfg [ 1 ] = CNTL2_ENF | CNTL2_S2FE ;
info - > scsi . cfg [ 2 ] = info - > ifcfg . cntl3 |
CNTL3_ADIDCHK | CNTL3_QTAG | CNTL3_G2CB | CNTL3_LBTM ;
info - > scsi . async_stp = fas216_syncperiod ( info , info - > ifcfg . asyncperiod ) ;
info - > rst_dev_status = - 1 ;
info - > rst_bus_status = - 1 ;
init_waitqueue_head ( & info - > eh_wait ) ;
init_timer ( & info - > eh_timer ) ;
info - > eh_timer . data = ( unsigned long ) info ;
info - > eh_timer . function = fas216_eh_timer ;
spin_lock_init ( & info - > host_lock ) ;
memset ( & info - > stats , 0 , sizeof ( info - > stats ) ) ;
msgqueue_initialise ( & info - > scsi . msgs ) ;
if ( ! queue_initialise ( & info - > queues . issue ) )
return - ENOMEM ;
if ( ! queue_initialise ( & info - > queues . disconnected ) ) {
queue_free ( & info - > queues . issue ) ;
return - ENOMEM ;
}
return 0 ;
}
/**
* fas216_add - initialise FAS / NCR / AMD SCSI ic .
* @ host : a driver - specific filled - out structure
* @ dev : parent device
*
* Initialise FAS / NCR / AMD SCSI ic .
* Returns : 0 on success
*/
int fas216_add ( struct Scsi_Host * host , struct device * dev )
{
FAS216_Info * info = ( FAS216_Info * ) host - > hostdata ;
int type , ret ;
if ( info - > ifcfg . clockrate < = 10 | | info - > ifcfg . clockrate > 40 ) {
printk ( KERN_CRIT " fas216: invalid clock rate %u MHz \n " ,
info - > ifcfg . clockrate ) ;
return - EINVAL ;
}
fas216_reset_state ( info ) ;
type = fas216_detect_type ( info ) ;
info - > scsi . type = chip_types [ type ] ;
udelay ( 300 ) ;
/*
* Initialise the chip correctly .
*/
fas216_init_chip ( info ) ;
/*
* Reset the SCSI bus . We don ' t want to see
* the resulting reset interrupt , so mask it
* out .
*/
fas216_writeb ( info , REG_CNTL1 , info - > scsi . cfg [ 0 ] | CNTL1_DISR ) ;
fas216_writeb ( info , REG_CMD , CMD_RESETSCSI ) ;
/*
* scsi standard says wait 250 ms
*/
spin_unlock_irq ( info - > host - > host_lock ) ;
msleep ( 100 * 1000 / 100 ) ;
spin_lock_irq ( info - > host - > host_lock ) ;
fas216_writeb ( info , REG_CNTL1 , info - > scsi . cfg [ 0 ] ) ;
fas216_readb ( info , REG_INST ) ;
fas216_checkmagic ( info ) ;
ret = scsi_add_host ( host , dev ) ;
if ( ret )
fas216_writeb ( info , REG_CMD , CMD_RESETCHIP ) ;
else
scsi_scan_host ( host ) ;
return ret ;
}
void fas216_remove ( struct Scsi_Host * host )
{
FAS216_Info * info = ( FAS216_Info * ) host - > hostdata ;
fas216_checkmagic ( info ) ;
scsi_remove_host ( host ) ;
fas216_writeb ( info , REG_CMD , CMD_RESETCHIP ) ;
scsi_host_put ( host ) ;
}
/**
* fas216_release - release all resources for FAS / NCR / AMD SCSI ic .
* @ host : a driver - specific filled - out structure
*
* release all resources and put everything to bed for FAS / NCR / AMD SCSI ic .
*/
void fas216_release ( struct Scsi_Host * host )
{
FAS216_Info * info = ( FAS216_Info * ) host - > hostdata ;
queue_free ( & info - > queues . disconnected ) ;
queue_free ( & info - > queues . issue ) ;
}
int fas216_print_host ( FAS216_Info * info , char * buffer )
{
return sprintf ( buffer ,
" \n "
" Chip : %s \n "
" Address: 0x%p \n "
" IRQ : %d \n "
" DMA : %d \n " ,
info - > scsi . type , info - > scsi . io_base ,
info - > scsi . irq , info - > scsi . dma ) ;
}
int fas216_print_stats ( FAS216_Info * info , char * buffer )
{
char * p = buffer ;
p + = sprintf ( p , " \n "
" Command Statistics: \n "
" Queued : %u \n "
" Issued : %u \n "
" Completed : %u \n "
" Reads : %u \n "
" Writes : %u \n "
" Others : %u \n "
" Disconnects: %u \n "
" Aborts : %u \n "
" Bus resets : %u \n "
" Host resets: %u \n " ,
info - > stats . queues , info - > stats . removes ,
info - > stats . fins , info - > stats . reads ,
info - > stats . writes , info - > stats . miscs ,
info - > stats . disconnects , info - > stats . aborts ,
info - > stats . bus_resets , info - > stats . host_resets ) ;
return p - buffer ;
}
int fas216_print_devices ( FAS216_Info * info , char * buffer )
{
struct fas216_device * dev ;
2005-10-31 20:32:08 +03:00
struct scsi_device * scd ;
2005-04-17 02:20:36 +04:00
char * p = buffer ;
p + = sprintf ( p , " Device/Lun TaggedQ Parity Sync \n " ) ;
shost_for_each_device ( scd , info - > host ) {
dev = & info - > device [ scd - > id ] ;
p + = sprintf ( p , " %d/%d " , scd - > id , scd - > lun ) ;
if ( scd - > tagged_supported )
p + = sprintf ( p , " %3sabled(%3d) " ,
scd - > simple_tags ? " en " : " dis " ,
scd - > current_tag ) ;
else
p + = sprintf ( p , " unsupported " ) ;
p + = sprintf ( p , " %3sabled " , dev - > parity_enabled ? " en " : " dis " ) ;
if ( dev - > sof )
p + = sprintf ( p , " offset %d, %d ns \n " ,
dev - > sof , dev - > period * 4 ) ;
else
p + = sprintf ( p , " async \n " ) ;
}
return p - buffer ;
}
EXPORT_SYMBOL ( fas216_init ) ;
EXPORT_SYMBOL ( fas216_add ) ;
EXPORT_SYMBOL ( fas216_queue_command ) ;
EXPORT_SYMBOL ( fas216_noqueue_command ) ;
EXPORT_SYMBOL ( fas216_intr ) ;
EXPORT_SYMBOL ( fas216_remove ) ;
EXPORT_SYMBOL ( fas216_release ) ;
EXPORT_SYMBOL ( fas216_eh_abort ) ;
EXPORT_SYMBOL ( fas216_eh_device_reset ) ;
EXPORT_SYMBOL ( fas216_eh_bus_reset ) ;
EXPORT_SYMBOL ( fas216_eh_host_reset ) ;
EXPORT_SYMBOL ( fas216_print_host ) ;
EXPORT_SYMBOL ( fas216_print_stats ) ;
EXPORT_SYMBOL ( fas216_print_devices ) ;
MODULE_AUTHOR ( " Russell King " ) ;
MODULE_DESCRIPTION ( " Generic FAS216/NCR53C9x driver core " ) ;
MODULE_LICENSE ( " GPL " ) ;