2005-04-17 02:20:36 +04:00
/*
* 53 c710 driver . Modified from Drew Eckhardts driver
* for 53 c810 by Richard Hirst [ richard @ sleepie . demon . co . uk ]
* Check out PERM_OPTIONS and EXPECTED_CLOCK , which may be defined in the
* relevant machine specific file ( eg . mvme16x . [ ch ] , amiga7xx . [ ch ] ) .
* There are also currently some defines at the top of 53 c7xx . scr .
* The chip type is # defined in script_asm . pl , as well as the Makefile .
* Host scsi ID expected to be 7 - see NCR53c7x0_init ( ) .
*
* I have removed the PCI code and some of the 53 c8xx specific code -
* simply to make this file smaller and easier to manage .
*
* MVME16x issues :
* Problems trying to read any chip registers in NCR53c7x0_init ( ) , as they
* may never have been set by 16 xBug ( eg . If kernel has come in over tftp ) .
*/
/*
* Adapted for Linux / m68k Amiga platforms for the A4000T / A4091 and
* WarpEngine SCSI controllers .
* By Alan Hourihane < alanh @ fairlite . demon . co . uk >
* Thanks to Richard Hirst for making it possible with the MVME additions
*/
/*
* 53 c710 rev 0 doesn ' t support add with carry . Rev 1 and 2 does . To
* overcome this problem you can define FORCE_DSA_ALIGNMENT , which ensures
* that the DSA address is always xxxxxx00 . If disconnection is not allowed ,
* then the script only ever tries to add small ( < 256 ) positive offsets to
* DSA , so lack of carry isn ' t a problem . FORCE_DSA_ALIGNMENT can , of course ,
* be defined for all chip revisions at a small cost in memory usage .
*/
# define FORCE_DSA_ALIGNMENT
/*
* Selection timer does not always work on the 53 c710 , depending on the
* timing at the last disconnect , if this is a problem for you , try
* using validids as detailed below .
*
* Options for the NCR7xx driver
*
* noasync : 0 - disables sync and asynchronous negotiation
* nosync : 0 - disables synchronous negotiation ( does async )
* nodisconnect : 0 - disables disconnection
* validids : 0 x ? ? - Bitmask field that disallows certain ID ' s .
* - e . g . 0x03 allows ID 0 , 1
* - 0x1F allows ID 0 , 1 , 2 , 3 , 4
* opthi : n - replace top word of options with ' n '
* optlo : n - replace bottom word of options with ' n '
* - ALWAYS SPECIFY opthi THEN optlo < < < < < < < < < <
*/
/*
* PERM_OPTIONS are driver options which will be enabled for all NCR boards
* in the system at driver initialization time .
*
* Don ' t THINK about touching these in PERM_OPTIONS :
* OPTION_MEMORY_MAPPED
* 680 x0 doesn ' t have an IO map !
*
* OPTION_DEBUG_TEST1
* Test 1 does bus mastering and interrupt tests , which will help weed
* out brain damaged main boards .
*
* Other PERM_OPTIONS settings are listed below . Note the actual options
* required are set in the relevant file ( mvme16x . c , amiga7xx . c , etc ) :
*
* OPTION_NO_ASYNC
* Don ' t negotiate for asynchronous transfers on the first command
* when OPTION_ALWAYS_SYNCHRONOUS is set . Useful for dain bramaged
* devices which do something bad rather than sending a MESSAGE
* REJECT back to us like they should if they can ' t cope .
*
* OPTION_SYNCHRONOUS
* Enable support for synchronous transfers . Target negotiated
* synchronous transfers will be responded to . To initiate
* a synchronous transfer request , call
*
* request_synchronous ( hostno , target )
*
* from within KGDB .
*
* OPTION_ALWAYS_SYNCHRONOUS
* Negotiate for synchronous transfers with every target after
* driver initialization or a SCSI bus reset . This is a bit dangerous ,
* since there are some dain bramaged SCSI devices which will accept
* SDTR messages but keep talking asynchronously .
*
* OPTION_DISCONNECT
* Enable support for disconnect / reconnect . To change the
* default setting on a given host adapter , call
*
* request_disconnect ( hostno , allow )
*
* where allow is non - zero to allow , 0 to disallow .
*
* If you really want to run 10 MHz FAST SCSI - II transfers , you should
* know that the NCR driver currently ignores parity information . Most
* systems do 5 MHz SCSI fine . I ' ve seen a lot that have problems faster
* than 8 MHz . To play it safe , we only request 5 MHz transfers .
*
* If you ' d rather get 10 MHz transfers , edit sdtr_message and change
* the fourth byte from 50 to 25.
*/
/*
* Sponsored by
* iX Multiuser Multitasking Magazine
* Hannover , Germany
* hm @ ix . de
*
* Copyright 1993 , 1994 , 1995 Drew Eckhardt
* Visionary Computing
* ( Unix and Linux consulting and custom programming )
* drew @ PoohSticks . ORG
* + 1 ( 303 ) 786 - 7975
*
* TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation .
*
* For more information , please consult
*
* NCR53C810
* SCSI I / O Processor
* Programmer ' s Guide
*
* NCR 53 C810
* PCI - SCSI I / O Processor
* Data Manual
*
* NCR 53 C810 / 53 C820
* PCI - SCSI I / O Processor Design In Guide
*
* For literature on Symbios Logic Inc . formerly NCR , SCSI ,
* and Communication products please call ( 800 ) 334 - 5454 or
* ( 719 ) 536 - 3300.
*
* PCI BIOS Specification Revision
* PCI Local Bus Specification
* PCI System Design Guide
*
* PCI Special Interest Group
* M / S HF3 - 15 A
* 5200 N . E . Elam Young Parkway
* Hillsboro , Oregon 97124 - 6497
* + 1 ( 503 ) 696 - 2000
* + 1 ( 800 ) 433 - 5177
*/
/*
* Design issues :
* The cumulative latency needed to propagate a read / write request
* through the file system , buffer cache , driver stacks , SCSI host , and
* SCSI device is ultimately the limiting factor in throughput once we
* have a sufficiently fast host adapter .
*
* So , to maximize performance we want to keep the ratio of latency to data
* transfer time to a minimum by
* 1. Minimizing the total number of commands sent ( typical command latency
* including drive and bus mastering host overhead is as high as 4.5 ms )
* to transfer a given amount of data .
*
* This is accomplished by placing no arbitrary limit on the number
* of scatter / gather buffers supported , since we can transfer 1 K
* per scatter / gather buffer without Eric ' s cluster patches ,
* 4 K with .
*
* 2. Minimizing the number of fatal interrupts serviced , since
* fatal interrupts halt the SCSI I / O processor . Basically ,
* this means offloading the practical maximum amount of processing
* to the SCSI chip .
*
* On the NCR53c810 / 820 / 720 , this is accomplished by using
* interrupt - on - the - fly signals when commands complete ,
* and only handling fatal errors and SDTR / WDTR messages
* in the host code .
*
* On the NCR53c710 , interrupts are generated as on the NCR53c8x0 ,
* only the lack of a interrupt - on - the - fly facility complicates
* things . Also , SCSI ID registers and commands are
* bit fielded rather than binary encoded .
*
* On the NCR53c700 and NCR53c700 - 66 , operations that are done via
* indirect , table mode on the more advanced chips must be
* replaced by calls through a jump table which
* acts as a surrogate for the DSA . Unfortunately , this
* will mean that we must service an interrupt for each
* disconnect / reconnect .
*
* 3. Eliminating latency by pipelining operations at the different levels .
*
* This driver allows a configurable number of commands to be enqueued
* for each target / lun combination ( experimentally , I have discovered
* that two seems to work best ) and will ultimately allow for
* SCSI - II tagged queuing .
*
*
* Architecture :
* This driver is built around a Linux queue of commands waiting to
* be executed , and a shared Linux / NCR array of commands to start . Commands
* are transferred to the array by the run_process_issue_queue ( ) function
* which is called whenever a command completes .
*
* As commands are completed , the interrupt routine is triggered ,
* looks for commands in the linked list of completed commands with
* valid status , removes these commands from a list of running commands ,
* calls the done routine , and flags their target / luns as not busy .
*
* Due to limitations in the intelligence of the NCR chips , certain
* concessions are made . In many cases , it is easier to dynamically
* generate / fix - up code rather than calculate on the NCR at run time .
* So , code is generated or fixed up for
*
* - Handling data transfers , using a variable number of MOVE instructions
* interspersed with CALL MSG_IN , WHEN MSGIN instructions .
*
* The DATAIN and DATAOUT routines are separate , so that an incorrect
* direction can be trapped , and space isn ' t wasted .
*
* It may turn out that we ' re better off using some sort
* of table indirect instruction in a loop with a variable
* sized table on the NCR53c710 and newer chips .
*
* - Checking for reselection ( NCR53c710 and better )
*
* - Handling the details of SCSI context switches ( NCR53c710 and better ) ,
* such as reprogramming appropriate synchronous parameters ,
* removing the dsa structure from the NCR ' s queue of outstanding
* commands , etc .
*
*/
# include <linux/module.h>
# include <linux/config.h>
# include <linux/types.h>
# include <asm/setup.h>
# include <asm/dma.h>
# include <asm/io.h>
# include <asm/system.h>
# include <linux/delay.h>
# include <linux/signal.h>
# include <linux/sched.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/slab.h>
# include <linux/vmalloc.h>
# include <linux/mm.h>
# include <linux/ioport.h>
# include <linux/time.h>
# include <linux/blkdev.h>
# include <linux/spinlock.h>
# include <linux/interrupt.h>
# include <asm/pgtable.h>
# ifdef CONFIG_AMIGA
# include <asm/amigahw.h>
# include <asm/amigaints.h>
# include <asm/irq.h>
# define BIG_ENDIAN
# define NO_IO_SPACE
# endif
# ifdef CONFIG_MVME16x
# include <asm/mvme16xhw.h>
# define BIG_ENDIAN
# define NO_IO_SPACE
# define VALID_IDS
# endif
# ifdef CONFIG_BVME6000
# include <asm/bvme6000hw.h>
# define BIG_ENDIAN
# define NO_IO_SPACE
# define VALID_IDS
# endif
# 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 "53c7xx.h"
# include <linux/stat.h>
# include <linux/stddef.h>
# ifdef NO_IO_SPACE
/*
* The following make the definitions in 53 c7xx . h ( write8 , etc ) smaller ,
* we don ' t have separate i / o space anyway .
*/
# undef inb
# undef outb
# undef inw
# undef outw
# undef inl
# undef outl
# define inb(x) 1
# define inw(x) 1
# define inl(x) 1
# define outb(x,y) 1
# define outw(x,y) 1
# define outl(x,y) 1
# endif
static int check_address ( unsigned long addr , int size ) ;
static void dump_events ( struct Scsi_Host * host , int count ) ;
static Scsi_Cmnd * return_outstanding_commands ( struct Scsi_Host * host ,
int free , int issue ) ;
static void hard_reset ( struct Scsi_Host * host ) ;
static void ncr_scsi_reset ( struct Scsi_Host * host ) ;
static void print_lots ( struct Scsi_Host * host ) ;
static void set_synchronous ( struct Scsi_Host * host , int target , int sxfer ,
int scntl3 , int now_connected ) ;
static int datapath_residual ( struct Scsi_Host * host ) ;
static const char * sbcl_to_phase ( int sbcl ) ;
static void print_progress ( Scsi_Cmnd * cmd ) ;
static void print_queues ( struct Scsi_Host * host ) ;
static void process_issue_queue ( unsigned long flags ) ;
static int shutdown ( struct Scsi_Host * host ) ;
static void abnormal_finished ( struct NCR53c7x0_cmd * cmd , int result ) ;
static int disable ( struct Scsi_Host * host ) ;
static int NCR53c7xx_run_tests ( struct Scsi_Host * host ) ;
static irqreturn_t NCR53c7x0_intr ( int irq , void * dev_id , struct pt_regs * regs ) ;
static void NCR53c7x0_intfly ( struct Scsi_Host * host ) ;
static int ncr_halt ( struct Scsi_Host * host ) ;
static void intr_phase_mismatch ( struct Scsi_Host * host , struct NCR53c7x0_cmd
* cmd ) ;
static void intr_dma ( struct Scsi_Host * host , struct NCR53c7x0_cmd * cmd ) ;
static void print_dsa ( struct Scsi_Host * host , u32 * dsa ,
const char * prefix ) ;
static int print_insn ( struct Scsi_Host * host , const u32 * insn ,
const char * prefix , int kernel ) ;
static void NCR53c7xx_dsa_fixup ( struct NCR53c7x0_cmd * cmd ) ;
static void NCR53c7x0_init_fixup ( struct Scsi_Host * host ) ;
static int NCR53c7x0_dstat_sir_intr ( struct Scsi_Host * host , struct
NCR53c7x0_cmd * cmd ) ;
static void NCR53c7x0_soft_reset ( struct Scsi_Host * host ) ;
/* Size of event list (per host adapter) */
static int track_events = 0 ;
static struct Scsi_Host * first_host = NULL ; /* Head of list of NCR boards */
static Scsi_Host_Template * the_template = NULL ;
/* NCR53c710 script handling code */
# include "53c7xx_d.h"
# ifdef A_int_debug_sync
# define DEBUG_SYNC_INTR A_int_debug_sync
# endif
int NCR53c7xx_script_len = sizeof ( SCRIPT ) ;
int NCR53c7xx_dsa_len = A_dsa_end + Ent_dsa_zero - Ent_dsa_code_template ;
# ifdef FORCE_DSA_ALIGNMENT
int CmdPageStart = ( 0 - Ent_dsa_zero - sizeof ( struct NCR53c7x0_cmd ) ) & 0xff ;
# endif
static char * setup_strings [ ] =
{ " " , " " , " " , " " , " " , " " , " " , " " } ;
# define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *))
# define SETUP_BUFFER_SIZE 200
static char setup_buffer [ SETUP_BUFFER_SIZE ] ;
static char setup_used [ MAX_SETUP_STRINGS ] ;
void ncr53c7xx_setup ( char * str , int * ints )
{
int i ;
char * p1 , * p2 ;
p1 = setup_buffer ;
* p1 = ' \0 ' ;
if ( str )
strncpy ( p1 , str , SETUP_BUFFER_SIZE - strlen ( setup_buffer ) ) ;
setup_buffer [ SETUP_BUFFER_SIZE - 1 ] = ' \0 ' ;
p1 = setup_buffer ;
i = 0 ;
while ( * p1 & & ( i < MAX_SETUP_STRINGS ) ) {
p2 = strchr ( p1 , ' , ' ) ;
if ( p2 ) {
* p2 = ' \0 ' ;
if ( p1 ! = p2 )
setup_strings [ i ] = p1 ;
p1 = p2 + 1 ;
i + + ;
}
else {
setup_strings [ i ] = p1 ;
break ;
}
}
for ( i = 0 ; i < MAX_SETUP_STRINGS ; i + + )
setup_used [ i ] = 0 ;
}
/* check_setup_strings() returns index if key found, 0 if not
*/
static int check_setup_strings ( char * key , int * flags , int * val , char * buf )
{
int x ;
char * cp ;
for ( x = 0 ; x < MAX_SETUP_STRINGS ; x + + ) {
if ( setup_used [ x ] )
continue ;
if ( ! strncmp ( setup_strings [ x ] , key , strlen ( key ) ) )
break ;
if ( ! strncmp ( setup_strings [ x ] , " next " , strlen ( " next " ) ) )
return 0 ;
}
if ( x = = MAX_SETUP_STRINGS )
return 0 ;
setup_used [ x ] = 1 ;
cp = setup_strings [ x ] + strlen ( key ) ;
* val = - 1 ;
if ( * cp ! = ' : ' )
return + + x ;
cp + + ;
if ( ( * cp > = ' 0 ' ) & & ( * cp < = ' 9 ' ) ) {
* val = simple_strtoul ( cp , NULL , 0 ) ;
}
return + + x ;
}
/*
* KNOWN BUGS :
* - There is some sort of conflict when the PPP driver is compiled with
* support for 16 channels ?
*
* - On systems which predate the 1.3 . x initialization order change ,
* the NCR driver will cause Cannot get free page messages to appear .
* These are harmless , but I don ' t know of an easy way to avoid them .
*
* - With OPTION_DISCONNECT , on two systems under unknown circumstances ,
* we get a PHASE MISMATCH with DSA set to zero ( suggests that we
* are occurring somewhere in the reselection code ) where
* DSP = some value DCMD | DBC = same value .
*
* Closer inspection suggests that we may be trying to execute
* some portion of the DSA ?
* scsi0 : handling residual transfer ( + 0 bytes from DMA FIFO )
* scsi0 : handling residual transfer ( + 0 bytes from DMA FIFO )
* scsi0 : no current command : unexpected phase MSGIN .
* DSP = 0x1c46cc , DCMD | DBC = 0x1c46ac , DSA = 0x0
* DSPS = 0x0 , TEMP = 0x1c3e70 , DMODE = 0x80
* scsi0 : DSP - >
* 001 c46cc : 0x001c46cc 0x00000000
* 001 c46d4 : 0x001c5ea0 0x000011f8
*
* Changed the print code in the phase_mismatch handler so
* that we call print_lots to try to diagnose this .
*
*/
/*
* Possible future direction of architecture for max performance :
*
* We ' re using a single start array for the NCR chip . This is
* sub - optimal , because we cannot add a command which would conflict with
* an executing command to this start queue , and therefore must insert the
* next command for a given I / T / L combination after the first has completed ;
* incurring our interrupt latency between SCSI commands .
*
* To allow further pipelining of the NCR and host CPU operation , we want
* to set things up so that immediately on termination of a command destined
* for a given LUN , we get that LUN busy again .
*
* To do this , we need to add a 32 bit pointer to which is jumped to
* on completion of a command . If no new command is available , this
* would point to the usual DSA issue queue select routine .
*
* If one were , it would point to a per - NCR53c7x0_cmd select routine
* which starts execution immediately , inserting the command at the head
* of the start queue if the NCR chip is selected or reselected .
*
* We would change so that we keep a list of outstanding commands
* for each unit , rather than a single running_list . We ' d insert
* a new command into the right running list ; if the NCR didn ' t
* have something running for that yet , we ' d put it in the
* start queue as well . Some magic needs to happen to handle the
* race condition between the first command terminating before the
* new one is written .
*
* Potential for profiling :
* Call do_gettimeofday ( struct timeval * tv ) to get 800 ns resolution .
*/
/*
* TODO :
* 1. To support WIDE transfers , not much needs to happen . We
* should do CHMOVE instructions instead of MOVEs when
* we have scatter / gather segments of uneven length . When
* we do this , we need to handle the case where we disconnect
* between segments .
*
* 2. Currently , when Icky things happen we do a FATAL ( ) . Instead ,
* we want to do an integrity check on the parts of the NCR hostdata
* structure which were initialized at boot time ; FATAL ( ) if that
* fails , and otherwise try to recover . Keep track of how many
* times this has happened within a single SCSI command ; if it
* gets excessive , then FATAL ( ) .
*
* 3. Parity checking is currently disabled , and a few things should
* happen here now that we support synchronous SCSI transfers :
* 1. On soft - reset , we shoould set the EPC ( Enable Parity Checking )
* and AAP ( Assert SATN / on parity error ) bits in SCNTL0 .
*
* 2. We should enable the parity interrupt in the SIEN0 register .
*
* 3. intr_phase_mismatch ( ) needs to believe that message out is
* always an " acceptable " phase to have a mismatch in . If
* the old phase was MSG_IN , we should send a MESSAGE PARITY
* error . If the old phase was something else , we should send
* a INITIATOR_DETECTED_ERROR message . Note that this could
* cause a RESTORE POINTERS message ; so we should handle that
* correctly first . Instead , we should probably do an
* initiator_abort .
*
* 4. MPEE bit of CTEST4 should be set so we get interrupted if
* we detect an error .
*
*
* 5. The initial code has been tested on the NCR53c810 . I don ' t
* have access to NCR53c700 , 700 - 66 ( Forex boards ) , NCR53c710
* ( NCR Pentium systems ) , NCR53c720 , NCR53c820 , or NCR53c825 boards to
* finish development on those platforms .
*
* NCR53c820 / 825 / 720 - need to add wide transfer support , including WDTR
* negotiation , programming of wide transfer capabilities
* on reselection and table indirect selection .
*
* NCR53c710 - need to add fatal interrupt or GEN code for
* command completion signaling . Need to modify all
* SDID , SCID , etc . registers , and table indirect select code
* since these use bit fielded ( ie 1 < < target ) instead of
* binary encoded target ids . Need to accommodate
* different register mappings , probably scan through
* the SCRIPT code and change the non SFBR register operand
* of all MOVE instructions .
*
* It is rather worse than this actually , the 710 corrupts
* both TEMP and DSA when you do a MOVE MEMORY . This
* screws you up all over the place . MOVE MEMORY 4 with a
* destination of DSA seems to work OK , which helps some .
* Richard Hirst richard @ sleepie . demon . co . uk
*
* NCR53c700 / 700 - 66 - need to add code to refix addresses on
* every nexus change , eliminate all table indirect code ,
* very messy .
*
* 6. The NCR53c7x0 series is very popular on other platforms that
* could be running Linux - ie , some high performance AMIGA SCSI
* boards use it .
*
* So , I should include # ifdef ' d code so that it is
* compatible with these systems .
*
* Specifically , the little Endian assumptions I made in my
* bit fields need to change , and if the NCR doesn ' t see memory
* the right way , we need to provide options to reverse words
* when the scripts are relocated .
*
* 7. Use vremap ( ) to access memory mapped boards .
*/
/*
* Allow for simultaneous existence of multiple SCSI scripts so we
* can have a single driver binary for all of the family .
*
* - one for NCR53c700 and NCR53c700 - 66 chips ( not yet supported )
* - one for rest ( only the NCR53c810 , 815 , 820 , and 825 are currently
* supported )
*
* So that we only need two SCSI scripts , we need to modify things so
* that we fixup register accesses in READ / WRITE instructions , and
* we ' ll also have to accommodate the bit vs . binary encoding of IDs
* with the 7 xx chips .
*/
# define ROUNDUP(adr,type) \
( ( void * ) ( ( ( long ) ( adr ) + sizeof ( type ) - 1 ) & ~ ( sizeof ( type ) - 1 ) ) )
/*
* Function : issue_to_cmd
*
* Purpose : convert jump instruction in issue array to NCR53c7x0_cmd
* structure pointer .
*
* Inputs ; issue - pointer to start of NOP or JUMP instruction
* in issue array .
*
* Returns : pointer to command on success ; 0 if opcode is NOP .
*/
static inline struct NCR53c7x0_cmd *
issue_to_cmd ( struct Scsi_Host * host , struct NCR53c7x0_hostdata * hostdata ,
u32 * issue )
{
return ( issue [ 0 ] ! = hostdata - > NOP_insn ) ?
/*
* If the IF TRUE bit is set , it ' s a JUMP instruction . The
* operand is a bus pointer to the dsa_begin routine for this DSA . The
* dsa field of the NCR53c7x0_cmd structure starts with the
* DSA code template . By converting to a virtual address ,
* subtracting the code template size , and offset of the
* dsa field , we end up with a pointer to the start of the
* structure ( alternatively , we could use the
* dsa_cmnd field , an anachronism from when we weren ' t
* sure what the relationship between the NCR structures
* and host structures were going to be .
*/
( struct NCR53c7x0_cmd * ) ( ( char * ) bus_to_virt ( issue [ 1 ] ) -
( hostdata - > E_dsa_code_begin - hostdata - > E_dsa_code_template ) -
offsetof ( struct NCR53c7x0_cmd , dsa ) )
/* If the IF TRUE bit is not set, it's a NOP */
: NULL ;
}
/*
* FIXME : we should junk these , in favor of synchronous_want and
* wide_want in the NCR53c7x0_hostdata structure .
*/
/* Template for "preferred" synchronous transfer parameters. */
static const unsigned char sdtr_message [ ] = {
# ifdef CONFIG_SCSI_NCR53C7xx_FAST
EXTENDED_MESSAGE , 3 /* length */ , EXTENDED_SDTR , 25 /* *4ns */ , 8 /* off */
# else
EXTENDED_MESSAGE , 3 /* length */ , EXTENDED_SDTR , 50 /* *4ns */ , 8 /* off */
# endif
} ;
/* Template to request asynchronous transfers */
static const unsigned char async_message [ ] = {
EXTENDED_MESSAGE , 3 /* length */ , EXTENDED_SDTR , 0 , 0 /* asynchronous */
} ;
/* Template for "preferred" WIDE transfer parameters */
static const unsigned char wdtr_message [ ] = {
EXTENDED_MESSAGE , 2 /* length */ , EXTENDED_WDTR , 1 /* 2^1 bytes */
} ;
#if 0
/*
* Function : struct Scsi_Host * find_host ( int host )
*
* Purpose : KGDB support function which translates a host number
* to a host structure .
*
* Inputs : host - number of SCSI host
*
* Returns : NULL on failure , pointer to host structure on success .
*/
static struct Scsi_Host *
find_host ( int host ) {
struct Scsi_Host * h ;
for ( h = first_host ; h & & h - > host_no ! = host ; h = h - > next ) ;
if ( ! h ) {
printk ( KERN_ALERT " scsi%d not found \n " , host ) ;
return NULL ;
} else if ( h - > hostt ! = the_template ) {
printk ( KERN_ALERT " scsi%d is not a NCR board \n " , host ) ;
return NULL ;
}
return h ;
}
#if 0
/*
* Function : request_synchronous ( int host , int target )
*
* Purpose : KGDB interface which will allow us to negotiate for
* synchronous transfers . This ill be replaced with a more
* integrated function ; perhaps a new entry in the scsi_host
* structure , accessible via an ioctl ( ) or perhaps / proc / scsi .
*
* Inputs : host - number of SCSI host ; target - number of target .
*
* Returns : 0 when negotiation has been setup for next SCSI command ,
* - 1 on failure .
*/
static int
request_synchronous ( int host , int target ) {
struct Scsi_Host * h ;
struct NCR53c7x0_hostdata * hostdata ;
unsigned long flags ;
if ( target < 0 ) {
printk ( KERN_ALERT " target %d is bogus \n " , target ) ;
return - 1 ;
}
if ( ! ( h = find_host ( host ) ) )
return - 1 ;
else if ( h - > this_id = = target ) {
printk ( KERN_ALERT " target %d is host ID \n " , target ) ;
return - 1 ;
}
else if ( target > h - > max_id ) {
printk ( KERN_ALERT " target %d exceeds maximum of %d \n " , target ,
h - > max_id ) ;
return - 1 ;
}
hostdata = ( struct NCR53c7x0_hostdata * ) h - > hostdata [ 0 ] ;
local_irq_save ( flags ) ;
if ( hostdata - > initiate_sdtr & ( 1 < < target ) ) {
local_irq_restore ( flags ) ;
printk ( KERN_ALERT " target %d already doing SDTR \n " , target ) ;
return - 1 ;
}
hostdata - > initiate_sdtr | = ( 1 < < target ) ;
local_irq_restore ( flags ) ;
return 0 ;
}
# endif
/*
* Function : request_disconnect ( int host , int on_or_off )
*
* Purpose : KGDB support function , tells us to allow or disallow
* disconnections .
*
* Inputs : host - number of SCSI host ; on_or_off - non - zero to allow ,
* zero to disallow .
*
* Returns : 0 on success , * - 1 on failure .
*/
static int
request_disconnect ( int host , int on_or_off ) {
struct Scsi_Host * h ;
struct NCR53c7x0_hostdata * hostdata ;
if ( ! ( h = find_host ( host ) ) )
return - 1 ;
hostdata = ( struct NCR53c7x0_hostdata * ) h - > hostdata [ 0 ] ;
if ( on_or_off )
hostdata - > options | = OPTION_DISCONNECT ;
else
hostdata - > options & = ~ OPTION_DISCONNECT ;
return 0 ;
}
# endif
/*
* Function : static void NCR53c7x0_driver_init ( struct Scsi_Host * host )
*
* Purpose : Initialize internal structures , as required on startup , or
* after a SCSI bus reset .
*
* Inputs : host - pointer to this host adapter ' s structure
*/
static void
NCR53c7x0_driver_init ( struct Scsi_Host * host ) {
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
int i , j ;
u32 * ncrcurrent ;
for ( i = 0 ; i < 16 ; + + i ) {
hostdata - > request_sense [ i ] = 0 ;
for ( j = 0 ; j < 8 ; + + j )
hostdata - > busy [ i ] [ j ] = 0 ;
set_synchronous ( host , i , /* sxfer */ 0 , hostdata - > saved_scntl3 , 0 ) ;
}
hostdata - > issue_queue = NULL ;
hostdata - > running_list = hostdata - > finished_queue =
hostdata - > ncrcurrent = NULL ;
for ( i = 0 , ncrcurrent = ( u32 * ) hostdata - > schedule ;
i < host - > can_queue ; + + i , ncrcurrent + = 2 ) {
ncrcurrent [ 0 ] = hostdata - > NOP_insn ;
ncrcurrent [ 1 ] = 0xdeadbeef ;
}
ncrcurrent [ 0 ] = ( ( DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP ) < < 24 ) | DBC_TCI_TRUE ;
ncrcurrent [ 1 ] = ( u32 ) virt_to_bus ( hostdata - > script ) +
hostdata - > E_wait_reselect ;
hostdata - > reconnect_dsa_head = 0 ;
hostdata - > addr_reconnect_dsa_head = ( u32 )
virt_to_bus ( ( void * ) & ( hostdata - > reconnect_dsa_head ) ) ;
hostdata - > expecting_iid = 0 ;
hostdata - > expecting_sto = 0 ;
if ( hostdata - > options & OPTION_ALWAYS_SYNCHRONOUS )
hostdata - > initiate_sdtr = 0xffff ;
else
hostdata - > initiate_sdtr = 0 ;
hostdata - > talked_to = 0 ;
hostdata - > idle = 1 ;
}
/*
* Function : static int clock_to_ccf_710 ( int clock )
*
* Purpose : Return the clock conversion factor for a given SCSI clock .
*
* Inputs : clock - SCSI clock expressed in Hz .
*
* Returns : ccf on success , - 1 on failure .
*/
static int
clock_to_ccf_710 ( int clock ) {
if ( clock < = 16666666 )
return - 1 ;
if ( clock < = 25000000 )
return 2 ; /* Divide by 1.0 */
else if ( clock < = 37500000 )
return 1 ; /* Divide by 1.5 */
else if ( clock < = 50000000 )
return 0 ; /* Divide by 2.0 */
else if ( clock < = 66000000 )
return 3 ; /* Divide by 3.0 */
else
return - 1 ;
}
/*
* Function : static int NCR53c7x0_init ( struct Scsi_Host * host )
*
* Purpose : initialize the internal structures for a given SCSI host
*
* Inputs : host - pointer to this host adapter ' s structure
*
* Preconditions : when this function is called , the chip_type
* field of the hostdata structure MUST have been set .
*
* Returns : 0 on success , - 1 on failure .
*/
int
NCR53c7x0_init ( struct Scsi_Host * host ) {
NCR53c7x0_local_declare ( ) ;
int i , ccf ;
unsigned char revision ;
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
/*
* There are some things which we need to know about in order to provide
* a semblance of support . Print ' em if they aren ' t what we expect ,
* otherwise don ' t add to the noise .
*
* - 1 means we don ' t know what to expect .
*/
int val , flags ;
char buf [ 32 ] ;
int expected_id = - 1 ;
int expected_clock = - 1 ;
int uninitialized = 0 ;
# ifdef NO_IO_SPACE
int expected_mapping = OPTION_MEMORY_MAPPED ;
# else
int expected_mapping = OPTION_IO_MAPPED ;
# endif
for ( i = 0 ; i < 7 ; i + + )
hostdata - > valid_ids [ i ] = 1 ; /* Default all ID's to scan */
/* Parse commandline flags */
if ( check_setup_strings ( " noasync " , & flags , & val , buf ) )
{
hostdata - > options | = OPTION_NO_ASYNC ;
hostdata - > options & = ~ ( OPTION_SYNCHRONOUS | OPTION_ALWAYS_SYNCHRONOUS ) ;
}
if ( check_setup_strings ( " nosync " , & flags , & val , buf ) )
{
hostdata - > options & = ~ ( OPTION_SYNCHRONOUS | OPTION_ALWAYS_SYNCHRONOUS ) ;
}
if ( check_setup_strings ( " nodisconnect " , & flags , & val , buf ) )
hostdata - > options & = ~ OPTION_DISCONNECT ;
if ( check_setup_strings ( " validids " , & flags , & val , buf ) )
{
for ( i = 0 ; i < 7 ; i + + )
hostdata - > valid_ids [ i ] = val & ( 1 < < i ) ;
}
if ( ( i = check_setup_strings ( " next " , & flags , & val , buf ) ) )
{
while ( i )
setup_used [ - - i ] = 1 ;
}
if ( check_setup_strings ( " opthi " , & flags , & val , buf ) )
hostdata - > options = ( long long ) val < < 32 ;
if ( check_setup_strings ( " optlo " , & flags , & val , buf ) )
hostdata - > options | = val ;
NCR53c7x0_local_setup ( host ) ;
switch ( hostdata - > chip ) {
case 710 :
case 770 :
hostdata - > dstat_sir_intr = NCR53c7x0_dstat_sir_intr ;
hostdata - > init_save_regs = NULL ;
hostdata - > dsa_fixup = NCR53c7xx_dsa_fixup ;
hostdata - > init_fixup = NCR53c7x0_init_fixup ;
hostdata - > soft_reset = NCR53c7x0_soft_reset ;
hostdata - > run_tests = NCR53c7xx_run_tests ;
expected_clock = hostdata - > scsi_clock ;
expected_id = 7 ;
break ;
default :
printk ( " scsi%d : chip type of %d is not supported yet, detaching. \n " ,
host - > host_no , hostdata - > chip ) ;
scsi_unregister ( host ) ;
return - 1 ;
}
/* Assign constants accessed by NCR */
hostdata - > NCR53c7xx_zero = 0 ;
hostdata - > NCR53c7xx_msg_reject = MESSAGE_REJECT ;
hostdata - > NCR53c7xx_msg_abort = ABORT ;
hostdata - > NCR53c7xx_msg_nop = NOP ;
hostdata - > NOP_insn = ( DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP ) < < 24 ;
if ( expected_mapping = = - 1 | |
( hostdata - > options & ( OPTION_MEMORY_MAPPED ) ) ! =
( expected_mapping & OPTION_MEMORY_MAPPED ) )
printk ( " scsi%d : using %s mapped access \n " , host - > host_no ,
( hostdata - > options & OPTION_MEMORY_MAPPED ) ? " memory " :
" io " ) ;
hostdata - > dmode = ( hostdata - > chip = = 700 | | hostdata - > chip = = 70066 ) ?
DMODE_REG_00 : DMODE_REG_10 ;
hostdata - > istat = ( ( hostdata - > chip / 100 ) = = 8 ) ?
ISTAT_REG_800 : ISTAT_REG_700 ;
/* We have to assume that this may be the first access to the chip, so
* we must set EA in DCNTL . */
NCR53c7x0_write8 ( DCNTL_REG , DCNTL_10_EA | DCNTL_10_COM ) ;
/* Only the ISTAT register is readable when the NCR is running, so make
sure it ' s halted . */
ncr_halt ( host ) ;
/*
* XXX - the NCR53c700 uses bitfielded registers for SCID , SDID , etc ,
* as does the 710 with one bit per SCSI ID . Conversely , the NCR
* uses a normal , 3 bit binary representation of these values .
*
* Get the rest of the NCR documentation , and FIND OUT where the change
* was .
*/
#if 0
/* May not be able to do this - chip my not have been set up yet */
tmp = hostdata - > this_id_mask = NCR53c7x0_read8 ( SCID_REG ) ;
for ( host - > this_id = 0 ; tmp ! = 1 ; tmp > > = 1 , + + host - > this_id ) ;
# else
host - > this_id = 7 ;
# endif
/*
* Note : we should never encounter a board setup for ID0 . So ,
* if we see ID0 , assume that it was uninitialized and set it
* to the industry standard 7.
*/
if ( ! host - > this_id ) {
printk ( " scsi%d : initiator ID was %d, changing to 7 \n " ,
host - > host_no , host - > this_id ) ;
host - > this_id = 7 ;
hostdata - > this_id_mask = 1 < < 7 ;
uninitialized = 1 ;
} ;
if ( expected_id = = - 1 | | host - > this_id ! = expected_id )
printk ( " scsi%d : using initiator ID %d \n " , host - > host_no ,
host - > this_id ) ;
/*
* Save important registers to allow a soft reset .
*/
/*
* CTEST7 controls cache snooping , burst mode , and support for
* external differential drivers . This isn ' t currently used - the
* default value may not be optimal anyway .
* Even worse , it may never have been set up since reset .
*/
hostdata - > saved_ctest7 = NCR53c7x0_read8 ( CTEST7_REG ) & CTEST7_SAVE ;
revision = ( NCR53c7x0_read8 ( CTEST8_REG ) & 0xF0 ) > > 4 ;
switch ( revision ) {
case 1 : revision = 0 ; break ;
case 2 : revision = 1 ; break ;
case 4 : revision = 2 ; break ;
case 8 : revision = 3 ; break ;
default : revision = 255 ; break ;
}
printk ( " scsi%d: Revision 0x%x \n " , host - > host_no , revision ) ;
if ( ( revision = = 0 | | revision = = 255 ) & & ( hostdata - > options & ( OPTION_SYNCHRONOUS | OPTION_DISCONNECT | OPTION_ALWAYS_SYNCHRONOUS ) ) )
{
printk ( " scsi%d: Disabling sync working and disconnect/reselect \n " ,
host - > host_no ) ;
hostdata - > options & = ~ ( OPTION_SYNCHRONOUS | OPTION_DISCONNECT | OPTION_ALWAYS_SYNCHRONOUS ) ;
}
/*
* On NCR53c700 series chips , DCNTL controls the SCSI clock divisor ,
* on 800 series chips , it allows for a totem - pole IRQ driver .
* NOTE saved_dcntl currently overwritten in init function .
* The value read here may be garbage anyway , MVME16x board at least
* does not initialise chip if kernel arrived via tftp .
*/
hostdata - > saved_dcntl = NCR53c7x0_read8 ( DCNTL_REG ) ;
/*
* DMODE controls DMA burst length , and on 700 series chips ,
* 286 mode and bus width
* NOTE : On MVME16x , chip may have been reset , so this could be a
* power - on / reset default value .
*/
hostdata - > saved_dmode = NCR53c7x0_read8 ( hostdata - > dmode ) ;
/*
* Now that burst length and enabled / disabled status is known ,
* clue the user in on it .
*/
ccf = clock_to_ccf_710 ( expected_clock ) ;
for ( i = 0 ; i < 16 ; + + i )
hostdata - > cmd_allocated [ i ] = 0 ;
if ( hostdata - > init_save_regs )
hostdata - > init_save_regs ( host ) ;
if ( hostdata - > init_fixup )
hostdata - > init_fixup ( host ) ;
if ( ! the_template ) {
the_template = host - > hostt ;
first_host = host ;
}
/*
* Linux SCSI drivers have always been plagued with initialization
* problems - some didn ' t work with the BIOS disabled since they expected
* initialization from it , some didn ' t work when the networking code
* was enabled and registers got scrambled , etc .
*
* To avoid problems like this , in the future , we will do a soft
* reset on the SCSI chip , taking it back to a sane state .
*/
hostdata - > soft_reset ( host ) ;
# if 1
hostdata - > debug_count_limit = - 1 ;
# else
hostdata - > debug_count_limit = 1 ;
# endif
hostdata - > intrs = - 1 ;
hostdata - > resets = - 1 ;
memcpy ( ( void * ) hostdata - > synchronous_want , ( void * ) sdtr_message ,
sizeof ( hostdata - > synchronous_want ) ) ;
NCR53c7x0_driver_init ( host ) ;
if ( request_irq ( host - > irq , NCR53c7x0_intr , SA_SHIRQ , " 53c7xx " , host ) )
{
printk ( " scsi%d : IRQ%d not free, detaching \n " ,
host - > host_no , host - > irq ) ;
goto err_unregister ;
}
if ( ( hostdata - > run_tests & & hostdata - > run_tests ( host ) = = - 1 ) | |
( hostdata - > options & OPTION_DEBUG_TESTS_ONLY ) ) {
/* XXX Should disable interrupts, etc. here */
goto err_free_irq ;
} else {
if ( host - > io_port ) {
host - > n_io_port = 128 ;
if ( ! request_region ( host - > io_port , host - > n_io_port , " ncr53c7xx " ) )
goto err_free_irq ;
}
}
if ( NCR53c7x0_read8 ( SBCL_REG ) & SBCL_BSY ) {
printk ( " scsi%d : bus wedge, doing SCSI reset \n " , host - > host_no ) ;
hard_reset ( host ) ;
}
return 0 ;
err_free_irq :
free_irq ( host - > irq , NCR53c7x0_intr ) ;
err_unregister :
scsi_unregister ( host ) ;
return - 1 ;
}
/*
* Function : int ncr53c7xx_init ( Scsi_Host_Template * tpnt , int board , int chip ,
* unsigned long base , int io_port , int irq , int dma , long long options ,
* int clock ) ;
*
* Purpose : initializes a NCR53c7 , 8 x0 based on base addresses ,
* IRQ , and DMA channel .
*
* Inputs : tpnt - Template for this SCSI adapter , board - board level
* product , chip - 710
*
* Returns : 0 on success , - 1 on failure .
*
*/
int
ncr53c7xx_init ( Scsi_Host_Template * tpnt , int board , int chip ,
unsigned long base , int io_port , int irq , int dma ,
long long options , int clock )
{
struct Scsi_Host * instance ;
struct NCR53c7x0_hostdata * hostdata ;
char chip_str [ 80 ] ;
int script_len = 0 , dsa_len = 0 , size = 0 , max_cmd_size = 0 ,
schedule_size = 0 , ok = 0 ;
void * tmp ;
unsigned long page ;
switch ( chip ) {
case 710 :
case 770 :
schedule_size = ( tpnt - > can_queue + 1 ) * 8 /* JUMP instruction size */ ;
script_len = NCR53c7xx_script_len ;
dsa_len = NCR53c7xx_dsa_len ;
options | = OPTION_INTFLY ;
sprintf ( chip_str , " NCR53c%d " , chip ) ;
break ;
default :
printk ( " scsi-ncr53c7xx : unsupported SCSI chip %d \n " , chip ) ;
return - 1 ;
}
printk ( " scsi-ncr53c7xx : %s at memory 0x%lx, io 0x%x, irq %d " ,
chip_str , base , io_port , irq ) ;
if ( dma = = DMA_NONE )
printk ( " \n " ) ;
else
printk ( " , dma %d \n " , dma ) ;
if ( options & OPTION_DEBUG_PROBE_ONLY ) {
printk ( " scsi-ncr53c7xx : probe only enabled, aborting initialization \n " ) ;
return - 1 ;
}
max_cmd_size = sizeof ( struct NCR53c7x0_cmd ) + dsa_len +
/* Size of dynamic part of command structure : */
2 * /* Worst case : we don't know if we need DATA IN or DATA out */
( 2 * /* Current instructions per scatter/gather segment */
tpnt - > sg_tablesize +
3 /* Current startup / termination required per phase */
) *
8 /* Each instruction is eight bytes */ ;
/* Allocate fixed part of hostdata, dynamic part to hold appropriate
SCSI SCRIPT ( tm ) plus a single , maximum - sized NCR53c7x0_cmd structure .
We need a NCR53c7x0_cmd structure for scan_scsis ( ) when we are
not loaded as a module , and when we ' re loaded as a module , we
can ' t use a non - dynamically allocated structure because modules
are vmalloc ( ) ' d , which can allow structures to cross page
boundaries and breaks our physical / virtual address assumptions
for DMA .
So , we stick it past the end of our hostdata structure .
ASSUMPTION :
Regardless of how many simultaneous SCSI commands we allow ,
the probe code only executes a _single_ instruction at a time ,
so we only need one here , and don ' t need to allocate NCR53c7x0_cmd
structures for each target until we are no longer in scan_scsis
and kmalloc ( ) has become functional ( memory_init ( ) happens
after all device driver initialization ) .
*/
size = sizeof ( struct NCR53c7x0_hostdata ) + script_len +
/* Note that alignment will be guaranteed, since we put the command
allocated at probe time after the fixed - up SCSI script , which
consists of 32 bit words , aligned on a 32 bit boundary . But
on a 64 bit machine we need 8 byte alignment for hostdata - > free , so
we add in another 4 bytes to take care of potential misalignment
*/
( sizeof ( void * ) - sizeof ( u32 ) ) + max_cmd_size + schedule_size ;
page = __get_free_pages ( GFP_ATOMIC , 1 ) ;
if ( page = = 0 )
{
printk ( KERN_ERR " 53c7xx: out of memory. \n " ) ;
return - ENOMEM ;
}
# ifdef FORCE_DSA_ALIGNMENT
/*
* 53 c710 rev .0 doesn ' t have an add - with - carry instruction .
* Ensure we allocate enough memory to force DSA alignment .
*/
size + = 256 ;
# endif
/* Size should be < 8K, so we can fit it in two pages. */
if ( size > 8192 ) {
printk ( KERN_ERR " 53c7xx: hostdata > 8K \n " ) ;
return - 1 ;
}
instance = scsi_register ( tpnt , 4 ) ;
if ( ! instance )
{
free_page ( page ) ;
return - 1 ;
}
instance - > hostdata [ 0 ] = page ;
memset ( ( void * ) instance - > hostdata [ 0 ] , 0 , 8192 ) ;
cache_push ( virt_to_phys ( ( void * ) ( instance - > hostdata [ 0 ] ) ) , 8192 ) ;
cache_clear ( virt_to_phys ( ( void * ) ( instance - > hostdata [ 0 ] ) ) , 8192 ) ;
kernel_set_cachemode ( ( void * ) instance - > hostdata [ 0 ] , 8192 , IOMAP_NOCACHE_SER ) ;
/* FIXME : if we ever support an ISA NCR53c7xx based board, we
need to check if the chip is running in a 16 bit mode , and if so
unregister it if it is past the 16 M ( 0x1000000 ) mark */
hostdata = ( struct NCR53c7x0_hostdata * ) instance - > hostdata [ 0 ] ;
hostdata - > size = size ;
hostdata - > script_count = script_len / sizeof ( u32 ) ;
hostdata - > board = board ;
hostdata - > chip = chip ;
/*
* Being memory mapped is more desirable , since
*
* - Memory accesses may be faster .
*
* - The destination and source address spaces are the same for
* all instructions , meaning we don ' t have to twiddle dmode or
* any other registers .
*
* So , we try for memory mapped , and if we don ' t get it ,
* we go for port mapped , and that failing we tell the user
* it can ' t work .
*/
if ( base ) {
instance - > base = base ;
/* Check for forced I/O mapping */
if ( ! ( options & OPTION_IO_MAPPED ) ) {
options | = OPTION_MEMORY_MAPPED ;
ok = 1 ;
}
} else {
options & = ~ OPTION_MEMORY_MAPPED ;
}
if ( io_port ) {
instance - > io_port = io_port ;
options | = OPTION_IO_MAPPED ;
ok = 1 ;
} else {
options & = ~ OPTION_IO_MAPPED ;
}
if ( ! ok ) {
printk ( " scsi%d : not initializing, no I/O or memory mapping known \n " ,
instance - > host_no ) ;
scsi_unregister ( instance ) ;
return - 1 ;
}
instance - > irq = irq ;
instance - > dma_channel = dma ;
hostdata - > options = options ;
hostdata - > dsa_len = dsa_len ;
hostdata - > max_cmd_size = max_cmd_size ;
hostdata - > num_cmds = 1 ;
hostdata - > scsi_clock = clock ;
/* Initialize single command */
tmp = ( hostdata - > script + hostdata - > script_count ) ;
# ifdef FORCE_DSA_ALIGNMENT
{
void * t = ROUNDUP ( tmp , void * ) ;
if ( ( ( u32 ) t & 0xff ) > CmdPageStart )
t = ( void * ) ( ( u32 ) t + 255 ) ;
t = ( void * ) ( ( ( u32 ) t & ~ 0xff ) + CmdPageStart ) ;
hostdata - > free = t ;
#if 0
printk ( " scsi: Registered size increased by 256 to %d \n " , size ) ;
printk ( " scsi: CmdPageStart = 0x%02x \n " , CmdPageStart ) ;
printk ( " scsi: tmp = 0x%08x, hostdata->free set to 0x%08x \n " ,
( u32 ) tmp , ( u32 ) t ) ;
# endif
}
# else
hostdata - > free = ROUNDUP ( tmp , void * ) ;
# endif
hostdata - > free - > real = tmp ;
hostdata - > free - > size = max_cmd_size ;
hostdata - > free - > free = NULL ;
hostdata - > free - > next = NULL ;
hostdata - > extra_allocate = 0 ;
/* Allocate command start code space */
hostdata - > schedule = ( chip = = 700 | | chip = = 70066 ) ?
NULL : ( u32 * ) ( ( char * ) hostdata - > free + max_cmd_size ) ;
/*
* For diagnostic purposes , we don ' t really care how fast things blaze .
* For profiling , we want to access the 800 ns resolution system clock ,
* using a ' C ' call on the host processor .
*
* Therefore , there ' s no need for the NCR chip to directly manipulate
* this data , and we should put it wherever is most convenient for
* Linux .
*/
if ( track_events )
hostdata - > events = ( struct NCR53c7x0_event * ) ( track_events ?
vmalloc ( sizeof ( struct NCR53c7x0_event ) * track_events ) : NULL ) ;
else
hostdata - > events = NULL ;
if ( hostdata - > events ) {
memset ( ( void * ) hostdata - > events , 0 , sizeof ( struct NCR53c7x0_event ) *
track_events ) ;
hostdata - > event_size = track_events ;
hostdata - > event_index = 0 ;
} else
hostdata - > event_size = 0 ;
return NCR53c7x0_init ( instance ) ;
}
/*
* Function : static void NCR53c7x0_init_fixup ( struct Scsi_Host * host )
*
* Purpose : copy and fixup the SCSI SCRIPTS ( tm ) code for this device .
*
* Inputs : host - pointer to this host adapter ' s structure
*
*/
static void
NCR53c7x0_init_fixup ( struct Scsi_Host * host ) {
NCR53c7x0_local_declare ( ) ;
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
unsigned char tmp ;
int i , ncr_to_memory , memory_to_ncr ;
u32 base ;
NCR53c7x0_local_setup ( host ) ;
/* XXX - NOTE : this code MUST be made endian aware */
/* Copy code into buffer that was allocated at detection time. */
memcpy ( ( void * ) hostdata - > script , ( void * ) SCRIPT ,
sizeof ( SCRIPT ) ) ;
/* Fixup labels */
for ( i = 0 ; i < PATCHES ; + + i )
hostdata - > script [ LABELPATCHES [ i ] ] + =
virt_to_bus ( hostdata - > script ) ;
/* Fixup addresses of constants that used to be EXTERNAL */
patch_abs_32 ( hostdata - > script , 0 , NCR53c7xx_msg_abort ,
virt_to_bus ( & ( hostdata - > NCR53c7xx_msg_abort ) ) ) ;
patch_abs_32 ( hostdata - > script , 0 , NCR53c7xx_msg_reject ,
virt_to_bus ( & ( hostdata - > NCR53c7xx_msg_reject ) ) ) ;
patch_abs_32 ( hostdata - > script , 0 , NCR53c7xx_zero ,
virt_to_bus ( & ( hostdata - > NCR53c7xx_zero ) ) ) ;
patch_abs_32 ( hostdata - > script , 0 , NCR53c7xx_sink ,
virt_to_bus ( & ( hostdata - > NCR53c7xx_sink ) ) ) ;
patch_abs_32 ( hostdata - > script , 0 , NOP_insn ,
virt_to_bus ( & ( hostdata - > NOP_insn ) ) ) ;
patch_abs_32 ( hostdata - > script , 0 , schedule ,
virt_to_bus ( ( void * ) hostdata - > schedule ) ) ;
/* Fixup references to external variables: */
for ( i = 0 ; i < EXTERNAL_PATCHES_LEN ; + + i )
hostdata - > script [ EXTERNAL_PATCHES [ i ] . offset ] + =
virt_to_bus ( EXTERNAL_PATCHES [ i ] . address ) ;
/*
* Fixup absolutes set at boot - time .
*
* All non - code absolute variables suffixed with " dsa_ " and " int_ "
* are constants , and need no fixup provided the assembler has done
* it for us ( I don ' t know what the " real " NCR assembler does in
* this case , my assembler does the right magic ) .
*/
patch_abs_rwri_data ( hostdata - > script , 0 , dsa_save_data_pointer ,
Ent_dsa_code_save_data_pointer - Ent_dsa_zero ) ;
patch_abs_rwri_data ( hostdata - > script , 0 , dsa_restore_pointers ,
Ent_dsa_code_restore_pointers - Ent_dsa_zero ) ;
patch_abs_rwri_data ( hostdata - > script , 0 , dsa_check_reselect ,
Ent_dsa_code_check_reselect - Ent_dsa_zero ) ;
/*
* Just for the hell of it , preserve the settings of
* Burst Length and Enable Read Line bits from the DMODE
* register . Make sure SCRIPTS start automagically .
*/
# if defined(CONFIG_MVME16x) || defined(CONFIG_BVME6000)
/* We know better what we want than 16xBug does! */
tmp = DMODE_10_BL_8 | DMODE_10_FC2 ;
# else
tmp = NCR53c7x0_read8 ( DMODE_REG_10 ) ;
tmp & = ( DMODE_BL_MASK | DMODE_10_FC2 | DMODE_10_FC1 | DMODE_710_PD |
DMODE_710_UO ) ;
# endif
if ( ! ( hostdata - > options & OPTION_MEMORY_MAPPED ) ) {
base = ( u32 ) host - > io_port ;
memory_to_ncr = tmp | DMODE_800_DIOM ;
ncr_to_memory = tmp | DMODE_800_SIOM ;
} else {
base = virt_to_bus ( ( void * ) host - > base ) ;
memory_to_ncr = ncr_to_memory = tmp ;
}
/* SCRATCHB_REG_10 == SCRATCHA_REG_800, as it happens */
patch_abs_32 ( hostdata - > script , 0 , addr_scratch , base + SCRATCHA_REG_800 ) ;
patch_abs_32 ( hostdata - > script , 0 , addr_temp , base + TEMP_REG ) ;
patch_abs_32 ( hostdata - > script , 0 , addr_dsa , base + DSA_REG ) ;
/*
* I needed some variables in the script to be accessible to
* both the NCR chip and the host processor . For these variables ,
* I made the arbitrary decision to store them directly in the
* hostdata structure rather than in the RELATIVE area of the
* SCRIPTS .
*/
patch_abs_rwri_data ( hostdata - > script , 0 , dmode_memory_to_memory , tmp ) ;
patch_abs_rwri_data ( hostdata - > script , 0 , dmode_memory_to_ncr , memory_to_ncr ) ;
patch_abs_rwri_data ( hostdata - > script , 0 , dmode_ncr_to_memory , ncr_to_memory ) ;
patch_abs_32 ( hostdata - > script , 0 , msg_buf ,
virt_to_bus ( ( void * ) & ( hostdata - > msg_buf ) ) ) ;
patch_abs_32 ( hostdata - > script , 0 , reconnect_dsa_head ,
virt_to_bus ( ( void * ) & ( hostdata - > reconnect_dsa_head ) ) ) ;
patch_abs_32 ( hostdata - > script , 0 , addr_reconnect_dsa_head ,
virt_to_bus ( ( void * ) & ( hostdata - > addr_reconnect_dsa_head ) ) ) ;
patch_abs_32 ( hostdata - > script , 0 , reselected_identify ,
virt_to_bus ( ( void * ) & ( hostdata - > reselected_identify ) ) ) ;
/* reselected_tag is currently unused */
#if 0
patch_abs_32 ( hostdata - > script , 0 , reselected_tag ,
virt_to_bus ( ( void * ) & ( hostdata - > reselected_tag ) ) ) ;
# endif
patch_abs_32 ( hostdata - > script , 0 , test_dest ,
virt_to_bus ( ( void * ) & hostdata - > test_dest ) ) ;
patch_abs_32 ( hostdata - > script , 0 , test_src ,
virt_to_bus ( & hostdata - > test_source ) ) ;
patch_abs_32 ( hostdata - > script , 0 , saved_dsa ,
virt_to_bus ( ( void * ) & hostdata - > saved2_dsa ) ) ;
patch_abs_32 ( hostdata - > script , 0 , emulfly ,
virt_to_bus ( ( void * ) & hostdata - > emulated_intfly ) ) ;
patch_abs_rwri_data ( hostdata - > script , 0 , dsa_check_reselect ,
( unsigned char ) ( Ent_dsa_code_check_reselect - Ent_dsa_zero ) ) ;
/* These are for event logging; the ncr_event enum contains the
actual interrupt numbers . */
# ifdef A_int_EVENT_SELECT
patch_abs_32 ( hostdata - > script , 0 , int_EVENT_SELECT , ( u32 ) EVENT_SELECT ) ;
# endif
# ifdef A_int_EVENT_DISCONNECT
patch_abs_32 ( hostdata - > script , 0 , int_EVENT_DISCONNECT , ( u32 ) EVENT_DISCONNECT ) ;
# endif
# ifdef A_int_EVENT_RESELECT
patch_abs_32 ( hostdata - > script , 0 , int_EVENT_RESELECT , ( u32 ) EVENT_RESELECT ) ;
# endif
# ifdef A_int_EVENT_COMPLETE
patch_abs_32 ( hostdata - > script , 0 , int_EVENT_COMPLETE , ( u32 ) EVENT_COMPLETE ) ;
# endif
# ifdef A_int_EVENT_IDLE
patch_abs_32 ( hostdata - > script , 0 , int_EVENT_IDLE , ( u32 ) EVENT_IDLE ) ;
# endif
# ifdef A_int_EVENT_SELECT_FAILED
patch_abs_32 ( hostdata - > script , 0 , int_EVENT_SELECT_FAILED ,
( u32 ) EVENT_SELECT_FAILED ) ;
# endif
# ifdef A_int_EVENT_BEFORE_SELECT
patch_abs_32 ( hostdata - > script , 0 , int_EVENT_BEFORE_SELECT ,
( u32 ) EVENT_BEFORE_SELECT ) ;
# endif
# ifdef A_int_EVENT_RESELECT_FAILED
patch_abs_32 ( hostdata - > script , 0 , int_EVENT_RESELECT_FAILED ,
( u32 ) EVENT_RESELECT_FAILED ) ;
# endif
/*
* Make sure the NCR and Linux code agree on the location of
* certain fields .
*/
hostdata - > E_accept_message = Ent_accept_message ;
hostdata - > E_command_complete = Ent_command_complete ;
hostdata - > E_cmdout_cmdout = Ent_cmdout_cmdout ;
hostdata - > E_data_transfer = Ent_data_transfer ;
hostdata - > E_debug_break = Ent_debug_break ;
hostdata - > E_dsa_code_template = Ent_dsa_code_template ;
hostdata - > E_dsa_code_template_end = Ent_dsa_code_template_end ;
hostdata - > E_end_data_transfer = Ent_end_data_transfer ;
hostdata - > E_initiator_abort = Ent_initiator_abort ;
hostdata - > E_msg_in = Ent_msg_in ;
hostdata - > E_other_transfer = Ent_other_transfer ;
hostdata - > E_other_in = Ent_other_in ;
hostdata - > E_other_out = Ent_other_out ;
hostdata - > E_reject_message = Ent_reject_message ;
hostdata - > E_respond_message = Ent_respond_message ;
hostdata - > E_select = Ent_select ;
hostdata - > E_select_msgout = Ent_select_msgout ;
hostdata - > E_target_abort = Ent_target_abort ;
# ifdef Ent_test_0
hostdata - > E_test_0 = Ent_test_0 ;
# endif
hostdata - > E_test_1 = Ent_test_1 ;
hostdata - > E_test_2 = Ent_test_2 ;
# ifdef Ent_test_3
hostdata - > E_test_3 = Ent_test_3 ;
# endif
hostdata - > E_wait_reselect = Ent_wait_reselect ;
hostdata - > E_dsa_code_begin = Ent_dsa_code_begin ;
hostdata - > dsa_cmdout = A_dsa_cmdout ;
hostdata - > dsa_cmnd = A_dsa_cmnd ;
hostdata - > dsa_datain = A_dsa_datain ;
hostdata - > dsa_dataout = A_dsa_dataout ;
hostdata - > dsa_end = A_dsa_end ;
hostdata - > dsa_msgin = A_dsa_msgin ;
hostdata - > dsa_msgout = A_dsa_msgout ;
hostdata - > dsa_msgout_other = A_dsa_msgout_other ;
hostdata - > dsa_next = A_dsa_next ;
hostdata - > dsa_select = A_dsa_select ;
hostdata - > dsa_start = Ent_dsa_code_template - Ent_dsa_zero ;
hostdata - > dsa_status = A_dsa_status ;
hostdata - > dsa_jump_dest = Ent_dsa_code_fix_jump - Ent_dsa_zero +
8 /* destination operand */ ;
/* sanity check */
if ( A_dsa_fields_start ! = Ent_dsa_code_template_end -
Ent_dsa_zero )
printk ( " scsi%d : NCR dsa_fields start is %d not %d \n " ,
host - > host_no , A_dsa_fields_start , Ent_dsa_code_template_end -
Ent_dsa_zero ) ;
printk ( " scsi%d : NCR code relocated to 0x%lx (virt 0x%p) \n " , host - > host_no ,
virt_to_bus ( hostdata - > script ) , hostdata - > script ) ;
}
/*
* Function : static int NCR53c7xx_run_tests ( struct Scsi_Host * host )
*
* Purpose : run various verification tests on the NCR chip ,
* including interrupt generation , and proper bus mastering
* operation .
*
* Inputs : host - a properly initialized Scsi_Host structure
*
* Preconditions : the NCR chip must be in a halted state .
*
* Returns : 0 if all tests were successful , - 1 on error .
*
*/
static int
NCR53c7xx_run_tests ( struct Scsi_Host * host ) {
NCR53c7x0_local_declare ( ) ;
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
unsigned long timeout ;
u32 start ;
int failed , i ;
unsigned long flags ;
NCR53c7x0_local_setup ( host ) ;
/* The NCR chip _must_ be idle to run the test scripts */
local_irq_save ( flags ) ;
if ( ! hostdata - > idle ) {
printk ( " scsi%d : chip not idle, aborting tests \n " , host - > host_no ) ;
local_irq_restore ( flags ) ;
return - 1 ;
}
/*
* Check for functional interrupts , this could work as an
* autoprobe routine .
*/
if ( ( hostdata - > options & OPTION_DEBUG_TEST1 ) & &
hostdata - > state ! = STATE_DISABLED ) {
hostdata - > idle = 0 ;
hostdata - > test_running = 1 ;
hostdata - > test_completed = - 1 ;
hostdata - > test_dest = 0 ;
hostdata - > test_source = 0xdeadbeef ;
start = virt_to_bus ( hostdata - > script ) + hostdata - > E_test_1 ;
hostdata - > state = STATE_RUNNING ;
printk ( " scsi%d : test 1 " , host - > host_no ) ;
NCR53c7x0_write32 ( DSP_REG , start ) ;
if ( hostdata - > options & OPTION_DEBUG_TRACE )
NCR53c7x0_write8 ( DCNTL_REG , hostdata - > saved_dcntl | DCNTL_SSM |
DCNTL_STD ) ;
printk ( " started \n " ) ;
local_irq_restore ( flags ) ;
/*
* This is currently a .5 second timeout , since ( in theory ) no slow
* board will take that long . In practice , we ' ve seen one
* pentium which occassionally fails with this , but works with
* 10 times as much ?
*/
timeout = jiffies + 5 * HZ / 10 ;
while ( ( hostdata - > test_completed = = - 1 ) & & time_before ( jiffies , timeout ) )
barrier ( ) ;
failed = 1 ;
if ( hostdata - > test_completed = = - 1 )
printk ( " scsi%d : driver test 1 timed out%s \n " , host - > host_no ,
( hostdata - > test_dest = = 0xdeadbeef ) ?
" due to lost interrupt. \n "
" Please verify that the correct IRQ is being used for your board, \n "
: " " ) ;
else if ( hostdata - > test_completed ! = 1 )
printk ( " scsi%d : test 1 bad interrupt value (%d) \n " ,
host - > host_no , hostdata - > test_completed ) ;
else
failed = ( hostdata - > test_dest ! = 0xdeadbeef ) ;
if ( hostdata - > test_dest ! = 0xdeadbeef ) {
printk ( " scsi%d : driver test 1 read 0x%x instead of 0xdeadbeef indicating a \n "
" probable cache invalidation problem. Please configure caching \n "
" as write-through or disabled \n " ,
host - > host_no , hostdata - > test_dest ) ;
}
if ( failed ) {
printk ( " scsi%d : DSP = 0x%p (script at 0x%p, start at 0x%x) \n " ,
host - > host_no , bus_to_virt ( NCR53c7x0_read32 ( DSP_REG ) ) ,
hostdata - > script , start ) ;
printk ( " scsi%d : DSPS = 0x%x \n " , host - > host_no ,
NCR53c7x0_read32 ( DSPS_REG ) ) ;
local_irq_restore ( flags ) ;
return - 1 ;
}
hostdata - > test_running = 0 ;
}
if ( ( hostdata - > options & OPTION_DEBUG_TEST2 ) & &
hostdata - > state ! = STATE_DISABLED ) {
u32 dsa [ 48 ] ;
unsigned char identify = IDENTIFY ( 0 , 0 ) ;
unsigned char cmd [ 6 ] ;
unsigned char data [ 36 ] ;
unsigned char status = 0xff ;
unsigned char msg = 0xff ;
cmd [ 0 ] = INQUIRY ;
cmd [ 1 ] = cmd [ 2 ] = cmd [ 3 ] = cmd [ 5 ] = 0 ;
cmd [ 4 ] = sizeof ( data ) ;
dsa [ 2 ] = 1 ;
dsa [ 3 ] = virt_to_bus ( & identify ) ;
dsa [ 4 ] = 6 ;
dsa [ 5 ] = virt_to_bus ( & cmd ) ;
dsa [ 6 ] = sizeof ( data ) ;
dsa [ 7 ] = virt_to_bus ( & data ) ;
dsa [ 8 ] = 1 ;
dsa [ 9 ] = virt_to_bus ( & status ) ;
dsa [ 10 ] = 1 ;
dsa [ 11 ] = virt_to_bus ( & msg ) ;
for ( i = 0 ; i < 6 ; + + i ) {
# ifdef VALID_IDS
if ( ! hostdata - > valid_ids [ i ] )
continue ;
# endif
local_irq_disable ( ) ;
if ( ! hostdata - > idle ) {
printk ( " scsi%d : chip not idle, aborting tests \n " , host - > host_no ) ;
local_irq_restore ( flags ) ;
return - 1 ;
}
/* 710: bit mapped scsi ID, async */
dsa [ 0 ] = ( 1 < < i ) < < 16 ;
hostdata - > idle = 0 ;
hostdata - > test_running = 2 ;
hostdata - > test_completed = - 1 ;
start = virt_to_bus ( hostdata - > script ) + hostdata - > E_test_2 ;
hostdata - > state = STATE_RUNNING ;
NCR53c7x0_write32 ( DSA_REG , virt_to_bus ( dsa ) ) ;
NCR53c7x0_write32 ( DSP_REG , start ) ;
if ( hostdata - > options & OPTION_DEBUG_TRACE )
NCR53c7x0_write8 ( DCNTL_REG , hostdata - > saved_dcntl |
DCNTL_SSM | DCNTL_STD ) ;
local_irq_restore ( flags ) ;
timeout = jiffies + 5 * HZ ; /* arbitrary */
while ( ( hostdata - > test_completed = = - 1 ) & & time_before ( jiffies , timeout ) )
barrier ( ) ;
NCR53c7x0_write32 ( DSA_REG , 0 ) ;
if ( hostdata - > test_completed = = 2 ) {
data [ 35 ] = 0 ;
printk ( " scsi%d : test 2 INQUIRY to target %d, lun 0 : %s \n " ,
host - > host_no , i , data + 8 ) ;
printk ( " scsi%d : status " , host - > host_no ) ;
2005-04-03 23:53:59 +04:00
scsi_print_status ( status ) ;
2005-04-17 02:20:36 +04:00
printk ( " \n scsi%d : message " , host - > host_no ) ;
2005-04-03 23:53:59 +04:00
scsi_print_msg ( & msg ) ;
2005-04-17 02:20:36 +04:00
printk ( " \n " ) ;
} else if ( hostdata - > test_completed = = 3 ) {
printk ( " scsi%d : test 2 no connection with target %d \n " ,
host - > host_no , i ) ;
if ( ! hostdata - > idle ) {
printk ( " scsi%d : not idle \n " , host - > host_no ) ;
local_irq_restore ( flags ) ;
return - 1 ;
}
} else if ( hostdata - > test_completed = = - 1 ) {
printk ( " scsi%d : test 2 timed out \n " , host - > host_no ) ;
local_irq_restore ( flags ) ;
return - 1 ;
}
hostdata - > test_running = 0 ;
}
}
local_irq_restore ( flags ) ;
return 0 ;
}
/*
* Function : static void NCR53c7xx_dsa_fixup ( struct NCR53c7x0_cmd * cmd )
*
* Purpose : copy the NCR53c8xx dsa structure into cmd ' s dsa buffer ,
* performing all necessary relocation .
*
* Inputs : cmd , a NCR53c7x0_cmd structure with a dsa area large
* enough to hold the NCR53c8xx dsa .
*/
static void
NCR53c7xx_dsa_fixup ( struct NCR53c7x0_cmd * cmd ) {
Scsi_Cmnd * c = cmd - > cmd ;
struct Scsi_Host * host = c - > device - > host ;
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
int i ;
memcpy ( cmd - > dsa , hostdata - > script + ( hostdata - > E_dsa_code_template / 4 ) ,
hostdata - > E_dsa_code_template_end - hostdata - > E_dsa_code_template ) ;
/*
* Note : within the NCR ' C ' code , dsa points to the _start_
* of the DSA structure , and _not_ the offset of dsa_zero within
* that structure used to facilitate shorter signed offsets
* for the 8 bit ALU .
*
* The implications of this are that
*
* - 32 bit A_dsa_ * absolute values require an additional
* dsa_zero added to their value to be correct , since they are
* relative to dsa_zero which is in essentially a separate
* space from the code symbols .
*
* - All other symbols require no special treatment .
*/
patch_abs_tci_data ( cmd - > dsa , Ent_dsa_code_template / sizeof ( u32 ) ,
dsa_temp_lun , c - > device - > lun ) ;
patch_abs_32 ( cmd - > dsa , Ent_dsa_code_template / sizeof ( u32 ) ,
dsa_temp_addr_next , virt_to_bus ( & cmd - > dsa_next_addr ) ) ;
patch_abs_32 ( cmd - > dsa , Ent_dsa_code_template / sizeof ( u32 ) ,
dsa_temp_next , virt_to_bus ( cmd - > dsa ) + Ent_dsa_zero -
Ent_dsa_code_template + A_dsa_next ) ;
patch_abs_32 ( cmd - > dsa , Ent_dsa_code_template / sizeof ( u32 ) ,
dsa_temp_sync , virt_to_bus ( ( void * ) hostdata - > sync [ c - > device - > id ] . script ) ) ;
patch_abs_32 ( cmd - > dsa , Ent_dsa_code_template / sizeof ( u32 ) ,
dsa_sscf_710 , virt_to_bus ( ( void * ) & hostdata - > sync [ c - > device - > id ] . sscf_710 ) ) ;
patch_abs_tci_data ( cmd - > dsa , Ent_dsa_code_template / sizeof ( u32 ) ,
dsa_temp_target , 1 < < c - > device - > id ) ;
/* XXX - new pointer stuff */
patch_abs_32 ( cmd - > dsa , Ent_dsa_code_template / sizeof ( u32 ) ,
dsa_temp_addr_saved_pointer , virt_to_bus ( & cmd - > saved_data_pointer ) ) ;
patch_abs_32 ( cmd - > dsa , Ent_dsa_code_template / sizeof ( u32 ) ,
dsa_temp_addr_saved_residual , virt_to_bus ( & cmd - > saved_residual ) ) ;
patch_abs_32 ( cmd - > dsa , Ent_dsa_code_template / sizeof ( u32 ) ,
dsa_temp_addr_residual , virt_to_bus ( & cmd - > residual ) ) ;
/* XXX - new start stuff */
patch_abs_32 ( cmd - > dsa , Ent_dsa_code_template / sizeof ( u32 ) ,
dsa_temp_addr_dsa_value , virt_to_bus ( & cmd - > dsa_addr ) ) ;
}
/*
* Function : run_process_issue_queue ( void )
*
* Purpose : insure that the coroutine is running and will process our
* request . process_issue_queue_running is checked / set here ( in an
* inline function ) rather than in process_issue_queue itself to reduce
* the chances of stack overflow .
*
*/
static volatile int process_issue_queue_running = 0 ;
static __inline__ void
run_process_issue_queue ( void ) {
unsigned long flags ;
local_irq_save ( flags ) ;
if ( ! process_issue_queue_running ) {
process_issue_queue_running = 1 ;
process_issue_queue ( flags ) ;
/*
* process_issue_queue_running is cleared in process_issue_queue
* once it can ' t do more work , and process_issue_queue exits with
* interrupts disabled .
*/
}
local_irq_restore ( flags ) ;
}
/*
* Function : static void abnormal_finished ( struct NCR53c7x0_cmd * cmd , int
* result )
*
* Purpose : mark SCSI command as finished , OR ' ing the host portion
* of the result word into the result field of the corresponding
* Scsi_Cmnd structure , and removing it from the internal queues .
*
* Inputs : cmd - command , result - entire result field
*
* Preconditions : the NCR chip should be in a halted state when
* abnormal_finished is run , since it modifies structures which
* the NCR expects to have exclusive access to .
*/
static void
abnormal_finished ( struct NCR53c7x0_cmd * cmd , int result ) {
Scsi_Cmnd * c = cmd - > cmd ;
struct Scsi_Host * host = c - > device - > host ;
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
unsigned long flags ;
int left , found ;
volatile struct NCR53c7x0_cmd * linux_search ;
volatile struct NCR53c7x0_cmd * volatile * linux_prev ;
volatile u32 * ncr_prev , * ncrcurrent , ncr_search ;
#if 0
printk ( " scsi%d: abnormal finished \n " , host - > host_no ) ;
# endif
local_irq_save ( flags ) ;
found = 0 ;
/*
* Traverse the NCR issue array until we find a match or run out
* of instructions . Instructions in the NCR issue array are
* either JUMP or NOP instructions , which are 2 words in length .
*/
for ( found = 0 , left = host - > can_queue , ncrcurrent = hostdata - > schedule ;
left > 0 ; - - left , ncrcurrent + = 2 )
{
if ( issue_to_cmd ( host , hostdata , ( u32 * ) ncrcurrent ) = = cmd )
{
ncrcurrent [ 0 ] = hostdata - > NOP_insn ;
ncrcurrent [ 1 ] = 0xdeadbeef ;
+ + found ;
break ;
}
}
/*
* Traverse the NCR reconnect list of DSA structures until we find
* a pointer to this dsa or have found too many command structures .
* We let prev point at the next field of the previous element or
* head of the list , so we don ' t do anything different for removing
* the head element .
*/
for ( left = host - > can_queue ,
ncr_search = hostdata - > reconnect_dsa_head ,
ncr_prev = & hostdata - > reconnect_dsa_head ;
left > = 0 & & ncr_search & &
( ( char * ) bus_to_virt ( ncr_search ) + hostdata - > dsa_start )
! = ( char * ) cmd - > dsa ;
ncr_prev = ( u32 * ) ( ( char * ) bus_to_virt ( ncr_search ) +
hostdata - > dsa_next ) , ncr_search = * ncr_prev , - - left ) ;
if ( left < 0 )
printk ( " scsi%d: loop detected in ncr reconncect list \n " ,
host - > host_no ) ;
else if ( ncr_search ) {
if ( found )
printk ( " scsi%d: scsi %ld in ncr issue array and reconnect lists \n " ,
host - > host_no , c - > pid ) ;
else {
volatile u32 * next = ( u32 * )
( ( char * ) bus_to_virt ( ncr_search ) + hostdata - > dsa_next ) ;
* ncr_prev = * next ;
/* If we're at the tail end of the issue queue, update that pointer too. */
found = 1 ;
}
}
/*
* Traverse the host running list until we find this command or discover
* we have too many elements , pointing linux_prev at the next field of the
* linux_previous element or head of the list , search at this element .
*/
for ( left = host - > can_queue , linux_search = hostdata - > running_list ,
linux_prev = & hostdata - > running_list ;
left > = 0 & & linux_search & & linux_search ! = cmd ;
linux_prev = & ( linux_search - > next ) ,
linux_search = linux_search - > next , - - left ) ;
if ( left < 0 )
printk ( " scsi%d: loop detected in host running list for scsi pid %ld \n " ,
host - > host_no , c - > pid ) ;
else if ( linux_search ) {
* linux_prev = linux_search - > next ;
- - hostdata - > busy [ c - > device - > id ] [ c - > device - > lun ] ;
}
/* Return the NCR command structure to the free list */
cmd - > next = hostdata - > free ;
hostdata - > free = cmd ;
c - > host_scribble = NULL ;
/* And return */
c - > result = result ;
c - > scsi_done ( c ) ;
local_irq_restore ( flags ) ;
run_process_issue_queue ( ) ;
}
/*
* Function : static void intr_break ( struct Scsi_Host * host ,
* struct NCR53c7x0_cmd * cmd )
*
* Purpose : Handler for breakpoint interrupts from a SCSI script
*
* Inputs : host - pointer to this host adapter ' s structure ,
* cmd - pointer to the command ( if any ) dsa was pointing
* to .
*
*/
static void
intr_break ( struct Scsi_Host * host , struct
NCR53c7x0_cmd * cmd ) {
NCR53c7x0_local_declare ( ) ;
struct NCR53c7x0_break * bp ;
#if 0
Scsi_Cmnd * c = cmd ? cmd - > cmd : NULL ;
# endif
u32 * dsp ;
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
unsigned long flags ;
NCR53c7x0_local_setup ( host ) ;
/*
* Find the break point corresponding to this address , and
* dump the appropriate debugging information to standard
* output .
*/
local_irq_save ( flags ) ;
dsp = ( u32 * ) bus_to_virt ( NCR53c7x0_read32 ( DSP_REG ) ) ;
for ( bp = hostdata - > breakpoints ; bp & & bp - > address ! = dsp ;
bp = bp - > next ) ;
if ( ! bp )
panic ( " scsi%d : break point interrupt from %p with no breakpoint! " ,
host - > host_no , dsp ) ;
/*
* Configure the NCR chip for manual start mode , so that we can
* point the DSP register at the instruction that follows the
* INT int_debug_break instruction .
*/
NCR53c7x0_write8 ( hostdata - > dmode ,
NCR53c7x0_read8 ( hostdata - > dmode ) | DMODE_MAN ) ;
/*
* And update the DSP register , using the size of the old
* instruction in bytes .
*/
local_irq_restore ( flags ) ;
}
/*
* Function : static void print_synchronous ( const char * prefix ,
* const unsigned char * msg )
*
* Purpose : print a pretty , user and machine parsable representation
* of a SDTR message , including the " real " parameters , data
* clock so we can tell transfer rate at a glance .
*
* Inputs ; prefix - text to prepend , msg - SDTR message ( 5 bytes )
*/
static void
print_synchronous ( const char * prefix , const unsigned char * msg ) {
if ( msg [ 4 ] ) {
int Hz = 1000000000 / ( msg [ 3 ] * 4 ) ;
int integer = Hz / 1000000 ;
int fraction = ( Hz - ( integer * 1000000 ) ) / 10000 ;
printk ( " %speriod %dns offset %d %d.%02dMHz %s SCSI%s \n " ,
prefix , ( int ) msg [ 3 ] * 4 , ( int ) msg [ 4 ] , integer , fraction ,
( ( ( msg [ 3 ] * 4 ) < 200 ) ? " FAST " : " synchronous " ) ,
( ( ( msg [ 3 ] * 4 ) < 200 ) ? " -II " : " " ) ) ;
} else
printk ( " %sasynchronous SCSI \n " , prefix ) ;
}
/*
* Function : static void set_synchronous ( struct Scsi_Host * host ,
* int target , int sxfer , int scntl3 , int now_connected )
*
* Purpose : reprogram transfers between the selected SCSI initiator and
* target with the given register values ; in the indirect
* select operand , reselection script , and chip registers .
*
* Inputs : host - NCR53c7 , 8 xx SCSI host , target - number SCSI target id ,
* sxfer and scntl3 - NCR registers . now_connected - if non - zero ,
* we should reprogram the registers now too .
*
* NOTE : For 53 c710 , scntl3 is actually used for SCF bits from
* SBCL , as we don ' t have a SCNTL3 .
*/
static void
set_synchronous ( struct Scsi_Host * host , int target , int sxfer , int scntl3 ,
int now_connected ) {
NCR53c7x0_local_declare ( ) ;
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
u32 * script ;
NCR53c7x0_local_setup ( host ) ;
/* These are eight bit registers */
sxfer & = 0xff ;
scntl3 & = 0xff ;
hostdata - > sync [ target ] . sxfer_sanity = sxfer ;
hostdata - > sync [ target ] . scntl3_sanity = scntl3 ;
/*
* HARD CODED : synchronous script is EIGHT words long . This
* must agree with 53 c7 .8 xx . h
*/
if ( ( hostdata - > chip ! = 700 ) & & ( hostdata - > chip ! = 70066 ) ) {
hostdata - > sync [ target ] . select_indirect = ( 1 < < target ) < < 16 |
( sxfer < < 8 ) ;
hostdata - > sync [ target ] . sscf_710 = scntl3 ;
script = ( u32 * ) hostdata - > sync [ target ] . script ;
/* XXX - add NCR53c7x0 code to reprogram SCF bits if we want to */
script [ 0 ] = ( ( DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY |
DCMD_RWRI_OP_MOVE ) < < 24 ) |
( SBCL_REG < < 16 ) | ( scntl3 < < 8 ) ;
script [ 1 ] = 0 ;
script + = 2 ;
script [ 0 ] = ( ( DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY |
DCMD_RWRI_OP_MOVE ) < < 24 ) |
( SXFER_REG < < 16 ) | ( sxfer < < 8 ) ;
script [ 1 ] = 0 ;
script + = 2 ;
# ifdef DEBUG_SYNC_INTR
if ( hostdata - > options & OPTION_DEBUG_DISCONNECT ) {
script [ 0 ] = ( ( DCMD_TYPE_TCI | DCMD_TCI_OP_INT ) < < 24 ) | DBC_TCI_TRUE ;
script [ 1 ] = DEBUG_SYNC_INTR ;
script + = 2 ;
}
# endif
script [ 0 ] = ( ( DCMD_TYPE_TCI | DCMD_TCI_OP_RETURN ) < < 24 ) | DBC_TCI_TRUE ;
script [ 1 ] = 0 ;
script + = 2 ;
}
if ( hostdata - > options & OPTION_DEBUG_SYNCHRONOUS )
printk ( " scsi%d : target %d sync parameters are sxfer=0x%x, scntl3=0x%x \n " ,
host - > host_no , target , sxfer , scntl3 ) ;
if ( now_connected ) {
NCR53c7x0_write8 ( SBCL_REG , scntl3 ) ;
NCR53c7x0_write8 ( SXFER_REG , sxfer ) ;
}
}
/*
* Function : static int asynchronous ( struct Scsi_Host * host , int target )
*
* Purpose : reprogram between the selected SCSI Host adapter and target
* ( assumed to be currently connected ) for asynchronous transfers .
*
* Inputs : host - SCSI host structure , target - numeric target ID .
*
* Preconditions : the NCR chip should be in one of the halted states
*/
static void
asynchronous ( struct Scsi_Host * host , int target ) {
NCR53c7x0_local_declare ( ) ;
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
NCR53c7x0_local_setup ( host ) ;
set_synchronous ( host , target , /* no offset */ 0 , hostdata - > saved_scntl3 ,
1 ) ;
printk ( " scsi%d : setting target %d to asynchronous SCSI \n " ,
host - > host_no , target ) ;
}
/*
* XXX - do we want to go out of our way ( ie , add extra code to selection
* in the NCR53c710 / NCR53c720 script ) to reprogram the synchronous
* conversion bits , or can we be content in just setting the
* sxfer bits ? I chose to do so [ richard @ sleepie . demon . co . uk ]
*/
/* Table for NCR53c8xx synchronous values */
/* This table is also correct for 710, allowing that scf=4 is equivalent
* of SSCF = 0 ( ie use DCNTL , divide by 3 ) for a 50.01 - 66.00 MHz clock .
* For any other clock values , we cannot use entries with SCF values of
* 4. I guess that for a 66 MHz clock , the slowest it will set is 2 MHz ,
* and for a 50 MHz clock , the slowest will be 2.27 Mhz . Should check
* that a device doesn ' t try and negotiate sync below these limits !
*/
static const struct {
int div ; /* Total clock divisor * 10 */
unsigned char scf ; /* */
unsigned char tp ; /* 4 + tp = xferp divisor */
} syncs [ ] = {
/* div scf tp div scf tp div scf tp */
{ 40 , 1 , 0 } , { 50 , 1 , 1 } , { 60 , 1 , 2 } ,
{ 70 , 1 , 3 } , { 75 , 2 , 1 } , { 80 , 1 , 4 } ,
{ 90 , 1 , 5 } , { 100 , 1 , 6 } , { 105 , 2 , 3 } ,
{ 110 , 1 , 7 } , { 120 , 2 , 4 } , { 135 , 2 , 5 } ,
{ 140 , 3 , 3 } , { 150 , 2 , 6 } , { 160 , 3 , 4 } ,
{ 165 , 2 , 7 } , { 180 , 3 , 5 } , { 200 , 3 , 6 } ,
{ 210 , 4 , 3 } , { 220 , 3 , 7 } , { 240 , 4 , 4 } ,
{ 270 , 4 , 5 } , { 300 , 4 , 6 } , { 330 , 4 , 7 }
} ;
/*
* Function : static void synchronous ( struct Scsi_Host * host , int target ,
* char * msg )
*
* Purpose : reprogram transfers between the selected SCSI initiator and
* target for synchronous SCSI transfers such that the synchronous
* offset is less than that requested and period at least as long
* as that requested . Also modify * msg such that it contains
* an appropriate response .
*
* Inputs : host - NCR53c7 , 8 xx SCSI host , target - number SCSI target id ,
* msg - synchronous transfer request .
*/
static void
synchronous ( struct Scsi_Host * host , int target , char * msg ) {
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
int desire , divisor , i , limit ;
unsigned char scntl3 , sxfer ;
/* The diagnostic message fits on one line, even with max. width integers */
char buf [ 80 ] ;
/* Desired transfer clock in Hz */
desire = 1000000000L / ( msg [ 3 ] * 4 ) ;
/* Scale the available SCSI clock by 10 so we get tenths */
divisor = ( hostdata - > scsi_clock * 10 ) / desire ;
/* NCR chips can handle at most an offset of 8 */
if ( msg [ 4 ] > 8 )
msg [ 4 ] = 8 ;
if ( hostdata - > options & OPTION_DEBUG_SDTR )
printk ( " scsi%d : optimal synchronous divisor of %d.%01d \n " ,
host - > host_no , divisor / 10 , divisor % 10 ) ;
limit = ( sizeof ( syncs ) / sizeof ( syncs [ 0 ] ) - 1 ) ;
for ( i = 0 ; ( i < limit ) & & ( divisor > syncs [ i ] . div ) ; + + i ) ;
if ( hostdata - > options & OPTION_DEBUG_SDTR )
printk ( " scsi%d : selected synchronous divisor of %d.%01d \n " ,
host - > host_no , syncs [ i ] . div / 10 , syncs [ i ] . div % 10 ) ;
msg [ 3 ] = ( ( 1000000000L / hostdata - > scsi_clock ) * syncs [ i ] . div / 10 / 4 ) ;
if ( hostdata - > options & OPTION_DEBUG_SDTR )
printk ( " scsi%d : selected synchronous period of %dns \n " , host - > host_no ,
msg [ 3 ] * 4 ) ;
scntl3 = syncs [ i ] . scf ;
sxfer = ( msg [ 4 ] < < SXFER_MO_SHIFT ) | ( syncs [ i ] . tp < < 4 ) ;
if ( hostdata - > options & OPTION_DEBUG_SDTR )
printk ( " scsi%d : sxfer=0x%x scntl3=0x%x \n " ,
host - > host_no , ( int ) sxfer , ( int ) scntl3 ) ;
set_synchronous ( host , target , sxfer , scntl3 , 1 ) ;
sprintf ( buf , " scsi%d : setting target %d to " , host - > host_no , target ) ;
print_synchronous ( buf , msg ) ;
}
/*
* Function : static int NCR53c7x0_dstat_sir_intr ( struct Scsi_Host * host ,
* struct NCR53c7x0_cmd * cmd )
*
* Purpose : Handler for INT generated instructions for the
* NCR53c810 / 820 SCSI SCRIPT
*
* Inputs : host - pointer to this host adapter ' s structure ,
* cmd - pointer to the command ( if any ) dsa was pointing
* to .
*
*/
static int
NCR53c7x0_dstat_sir_intr ( struct Scsi_Host * host , struct
NCR53c7x0_cmd * cmd ) {
NCR53c7x0_local_declare ( ) ;
int print ;
Scsi_Cmnd * c = cmd ? cmd - > cmd : NULL ;
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
u32 dsps , * dsp ; /* Argument of the INT instruction */
NCR53c7x0_local_setup ( host ) ;
dsps = NCR53c7x0_read32 ( DSPS_REG ) ;
dsp = ( u32 * ) bus_to_virt ( NCR53c7x0_read32 ( DSP_REG ) ) ;
/* RGH 150597: Frig. Commands which fail with Check Condition are
* Flagged as successful - hack dsps to indicate check condition */
#if 0
/* RGH 200597: Need to disable for BVME6000, as it gets Check Conditions
* and then dies . Seems to handle Check Condition at startup , but
* not mid kernel build . */
if ( dsps = = A_int_norm_emulateintfly & & cmd & & cmd - > result = = 2 )
dsps = A_int_err_check_condition ;
# endif
if ( hostdata - > options & OPTION_DEBUG_INTR )
printk ( " scsi%d : DSPS = 0x%x \n " , host - > host_no , dsps ) ;
switch ( dsps ) {
case A_int_msg_1 :
print = 1 ;
switch ( hostdata - > msg_buf [ 0 ] ) {
/*
* Unless we ' ve initiated synchronous negotiation , I don ' t
* think that this should happen .
*/
case MESSAGE_REJECT :
hostdata - > dsp = hostdata - > script + hostdata - > E_accept_message /
sizeof ( u32 ) ;
hostdata - > dsp_changed = 1 ;
if ( cmd & & ( cmd - > flags & CMD_FLAG_SDTR ) ) {
printk ( " scsi%d : target %d rejected SDTR \n " , host - > host_no ,
c - > device - > id ) ;
cmd - > flags & = ~ CMD_FLAG_SDTR ;
asynchronous ( host , c - > device - > id ) ;
print = 0 ;
}
break ;
case INITIATE_RECOVERY :
printk ( " scsi%d : extended contingent allegiance not supported yet, rejecting \n " ,
host - > host_no ) ;
/* Fall through to default */
hostdata - > dsp = hostdata - > script + hostdata - > E_reject_message /
sizeof ( u32 ) ;
hostdata - > dsp_changed = 1 ;
break ;
default :
printk ( " scsi%d : unsupported message, rejecting \n " ,
host - > host_no ) ;
hostdata - > dsp = hostdata - > script + hostdata - > E_reject_message /
sizeof ( u32 ) ;
hostdata - > dsp_changed = 1 ;
}
if ( print ) {
printk ( " scsi%d : received message " , host - > host_no ) ;
if ( c )
printk ( " from target %d lun %d " , c - > device - > id , c - > device - > lun ) ;
2005-04-03 23:53:59 +04:00
scsi_print_msg ( ( unsigned char * ) hostdata - > msg_buf ) ;
2005-04-17 02:20:36 +04:00
printk ( " \n " ) ;
}
return SPECIFIC_INT_NOTHING ;
case A_int_msg_sdtr :
/*
* At this point , hostdata - > msg_buf contains
* 0 EXTENDED MESSAGE
* 1 length
* 2 SDTR
* 3 period * 4 ns
* 4 offset
*/
if ( cmd ) {
char buf [ 80 ] ;
sprintf ( buf , " scsi%d : target %d %s " , host - > host_no , c - > device - > id ,
( cmd - > flags & CMD_FLAG_SDTR ) ? " accepting " : " requesting " ) ;
print_synchronous ( buf , ( unsigned char * ) hostdata - > msg_buf ) ;
/*
* Initiator initiated , won ' t happen unless synchronous
* transfers are enabled . If we get a SDTR message in
* response to our SDTR , we should program our parameters
* such that
* offset < = requested offset
* period > = requested period
*/
if ( cmd - > flags & CMD_FLAG_SDTR ) {
cmd - > flags & = ~ CMD_FLAG_SDTR ;
if ( hostdata - > msg_buf [ 4 ] )
synchronous ( host , c - > device - > id , ( unsigned char * )
hostdata - > msg_buf ) ;
else
asynchronous ( host , c - > device - > id ) ;
hostdata - > dsp = hostdata - > script + hostdata - > E_accept_message /
sizeof ( u32 ) ;
hostdata - > dsp_changed = 1 ;
return SPECIFIC_INT_NOTHING ;
} else {
if ( hostdata - > options & OPTION_SYNCHRONOUS ) {
cmd - > flags | = CMD_FLAG_DID_SDTR ;
synchronous ( host , c - > device - > id , ( unsigned char * )
hostdata - > msg_buf ) ;
} else {
hostdata - > msg_buf [ 4 ] = 0 ; /* 0 offset = async */
asynchronous ( host , c - > device - > id ) ;
}
patch_dsa_32 ( cmd - > dsa , dsa_msgout_other , 0 , 5 ) ;
patch_dsa_32 ( cmd - > dsa , dsa_msgout_other , 1 , ( u32 )
virt_to_bus ( ( void * ) & hostdata - > msg_buf ) ) ;
hostdata - > dsp = hostdata - > script +
hostdata - > E_respond_message / sizeof ( u32 ) ;
hostdata - > dsp_changed = 1 ;
}
return SPECIFIC_INT_NOTHING ;
}
/* Fall through to abort if we couldn't find a cmd, and
therefore a dsa structure to twiddle */
case A_int_msg_wdtr :
hostdata - > dsp = hostdata - > script + hostdata - > E_reject_message /
sizeof ( u32 ) ;
hostdata - > dsp_changed = 1 ;
return SPECIFIC_INT_NOTHING ;
case A_int_err_unexpected_phase :
if ( hostdata - > options & OPTION_DEBUG_INTR )
printk ( " scsi%d : unexpected phase \n " , host - > host_no ) ;
return SPECIFIC_INT_ABORT ;
case A_int_err_selected :
if ( ( hostdata - > chip / 100 ) = = 8 )
printk ( " scsi%d : selected by target %d \n " , host - > host_no ,
( int ) NCR53c7x0_read8 ( SDID_REG_800 ) & 7 ) ;
else
printk ( " scsi%d : selected by target LCRC=0x%02x \n " , host - > host_no ,
( int ) NCR53c7x0_read8 ( LCRC_REG_10 ) ) ;
hostdata - > dsp = hostdata - > script + hostdata - > E_target_abort /
sizeof ( u32 ) ;
hostdata - > dsp_changed = 1 ;
return SPECIFIC_INT_NOTHING ;
case A_int_err_unexpected_reselect :
if ( ( hostdata - > chip / 100 ) = = 8 )
printk ( " scsi%d : unexpected reselect by target %d lun %d \n " ,
host - > host_no , ( int ) NCR53c7x0_read8 ( SDID_REG_800 ) & 7 ,
hostdata - > reselected_identify & 7 ) ;
else
printk ( " scsi%d : unexpected reselect LCRC=0x%02x \n " , host - > host_no ,
( int ) NCR53c7x0_read8 ( LCRC_REG_10 ) ) ;
hostdata - > dsp = hostdata - > script + hostdata - > E_initiator_abort /
sizeof ( u32 ) ;
hostdata - > dsp_changed = 1 ;
return SPECIFIC_INT_NOTHING ;
/*
* Since contingent allegiance conditions are cleared by the next
* command issued to a target , we must issue a REQUEST SENSE
* command after receiving a CHECK CONDITION status , before
* another command is issued .
*
* Since this NCR53c7x0_cmd will be freed after use , we don ' t
* care if we step on the various fields , so modify a few things .
*/
case A_int_err_check_condition :
#if 0
if ( hostdata - > options & OPTION_DEBUG_INTR )
# endif
printk ( " scsi%d : CHECK CONDITION \n " , host - > host_no ) ;
if ( ! c ) {
printk ( " scsi%d : CHECK CONDITION with no SCSI command \n " ,
host - > host_no ) ;
return SPECIFIC_INT_PANIC ;
}
/*
* FIXME : this uses the normal one - byte selection message .
* We may want to renegotiate for synchronous & WIDE transfers
* since these could be the crux of our problem .
*
hostdata - > NOP_insn * FIXME : once SCSI - II tagged queuing is implemented , we ' ll
* have to set this up so that the rest of the DSA
* agrees with this being an untagged queue ' d command .
*/
patch_dsa_32 ( cmd - > dsa , dsa_msgout , 0 , 1 ) ;
/*
* Modify the table indirect for COMMAND OUT phase , since
* Request Sense is a six byte command .
*/
patch_dsa_32 ( cmd - > dsa , dsa_cmdout , 0 , 6 ) ;
/*
* The CDB is now mirrored in our local non - cached
* structure , but keep the old structure up to date as well ,
* just in case anyone looks at it .
*/
/*
* XXX Need to worry about data buffer alignment / cache state
* XXX here , but currently never get A_int_err_check_condition ,
* XXX so ignore problem for now .
*/
cmd - > cmnd [ 0 ] = c - > cmnd [ 0 ] = REQUEST_SENSE ;
cmd - > cmnd [ 0 ] = c - > cmnd [ 1 ] & = 0xe0 ; /* Zero all but LUN */
cmd - > cmnd [ 0 ] = c - > cmnd [ 2 ] = 0 ;
cmd - > cmnd [ 0 ] = c - > cmnd [ 3 ] = 0 ;
cmd - > cmnd [ 0 ] = c - > cmnd [ 4 ] = sizeof ( c - > sense_buffer ) ;
cmd - > cmnd [ 0 ] = c - > cmnd [ 5 ] = 0 ;
/*
* Disable dataout phase , and program datain to transfer to the
* sense buffer , and add a jump to other_transfer after the
* command so overflow / underrun conditions are detected .
*/
patch_dsa_32 ( cmd - > dsa , dsa_dataout , 0 ,
virt_to_bus ( hostdata - > script ) + hostdata - > E_other_transfer ) ;
patch_dsa_32 ( cmd - > dsa , dsa_datain , 0 ,
virt_to_bus ( cmd - > data_transfer_start ) ) ;
cmd - > data_transfer_start [ 0 ] = ( ( ( DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I |
DCMD_BMI_IO ) ) < < 24 ) | sizeof ( c - > sense_buffer ) ;
cmd - > data_transfer_start [ 1 ] = ( u32 ) virt_to_bus ( c - > sense_buffer ) ;
cmd - > data_transfer_start [ 2 ] = ( ( DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP )
< < 24 ) | DBC_TCI_TRUE ;
cmd - > data_transfer_start [ 3 ] = ( u32 ) virt_to_bus ( hostdata - > script ) +
hostdata - > E_other_transfer ;
/*
* Currently , this command is flagged as completed , ie
* it has valid status and message data . Reflag it as
* incomplete . Q - need to do something so that original
* status , etc are used .
*/
cmd - > result = cmd - > cmd - > result = 0xffff ;
/*
* Restart command as a REQUEST SENSE .
*/
hostdata - > dsp = ( u32 * ) hostdata - > script + hostdata - > E_select /
sizeof ( u32 ) ;
hostdata - > dsp_changed = 1 ;
return SPECIFIC_INT_NOTHING ;
case A_int_debug_break :
return SPECIFIC_INT_BREAK ;
case A_int_norm_aborted :
hostdata - > dsp = ( u32 * ) hostdata - > schedule ;
hostdata - > dsp_changed = 1 ;
if ( cmd )
abnormal_finished ( cmd , DID_ERROR < < 16 ) ;
return SPECIFIC_INT_NOTHING ;
case A_int_norm_emulateintfly :
NCR53c7x0_intfly ( host ) ;
return SPECIFIC_INT_NOTHING ;
case A_int_test_1 :
case A_int_test_2 :
hostdata - > idle = 1 ;
hostdata - > test_completed = ( dsps - A_int_test_1 ) / 0x00010000 + 1 ;
if ( hostdata - > options & OPTION_DEBUG_INTR )
printk ( " scsi%d : test%d complete \n " , host - > host_no ,
hostdata - > test_completed ) ;
return SPECIFIC_INT_NOTHING ;
# ifdef A_int_debug_reselected_ok
case A_int_debug_reselected_ok :
if ( hostdata - > options & ( OPTION_DEBUG_SCRIPT | OPTION_DEBUG_INTR |
OPTION_DEBUG_DISCONNECT ) ) {
/*
* Note - this dsa is not based on location relative to
* the command structure , but to location relative to the
* DSA register
*/
u32 * dsa ;
dsa = ( u32 * ) bus_to_virt ( NCR53c7x0_read32 ( DSA_REG ) ) ;
printk ( " scsi%d : reselected_ok (DSA = 0x%x (virt 0x%p) \n " ,
host - > host_no , NCR53c7x0_read32 ( DSA_REG ) , dsa ) ;
printk ( " scsi%d : resume address is 0x%x (virt 0x%p) \n " ,
host - > host_no , cmd - > saved_data_pointer ,
bus_to_virt ( cmd - > saved_data_pointer ) ) ;
print_insn ( host , hostdata - > script + Ent_reselected_ok /
sizeof ( u32 ) , " " , 1 ) ;
if ( ( hostdata - > chip / 100 ) = = 8 )
printk ( " scsi%d : sxfer=0x%x, scntl3=0x%x \n " ,
host - > host_no , NCR53c7x0_read8 ( SXFER_REG ) ,
NCR53c7x0_read8 ( SCNTL3_REG_800 ) ) ;
else
printk ( " scsi%d : sxfer=0x%x, cannot read SBCL \n " ,
host - > host_no , NCR53c7x0_read8 ( SXFER_REG ) ) ;
if ( c ) {
print_insn ( host , ( u32 * )
hostdata - > sync [ c - > device - > id ] . script , " " , 1 ) ;
print_insn ( host , ( u32 * )
hostdata - > sync [ c - > device - > id ] . script + 2 , " " , 1 ) ;
}
}
return SPECIFIC_INT_RESTART ;
# endif
# ifdef A_int_debug_reselect_check
case A_int_debug_reselect_check :
if ( hostdata - > options & ( OPTION_DEBUG_SCRIPT | OPTION_DEBUG_INTR ) ) {
u32 * dsa ;
#if 0
u32 * code ;
# endif
/*
* Note - this dsa is not based on location relative to
* the command structure , but to location relative to the
* DSA register
*/
dsa = bus_to_virt ( NCR53c7x0_read32 ( DSA_REG ) ) ;
printk ( " scsi%d : reselected_check_next (DSA = 0x%lx (virt 0x%p)) \n " ,
host - > host_no , virt_to_bus ( dsa ) , dsa ) ;
if ( dsa ) {
printk ( " scsi%d : resume address is 0x%x (virt 0x%p) \n " ,
host - > host_no , cmd - > saved_data_pointer ,
bus_to_virt ( cmd - > saved_data_pointer ) ) ;
#if 0
printk ( " scsi%d : template code : \n " , host - > host_no ) ;
for ( code = dsa + ( Ent_dsa_code_check_reselect - Ent_dsa_zero )
/ sizeof ( u32 ) ; code < ( dsa + Ent_dsa_zero / sizeof ( u32 ) ) ;
code + = print_insn ( host , code , " " , 1 ) ) ;
# endif
}
print_insn ( host , hostdata - > script + Ent_reselected_ok /
sizeof ( u32 ) , " " , 1 ) ;
}
return SPECIFIC_INT_RESTART ;
# endif
# ifdef A_int_debug_dsa_schedule
case A_int_debug_dsa_schedule :
if ( hostdata - > options & ( OPTION_DEBUG_SCRIPT | OPTION_DEBUG_INTR ) ) {
u32 * dsa ;
/*
* Note - this dsa is not based on location relative to
* the command structure , but to location relative to the
* DSA register
*/
dsa = ( u32 * ) bus_to_virt ( NCR53c7x0_read32 ( DSA_REG ) ) ;
printk ( " scsi%d : dsa_schedule (old DSA = 0x%lx (virt 0x%p)) \n " ,
host - > host_no , virt_to_bus ( dsa ) , dsa ) ;
if ( dsa )
printk ( " scsi%d : resume address is 0x%x (virt 0x%p) \n "
" (temp was 0x%x (virt 0x%p)) \n " ,
host - > host_no , cmd - > saved_data_pointer ,
bus_to_virt ( cmd - > saved_data_pointer ) ,
NCR53c7x0_read32 ( TEMP_REG ) ,
bus_to_virt ( NCR53c7x0_read32 ( TEMP_REG ) ) ) ;
}
return SPECIFIC_INT_RESTART ;
# endif
# ifdef A_int_debug_scheduled
case A_int_debug_scheduled :
if ( hostdata - > options & ( OPTION_DEBUG_SCRIPT | OPTION_DEBUG_INTR ) ) {
printk ( " scsi%d : new I/O 0x%x (virt 0x%p) scheduled \n " ,
host - > host_no , NCR53c7x0_read32 ( DSA_REG ) ,
bus_to_virt ( NCR53c7x0_read32 ( DSA_REG ) ) ) ;
}
return SPECIFIC_INT_RESTART ;
# endif
# ifdef A_int_debug_idle
case A_int_debug_idle :
if ( hostdata - > options & ( OPTION_DEBUG_SCRIPT | OPTION_DEBUG_INTR ) ) {
printk ( " scsi%d : idle \n " , host - > host_no ) ;
}
return SPECIFIC_INT_RESTART ;
# endif
# ifdef A_int_debug_cmd
case A_int_debug_cmd :
if ( hostdata - > options & ( OPTION_DEBUG_SCRIPT | OPTION_DEBUG_INTR ) ) {
printk ( " scsi%d : command sent \n " ) ;
}
return SPECIFIC_INT_RESTART ;
# endif
# ifdef A_int_debug_dsa_loaded
case A_int_debug_dsa_loaded :
if ( hostdata - > options & ( OPTION_DEBUG_SCRIPT | OPTION_DEBUG_INTR ) ) {
printk ( " scsi%d : DSA loaded with 0x%x (virt 0x%p) \n " , host - > host_no ,
NCR53c7x0_read32 ( DSA_REG ) ,
bus_to_virt ( NCR53c7x0_read32 ( DSA_REG ) ) ) ;
}
return SPECIFIC_INT_RESTART ;
# endif
# ifdef A_int_debug_reselected
case A_int_debug_reselected :
if ( hostdata - > options & ( OPTION_DEBUG_SCRIPT | OPTION_DEBUG_INTR |
OPTION_DEBUG_DISCONNECT ) ) {
if ( ( hostdata - > chip / 100 ) = = 8 )
printk ( " scsi%d : reselected by target %d lun %d \n " ,
host - > host_no , ( int ) NCR53c7x0_read8 ( SDID_REG_800 ) & ~ 0x80 ,
( int ) hostdata - > reselected_identify & 7 ) ;
else
printk ( " scsi%d : reselected by LCRC=0x%02x lun %d \n " ,
host - > host_no , ( int ) NCR53c7x0_read8 ( LCRC_REG_10 ) ,
( int ) hostdata - > reselected_identify & 7 ) ;
print_queues ( host ) ;
}
return SPECIFIC_INT_RESTART ;
# endif
# ifdef A_int_debug_disconnect_msg
case A_int_debug_disconnect_msg :
if ( hostdata - > options & ( OPTION_DEBUG_SCRIPT | OPTION_DEBUG_INTR ) ) {
if ( c )
printk ( " scsi%d : target %d lun %d disconnecting \n " ,
host - > host_no , c - > device - > id , c - > device - > lun ) ;
else
printk ( " scsi%d : unknown target disconnecting \n " ,
host - > host_no ) ;
}
return SPECIFIC_INT_RESTART ;
# endif
# ifdef A_int_debug_disconnected
case A_int_debug_disconnected :
if ( hostdata - > options & ( OPTION_DEBUG_SCRIPT | OPTION_DEBUG_INTR |
OPTION_DEBUG_DISCONNECT ) ) {
printk ( " scsi%d : disconnected, new queues are \n " ,
host - > host_no ) ;
print_queues ( host ) ;
#if 0
/* Not valid on ncr53c710! */
printk ( " scsi%d : sxfer=0x%x, scntl3=0x%x \n " ,
host - > host_no , NCR53c7x0_read8 ( SXFER_REG ) ,
NCR53c7x0_read8 ( SCNTL3_REG_800 ) ) ;
# endif
if ( c ) {
print_insn ( host , ( u32 * )
hostdata - > sync [ c - > device - > id ] . script , " " , 1 ) ;
print_insn ( host , ( u32 * )
hostdata - > sync [ c - > device - > id ] . script + 2 , " " , 1 ) ;
}
}
return SPECIFIC_INT_RESTART ;
# endif
# ifdef A_int_debug_panic
case A_int_debug_panic :
printk ( " scsi%d : int_debug_panic received \n " , host - > host_no ) ;
print_lots ( host ) ;
return SPECIFIC_INT_PANIC ;
# endif
# ifdef A_int_debug_saved
case A_int_debug_saved :
if ( hostdata - > options & ( OPTION_DEBUG_SCRIPT | OPTION_DEBUG_INTR |
OPTION_DEBUG_DISCONNECT ) ) {
printk ( " scsi%d : saved data pointer 0x%x (virt 0x%p) \n " ,
host - > host_no , cmd - > saved_data_pointer ,
bus_to_virt ( cmd - > saved_data_pointer ) ) ;
print_progress ( c ) ;
}
return SPECIFIC_INT_RESTART ;
# endif
# ifdef A_int_debug_restored
case A_int_debug_restored :
if ( hostdata - > options & ( OPTION_DEBUG_SCRIPT | OPTION_DEBUG_INTR |
OPTION_DEBUG_DISCONNECT ) ) {
if ( cmd ) {
int size ;
printk ( " scsi%d : restored data pointer 0x%x (virt 0x%p) \n " ,
host - > host_no , cmd - > saved_data_pointer , bus_to_virt (
cmd - > saved_data_pointer ) ) ;
size = print_insn ( host , ( u32 * )
bus_to_virt ( cmd - > saved_data_pointer ) , " " , 1 ) ;
size = print_insn ( host , ( u32 * )
bus_to_virt ( cmd - > saved_data_pointer ) + size , " " , 1 ) ;
print_progress ( c ) ;
}
#if 0
printk ( " scsi%d : datapath residual %d \n " ,
host - > host_no , datapath_residual ( host ) ) ;
# endif
}
return SPECIFIC_INT_RESTART ;
# endif
# ifdef A_int_debug_sync
case A_int_debug_sync :
if ( hostdata - > options & ( OPTION_DEBUG_SCRIPT | OPTION_DEBUG_INTR |
OPTION_DEBUG_DISCONNECT | OPTION_DEBUG_SDTR ) ) {
unsigned char sxfer = NCR53c7x0_read8 ( SXFER_REG ) , scntl3 ;
if ( ( hostdata - > chip / 100 ) = = 8 ) {
scntl3 = NCR53c7x0_read8 ( SCNTL3_REG_800 ) ;
if ( c ) {
if ( sxfer ! = hostdata - > sync [ c - > device - > id ] . sxfer_sanity | |
scntl3 ! = hostdata - > sync [ c - > device - > id ] . scntl3_sanity ) {
printk ( " scsi%d : sync sanity check failed sxfer=0x%x, scntl3=0x%x " ,
host - > host_no , sxfer , scntl3 ) ;
NCR53c7x0_write8 ( SXFER_REG , sxfer ) ;
NCR53c7x0_write8 ( SCNTL3_REG_800 , scntl3 ) ;
}
} else
printk ( " scsi%d : unknown command sxfer=0x%x, scntl3=0x%x \n " ,
host - > host_no , ( int ) sxfer , ( int ) scntl3 ) ;
} else {
if ( c ) {
if ( sxfer ! = hostdata - > sync [ c - > device - > id ] . sxfer_sanity ) {
printk ( " scsi%d : sync sanity check failed sxfer=0x%x " ,
host - > host_no , sxfer ) ;
NCR53c7x0_write8 ( SXFER_REG , sxfer ) ;
NCR53c7x0_write8 ( SBCL_REG ,
hostdata - > sync [ c - > device - > id ] . sscf_710 ) ;
}
} else
printk ( " scsi%d : unknown command sxfer=0x%x \n " ,
host - > host_no , ( int ) sxfer ) ;
}
}
return SPECIFIC_INT_RESTART ;
# endif
# ifdef A_int_debug_datain
case A_int_debug_datain :
if ( hostdata - > options & ( OPTION_DEBUG_SCRIPT | OPTION_DEBUG_INTR |
OPTION_DEBUG_DISCONNECT | OPTION_DEBUG_SDTR ) ) {
int size ;
if ( ( hostdata - > chip / 100 ) = = 8 )
printk ( " scsi%d : In do_datain (%s) sxfer=0x%x, scntl3=0x%x \n "
" datapath residual=%d \n " ,
host - > host_no , sbcl_to_phase ( NCR53c7x0_read8 ( SBCL_REG ) ) ,
( int ) NCR53c7x0_read8 ( SXFER_REG ) ,
( int ) NCR53c7x0_read8 ( SCNTL3_REG_800 ) ,
datapath_residual ( host ) ) ;
else
printk ( " scsi%d : In do_datain (%s) sxfer=0x%x \n "
" datapath residual=%d \n " ,
host - > host_no , sbcl_to_phase ( NCR53c7x0_read8 ( SBCL_REG ) ) ,
( int ) NCR53c7x0_read8 ( SXFER_REG ) ,
datapath_residual ( host ) ) ;
print_insn ( host , dsp , " " , 1 ) ;
size = print_insn ( host , ( u32 * ) bus_to_virt ( dsp [ 1 ] ) , " " , 1 ) ;
print_insn ( host , ( u32 * ) bus_to_virt ( dsp [ 1 ] ) + size , " " , 1 ) ;
}
return SPECIFIC_INT_RESTART ;
# endif
# ifdef A_int_debug_check_dsa
case A_int_debug_check_dsa :
if ( NCR53c7x0_read8 ( SCNTL1_REG ) & SCNTL1_CON ) {
int sdid ;
int tmp ;
char * where ;
if ( hostdata - > chip / 100 = = 8 )
sdid = NCR53c7x0_read8 ( SDID_REG_800 ) & 15 ;
else {
tmp = NCR53c7x0_read8 ( SDID_REG_700 ) ;
if ( ! tmp )
panic ( " SDID_REG_700 = 0 " ) ;
tmp > > = 1 ;
sdid = 0 ;
while ( tmp ) {
tmp > > = 1 ;
sdid + + ;
}
}
where = dsp - NCR53c7x0_insn_size ( NCR53c7x0_read8
( DCMD_REG ) ) = = hostdata - > script +
Ent_select_check_dsa / sizeof ( u32 ) ?
" selection " : " reselection " ;
if ( c & & sdid ! = c - > device - > id ) {
printk ( " scsi%d : SDID target %d != DSA target %d at %s \n " ,
host - > host_no , sdid , c - > device - > id , where ) ;
print_lots ( host ) ;
dump_events ( host , 20 ) ;
return SPECIFIC_INT_PANIC ;
}
}
return SPECIFIC_INT_RESTART ;
# endif
default :
if ( ( dsps & 0xff000000 ) = = 0x03000000 ) {
printk ( " scsi%d : misc debug interrupt 0x%x \n " ,
host - > host_no , dsps ) ;
return SPECIFIC_INT_RESTART ;
} else if ( ( dsps & 0xff000000 ) = = 0x05000000 ) {
if ( hostdata - > events ) {
struct NCR53c7x0_event * event ;
+ + hostdata - > event_index ;
if ( hostdata - > event_index > = hostdata - > event_size )
hostdata - > event_index = 0 ;
event = ( struct NCR53c7x0_event * ) hostdata - > events +
hostdata - > event_index ;
event - > event = ( enum ncr_event ) dsps ;
event - > dsa = bus_to_virt ( NCR53c7x0_read32 ( DSA_REG ) ) ;
if ( NCR53c7x0_read8 ( SCNTL1_REG ) & SCNTL1_CON ) {
if ( hostdata - > chip / 100 = = 8 )
event - > target = NCR53c7x0_read8 ( SSID_REG_800 ) ;
else {
unsigned char tmp , sdid ;
tmp = NCR53c7x0_read8 ( SDID_REG_700 ) ;
if ( ! tmp )
panic ( " SDID_REG_700 = 0 " ) ;
tmp > > = 1 ;
sdid = 0 ;
while ( tmp ) {
tmp > > = 1 ;
sdid + + ;
}
event - > target = sdid ;
}
}
else
event - > target = 255 ;
if ( event - > event = = EVENT_RESELECT )
event - > lun = hostdata - > reselected_identify & 0xf ;
else if ( c )
event - > lun = c - > device - > lun ;
else
event - > lun = 255 ;
do_gettimeofday ( & ( event - > time ) ) ;
if ( c ) {
event - > pid = c - > pid ;
memcpy ( ( void * ) event - > cmnd , ( void * ) c - > cmnd ,
sizeof ( event - > cmnd ) ) ;
} else {
event - > pid = - 1 ;
}
}
return SPECIFIC_INT_RESTART ;
}
printk ( " scsi%d : unknown user interrupt 0x%x \n " ,
host - > host_no , ( unsigned ) dsps ) ;
return SPECIFIC_INT_PANIC ;
}
}
/*
* XXX - the stock NCR assembler won ' t output the scriptu . h file ,
* which undefine ' s all # define ' d CPP symbols from the script . h
* file , which will create problems if you use multiple scripts
* with the same symbol names .
*
* If you insist on using NCR ' s assembler , you could generate
* scriptu . h from script . h using something like
*
* grep # define script . h | \
* sed ' s / # define [ ] [ ] * \ ( [ _a - zA - Z ] [ _a - zA - Z0 - 9 ] * \ ) . * $ / # undefine \ 1 / ' \
* > scriptu . h
*/
# include "53c7xx_u.h"
/* XXX - add alternate script handling code here */
/*
* Function : static void NCR537xx_soft_reset ( struct Scsi_Host * host )
*
* Purpose : perform a soft reset of the NCR53c7xx chip
*
* Inputs : host - pointer to this host adapter ' s structure
*
* Preconditions : NCR53c7x0_init must have been called for this
* host .
*
*/
static void
NCR53c7x0_soft_reset ( struct Scsi_Host * host ) {
NCR53c7x0_local_declare ( ) ;
unsigned long flags ;
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
NCR53c7x0_local_setup ( host ) ;
local_irq_save ( flags ) ;
/* Disable scsi chip and s/w level 7 ints */
# ifdef CONFIG_MVME16x
if ( MACH_IS_MVME16x )
{
volatile unsigned long v ;
v = * ( volatile unsigned long * ) 0xfff4006c ;
v & = ~ 0x8000 ;
* ( volatile unsigned long * ) 0xfff4006c = v ;
v = * ( volatile unsigned long * ) 0xfff4202c ;
v & = ~ 0x10 ;
* ( volatile unsigned long * ) 0xfff4202c = v ;
}
# endif
/* Anything specific for your hardware? */
/*
* Do a soft reset of the chip so that everything is
* reinitialized to the power - on state .
*
* Basically follow the procedure outlined in the NCR53c700
* data manual under Chapter Six , How to Use , Steps Necessary to
* Start SCRIPTS , with the exception of actually starting the
* script and setting up the synchronous transfer gunk .
*/
/* Should we reset the scsi bus here??????????????????? */
NCR53c7x0_write8 ( ISTAT_REG_700 , ISTAT_10_SRST ) ;
NCR53c7x0_write8 ( ISTAT_REG_700 , 0 ) ;
/*
* saved_dcntl is set up in NCR53c7x0_init ( ) before it is overwritten
* here . We should have some better way of working out the CF bit
* setting . .
*/
hostdata - > saved_dcntl = DCNTL_10_EA | DCNTL_10_COM ;
if ( hostdata - > scsi_clock > 50000000 )
hostdata - > saved_dcntl | = DCNTL_700_CF_3 ;
else
if ( hostdata - > scsi_clock > 37500000 )
hostdata - > saved_dcntl | = DCNTL_700_CF_2 ;
#if 0
else
/* Any clocks less than 37.5MHz? */
# endif
if ( hostdata - > options & OPTION_DEBUG_TRACE )
NCR53c7x0_write8 ( DCNTL_REG , hostdata - > saved_dcntl | DCNTL_SSM ) ;
else
NCR53c7x0_write8 ( DCNTL_REG , hostdata - > saved_dcntl ) ;
/* Following disables snooping - snooping is not required, as non-
* cached pages are used for shared data , and appropriate use is
* made of cache_push / cache_clear . Indeed , for 68060
* enabling snooping causes disk corruption of ext2fs free block
* bitmaps and the like . If you have a 68060 with snooping hardwared
* on , then you need to enable CONFIG_060_WRITETHROUGH .
*/
NCR53c7x0_write8 ( CTEST7_REG , CTEST7_10_TT1 | CTEST7_STD ) ;
/* Actually burst of eight, according to my 53c710 databook */
NCR53c7x0_write8 ( hostdata - > dmode , DMODE_10_BL_8 | DMODE_10_FC2 ) ;
NCR53c7x0_write8 ( SCID_REG , 1 < < host - > this_id ) ;
NCR53c7x0_write8 ( SBCL_REG , 0 ) ;
NCR53c7x0_write8 ( SCNTL1_REG , SCNTL1_ESR_700 ) ;
NCR53c7x0_write8 ( SCNTL0_REG , ( ( hostdata - > options & OPTION_PARITY ) ?
SCNTL0_EPC : 0 ) | SCNTL0_EPG_700 | SCNTL0_ARB1 | SCNTL0_ARB2 ) ;
/*
* Enable all interrupts , except parity which we only want when
* the user requests it .
*/
NCR53c7x0_write8 ( DIEN_REG , DIEN_700_BF |
DIEN_ABRT | DIEN_SSI | DIEN_SIR | DIEN_700_OPC ) ;
NCR53c7x0_write8 ( SIEN_REG_700 , ( ( hostdata - > options & OPTION_PARITY ) ?
SIEN_PAR : 0 ) | SIEN_700_STO | SIEN_RST | SIEN_UDC |
SIEN_SGE | SIEN_MA ) ;
# ifdef CONFIG_MVME16x
if ( MACH_IS_MVME16x )
{
volatile unsigned long v ;
/* Enable scsi chip and s/w level 7 ints */
v = * ( volatile unsigned long * ) 0xfff40080 ;
v = ( v & ~ ( 0xf < < 28 ) ) | ( 4 < < 28 ) ;
* ( volatile unsigned long * ) 0xfff40080 = v ;
v = * ( volatile unsigned long * ) 0xfff4006c ;
v | = 0x8000 ;
* ( volatile unsigned long * ) 0xfff4006c = v ;
v = * ( volatile unsigned long * ) 0xfff4202c ;
v = ( v & ~ 0xff ) | 0x10 | 4 ;
* ( volatile unsigned long * ) 0xfff4202c = v ;
}
# endif
/* Anything needed for your hardware? */
local_irq_restore ( flags ) ;
}
/*
* Function static struct NCR53c7x0_cmd * allocate_cmd ( Scsi_Cmnd * cmd )
*
* Purpose : Return the first free NCR53c7x0_cmd structure ( which are
* reused in a LIFO manner to minimize cache thrashing ) .
*
* Side effects : If we haven ' t yet scheduled allocation of NCR53c7x0_cmd
* structures for this device , do so . Attempt to complete all scheduled
* allocations using get_zeroed_page ( ) , putting NCR53c7x0_cmd structures on
* the free list . Teach programmers not to drink and hack .
*
* Inputs : cmd - SCSI command
*
* Returns : NCR53c7x0_cmd structure allocated on behalf of cmd ;
* NULL on failure .
*/
static void
my_free_page ( void * addr , int dummy )
{
/* XXX This assumes default cache mode to be IOMAP_FULL_CACHING, which
* XXX may be invalid ( CONFIG_060_WRITETHROUGH )
*/
kernel_set_cachemode ( ( void * ) addr , 4096 , IOMAP_FULL_CACHING ) ;
free_page ( ( u32 ) addr ) ;
}
static struct NCR53c7x0_cmd *
allocate_cmd ( Scsi_Cmnd * cmd ) {
struct Scsi_Host * host = cmd - > device - > host ;
struct NCR53c7x0_hostdata * hostdata =
( struct NCR53c7x0_hostdata * ) host - > hostdata [ 0 ] ;
u32 real ; /* Real address */
int size ; /* Size of *tmp */
struct NCR53c7x0_cmd * tmp ;
unsigned long flags ;
if ( hostdata - > options & OPTION_DEBUG_ALLOCATION )
printk ( " scsi%d : num_cmds = %d, can_queue = %d \n "
" target = %d, lun = %d, %s \n " ,
host - > host_no , hostdata - > num_cmds , host - > can_queue ,
cmd - > device - > id , cmd - > device - > lun , ( hostdata - > cmd_allocated [ cmd - > device - > id ] &
( 1 < < cmd - > device - > lun ) ) ? " already allocated " : " not allocated " ) ;
/*
* If we have not yet reserved commands for this I_T_L nexus , and
* the device exists ( as indicated by permanent Scsi_Cmnd structures
* being allocated under 1.3 . x , or being outside of scan_scsis in
* 1.2 . x ) , do so now .
*/
if ( ! ( hostdata - > cmd_allocated [ cmd - > device - > id ] & ( 1 < < cmd - > device - > lun ) ) & &
cmd - > device & & cmd - > device - > has_cmdblocks ) {
if ( ( hostdata - > extra_allocate + hostdata - > num_cmds ) < host - > can_queue )
hostdata - > extra_allocate + = host - > cmd_per_lun ;
hostdata - > cmd_allocated [ cmd - > device - > id ] | = ( 1 < < cmd - > device - > lun ) ;
}
for ( ; hostdata - > extra_allocate > 0 ; - - hostdata - > extra_allocate ,
+ + hostdata - > num_cmds ) {
/* historically, kmalloc has returned unaligned addresses; pad so we
have enough room to ROUNDUP */
size = hostdata - > max_cmd_size + sizeof ( void * ) ;
# ifdef FORCE_DSA_ALIGNMENT
/*
* 53 c710 rev .0 doesn ' t have an add - with - carry instruction .
* Ensure we allocate enough memory to force alignment .
*/
size + = 256 ;
# endif
/* FIXME: for ISA bus '7xx chips, we need to or GFP_DMA in here */
if ( size > 4096 ) {
printk ( KERN_ERR " 53c7xx: allocate_cmd size > 4K \n " ) ;
return NULL ;
}
real = get_zeroed_page ( GFP_ATOMIC ) ;
if ( real = = 0 )
return NULL ;
memset ( ( void * ) real , 0 , 4096 ) ;
cache_push ( virt_to_phys ( ( void * ) real ) , 4096 ) ;
cache_clear ( virt_to_phys ( ( void * ) real ) , 4096 ) ;
kernel_set_cachemode ( ( void * ) real , 4096 , IOMAP_NOCACHE_SER ) ;
tmp = ROUNDUP ( real , void * ) ;
# ifdef FORCE_DSA_ALIGNMENT
{
if ( ( ( u32 ) tmp & 0xff ) > CmdPageStart )
tmp = ( struct NCR53c7x0_cmd * ) ( ( u32 ) tmp + 255 ) ;
tmp = ( struct NCR53c7x0_cmd * ) ( ( ( u32 ) tmp & ~ 0xff ) + CmdPageStart ) ;
#if 0
printk ( " scsi: size = %d, real = 0x%08x, tmp set to 0x%08x \n " ,
size , real , ( u32 ) tmp ) ;
# endif
}
# endif
tmp - > real = ( void * ) real ;
tmp - > size = size ;
tmp - > free = ( ( void ( * ) ( void * , int ) ) my_free_page ) ;
local_irq_save ( flags ) ;
tmp - > next = hostdata - > free ;
hostdata - > free = tmp ;
local_irq_restore ( flags ) ;
}
local_irq_save ( flags ) ;
tmp = ( struct NCR53c7x0_cmd * ) hostdata - > free ;
if ( tmp ) {
hostdata - > free = tmp - > next ;
}
local_irq_restore ( flags ) ;
if ( ! tmp )
printk ( " scsi%d : can't allocate command for target %d lun %d \n " ,
host - > host_no , cmd - > device - > id , cmd - > device - > lun ) ;
return tmp ;
}
/*
* Function static struct NCR53c7x0_cmd * create_cmd ( Scsi_Cmnd * cmd )
*
*
* Purpose : allocate a NCR53c7x0_cmd structure , initialize it based on the
* Scsi_Cmnd structure passed in cmd , including dsa and Linux field
* initialization , and dsa code relocation .
*
* Inputs : cmd - SCSI command
*
* Returns : NCR53c7x0_cmd structure corresponding to cmd ,
* NULL on failure .
*/
static struct NCR53c7x0_cmd *
create_cmd ( Scsi_Cmnd * cmd ) {
NCR53c7x0_local_declare ( ) ;
struct Scsi_Host * host = cmd - > device - > host ;
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
struct NCR53c7x0_cmd * tmp ; /* NCR53c7x0_cmd structure for this command */
int datain , /* Number of instructions per phase */
dataout ;
int data_transfer_instructions , /* Count of dynamic instructions */
i ; /* Counter */
u32 * cmd_datain , /* Address of datain/dataout code */
* cmd_dataout ; /* Incremented as we assemble */
# ifdef notyet
unsigned char * msgptr ; /* Current byte in select message */
int msglen ; /* Length of whole select message */
# endif
unsigned long flags ;
u32 exp_select_indirect ; /* Used in sanity check */
NCR53c7x0_local_setup ( cmd - > device - > host ) ;
if ( ! ( tmp = allocate_cmd ( cmd ) ) )
return NULL ;
/*
* Copy CDB and initialised result fields from Scsi_Cmnd to NCR53c7x0_cmd .
* We do this because NCR53c7x0_cmd may have a special cache mode
* selected to cope with lack of bus snooping , etc .
*/
memcpy ( tmp - > cmnd , cmd - > cmnd , 12 ) ;
tmp - > result = cmd - > result ;
/*
* Decide whether we need to generate commands for DATA IN ,
* DATA OUT , neither , or both based on the SCSI command
*/
switch ( cmd - > cmnd [ 0 ] ) {
/* These commands do DATA IN */
case INQUIRY :
case MODE_SENSE :
case READ_6 :
case READ_10 :
case READ_CAPACITY :
case REQUEST_SENSE :
case READ_BLOCK_LIMITS :
case READ_TOC :
datain = 2 * ( cmd - > use_sg ? cmd - > use_sg : 1 ) + 3 ;
dataout = 0 ;
break ;
/* These commands do DATA OUT */
case MODE_SELECT :
case WRITE_6 :
case WRITE_10 :
#if 0
printk ( " scsi%d : command is " , host - > host_no ) ;
2005-04-03 23:53:59 +04:00
__scsi_print_command ( cmd - > cmnd ) ;
2005-04-17 02:20:36 +04:00
# endif
#if 0
printk ( " scsi%d : %d scatter/gather segments \n " , host - > host_no ,
cmd - > use_sg ) ;
# endif
datain = 0 ;
dataout = 2 * ( cmd - > use_sg ? cmd - > use_sg : 1 ) + 3 ;
#if 0
hostdata - > options | = OPTION_DEBUG_INTR ;
# endif
break ;
/*
* These commands do no data transfer , we should force an
* interrupt if a data phase is attempted on them .
*/
case TEST_UNIT_READY :
case ALLOW_MEDIUM_REMOVAL :
case START_STOP :
datain = dataout = 0 ;
break ;
/*
* We don ' t know about these commands , so generate code to handle
* both DATA IN and DATA OUT phases . More efficient to identify them
* and add them to the above cases .
*/
default :
printk ( " scsi%d : datain+dataout for command " , host - > host_no ) ;
2005-04-03 23:53:59 +04:00
__scsi_print_command ( cmd - > cmnd ) ;
2005-04-17 02:20:36 +04:00
datain = dataout = 2 * ( cmd - > use_sg ? cmd - > use_sg : 1 ) + 3 ;
}
/*
* New code : so that active pointers work correctly regardless
* of where the saved data pointer is at , we want to immediately
* enter the dynamic code after selection , and on a non - data
* phase perform a CALL to the non - data phase handler , with
* returns back to this address .
*
* If a phase mismatch is encountered in the middle of a
* Block MOVE instruction , we want to _leave_ that instruction
* unchanged as the current case is , modify a temporary buffer ,
* and point the active pointer ( TEMP ) at that .
*
* Furthermore , we want to implement a saved data pointer ,
* set by the SAVE_DATA_POINTERs message .
*
* So , the data transfer segments will change to
* CALL data_transfer , WHEN NOT data phase
* MOVE x , x , WHEN data phase
* ( repeat )
* JUMP other_transfer
*/
data_transfer_instructions = datain + dataout ;
/*
* When we perform a request sense , we overwrite various things ,
* including the data transfer code . Make sure we have enough
* space to do that .
*/
if ( data_transfer_instructions < 2 )
data_transfer_instructions = 2 ;
/*
* The saved data pointer is set up so that a RESTORE POINTERS message
* will start the data transfer over at the beginning .
*/
tmp - > saved_data_pointer = virt_to_bus ( hostdata - > script ) +
hostdata - > E_data_transfer ;
/*
* Initialize Linux specific fields .
*/
tmp - > cmd = cmd ;
tmp - > next = NULL ;
tmp - > flags = 0 ;
tmp - > dsa_next_addr = virt_to_bus ( tmp - > dsa ) + hostdata - > dsa_next -
hostdata - > dsa_start ;
tmp - > dsa_addr = virt_to_bus ( tmp - > dsa ) - hostdata - > dsa_start ;
/*
* Calculate addresses of dynamic code to fill in DSA
*/
tmp - > data_transfer_start = tmp - > dsa + ( hostdata - > dsa_end -
hostdata - > dsa_start ) / sizeof ( u32 ) ;
tmp - > data_transfer_end = tmp - > data_transfer_start +
2 * data_transfer_instructions ;
cmd_datain = datain ? tmp - > data_transfer_start : NULL ;
cmd_dataout = dataout ? ( datain ? cmd_datain + 2 * datain : tmp - >
data_transfer_start ) : NULL ;
/*
* Fill in the NCR53c7x0_cmd structure as follows
* dsa , with fixed up DSA code
* datain code
* dataout code
*/
/* Copy template code into dsa and perform all necessary fixups */
if ( hostdata - > dsa_fixup )
hostdata - > dsa_fixup ( tmp ) ;
patch_dsa_32 ( tmp - > dsa , dsa_next , 0 , 0 ) ;
/*
* XXX is this giving 53 c710 access to the Scsi_Cmnd in some way ?
* Do we need to change it for caching reasons ?
*/
patch_dsa_32 ( tmp - > dsa , dsa_cmnd , 0 , virt_to_bus ( cmd ) ) ;
if ( hostdata - > options & OPTION_DEBUG_SYNCHRONOUS ) {
exp_select_indirect = ( ( 1 < < cmd - > device - > id ) < < 16 ) |
( hostdata - > sync [ cmd - > device - > id ] . sxfer_sanity < < 8 ) ;
if ( hostdata - > sync [ cmd - > device - > id ] . select_indirect ! =
exp_select_indirect ) {
printk ( " scsi%d : sanity check failed select_indirect=0x%x \n " ,
host - > host_no , hostdata - > sync [ cmd - > device - > id ] . select_indirect ) ;
FATAL ( host ) ;
}
}
patch_dsa_32 ( tmp - > dsa , dsa_select , 0 ,
hostdata - > sync [ cmd - > device - > id ] . select_indirect ) ;
/*
* Right now , we ' ll do the WIDE and SYNCHRONOUS negotiations on
* different commands ; although it should be trivial to do them
* both at the same time .
*/
if ( hostdata - > initiate_wdtr & ( 1 < < cmd - > device - > id ) ) {
memcpy ( ( void * ) ( tmp - > select + 1 ) , ( void * ) wdtr_message ,
sizeof ( wdtr_message ) ) ;
patch_dsa_32 ( tmp - > dsa , dsa_msgout , 0 , 1 + sizeof ( wdtr_message ) ) ;
local_irq_save ( flags ) ;
hostdata - > initiate_wdtr & = ~ ( 1 < < cmd - > device - > id ) ;
local_irq_restore ( flags ) ;
} else if ( hostdata - > initiate_sdtr & ( 1 < < cmd - > device - > id ) ) {
memcpy ( ( void * ) ( tmp - > select + 1 ) , ( void * ) sdtr_message ,
sizeof ( sdtr_message ) ) ;
patch_dsa_32 ( tmp - > dsa , dsa_msgout , 0 , 1 + sizeof ( sdtr_message ) ) ;
tmp - > flags | = CMD_FLAG_SDTR ;
local_irq_save ( flags ) ;
hostdata - > initiate_sdtr & = ~ ( 1 < < cmd - > device - > id ) ;
local_irq_restore ( flags ) ;
}
# if 1
else if ( ! ( hostdata - > talked_to & ( 1 < < cmd - > device - > id ) ) & &
! ( hostdata - > options & OPTION_NO_ASYNC ) ) {
memcpy ( ( void * ) ( tmp - > select + 1 ) , ( void * ) async_message ,
sizeof ( async_message ) ) ;
patch_dsa_32 ( tmp - > dsa , dsa_msgout , 0 , 1 + sizeof ( async_message ) ) ;
tmp - > flags | = CMD_FLAG_SDTR ;
}
# endif
else
patch_dsa_32 ( tmp - > dsa , dsa_msgout , 0 , 1 ) ;
hostdata - > talked_to | = ( 1 < < cmd - > device - > id ) ;
tmp - > select [ 0 ] = ( hostdata - > options & OPTION_DISCONNECT ) ?
IDENTIFY ( 1 , cmd - > device - > lun ) : IDENTIFY ( 0 , cmd - > device - > lun ) ;
patch_dsa_32 ( tmp - > dsa , dsa_msgout , 1 , virt_to_bus ( tmp - > select ) ) ;
patch_dsa_32 ( tmp - > dsa , dsa_cmdout , 0 , cmd - > cmd_len ) ;
patch_dsa_32 ( tmp - > dsa , dsa_cmdout , 1 , virt_to_bus ( tmp - > cmnd ) ) ;
patch_dsa_32 ( tmp - > dsa , dsa_dataout , 0 , cmd_dataout ?
virt_to_bus ( cmd_dataout )
: virt_to_bus ( hostdata - > script ) + hostdata - > E_other_transfer ) ;
patch_dsa_32 ( tmp - > dsa , dsa_datain , 0 , cmd_datain ?
virt_to_bus ( cmd_datain )
: virt_to_bus ( hostdata - > script ) + hostdata - > E_other_transfer ) ;
/*
* XXX - need to make endian aware , should use separate variables
* for both status and message bytes .
*/
patch_dsa_32 ( tmp - > dsa , dsa_msgin , 0 , 1 ) ;
/*
* FIXME : these only works for little endian . We probably want to
* provide message and status fields in the NCR53c7x0_cmd
* structure , and assign them to cmd - > result when we ' re done .
*/
# ifdef BIG_ENDIAN
patch_dsa_32 ( tmp - > dsa , dsa_msgin , 1 , virt_to_bus ( & tmp - > result ) + 2 ) ;
patch_dsa_32 ( tmp - > dsa , dsa_status , 0 , 1 ) ;
patch_dsa_32 ( tmp - > dsa , dsa_status , 1 , virt_to_bus ( & tmp - > result ) + 3 ) ;
# else
patch_dsa_32 ( tmp - > dsa , dsa_msgin , 1 , virt_to_bus ( & tmp - > result ) + 1 ) ;
patch_dsa_32 ( tmp - > dsa , dsa_status , 0 , 1 ) ;
patch_dsa_32 ( tmp - > dsa , dsa_status , 1 , virt_to_bus ( & tmp - > result ) ) ;
# endif
patch_dsa_32 ( tmp - > dsa , dsa_msgout_other , 0 , 1 ) ;
patch_dsa_32 ( tmp - > dsa , dsa_msgout_other , 1 ,
virt_to_bus ( & ( hostdata - > NCR53c7xx_msg_nop ) ) ) ;
/*
* Generate code for zero or more of the DATA IN , DATA OUT phases
* in the format
*
* CALL data_transfer , WHEN NOT phase
* MOVE first buffer length , first buffer address , WHEN phase
* . . .
* MOVE last buffer length , last buffer address , WHEN phase
* JUMP other_transfer
*/
/*
* See if we ' re getting to data transfer by generating an unconditional
* interrupt .
*/
#if 0
if ( datain ) {
cmd_datain [ 0 ] = 0x98080000 ;
cmd_datain [ 1 ] = 0x03ffd00d ;
cmd_datain + = 2 ;
}
# endif
/*
* XXX - I ' m undecided whether all of this nonsense is faster
* in the long run , or whether I should just go and implement a loop
* on the NCR chip using table indirect mode ?
*
* In any case , this is how it _must_ be done for 53 c700 / 700 - 66 chips ,
* so this stays even when we come up with something better .
*
* When we ' re limited to 1 simultaneous command , no overlapping processing ,
* we ' re seeing 630 K / sec , with 7 % CPU usage on a slow Syquest 45 M
* drive .
*
* Not bad , not good . We ' ll see .
*/
tmp - > bounce . len = 0 ; /* Assume aligned buffer */
for ( i = 0 ; cmd - > use_sg ? ( i < cmd - > use_sg ) : ! i ; cmd_datain + = 4 ,
cmd_dataout + = 4 , + + i ) {
u32 vbuf = cmd - > use_sg
? ( u32 ) page_address ( ( ( struct scatterlist * ) cmd - > buffer ) [ i ] . page ) +
( ( struct scatterlist * ) cmd - > buffer ) [ i ] . offset
: ( u32 ) ( cmd - > request_buffer ) ;
u32 bbuf = virt_to_bus ( ( void * ) vbuf ) ;
u32 count = cmd - > use_sg ?
( ( struct scatterlist * ) cmd - > buffer ) [ i ] . length :
cmd - > request_bufflen ;
/*
* If we have buffers which are not aligned with 16 byte cache
* lines , then we just hope nothing accesses the other parts of
* those cache lines while the transfer is in progress . That would
* fill the cache , and subsequent reads of the dma data would pick
* up the wrong thing .
* XXX We need a bounce buffer to handle that correctly .
*/
if ( ( ( bbuf & 15 ) | | ( count & 15 ) ) & & ( datain | | dataout ) )
{
/* Bounce buffer needed */
if ( cmd - > use_sg )
printk ( " 53c7xx: Non-aligned buffer with use_sg \n " ) ;
else if ( datain & & dataout )
printk ( " 53c7xx: Non-aligned buffer with datain && dataout \n " ) ;
else if ( count > 256 )
printk ( " 53c7xx: Non-aligned transfer > 256 bytes \n " ) ;
else
{
if ( datain )
{
tmp - > bounce . len = count ;
tmp - > bounce . addr = vbuf ;
bbuf = virt_to_bus ( tmp - > bounce . buf ) ;
tmp - > bounce . buf [ 0 ] = 0xff ;
tmp - > bounce . buf [ 1 ] = 0xfe ;
tmp - > bounce . buf [ 2 ] = 0xfd ;
tmp - > bounce . buf [ 3 ] = 0xfc ;
}
if ( dataout )
{
memcpy ( ( void * ) tmp - > bounce . buf , ( void * ) vbuf , count ) ;
bbuf = virt_to_bus ( tmp - > bounce . buf ) ;
}
}
}
if ( datain ) {
cache_clear ( virt_to_phys ( ( void * ) vbuf ) , count ) ;
/* CALL other_in, WHEN NOT DATA_IN */
cmd_datain [ 0 ] = ( ( DCMD_TYPE_TCI | DCMD_TCI_OP_CALL |
DCMD_TCI_IO ) < < 24 ) |
DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE ;
cmd_datain [ 1 ] = virt_to_bus ( hostdata - > script ) +
hostdata - > E_other_in ;
/* MOVE count, buf, WHEN DATA_IN */
cmd_datain [ 2 ] = ( ( DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | DCMD_BMI_IO )
< < 24 ) | count ;
cmd_datain [ 3 ] = bbuf ;
#if 0
print_insn ( host , cmd_datain , " dynamic " , 1 ) ;
print_insn ( host , cmd_datain + 2 , " dynamic " , 1 ) ;
# endif
}
if ( dataout ) {
cache_push ( virt_to_phys ( ( void * ) vbuf ) , count ) ;
/* CALL other_out, WHEN NOT DATA_OUT */
cmd_dataout [ 0 ] = ( ( DCMD_TYPE_TCI | DCMD_TCI_OP_CALL ) < < 24 ) |
DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE ;
cmd_dataout [ 1 ] = virt_to_bus ( hostdata - > script ) +
hostdata - > E_other_out ;
/* MOVE count, buf, WHEN DATA+OUT */
cmd_dataout [ 2 ] = ( ( DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I ) < < 24 )
| count ;
cmd_dataout [ 3 ] = bbuf ;
#if 0
print_insn ( host , cmd_dataout , " dynamic " , 1 ) ;
print_insn ( host , cmd_dataout + 2 , " dynamic " , 1 ) ;
# endif
}
}
/*
* Install JUMP instructions after the data transfer routines to return
* control to the do_other_transfer routines .
*/
if ( datain ) {
cmd_datain [ 0 ] = ( ( DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP ) < < 24 ) |
DBC_TCI_TRUE ;
cmd_datain [ 1 ] = virt_to_bus ( hostdata - > script ) +
hostdata - > E_other_transfer ;
#if 0
print_insn ( host , cmd_datain , " dynamic jump " , 1 ) ;
# endif
cmd_datain + = 2 ;
}
#if 0
if ( datain ) {
cmd_datain [ 0 ] = 0x98080000 ;
cmd_datain [ 1 ] = 0x03ffdeed ;
cmd_datain + = 2 ;
}
# endif
if ( dataout ) {
cmd_dataout [ 0 ] = ( ( DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP ) < < 24 ) |
DBC_TCI_TRUE ;
cmd_dataout [ 1 ] = virt_to_bus ( hostdata - > script ) +
hostdata - > E_other_transfer ;
#if 0
print_insn ( host , cmd_dataout , " dynamic jump " , 1 ) ;
# endif
cmd_dataout + = 2 ;
}
return tmp ;
}
/*
* Function : int NCR53c7xx_queue_command ( Scsi_Cmnd * cmd ,
* void ( * done ) ( Scsi_Cmnd * ) )
*
* Purpose : enqueues a SCSI command
*
* Inputs : cmd - SCSI command , done - function called on completion , with
* a pointer to the command descriptor .
*
* Returns : 0
*
* Side effects :
* cmd is added to the per instance driver issue_queue , with major
* twiddling done to the host specific fields of cmd . If the
* process_issue_queue coroutine isn ' t running , it is restarted .
*
* NOTE : we use the host_scribble field of the Scsi_Cmnd structure to
* hold our own data , and pervert the ptr field of the SCp field
* to create a linked list .
*/
int
NCR53c7xx_queue_command ( Scsi_Cmnd * cmd , void ( * done ) ( Scsi_Cmnd * ) ) {
struct Scsi_Host * host = cmd - > device - > host ;
struct NCR53c7x0_hostdata * hostdata =
( struct NCR53c7x0_hostdata * ) host - > hostdata [ 0 ] ;
unsigned long flags ;
Scsi_Cmnd * tmp ;
cmd - > scsi_done = done ;
cmd - > host_scribble = NULL ;
cmd - > SCp . ptr = NULL ;
cmd - > SCp . buffer = NULL ;
# ifdef VALID_IDS
/* Ignore commands on invalid IDs */
if ( ! hostdata - > valid_ids [ cmd - > device - > id ] ) {
printk ( " scsi%d : ignoring target %d lun %d \n " , host - > host_no ,
cmd - > device - > id , cmd - > device - > lun ) ;
cmd - > result = ( DID_BAD_TARGET < < 16 ) ;
done ( cmd ) ;
return 0 ;
}
# endif
local_irq_save ( flags ) ;
if ( ( hostdata - > options & ( OPTION_DEBUG_INIT_ONLY | OPTION_DEBUG_PROBE_ONLY ) )
| | ( ( hostdata - > options & OPTION_DEBUG_TARGET_LIMIT ) & &
! ( hostdata - > debug_lun_limit [ cmd - > device - > id ] & ( 1 < < cmd - > device - > lun ) ) )
# ifdef LINUX_1_2
| | cmd - > device - > id > 7
# else
| | cmd - > device - > id > host - > max_id
# endif
| | cmd - > device - > id = = host - > this_id
| | hostdata - > state = = STATE_DISABLED ) {
printk ( " scsi%d : disabled or bad target %d lun %d \n " , host - > host_no ,
cmd - > device - > id , cmd - > device - > lun ) ;
cmd - > result = ( DID_BAD_TARGET < < 16 ) ;
done ( cmd ) ;
local_irq_restore ( flags ) ;
return 0 ;
}
if ( ( hostdata - > options & OPTION_DEBUG_NCOMMANDS_LIMIT ) & &
( hostdata - > debug_count_limit = = 0 ) ) {
printk ( " scsi%d : maximum commands exceeded \n " , host - > host_no ) ;
cmd - > result = ( DID_BAD_TARGET < < 16 ) ;
done ( cmd ) ;
local_irq_restore ( flags ) ;
return 0 ;
}
if ( hostdata - > options & OPTION_DEBUG_READ_ONLY ) {
switch ( cmd - > cmnd [ 0 ] ) {
case WRITE_6 :
case WRITE_10 :
printk ( " scsi%d : WRITE attempted with NO_WRITE debugging flag set \n " ,
host - > host_no ) ;
cmd - > result = ( DID_BAD_TARGET < < 16 ) ;
done ( cmd ) ;
local_irq_restore ( flags ) ;
return 0 ;
}
}
if ( ( hostdata - > options & OPTION_DEBUG_TARGET_LIMIT ) & &
hostdata - > debug_count_limit ! = - 1 )
- - hostdata - > debug_count_limit ;
cmd - > result = 0xffff ; /* The NCR will overwrite message
and status with valid data */
cmd - > host_scribble = ( unsigned char * ) tmp = create_cmd ( cmd ) ;
/*
* REQUEST SENSE commands are inserted at the head of the queue
* so that we do not clear the contingent allegiance condition
* they may be looking at .
*/
if ( ! ( hostdata - > issue_queue ) | | ( cmd - > cmnd [ 0 ] = = REQUEST_SENSE ) ) {
cmd - > SCp . ptr = ( unsigned char * ) hostdata - > issue_queue ;
hostdata - > issue_queue = cmd ;
} else {
for ( tmp = ( Scsi_Cmnd * ) hostdata - > issue_queue ; tmp - > SCp . ptr ;
tmp = ( Scsi_Cmnd * ) tmp - > SCp . ptr ) ;
tmp - > SCp . ptr = ( unsigned char * ) cmd ;
}
local_irq_restore ( flags ) ;
run_process_issue_queue ( ) ;
return 0 ;
}
/*
* Function : void to_schedule_list ( struct Scsi_Host * host ,
* struct NCR53c7x0_hostdata * hostdata , Scsi_Cmnd * cmd )
*
* Purpose : takes a SCSI command which was just removed from the
* issue queue , and deals with it by inserting it in the first
* free slot in the schedule list or by terminating it immediately .
*
* Inputs :
* host - SCSI host adapter ; hostdata - hostdata structure for
* this adapter ; cmd - a pointer to the command ; should have
* the host_scribble field initialized to point to a valid
*
* Side effects :
* cmd is added to the per instance schedule list , with minor
* twiddling done to the host specific fields of cmd .
*
*/
static __inline__ void
to_schedule_list ( struct Scsi_Host * host , struct NCR53c7x0_hostdata * hostdata ,
struct NCR53c7x0_cmd * cmd ) {
NCR53c7x0_local_declare ( ) ;
Scsi_Cmnd * tmp = cmd - > cmd ;
unsigned long flags ;
/* dsa start is negative, so subtraction is used */
volatile u32 * ncrcurrent ;
int i ;
NCR53c7x0_local_setup ( host ) ;
#if 0
printk ( " scsi%d : new dsa is 0x%lx (virt 0x%p) \n " , host - > host_no ,
virt_to_bus ( hostdata - > dsa ) , hostdata - > dsa ) ;
# endif
local_irq_save ( flags ) ;
/*
* Work around race condition : if an interrupt fired and we
* got disabled forget about this command .
*/
if ( hostdata - > state = = STATE_DISABLED ) {
printk ( " scsi%d : driver disabled \n " , host - > host_no ) ;
tmp - > result = ( DID_BAD_TARGET < < 16 ) ;
cmd - > next = ( struct NCR53c7x0_cmd * ) hostdata - > free ;
hostdata - > free = cmd ;
tmp - > scsi_done ( tmp ) ;
local_irq_restore ( flags ) ;
return ;
}
for ( i = host - > can_queue , ncrcurrent = hostdata - > schedule ;
i > 0 & & ncrcurrent [ 0 ] ! = hostdata - > NOP_insn ;
- - i , ncrcurrent + = 2 /* JUMP instructions are two words */ ) ;
if ( i > 0 ) {
+ + hostdata - > busy [ tmp - > device - > id ] [ tmp - > device - > lun ] ;
cmd - > next = hostdata - > running_list ;
hostdata - > running_list = cmd ;
/* Restore this instruction to a NOP once the command starts */
cmd - > dsa [ ( hostdata - > dsa_jump_dest - hostdata - > dsa_start ) /
sizeof ( u32 ) ] = ( u32 ) virt_to_bus ( ( void * ) ncrcurrent ) ;
/* Replace the current jump operand. */
ncrcurrent [ 1 ] =
virt_to_bus ( ( void * ) cmd - > dsa ) + hostdata - > E_dsa_code_begin -
hostdata - > E_dsa_code_template ;
/* Replace the NOP instruction with a JUMP */
ncrcurrent [ 0 ] = ( ( DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP ) < < 24 ) |
DBC_TCI_TRUE ;
} else {
printk ( " scsi%d: no free slot \n " , host - > host_no ) ;
disable ( host ) ;
tmp - > result = ( DID_ERROR < < 16 ) ;
cmd - > next = ( struct NCR53c7x0_cmd * ) hostdata - > free ;
hostdata - > free = cmd ;
tmp - > scsi_done ( tmp ) ;
local_irq_restore ( flags ) ;
return ;
}
/*
* If the NCR chip is in an idle state , start it running the scheduler
* immediately . Otherwise , signal the chip to jump to schedule as
* soon as it is idle .
*/
if ( hostdata - > idle ) {
hostdata - > idle = 0 ;
hostdata - > state = STATE_RUNNING ;
NCR53c7x0_write32 ( DSP_REG , virt_to_bus ( ( void * ) hostdata - > schedule ) ) ;
if ( hostdata - > options & OPTION_DEBUG_TRACE )
NCR53c7x0_write8 ( DCNTL_REG , hostdata - > saved_dcntl |
DCNTL_SSM | DCNTL_STD ) ;
} else {
NCR53c7x0_write8 ( hostdata - > istat , ISTAT_10_SIGP ) ;
}
local_irq_restore ( flags ) ;
}
/*
* Function : busyp ( struct Scsi_Host * host , struct NCR53c7x0_hostdata
* * hostdata , Scsi_Cmnd * cmd )
*
* Purpose : decide if we can pass the given SCSI command on to the
* device in question or not .
*
* Returns : non - zero when we ' re busy , 0 when we aren ' t .
*/
static __inline__ int
busyp ( struct Scsi_Host * host , struct NCR53c7x0_hostdata * hostdata ,
Scsi_Cmnd * cmd ) {
/* FIXME : in the future, this needs to accommodate SCSI-II tagged
queuing , and we may be able to play with fairness here a bit .
*/
return hostdata - > busy [ cmd - > device - > id ] [ cmd - > device - > lun ] ;
}
/*
* Function : process_issue_queue ( void )
*
* Purpose : transfer commands from the issue queue to NCR start queue
* of each NCR53c7 / 8 xx in the system , avoiding kernel stack
* overflows when the scsi_done ( ) function is invoked recursively .
*
* NOTE : process_issue_queue exits with interrupts * disabled * , so the
* caller must reenable them if it desires .
*
* NOTE : process_issue_queue should be called from both
* NCR53c7x0_queue_command ( ) and from the interrupt handler
* after command completion in case NCR53c7x0_queue_command ( )
* isn ' t invoked again but we ' ve freed up resources that are
* needed .
*/
static void
process_issue_queue ( unsigned long flags ) {
Scsi_Cmnd * tmp , * prev ;
struct Scsi_Host * host ;
struct NCR53c7x0_hostdata * hostdata ;
int done ;
/*
* We run ( with interrupts disabled ) until we ' re sure that none of
* the host adapters have anything that can be done , at which point
* we set process_issue_queue_running to 0 and exit .
*
* Interrupts are enabled before doing various other internal
* instructions , after we ' ve decided that we need to run through
* the loop again .
*
*/
do {
local_irq_disable ( ) ; /* Freeze request queues */
done = 1 ;
for ( host = first_host ; host & & host - > hostt = = the_template ;
host = host - > next ) {
hostdata = ( struct NCR53c7x0_hostdata * ) host - > hostdata [ 0 ] ;
local_irq_disable ( ) ;
if ( hostdata - > issue_queue ) {
if ( hostdata - > state = = STATE_DISABLED ) {
tmp = ( Scsi_Cmnd * ) hostdata - > issue_queue ;
hostdata - > issue_queue = ( Scsi_Cmnd * ) tmp - > SCp . ptr ;
tmp - > result = ( DID_BAD_TARGET < < 16 ) ;
if ( tmp - > host_scribble ) {
( ( struct NCR53c7x0_cmd * ) tmp - > host_scribble ) - > next =
hostdata - > free ;
hostdata - > free =
( struct NCR53c7x0_cmd * ) tmp - > host_scribble ;
tmp - > host_scribble = NULL ;
}
tmp - > scsi_done ( tmp ) ;
done = 0 ;
} else
for ( tmp = ( Scsi_Cmnd * ) hostdata - > issue_queue ,
prev = NULL ; tmp ; prev = tmp , tmp = ( Scsi_Cmnd * )
tmp - > SCp . ptr )
if ( ! tmp - > host_scribble | |
! busyp ( host , hostdata , tmp ) ) {
if ( prev )
prev - > SCp . ptr = tmp - > SCp . ptr ;
else
hostdata - > issue_queue = ( Scsi_Cmnd * )
tmp - > SCp . ptr ;
tmp - > SCp . ptr = NULL ;
if ( tmp - > host_scribble ) {
if ( hostdata - > options & OPTION_DEBUG_QUEUES )
printk ( " scsi%d : moving command for target %d lun %d to start list \n " ,
host - > host_no , tmp - > device - > id , tmp - > device - > lun ) ;
to_schedule_list ( host , hostdata ,
( struct NCR53c7x0_cmd * )
tmp - > host_scribble ) ;
} else {
if ( ( ( tmp - > result & 0xff ) = = 0xff ) | |
( ( tmp - > result & 0xff00 ) = = 0xff00 ) ) {
printk ( " scsi%d : danger Will Robinson! \n " ,
host - > host_no ) ;
tmp - > result = DID_ERROR < < 16 ;
disable ( host ) ;
}
tmp - > scsi_done ( tmp ) ;
}
done = 0 ;
} /* if target/lun is not busy */
} /* if hostdata->issue_queue */
if ( ! done )
local_irq_restore ( flags ) ;
} /* for host */
} while ( ! done ) ;
process_issue_queue_running = 0 ;
}
/*
* Function : static void intr_scsi ( struct Scsi_Host * host ,
* struct NCR53c7x0_cmd * cmd )
*
* Purpose : handle all SCSI interrupts , indicated by the setting
* of the SIP bit in the ISTAT register .
*
* Inputs : host , cmd - host and NCR command causing the interrupt , cmd
* may be NULL .
*/
static void
intr_scsi ( struct Scsi_Host * host , struct NCR53c7x0_cmd * cmd ) {
NCR53c7x0_local_declare ( ) ;
struct NCR53c7x0_hostdata * hostdata =
( struct NCR53c7x0_hostdata * ) host - > hostdata [ 0 ] ;
unsigned char sstat0_sist0 , sist1 , /* Registers */
fatal ; /* Did a fatal interrupt
occur ? */
NCR53c7x0_local_setup ( host ) ;
fatal = 0 ;
sstat0_sist0 = NCR53c7x0_read8 ( SSTAT0_REG ) ;
sist1 = 0 ;
if ( hostdata - > options & OPTION_DEBUG_INTR )
printk ( " scsi%d : SIST0 0x%0x, SIST1 0x%0x \n " , host - > host_no ,
sstat0_sist0 , sist1 ) ;
/* 250ms selection timeout */
if ( sstat0_sist0 & SSTAT0_700_STO ) {
fatal = 1 ;
if ( hostdata - > options & OPTION_DEBUG_INTR ) {
printk ( " scsi%d : Selection Timeout \n " , host - > host_no ) ;
if ( cmd ) {
printk ( " scsi%d : target %d, lun %d, command " ,
host - > host_no , cmd - > cmd - > device - > id , cmd - > cmd - > device - > lun ) ;
2005-04-03 23:53:59 +04:00
__scsi_print_command ( cmd - > cmd - > cmnd ) ;
2005-04-17 02:20:36 +04:00
printk ( " scsi%d : dsp = 0x%x (virt 0x%p) \n " , host - > host_no ,
NCR53c7x0_read32 ( DSP_REG ) ,
bus_to_virt ( NCR53c7x0_read32 ( DSP_REG ) ) ) ;
} else {
printk ( " scsi%d : no command \n " , host - > host_no ) ;
}
}
/*
* XXX - question : how do we want to handle the Illegal Instruction
* interrupt , which may occur before or after the Selection Timeout
* interrupt ?
*/
if ( 1 ) {
hostdata - > idle = 1 ;
hostdata - > expecting_sto = 0 ;
if ( hostdata - > test_running ) {
hostdata - > test_running = 0 ;
hostdata - > test_completed = 3 ;
} else if ( cmd ) {
abnormal_finished ( cmd , DID_BAD_TARGET < < 16 ) ;
}
#if 0
hostdata - > intrs = 0 ;
# endif
}
}
/*
* FIXME : in theory , we can also get a UDC when a STO occurs .
*/
if ( sstat0_sist0 & SSTAT0_UDC ) {
fatal = 1 ;
if ( cmd ) {
printk ( " scsi%d : target %d lun %d unexpected disconnect \n " ,
host - > host_no , cmd - > cmd - > device - > id , cmd - > cmd - > device - > lun ) ;
print_lots ( host ) ;
abnormal_finished ( cmd , DID_ERROR < < 16 ) ;
} else
printk ( " scsi%d : unexpected disconnect (no command) \n " ,
host - > host_no ) ;
hostdata - > dsp = ( u32 * ) hostdata - > schedule ;
hostdata - > dsp_changed = 1 ;
}
/* SCSI PARITY error */
if ( sstat0_sist0 & SSTAT0_PAR ) {
fatal = 1 ;
if ( cmd & & cmd - > cmd ) {
printk ( " scsi%d : target %d lun %d parity error. \n " ,
host - > host_no , cmd - > cmd - > device - > id , cmd - > cmd - > device - > lun ) ;
abnormal_finished ( cmd , DID_PARITY < < 16 ) ;
} else
printk ( " scsi%d : parity error \n " , host - > host_no ) ;
/* Should send message out, parity error */
/* XXX - Reduce synchronous transfer rate! */
hostdata - > dsp = hostdata - > script + hostdata - > E_initiator_abort /
sizeof ( u32 ) ;
hostdata - > dsp_changed = 1 ;
/* SCSI GROSS error */
}
if ( sstat0_sist0 & SSTAT0_SGE ) {
fatal = 1 ;
printk ( " scsi%d : gross error, saved2_dsa = 0x%x \n " , host - > host_no ,
( unsigned int ) hostdata - > saved2_dsa ) ;
print_lots ( host ) ;
/*
* A SCSI gross error may occur when we have
*
* - A synchronous offset which causes the SCSI FIFO to be overwritten .
*
* - A REQ which causes the maximum synchronous offset programmed in
* the SXFER register to be exceeded .
*
* - A phase change with an outstanding synchronous offset .
*
* - Residual data in the synchronous data FIFO , with a transfer
* other than a synchronous receive is started . $ #
*/
/* XXX Should deduce synchronous transfer rate! */
hostdata - > dsp = hostdata - > script + hostdata - > E_initiator_abort /
sizeof ( u32 ) ;
hostdata - > dsp_changed = 1 ;
/* Phase mismatch */
}
if ( sstat0_sist0 & SSTAT0_MA ) {
fatal = 1 ;
if ( hostdata - > options & OPTION_DEBUG_INTR )
printk ( " scsi%d : SSTAT0_MA \n " , host - > host_no ) ;
intr_phase_mismatch ( host , cmd ) ;
}
#if 0
if ( sstat0_sist0 & SIST0_800_RSL )
printk ( " scsi%d : Oh no Mr. Bill! \n " , host - > host_no ) ;
# endif
/*
* If a fatal SCSI interrupt occurs , we must insure that the DMA and
* SCSI FIFOs were flushed .
*/
if ( fatal ) {
if ( ! hostdata - > dstat_valid ) {
hostdata - > dstat = NCR53c7x0_read8 ( DSTAT_REG ) ;
hostdata - > dstat_valid = 1 ;
}
if ( ! ( hostdata - > dstat & DSTAT_DFE ) ) {
printk ( " scsi%d : DMA FIFO not empty \n " , host - > host_no ) ;
/*
* Really need to check this code for 710 RGH .
* Havn ' t seen any problems , but maybe we should FLUSH before
* clearing sometimes .
*/
NCR53c7x0_write8 ( CTEST8_REG , CTEST8_10_CLF ) ;
while ( NCR53c7x0_read8 ( CTEST8_REG ) & CTEST8_10_CLF )
;
hostdata - > dstat | = DSTAT_DFE ;
}
}
}
# ifdef CYCLIC_TRACE
/*
* The following implements a cyclic log of instructions executed , if you turn
* TRACE on . It will also print the log for you . Very useful when debugging
* 53 c710 support , possibly not really needed any more .
*/
u32 insn_log [ 4096 ] ;
u32 insn_log_index = 0 ;
void log1 ( u32 i )
{
insn_log [ insn_log_index + + ] = i ;
if ( insn_log_index = = 4096 )
insn_log_index = 0 ;
}
void log_insn ( u32 * ip )
{
log1 ( ( u32 ) ip ) ;
log1 ( * ip ) ;
log1 ( * ( ip + 1 ) ) ;
if ( ( ( * ip > > 24 ) & DCMD_TYPE_MASK ) = = DCMD_TYPE_MMI )
log1 ( * ( ip + 2 ) ) ;
}
void dump_log ( void )
{
int cnt = 0 ;
int i = insn_log_index ;
int size ;
struct Scsi_Host * host = first_host ;
while ( cnt < 4096 ) {
printk ( " %08x (+%6x): " , insn_log [ i ] , ( insn_log [ i ] - ( u32 ) & ( ( ( struct NCR53c7x0_hostdata * ) host - > hostdata [ 0 ] ) - > script ) ) / 4 ) ;
if ( + + i = = 4096 )
i = 0 ;
cnt + + ;
if ( ( ( insn_log [ i ] > > 24 ) & DCMD_TYPE_MASK ) = = DCMD_TYPE_MMI )
size = 3 ;
else
size = 2 ;
while ( size - - ) {
printk ( " %08x " , insn_log [ i ] ) ;
if ( + + i = = 4096 )
i = 0 ;
cnt + + ;
}
printk ( " \n " ) ;
}
}
# endif
/*
* Function : static void NCR53c7x0_intfly ( struct Scsi_Host * host )
*
* Purpose : Scan command queue for specified host , looking for completed
* commands .
*
* Inputs : Scsi_Host pointer .
*
* This is called from the interrupt handler , when a simulated INTFLY
* interrupt occurs .
*/
static void
NCR53c7x0_intfly ( struct Scsi_Host * host )
{
NCR53c7x0_local_declare ( ) ;
struct NCR53c7x0_hostdata * hostdata ; /* host->hostdata[0] */
struct NCR53c7x0_cmd * cmd , /* command which halted */
* * cmd_prev_ptr ;
unsigned long flags ;
char search_found = 0 ; /* Got at least one ? */
hostdata = ( struct NCR53c7x0_hostdata * ) host - > hostdata [ 0 ] ;
NCR53c7x0_local_setup ( host ) ;
if ( hostdata - > options & OPTION_DEBUG_INTR )
printk ( " scsi%d : INTFLY \n " , host - > host_no ) ;
/*
* Traverse our list of running commands , and look
* for those with valid ( non - 0xff ff ) status and message
* bytes encoded in the result which signify command
* completion .
*/
local_irq_save ( flags ) ;
restart :
for ( cmd_prev_ptr = ( struct NCR53c7x0_cmd * * ) & ( hostdata - > running_list ) ,
cmd = ( struct NCR53c7x0_cmd * ) hostdata - > running_list ; cmd ;
cmd_prev_ptr = ( struct NCR53c7x0_cmd * * ) & ( cmd - > next ) ,
cmd = ( struct NCR53c7x0_cmd * ) cmd - > next )
{
Scsi_Cmnd * tmp ;
if ( ! cmd ) {
printk ( " scsi%d : very weird. \n " , host - > host_no ) ;
break ;
}
if ( ! ( tmp = cmd - > cmd ) ) {
printk ( " scsi%d : weird. NCR53c7x0_cmd has no Scsi_Cmnd \n " ,
host - > host_no ) ;
continue ;
}
/* Copy the result over now; may not be complete,
* but subsequent tests may as well be done on
* cached memory .
*/
tmp - > result = cmd - > result ;
if ( ( ( tmp - > result & 0xff ) = = 0xff ) | |
( ( tmp - > result & 0xff00 ) = = 0xff00 ) )
continue ;
search_found = 1 ;
if ( cmd - > bounce . len )
memcpy ( ( void * ) cmd - > bounce . addr ,
( void * ) cmd - > bounce . buf , cmd - > bounce . len ) ;
/* Important - remove from list _before_ done is called */
if ( cmd_prev_ptr )
* cmd_prev_ptr = ( struct NCR53c7x0_cmd * ) cmd - > next ;
- - hostdata - > busy [ tmp - > device - > id ] [ tmp - > device - > lun ] ;
cmd - > next = hostdata - > free ;
hostdata - > free = cmd ;
tmp - > host_scribble = NULL ;
if ( hostdata - > options & OPTION_DEBUG_INTR ) {
printk ( " scsi%d : command complete : pid %lu, id %d,lun %d result 0x%x " ,
host - > host_no , tmp - > pid , tmp - > device - > id , tmp - > device - > lun , tmp - > result ) ;
2005-04-03 23:53:59 +04:00
__scsi_print_command ( tmp - > cmnd ) ;
2005-04-17 02:20:36 +04:00
}
tmp - > scsi_done ( tmp ) ;
goto restart ;
}
local_irq_restore ( flags ) ;
if ( ! search_found ) {
printk ( " scsi%d : WARNING : INTFLY with no completed commands. \n " ,
host - > host_no ) ;
} else {
run_process_issue_queue ( ) ;
}
return ;
}
/*
* Function : static irqreturn_t NCR53c7x0_intr ( int irq , void * dev_id , struct pt_regs * regs )
*
* Purpose : handle NCR53c7x0 interrupts for all NCR devices sharing
* the same IRQ line .
*
* Inputs : Since we ' re using the SA_INTERRUPT interrupt handler
* semantics , irq indicates the interrupt which invoked
* this handler .
*
* On the 710 we simualte an INTFLY with a script interrupt , and the
* script interrupt handler will call back to this function .
*/
static irqreturn_t
NCR53c7x0_intr ( int irq , void * dev_id , struct pt_regs * regs )
{
NCR53c7x0_local_declare ( ) ;
struct Scsi_Host * host ; /* Host we are looking at */
unsigned char istat ; /* Values of interrupt regs */
struct NCR53c7x0_hostdata * hostdata ; /* host->hostdata[0] */
struct NCR53c7x0_cmd * cmd ; /* command which halted */
u32 * dsa ; /* DSA */
int handled = 0 ;
# ifdef NCR_DEBUG
char buf [ 80 ] ; /* Debugging sprintf buffer */
size_t buflen ; /* Length of same */
# endif
host = ( struct Scsi_Host * ) dev_id ;
hostdata = ( struct NCR53c7x0_hostdata * ) host - > hostdata [ 0 ] ;
NCR53c7x0_local_setup ( host ) ;
/*
* Only read istat once per loop , since reading it again will unstack
* interrupts
*/
while ( ( istat = NCR53c7x0_read8 ( hostdata - > istat ) ) & ( ISTAT_SIP | ISTAT_DIP ) ) {
handled = 1 ;
hostdata - > dsp_changed = 0 ;
hostdata - > dstat_valid = 0 ;
hostdata - > state = STATE_HALTED ;
if ( NCR53c7x0_read8 ( SSTAT2_REG ) & SSTAT2_FF_MASK )
printk ( " scsi%d : SCSI FIFO not empty \n " , host - > host_no ) ;
/*
* NCR53c700 and NCR53c700 - 66 change the current SCSI
* process , hostdata - > ncrcurrent , in the Linux driver so
* cmd = hostdata - > ncrcurrent .
*
* With other chips , we must look through the commands
* executing and find the command structure which
* corresponds to the DSA register .
*/
if ( hostdata - > options & OPTION_700 ) {
cmd = ( struct NCR53c7x0_cmd * ) hostdata - > ncrcurrent ;
} else {
dsa = bus_to_virt ( NCR53c7x0_read32 ( DSA_REG ) ) ;
for ( cmd = ( struct NCR53c7x0_cmd * ) hostdata - > running_list ;
cmd & & ( dsa + ( hostdata - > dsa_start / sizeof ( u32 ) ) ) ! = cmd - > dsa ;
cmd = ( struct NCR53c7x0_cmd * ) ( cmd - > next ) )
;
}
if ( hostdata - > options & OPTION_DEBUG_INTR ) {
if ( cmd ) {
printk ( " scsi%d : interrupt for pid %lu, id %d, lun %d " ,
host - > host_no , cmd - > cmd - > pid , ( int ) cmd - > cmd - > device - > id ,
( int ) cmd - > cmd - > device - > lun ) ;
2005-04-03 23:53:59 +04:00
__scsi_print_command ( cmd - > cmd - > cmnd ) ;
2005-04-17 02:20:36 +04:00
} else {
printk ( " scsi%d : no active command \n " , host - > host_no ) ;
}
}
if ( istat & ISTAT_SIP ) {
if ( hostdata - > options & OPTION_DEBUG_INTR )
printk ( " scsi%d : ISTAT_SIP \n " , host - > host_no ) ;
intr_scsi ( host , cmd ) ;
}
if ( istat & ISTAT_DIP ) {
if ( hostdata - > options & OPTION_DEBUG_INTR )
printk ( " scsi%d : ISTAT_DIP \n " , host - > host_no ) ;
intr_dma ( host , cmd ) ;
}
if ( ! hostdata - > dstat_valid ) {
hostdata - > dstat = NCR53c7x0_read8 ( DSTAT_REG ) ;
hostdata - > dstat_valid = 1 ;
}
if ( ! ( hostdata - > dstat & DSTAT_DFE ) ) {
printk ( " scsi%d : DMA FIFO not empty \n " , host - > host_no ) ;
/* Really need to check this out for 710 RGH */
NCR53c7x0_write8 ( CTEST8_REG , CTEST8_10_CLF ) ;
while ( NCR53c7x0_read8 ( CTEST8_REG ) & CTEST8_10_CLF )
;
hostdata - > dstat | = DSTAT_DFE ;
}
if ( ! hostdata - > idle & & hostdata - > state = = STATE_HALTED ) {
if ( ! hostdata - > dsp_changed )
hostdata - > dsp = ( u32 * ) bus_to_virt ( NCR53c7x0_read32 ( DSP_REG ) ) ;
#if 0
printk ( " scsi%d : new dsp is 0x%lx (virt 0x%p) \n " ,
host - > host_no , virt_to_bus ( hostdata - > dsp ) , hostdata - > dsp ) ;
# endif
hostdata - > state = STATE_RUNNING ;
NCR53c7x0_write32 ( DSP_REG , virt_to_bus ( hostdata - > dsp ) ) ;
if ( hostdata - > options & OPTION_DEBUG_TRACE ) {
# ifdef CYCLIC_TRACE
log_insn ( hostdata - > dsp ) ;
# else
print_insn ( host , hostdata - > dsp , " t " , 1 ) ;
# endif
NCR53c7x0_write8 ( DCNTL_REG ,
hostdata - > saved_dcntl | DCNTL_SSM | DCNTL_STD ) ;
}
}
}
return IRQ_HANDLED ;
}
/*
* Function : static int abort_connected ( struct Scsi_Host * host )
*
* Purpose : Assuming that the NCR SCSI processor is currently
* halted , break the currently established nexus . Clean
* up of the NCR53c7x0_cmd and Scsi_Cmnd structures should
* be done on receipt of the abort interrupt .
*
* Inputs : host - SCSI host
*
*/
static int
abort_connected ( struct Scsi_Host * host ) {
# ifdef NEW_ABORT
NCR53c7x0_local_declare ( ) ;
# endif
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
/* FIXME : this probably should change for production kernels; at the
least , counter should move to a per - host structure . */
static int counter = 5 ;
# ifdef NEW_ABORT
int sstat , phase , offset ;
u32 * script ;
NCR53c7x0_local_setup ( host ) ;
# endif
if ( - - counter < = 0 ) {
disable ( host ) ;
return 0 ;
}
printk ( " scsi%d : DANGER : abort_connected() called \n " ,
host - > host_no ) ;
# ifdef NEW_ABORT
/*
* New strategy : Rather than using a generic abort routine ,
* we ' ll specifically try to source or sink the appropriate
* amount of data for the phase we ' re currently in ( taking into
* account the current synchronous offset )
*/
sstat = ( NCR53c8x0_read8 ( SSTAT2_REG ) ;
offset = OFFSET ( sstat & SSTAT2_FF_MASK ) > > SSTAT2_FF_SHIFT ;
phase = sstat & SSTAT2_PHASE_MASK ;
/*
* SET ATN
* MOVE source_or_sink , WHEN CURRENT PHASE
* < repeat for each outstanding byte >
* JUMP send_abort_message
*/
script = hostdata - > abort_script = kmalloc (
8 /* instruction size */ * (
1 /* set ATN */ +
( ! offset ? 1 : offset ) /* One transfer per outstanding byte */ +
1 /* send abort message */ ) ,
GFP_ATOMIC ) ;
# else /* def NEW_ABORT */
hostdata - > dsp = hostdata - > script + hostdata - > E_initiator_abort /
sizeof ( u32 ) ;
# endif /* def NEW_ABORT */
hostdata - > dsp_changed = 1 ;
/* XXX - need to flag the command as aborted after the abort_connected
code runs
*/
return 0 ;
}
/*
* Function : static int datapath_residual ( Scsi_Host * host )
*
* Purpose : return residual data count of what ' s in the chip .
*
* Inputs : host - SCSI host
*/
static int
datapath_residual ( struct Scsi_Host * host ) {
NCR53c7x0_local_declare ( ) ;
int count , synchronous , sstat ;
unsigned int ddir ;
NCR53c7x0_local_setup ( host ) ;
/* COMPAT : the 700 and 700-66 need to use DFIFO_00_BO_MASK */
count = ( ( NCR53c7x0_read8 ( DFIFO_REG ) & DFIFO_10_BO_MASK ) -
( NCR53c7x0_read32 ( DBC_REG ) & DFIFO_10_BO_MASK ) ) & DFIFO_10_BO_MASK ;
synchronous = NCR53c7x0_read8 ( SXFER_REG ) & SXFER_MO_MASK ;
/* COMPAT : DDIR is elsewhere on non-'8xx chips. */
ddir = NCR53c7x0_read8 ( CTEST0_REG_700 ) & CTEST0_700_DDIR ;
if ( ddir ) {
/* Receive */
if ( synchronous )
count + = ( NCR53c7x0_read8 ( SSTAT2_REG ) & SSTAT2_FF_MASK ) > > SSTAT2_FF_SHIFT ;
else
if ( NCR53c7x0_read8 ( SSTAT1_REG ) & SSTAT1_ILF )
+ + count ;
} else {
/* Send */
sstat = NCR53c7x0_read8 ( SSTAT1_REG ) ;
if ( sstat & SSTAT1_OLF )
+ + count ;
if ( synchronous & & ( sstat & SSTAT1_ORF ) )
+ + count ;
}
return count ;
}
/*
* Function : static const char * sbcl_to_phase ( int sbcl ) _
*
* Purpose : Convert SBCL register to user - parsable phase representation
*
* Inputs : sbcl - value of sbcl register
*/
static const char *
sbcl_to_phase ( int sbcl ) {
switch ( sbcl & SBCL_PHASE_MASK ) {
case SBCL_PHASE_DATAIN :
return " DATAIN " ;
case SBCL_PHASE_DATAOUT :
return " DATAOUT " ;
case SBCL_PHASE_MSGIN :
return " MSGIN " ;
case SBCL_PHASE_MSGOUT :
return " MSGOUT " ;
case SBCL_PHASE_CMDOUT :
return " CMDOUT " ;
case SBCL_PHASE_STATIN :
return " STATUSIN " ;
default :
return " unknown " ;
}
}
/*
* Function : static const char * sstat2_to_phase ( int sstat ) _
*
* Purpose : Convert SSTAT2 register to user - parsable phase representation
*
* Inputs : sstat - value of sstat register
*/
static const char *
sstat2_to_phase ( int sstat ) {
switch ( sstat & SSTAT2_PHASE_MASK ) {
case SSTAT2_PHASE_DATAIN :
return " DATAIN " ;
case SSTAT2_PHASE_DATAOUT :
return " DATAOUT " ;
case SSTAT2_PHASE_MSGIN :
return " MSGIN " ;
case SSTAT2_PHASE_MSGOUT :
return " MSGOUT " ;
case SSTAT2_PHASE_CMDOUT :
return " CMDOUT " ;
case SSTAT2_PHASE_STATIN :
return " STATUSIN " ;
default :
return " unknown " ;
}
}
/*
* Function : static void intr_phase_mismatch ( struct Scsi_Host * host ,
* struct NCR53c7x0_cmd * cmd )
*
* Purpose : Handle phase mismatch interrupts
*
* Inputs : host , cmd - host and NCR command causing the interrupt , cmd
* may be NULL .
*
* Side effects : The abort_connected ( ) routine is called or the NCR chip
* is restarted , jumping to the command_complete entry point , or
* patching the address and transfer count of the current instruction
* and calling the msg_in entry point as appropriate .
*/
static void
intr_phase_mismatch ( struct Scsi_Host * host , struct NCR53c7x0_cmd * cmd ) {
NCR53c7x0_local_declare ( ) ;
u32 dbc_dcmd , * dsp , * dsp_next ;
unsigned char dcmd , sbcl ;
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
int residual ;
enum { ACTION_ABORT , ACTION_ABORT_PRINT , ACTION_CONTINUE } action =
ACTION_ABORT_PRINT ;
const char * where = NULL ;
NCR53c7x0_local_setup ( host ) ;
/*
* Corrective action is based on where in the SCSI SCRIPT ( tm ) the error
* occurred , as well as which SCSI phase we are currently in .
*/
dsp_next = bus_to_virt ( NCR53c7x0_read32 ( DSP_REG ) ) ;
/*
* Fetch the current instruction , and remove the operands for easier
* interpretation .
*/
dbc_dcmd = NCR53c7x0_read32 ( DBC_REG ) ;
dcmd = ( dbc_dcmd & 0xff000000 ) > > 24 ;
/*
* Like other processors , the NCR adjusts the instruction pointer before
* instruction decode . Set the DSP address back to what it should
* be for this instruction based on its size ( 2 or 3 32 bit words ) .
*/
dsp = dsp_next - NCR53c7x0_insn_size ( dcmd ) ;
/*
* Read new SCSI phase from the SBCL lines . Since all of our code uses
* a WHEN conditional instead of an IF conditional , we don ' t need to
* wait for a new REQ .
*/
sbcl = NCR53c7x0_read8 ( SBCL_REG ) & SBCL_PHASE_MASK ;
if ( ! cmd ) {
action = ACTION_ABORT_PRINT ;
where = " no current command " ;
/*
* The way my SCSI SCRIPTS ( tm ) are architected , recoverable phase
* mismatches should only occur where we ' re doing a multi - byte
* BMI instruction . Specifically , this means
*
* - select messages ( a SCSI - I target may ignore additional messages
* after the IDENTIFY ; any target may reject a SDTR or WDTR )
*
* - command out ( targets may send a message to signal an error
* condition , or go into STATUSIN after they ' ve decided
* they don ' t like the command .
*
* - reply_message ( targets may reject a multi - byte message in the
* middle )
*
* - data transfer routines ( command completion with buffer space
* left , disconnect message , or error message )
*/
} else if ( ( ( dsp > = cmd - > data_transfer_start & &
dsp < cmd - > data_transfer_end ) ) | | dsp = = ( cmd - > residual + 2 ) ) {
if ( ( dcmd & ( DCMD_TYPE_MASK | DCMD_BMI_OP_MASK | DCMD_BMI_INDIRECT |
DCMD_BMI_MSG | DCMD_BMI_CD ) ) = = ( DCMD_TYPE_BMI |
DCMD_BMI_OP_MOVE_I ) ) {
residual = datapath_residual ( host ) ;
if ( hostdata - > options & OPTION_DEBUG_DISCONNECT )
printk ( " scsi%d : handling residual transfer (+ %d bytes from DMA FIFO) \n " ,
host - > host_no , residual ) ;
/*
* The first instruction is a CALL to the alternate handler for
* this data transfer phase , so we can do calls to
* munge_msg_restart as we would if control were passed
* from normal dynamic code .
*/
if ( dsp ! = cmd - > residual + 2 ) {
cmd - > residual [ 0 ] = ( ( DCMD_TYPE_TCI | DCMD_TCI_OP_CALL |
( ( dcmd & DCMD_BMI_IO ) ? DCMD_TCI_IO : 0 ) ) < < 24 ) |
DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE ;
cmd - > residual [ 1 ] = virt_to_bus ( hostdata - > script )
+ ( ( dcmd & DCMD_BMI_IO )
? hostdata - > E_other_in : hostdata - > E_other_out ) ;
}
/*
* The second instruction is the a data transfer block
* move instruction , reflecting the pointer and count at the
* time of the phase mismatch .
*/
cmd - > residual [ 2 ] = dbc_dcmd + residual ;
cmd - > residual [ 3 ] = NCR53c7x0_read32 ( DNAD_REG ) - residual ;
/*
* The third and final instruction is a jump to the instruction
* which follows the instruction which had to be ' split '
*/
if ( dsp ! = cmd - > residual + 2 ) {
cmd - > residual [ 4 ] = ( ( DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP )
< < 24 ) | DBC_TCI_TRUE ;
cmd - > residual [ 5 ] = virt_to_bus ( dsp_next ) ;
}
/*
* For the sake of simplicity , transfer control to the
* conditional CALL at the start of the residual buffer .
*/
hostdata - > dsp = cmd - > residual ;
hostdata - > dsp_changed = 1 ;
action = ACTION_CONTINUE ;
} else {
where = " non-BMI dynamic DSA code " ;
action = ACTION_ABORT_PRINT ;
}
} else if ( dsp = = ( hostdata - > script + hostdata - > E_select_msgout / 4 + 2 ) ) {
/* RGH 290697: Added +2 above, to compensate for the script
* instruction which disables the selection timer . */
/* Release ATN */
NCR53c7x0_write8 ( SOCL_REG , 0 ) ;
switch ( sbcl ) {
/*
* Some devices ( SQ555 come to mind ) grab the IDENTIFY message
* sent on selection , and decide to go into COMMAND OUT phase
* rather than accepting the rest of the messages or rejecting
* them . Handle these devices gracefully .
*/
case SBCL_PHASE_CMDOUT :
hostdata - > dsp = dsp + 2 /* two _words_ */ ;
hostdata - > dsp_changed = 1 ;
printk ( " scsi%d : target %d ignored SDTR and went into COMMAND OUT \n " ,
host - > host_no , cmd - > cmd - > device - > id ) ;
cmd - > flags & = ~ CMD_FLAG_SDTR ;
action = ACTION_CONTINUE ;
break ;
case SBCL_PHASE_MSGIN :
hostdata - > dsp = hostdata - > script + hostdata - > E_msg_in /
sizeof ( u32 ) ;
hostdata - > dsp_changed = 1 ;
action = ACTION_CONTINUE ;
break ;
default :
where = " select message out " ;
action = ACTION_ABORT_PRINT ;
}
/*
* Some SCSI devices will interpret a command as they read the bytes
* off the SCSI bus , and may decide that the command is Bogus before
* they ' ve read the entire command off the bus .
*/
} else if ( dsp = = hostdata - > script + hostdata - > E_cmdout_cmdout / sizeof
( u32 ) ) {
hostdata - > dsp = hostdata - > script + hostdata - > E_data_transfer /
sizeof ( u32 ) ;
hostdata - > dsp_changed = 1 ;
action = ACTION_CONTINUE ;
/* FIXME : we need to handle message reject, etc. within msg_respond. */
# ifdef notyet
} else if ( dsp = = hostdata - > script + hostdata - > E_reply_message ) {
switch ( sbcl ) {
/* Any other phase mismatches abort the currently executing command. */
# endif
} else {
where = " unknown location " ;
action = ACTION_ABORT_PRINT ;
}
/* Flush DMA FIFO */
if ( ! hostdata - > dstat_valid ) {
hostdata - > dstat = NCR53c7x0_read8 ( DSTAT_REG ) ;
hostdata - > dstat_valid = 1 ;
}
if ( ! ( hostdata - > dstat & DSTAT_DFE ) ) {
/* Really need to check this out for 710 RGH */
NCR53c7x0_write8 ( CTEST8_REG , CTEST8_10_CLF ) ;
while ( NCR53c7x0_read8 ( CTEST8_REG ) & CTEST8_10_CLF ) ;
hostdata - > dstat | = DSTAT_DFE ;
}
switch ( action ) {
case ACTION_ABORT_PRINT :
printk ( " scsi%d : %s : unexpected phase %s. \n " ,
host - > host_no , where ? where : " unknown location " ,
sbcl_to_phase ( sbcl ) ) ;
print_lots ( host ) ;
/* Fall through to ACTION_ABORT */
case ACTION_ABORT :
abort_connected ( host ) ;
break ;
case ACTION_CONTINUE :
break ;
}
#if 0
if ( hostdata - > dsp_changed ) {
printk ( " scsi%d: new dsp 0x%p \n " , host - > host_no , hostdata - > dsp ) ;
print_insn ( host , hostdata - > dsp , " " , 1 ) ;
}
# endif
}
/*
* Function : static void intr_bf ( struct Scsi_Host * host ,
* struct NCR53c7x0_cmd * cmd )
*
* Purpose : handle BUS FAULT interrupts
*
* Inputs : host , cmd - host and NCR command causing the interrupt , cmd
* may be NULL .
*/
static void
intr_bf ( struct Scsi_Host * host , struct NCR53c7x0_cmd * cmd ) {
NCR53c7x0_local_declare ( ) ;
u32 * dsp ,
* next_dsp , /* Current dsp */
* dsa ,
dbc_dcmd ; /* DCMD (high eight bits) + DBC */
char * reason = NULL ;
/* Default behavior is for a silent error, with a retry until we've
exhausted retries . */
enum { MAYBE , ALWAYS , NEVER } retry = MAYBE ;
int report = 0 ;
NCR53c7x0_local_setup ( host ) ;
dbc_dcmd = NCR53c7x0_read32 ( DBC_REG ) ;
next_dsp = bus_to_virt ( NCR53c7x0_read32 ( DSP_REG ) ) ;
dsp = next_dsp - NCR53c7x0_insn_size ( ( dbc_dcmd > > 24 ) & 0xff ) ;
/* FIXME - check chip type */
dsa = bus_to_virt ( NCR53c7x0_read32 ( DSA_REG ) ) ;
/*
* Bus faults can be caused by either a Bad Address or
* Target Abort . We should check the Received Target Abort
* bit of the PCI status register and Master Abort Bit .
*
* - Master Abort bit indicates that no device claimed
* the address with DEVSEL within five clocks
*
* - Target Abort bit indicates that a target claimed it ,
* but changed its mind once it saw the byte enables .
*
*/
/* 53c710, not PCI system */
report = 1 ;
reason = " Unknown " ;
# ifndef notyet
report = 1 ;
# endif
if ( report & & reason )
{
printk ( KERN_ALERT " scsi%d : BUS FAULT reason = %s \n " ,
host - > host_no , reason ? reason : " unknown " ) ;
print_lots ( host ) ;
}
# ifndef notyet
retry = NEVER ;
# endif
/*
* TODO : we should attempt to recover from any spurious bus
* faults . After X retries , we should figure that things are
* sufficiently wedged , and call NCR53c7xx_reset .
*
* This code should only get executed once we ' ve decided that we
* cannot retry .
*/
if ( retry = = NEVER ) {
printk ( KERN_ALERT " mail richard@sleepie.demon.co.uk \n " ) ;
FATAL ( host ) ;
}
}
/*
* Function : static void intr_dma ( struct Scsi_Host * host ,
* struct NCR53c7x0_cmd * cmd )
*
* Purpose : handle all DMA interrupts , indicated by the setting
* of the DIP bit in the ISTAT register .
*
* Inputs : host , cmd - host and NCR command causing the interrupt , cmd
* may be NULL .
*/
static void
intr_dma ( struct Scsi_Host * host , struct NCR53c7x0_cmd * cmd ) {
NCR53c7x0_local_declare ( ) ;
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
unsigned char dstat ; /* DSTAT */
u32 * dsp ,
* next_dsp , /* Current dsp */
* dsa ,
dbc_dcmd ; /* DCMD (high eight bits) + DBC */
int tmp ;
unsigned long flags ;
NCR53c7x0_local_setup ( host ) ;
if ( ! hostdata - > dstat_valid ) {
hostdata - > dstat = NCR53c7x0_read8 ( DSTAT_REG ) ;
hostdata - > dstat_valid = 1 ;
}
dstat = hostdata - > dstat ;
if ( hostdata - > options & OPTION_DEBUG_INTR )
printk ( " scsi%d : DSTAT=0x%x \n " , host - > host_no , ( int ) dstat ) ;
dbc_dcmd = NCR53c7x0_read32 ( DBC_REG ) ;
next_dsp = bus_to_virt ( NCR53c7x0_read32 ( DSP_REG ) ) ;
dsp = next_dsp - NCR53c7x0_insn_size ( ( dbc_dcmd > > 24 ) & 0xff ) ;
/* XXX - check chip type */
dsa = bus_to_virt ( NCR53c7x0_read32 ( DSA_REG ) ) ;
/*
* DSTAT_ABRT is the aborted interrupt . This is set whenever the
* SCSI chip is aborted .
*
* With NCR53c700 and NCR53c700 - 66 style chips , we should only
* get this when the chip is currently running the accept
* reselect / select code and we have set the abort bit in the
* ISTAT register .
*
*/
if ( dstat & DSTAT_ABRT ) {
#if 0
/* XXX - add code here to deal with normal abort */
if ( ( hostdata - > options & OPTION_700 ) & & ( hostdata - > state = =
STATE_ABORTING ) ) {
} else
# endif
{
printk ( KERN_ALERT " scsi%d : unexpected abort interrupt at \n "
" " , host - > host_no ) ;
print_insn ( host , dsp , KERN_ALERT " s " , 1 ) ;
FATAL ( host ) ;
}
}
/*
* DSTAT_SSI is the single step interrupt . Should be generated
* whenever we have single stepped or are tracing .
*/
if ( dstat & DSTAT_SSI ) {
if ( hostdata - > options & OPTION_DEBUG_TRACE ) {
/* Don't print instr. until we write DSP at end of intr function */
} else if ( hostdata - > options & OPTION_DEBUG_SINGLE ) {
print_insn ( host , dsp , " s " , 0 ) ;
local_irq_save ( flags ) ;
/* XXX - should we do this, or can we get away with writing dsp? */
NCR53c7x0_write8 ( DCNTL_REG , ( NCR53c7x0_read8 ( DCNTL_REG ) &
~ DCNTL_SSM ) | DCNTL_STD ) ;
local_irq_restore ( flags ) ;
} else {
printk ( KERN_ALERT " scsi%d : unexpected single step interrupt at \n "
" " , host - > host_no ) ;
print_insn ( host , dsp , KERN_ALERT " " , 1 ) ;
printk ( KERN_ALERT " mail drew@PoohSticks.ORG \n " ) ;
FATAL ( host ) ;
}
}
/*
* DSTAT_IID / DSTAT_OPC ( same bit , same meaning , only the name
* is different ) is generated whenever an illegal instruction is
* encountered .
*
* XXX - we may want to emulate INTFLY here , so we can use
* the same SCSI SCRIPT ( tm ) for NCR53c710 through NCR53c810
* chips .
*/
if ( dstat & DSTAT_OPC ) {
/*
* Ascertain if this IID interrupts occurred before or after a STO
* interrupt . Since the interrupt handling code now leaves
* DSP unmodified until _after_ all stacked interrupts have been
* processed , reading the DSP returns the original DSP register .
* This means that if dsp lies between the select code , and
* message out following the selection code ( where the IID interrupt
* would have to have occurred by due to the implicit wait for REQ ) ,
* we have an IID interrupt resulting from a STO condition and
* can ignore it .
*/
if ( ( ( dsp > = ( hostdata - > script + hostdata - > E_select / sizeof ( u32 ) ) ) & &
( dsp < = ( hostdata - > script + hostdata - > E_select_msgout /
sizeof ( u32 ) + 8 ) ) ) | | ( hostdata - > test_running = = 2 ) ) {
if ( hostdata - > options & OPTION_DEBUG_INTR )
printk ( " scsi%d : ignoring DSTAT_IID for SSTAT_STO \n " ,
host - > host_no ) ;
if ( hostdata - > expecting_iid ) {
hostdata - > expecting_iid = 0 ;
hostdata - > idle = 1 ;
if ( hostdata - > test_running = = 2 ) {
hostdata - > test_running = 0 ;
hostdata - > test_completed = 3 ;
} else if ( cmd )
abnormal_finished ( cmd , DID_BAD_TARGET < < 16 ) ;
} else {
hostdata - > expecting_sto = 1 ;
}
/*
* We can ' t guarantee we ' ll be able to execute the WAIT DISCONNECT
* instruction within the 3.4 us of bus free and arbitration delay
* that a target can RESELECT in and assert REQ after we ' ve dropped
* ACK . If this happens , we ' ll get an illegal instruction interrupt .
* Doing away with the WAIT DISCONNECT instructions broke everything ,
* so instead I ' ll settle for moving one WAIT DISCONNECT a few
* instructions closer to the CLEAR ACK before it to minimize the
* chances of this happening , and handle it if it occurs anyway .
*
* Simply continue with what we were doing , and control should
* be transferred to the schedule routine which will ultimately
* pass control onto the reselection or selection ( not yet )
* code .
*/
} else if ( dbc_dcmd = = 0x48000000 & & ( NCR53c7x0_read8 ( SBCL_REG ) &
SBCL_REQ ) ) {
if ( ! ( hostdata - > options & OPTION_NO_PRINT_RACE ) )
{
printk ( " scsi%d: REQ before WAIT DISCONNECT IID \n " ,
host - > host_no ) ;
hostdata - > options | = OPTION_NO_PRINT_RACE ;
}
} else {
printk ( KERN_ALERT " scsi%d : invalid instruction \n " , host - > host_no ) ;
print_lots ( host ) ;
printk ( KERN_ALERT " mail Richard@sleepie.demon.co.uk with ALL \n "
" boot messages and diagnostic output \n " ) ;
FATAL ( host ) ;
}
}
/*
* DSTAT_BF are bus fault errors . DSTAT_800_BF is valid for 710 also .
*/
if ( dstat & DSTAT_800_BF ) {
intr_bf ( host , cmd ) ;
}
/*
* DSTAT_SIR interrupts are generated by the execution of
* the INT instruction . Since the exact values available
* are determined entirely by the SCSI script running ,
* and are local to a particular script , a unique handler
* is called for each script .
*/
if ( dstat & DSTAT_SIR ) {
if ( hostdata - > options & OPTION_DEBUG_INTR )
printk ( " scsi%d : DSTAT_SIR \n " , host - > host_no ) ;
switch ( ( tmp = hostdata - > dstat_sir_intr ( host , cmd ) ) ) {
case SPECIFIC_INT_NOTHING :
case SPECIFIC_INT_RESTART :
break ;
case SPECIFIC_INT_ABORT :
abort_connected ( host ) ;
break ;
case SPECIFIC_INT_PANIC :
printk ( KERN_ALERT " scsi%d : failure at " , host - > host_no ) ;
print_insn ( host , dsp , KERN_ALERT " " , 1 ) ;
printk ( KERN_ALERT " dstat_sir_intr() returned SPECIFIC_INT_PANIC \n " ) ;
FATAL ( host ) ;
break ;
case SPECIFIC_INT_BREAK :
intr_break ( host , cmd ) ;
break ;
default :
printk ( KERN_ALERT " scsi%d : failure at " , host - > host_no ) ;
print_insn ( host , dsp , KERN_ALERT " " , 1 ) ;
printk ( KERN_ALERT " dstat_sir_intr() returned unknown value %d \n " ,
tmp ) ;
FATAL ( host ) ;
}
}
}
/*
* Function : static int print_insn ( struct Scsi_Host * host ,
* u32 * insn , int kernel )
*
* Purpose : print numeric representation of the instruction pointed
* to by insn to the debugging or kernel message buffer
* as appropriate .
*
* If desired , a user level program can interpret this
* information .
*
* Inputs : host , insn - host , pointer to instruction , prefix -
* string to prepend , kernel - use printk instead of debugging buffer .
*
* Returns : size , in u32s , of instruction printed .
*/
/*
* FIXME : should change kernel parameter so that it takes an ENUM
* specifying severity - either KERN_ALERT or KERN_PANIC so
* all panic messages are output with the same severity .
*/
static int
print_insn ( struct Scsi_Host * host , const u32 * insn ,
const char * prefix , int kernel ) {
char buf [ 160 ] , /* Temporary buffer and pointer. ICKY
arbitrary length . */
* tmp ;
unsigned char dcmd ; /* dcmd register for *insn */
int size ;
/*
* Check to see if the instruction pointer is not bogus before
* indirecting through it ; avoiding red - zone at start of
* memory .
*
* FIXME : icky magic needs to happen here on non - intel boxes which
* don ' t have kernel memory mapped in like this . Might be reasonable
* to use vverify ( ) ?
*/
if ( virt_to_phys ( ( void * ) insn ) < PAGE_SIZE | |
virt_to_phys ( ( void * ) ( insn + 8 ) ) > virt_to_phys ( high_memory ) | |
( ( ( ( dcmd = ( insn [ 0 ] > > 24 ) & 0xff ) & DCMD_TYPE_MMI ) = = DCMD_TYPE_MMI ) & &
virt_to_phys ( ( void * ) ( insn + 12 ) ) > virt_to_phys ( high_memory ) ) ) {
size = 0 ;
sprintf ( buf , " %s%p: address out of range \n " ,
prefix , insn ) ;
} else {
/*
* FIXME : ( void * ) cast in virt_to_bus should be unnecessary , because
* it should take const void * as argument .
*/
# if !defined(CONFIG_MVME16x) && !defined(CONFIG_BVME6000)
sprintf ( buf , " %s0x%lx (virt 0x%p) : 0x%08x 0x%08x (virt 0x%p) " ,
( prefix ? prefix : " " ) , virt_to_bus ( ( void * ) insn ) , insn ,
insn [ 0 ] , insn [ 1 ] , bus_to_virt ( insn [ 1 ] ) ) ;
# else
/* Remove virtual addresses to reduce output, as they are the same */
sprintf ( buf , " %s0x%x (+%x) : 0x%08x 0x%08x " ,
( prefix ? prefix : " " ) , ( u32 ) insn , ( ( u32 ) insn -
( u32 ) & ( ( ( struct NCR53c7x0_hostdata * ) host - > hostdata [ 0 ] ) - > script ) ) / 4 ,
insn [ 0 ] , insn [ 1 ] ) ;
# endif
tmp = buf + strlen ( buf ) ;
if ( ( dcmd & DCMD_TYPE_MASK ) = = DCMD_TYPE_MMI ) {
# if !defined(CONFIG_MVME16x) && !defined(CONFIG_BVME6000)
sprintf ( tmp , " 0x%08x (virt 0x%p) \n " , insn [ 2 ] ,
bus_to_virt ( insn [ 2 ] ) ) ;
# else
/* Remove virtual addr to reduce output, as it is the same */
sprintf ( tmp , " 0x%08x \n " , insn [ 2 ] ) ;
# endif
size = 3 ;
} else {
sprintf ( tmp , " \n " ) ;
size = 2 ;
}
}
if ( kernel )
printk ( " %s " , buf ) ;
# ifdef NCR_DEBUG
else {
size_t len = strlen ( buf ) ;
debugger_kernel_write ( host , buf , len ) ;
}
# endif
return size ;
}
/*
* Function : int NCR53c7xx_abort ( Scsi_Cmnd * cmd )
*
* Purpose : Abort an errant SCSI command , doing all necessary
* cleanup of the issue_queue , running_list , shared Linux / NCR
* dsa issue and reconnect queues .
*
* Inputs : cmd - command to abort , code - entire result field
*
* Returns : 0 on success , - 1 on failure .
*/
int
NCR53c7xx_abort ( Scsi_Cmnd * cmd ) {
NCR53c7x0_local_declare ( ) ;
struct Scsi_Host * host = cmd - > device - > host ;
struct NCR53c7x0_hostdata * hostdata = host ? ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] : NULL ;
unsigned long flags ;
struct NCR53c7x0_cmd * curr , * * prev ;
Scsi_Cmnd * me , * * last ;
#if 0
static long cache_pid = - 1 ;
# endif
if ( ! host ) {
printk ( " Bogus SCSI command pid %ld; no host structure \n " ,
cmd - > pid ) ;
return SCSI_ABORT_ERROR ;
} else if ( ! hostdata ) {
printk ( " Bogus SCSI host %d; no hostdata \n " , host - > host_no ) ;
return SCSI_ABORT_ERROR ;
}
NCR53c7x0_local_setup ( host ) ;
/*
* CHECK : I don ' t think that reading ISTAT will unstack any interrupts ,
* since we need to write the INTF bit to clear it , and SCSI / DMA
* interrupts don ' t clear until we read SSTAT / SIST and DSTAT registers .
*
* See that this is the case . Appears to be correct on the 710 , at least .
*
* I suspect that several of our failures may be coming from a new fatal
* interrupt ( possibly due to a phase mismatch ) happening after we ' ve left
* the interrupt handler , but before the PIC has had the interrupt condition
* cleared .
*/
if ( NCR53c7x0_read8 ( hostdata - > istat ) & ( ISTAT_DIP | ISTAT_SIP ) ) {
printk ( " scsi%d : dropped interrupt for command %ld \n " , host - > host_no ,
cmd - > pid ) ;
NCR53c7x0_intr ( host - > irq , NULL , NULL ) ;
return SCSI_ABORT_BUSY ;
}
local_irq_save ( flags ) ;
#if 0
if ( cache_pid = = cmd - > pid )
panic ( " scsi%d : bloody fetus %d \n " , host - > host_no , cmd - > pid ) ;
else
cache_pid = cmd - > pid ;
# endif
/*
* The command could be hiding in the issue_queue . This would be very
* nice , as commands can ' t be moved from the high level driver ' s issue queue
* into the shared queue until an interrupt routine is serviced , and this
* moving is atomic .
*
* If this is the case , we don ' t have to worry about anything - we simply
* pull the command out of the old queue , and call it aborted .
*/
for ( me = ( Scsi_Cmnd * ) hostdata - > issue_queue ,
last = ( Scsi_Cmnd * * ) & ( hostdata - > issue_queue ) ;
me & & me ! = cmd ; last = ( Scsi_Cmnd * * ) & ( me - > SCp . ptr ) ,
me = ( Scsi_Cmnd * ) me - > SCp . ptr ) ;
if ( me ) {
* last = ( Scsi_Cmnd * ) me - > SCp . ptr ;
if ( me - > host_scribble ) {
( ( struct NCR53c7x0_cmd * ) me - > host_scribble ) - > next = hostdata - > free ;
hostdata - > free = ( struct NCR53c7x0_cmd * ) me - > host_scribble ;
me - > host_scribble = NULL ;
}
cmd - > result = DID_ABORT < < 16 ;
cmd - > scsi_done ( cmd ) ;
printk ( " scsi%d : found command %ld in Linux issue queue \n " ,
host - > host_no , me - > pid ) ;
local_irq_restore ( flags ) ;
run_process_issue_queue ( ) ;
return SCSI_ABORT_SUCCESS ;
}
/*
* That failing , the command could be in our list of already executing
* commands . If this is the case , drastic measures are called for .
*/
for ( curr = ( struct NCR53c7x0_cmd * ) hostdata - > running_list ,
prev = ( struct NCR53c7x0_cmd * * ) & ( hostdata - > running_list ) ;
curr & & curr - > cmd ! = cmd ; prev = ( struct NCR53c7x0_cmd * * )
& ( curr - > next ) , curr = ( struct NCR53c7x0_cmd * ) curr - > next ) ;
if ( curr ) {
if ( ( curr - > result & 0xff ) ! = 0xff & & ( curr - > result & 0xff00 ) ! = 0xff00 ) {
cmd - > result = curr - > result ;
if ( prev )
* prev = ( struct NCR53c7x0_cmd * ) curr - > next ;
curr - > next = ( struct NCR53c7x0_cmd * ) hostdata - > free ;
cmd - > host_scribble = NULL ;
hostdata - > free = curr ;
cmd - > scsi_done ( cmd ) ;
printk ( " scsi%d : found finished command %ld in running list \n " ,
host - > host_no , cmd - > pid ) ;
local_irq_restore ( flags ) ;
return SCSI_ABORT_NOT_RUNNING ;
} else {
printk ( " scsi%d : DANGER : command running, can not abort. \n " ,
cmd - > device - > host - > host_no ) ;
local_irq_restore ( flags ) ;
return SCSI_ABORT_BUSY ;
}
}
/*
* And if we couldn ' t find it in any of our queues , it must have been
* a dropped interrupt .
*/
curr = ( struct NCR53c7x0_cmd * ) cmd - > host_scribble ;
if ( curr ) {
curr - > next = hostdata - > free ;
hostdata - > free = curr ;
cmd - > host_scribble = NULL ;
}
if ( curr = = NULL | | ( ( curr - > result & 0xff00 ) = = 0xff00 ) | |
( ( curr - > result & 0xff ) = = 0xff ) ) {
printk ( " scsi%d : did this command ever run? \n " , host - > host_no ) ;
cmd - > result = DID_ABORT < < 16 ;
} else {
printk ( " scsi%d : probably lost INTFLY, normal completion \n " ,
host - > host_no ) ;
cmd - > result = curr - > result ;
/*
* FIXME : We need to add an additional flag which indicates if a
* command was ever counted as BUSY , so if we end up here we can
* decrement the busy count if and only if it is necessary .
*/
- - hostdata - > busy [ cmd - > device - > id ] [ cmd - > device - > lun ] ;
}
local_irq_restore ( flags ) ;
cmd - > scsi_done ( cmd ) ;
/*
* We need to run process_issue_queue since termination of this command
* may allow another queued command to execute first ?
*/
return SCSI_ABORT_NOT_RUNNING ;
}
/*
* Function : int NCR53c7xx_reset ( Scsi_Cmnd * cmd )
*
* Purpose : perform a hard reset of the SCSI bus and NCR
* chip .
*
* Inputs : cmd - command which caused the SCSI RESET
*
* Returns : 0 on success .
*/
int
NCR53c7xx_reset ( Scsi_Cmnd * cmd , unsigned int reset_flags ) {
NCR53c7x0_local_declare ( ) ;
unsigned long flags ;
int found = 0 ;
struct NCR53c7x0_cmd * c ;
Scsi_Cmnd * tmp ;
/*
* When we call scsi_done ( ) , it ' s going to wake up anything sleeping on the
* resources which were in use by the aborted commands , and we ' ll start to
* get new commands .
*
* We can ' t let this happen until after we ' ve re - initialized the driver
* structures , and can ' t reinitialize those structures until after we ' ve
* dealt with their contents .
*
* So , we need to find all of the commands which were running , stick
* them on a linked list of completed commands ( we ' ll use the host_scribble
* pointer ) , do our reinitialization , and then call the done function for
* each command .
*/
Scsi_Cmnd * nuke_list = NULL ;
struct Scsi_Host * host = cmd - > device - > host ;
struct NCR53c7x0_hostdata * hostdata =
( struct NCR53c7x0_hostdata * ) host - > hostdata [ 0 ] ;
NCR53c7x0_local_setup ( host ) ;
local_irq_save ( flags ) ;
ncr_halt ( host ) ;
print_lots ( host ) ;
dump_events ( host , 30 ) ;
ncr_scsi_reset ( host ) ;
for ( tmp = nuke_list = return_outstanding_commands ( host , 1 /* free */ ,
0 /* issue */ ) ; tmp ; tmp = ( Scsi_Cmnd * ) tmp - > SCp . buffer )
if ( tmp = = cmd ) {
found = 1 ;
break ;
}
/*
* If we didn ' t find the command which caused this reset in our running
* list , then we ' ve lost it . See that it terminates normally anyway .
*/
if ( ! found ) {
c = ( struct NCR53c7x0_cmd * ) cmd - > host_scribble ;
if ( c ) {
cmd - > host_scribble = NULL ;
c - > next = hostdata - > free ;
hostdata - > free = c ;
} else
printk ( " scsi%d: lost command %ld \n " , host - > host_no , cmd - > pid ) ;
cmd - > SCp . buffer = ( struct scatterlist * ) nuke_list ;
nuke_list = cmd ;
}
NCR53c7x0_driver_init ( host ) ;
hostdata - > soft_reset ( host ) ;
if ( hostdata - > resets = = 0 )
disable ( host ) ;
else if ( hostdata - > resets ! = - 1 )
- - hostdata - > resets ;
local_irq_restore ( flags ) ;
for ( ; nuke_list ; nuke_list = tmp ) {
tmp = ( Scsi_Cmnd * ) nuke_list - > SCp . buffer ;
nuke_list - > result = DID_RESET < < 16 ;
nuke_list - > scsi_done ( nuke_list ) ;
}
local_irq_restore ( flags ) ;
return SCSI_RESET_SUCCESS ;
}
/*
* The NCR SDMS bios follows Annex A of the SCSI - CAM draft , and
* therefore shares the scsicam_bios_param function .
*/
/*
* Function : int insn_to_offset ( Scsi_Cmnd * cmd , u32 * insn )
*
* Purpose : convert instructions stored at NCR pointer into data
* pointer offset .
*
* Inputs : cmd - SCSI command ; insn - pointer to instruction . Either current
* DSP , or saved data pointer .
*
* Returns : offset on success , - 1 on failure .
*/
static int
insn_to_offset ( Scsi_Cmnd * cmd , u32 * insn ) {
struct NCR53c7x0_hostdata * hostdata =
( struct NCR53c7x0_hostdata * ) cmd - > device - > host - > hostdata [ 0 ] ;
struct NCR53c7x0_cmd * ncmd =
( struct NCR53c7x0_cmd * ) cmd - > host_scribble ;
int offset = 0 , buffers ;
struct scatterlist * segment ;
char * ptr ;
int found = 0 ;
/*
* With the current code implementation , if the insn is inside dynamically
* generated code , the data pointer will be the instruction preceding
* the next transfer segment .
*/
if ( ! check_address ( ( unsigned long ) ncmd , sizeof ( struct NCR53c7x0_cmd ) ) & &
( ( insn > = ncmd - > data_transfer_start & &
insn < ncmd - > data_transfer_end ) | |
( insn > = ncmd - > residual & &
insn < ( ncmd - > residual +
sizeof ( ncmd - > residual ) ) ) ) ) {
ptr = bus_to_virt ( insn [ 3 ] ) ;
if ( ( buffers = cmd - > use_sg ) ) {
for ( offset = 0 ,
segment = ( struct scatterlist * ) cmd - > buffer ;
buffers & & ! ( ( found = ( ( ptr > = ( char * ) page_address ( segment - > page ) + segment - > offset ) & &
( ptr < ( ( char * ) page_address ( segment - > page ) + segment - > offset + segment - > length ) ) ) ) ) ;
- - buffers , offset + = segment - > length , + + segment )
#if 0
printk ( " scsi%d: comparing 0x%p to 0x%p \n " ,
cmd - > device - > host - > host_no , saved , page_address ( segment - > page + segment - > offset ) ;
# else
;
# endif
offset + = ptr - ( ( char * ) page_address ( segment - > page ) + segment - > offset ) ;
} else {
found = 1 ;
offset = ptr - ( char * ) ( cmd - > request_buffer ) ;
}
} else if ( ( insn > = hostdata - > script +
hostdata - > E_data_transfer / sizeof ( u32 ) ) & &
( insn < = hostdata - > script +
hostdata - > E_end_data_transfer / sizeof ( u32 ) ) ) {
found = 1 ;
offset = 0 ;
}
return found ? offset : - 1 ;
}
/*
* Function : void print_progress ( Scsi_Cmnd * cmd )
*
* Purpose : print the current location of the saved data pointer
*
* Inputs : cmd - command we are interested in
*
*/
static void
print_progress ( Scsi_Cmnd * cmd ) {
NCR53c7x0_local_declare ( ) ;
struct NCR53c7x0_cmd * ncmd =
( struct NCR53c7x0_cmd * ) cmd - > host_scribble ;
int offset , i ;
char * where ;
u32 * ptr ;
NCR53c7x0_local_setup ( cmd - > device - > host ) ;
if ( check_address ( ( unsigned long ) ncmd , sizeof ( struct NCR53c7x0_cmd ) ) = = 0 )
{
printk ( " \n NCR53c7x0_cmd fields: \n " ) ;
printk ( " bounce.len=0x%x, addr=0x%0x, buf[]=0x%02x %02x %02x %02x \n " ,
ncmd - > bounce . len , ncmd - > bounce . addr , ncmd - > bounce . buf [ 0 ] ,
ncmd - > bounce . buf [ 1 ] , ncmd - > bounce . buf [ 2 ] , ncmd - > bounce . buf [ 3 ] ) ;
printk ( " result=%04x, cdb[0]=0x%02x \n " , ncmd - > result , ncmd - > cmnd [ 0 ] ) ;
}
for ( i = 0 ; i < 2 ; + + i ) {
if ( check_address ( ( unsigned long ) ncmd ,
sizeof ( struct NCR53c7x0_cmd ) ) = = - 1 )
continue ;
if ( ! i ) {
where = " saved " ;
ptr = bus_to_virt ( ncmd - > saved_data_pointer ) ;
} else {
where = " active " ;
ptr = bus_to_virt ( NCR53c7x0_read32 ( DSP_REG ) -
NCR53c7x0_insn_size ( NCR53c7x0_read8 ( DCMD_REG ) ) *
sizeof ( u32 ) ) ;
}
offset = insn_to_offset ( cmd , ptr ) ;
if ( offset ! = - 1 )
printk ( " scsi%d : %s data pointer at offset %d \n " ,
cmd - > device - > host - > host_no , where , offset ) ;
else {
int size ;
printk ( " scsi%d : can't determine %s data pointer offset \n " ,
cmd - > device - > host - > host_no , where ) ;
if ( ncmd ) {
size = print_insn ( cmd - > device - > host ,
bus_to_virt ( ncmd - > saved_data_pointer ) , " " , 1 ) ;
print_insn ( cmd - > device - > host ,
bus_to_virt ( ncmd - > saved_data_pointer ) + size * sizeof ( u32 ) ,
" " , 1 ) ;
}
}
}
}
static void
print_dsa ( struct Scsi_Host * host , u32 * dsa , const char * prefix ) {
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
int i , len ;
char * ptr ;
Scsi_Cmnd * cmd ;
if ( check_address ( ( unsigned long ) dsa , hostdata - > dsa_end -
hostdata - > dsa_start ) = = - 1 ) {
printk ( " scsi%d : bad dsa virt 0x%p \n " , host - > host_no , dsa ) ;
return ;
}
printk ( " %sscsi%d : dsa at phys 0x%lx (virt 0x%p) \n "
" + %d : dsa_msgout length = %u, data = 0x%x (virt 0x%p) \n " ,
prefix ? prefix : " " ,
host - > host_no , virt_to_bus ( dsa ) , dsa , hostdata - > dsa_msgout ,
dsa [ hostdata - > dsa_msgout / sizeof ( u32 ) ] ,
dsa [ hostdata - > dsa_msgout / sizeof ( u32 ) + 1 ] ,
bus_to_virt ( dsa [ hostdata - > dsa_msgout / sizeof ( u32 ) + 1 ] ) ) ;
/*
* Only print messages if they ' re sane in length so we don ' t
* blow the kernel printk buffer on something which won ' t buy us
* anything .
*/
if ( dsa [ hostdata - > dsa_msgout / sizeof ( u32 ) ] <
sizeof ( hostdata - > free - > select ) )
for ( i = dsa [ hostdata - > dsa_msgout / sizeof ( u32 ) ] ,
ptr = bus_to_virt ( dsa [ hostdata - > dsa_msgout / sizeof ( u32 ) + 1 ] ) ;
i > 0 & & ! check_address ( ( unsigned long ) ptr , 1 ) ;
ptr + = len , i - = len ) {
printk ( " " ) ;
2005-04-03 23:53:59 +04:00
len = scsi_print_msg ( ptr ) ;
2005-04-17 02:20:36 +04:00
printk ( " \n " ) ;
if ( ! len )
break ;
}
printk ( " + %d : select_indirect = 0x%x \n " ,
hostdata - > dsa_select , dsa [ hostdata - > dsa_select / sizeof ( u32 ) ] ) ;
cmd = ( Scsi_Cmnd * ) bus_to_virt ( dsa [ hostdata - > dsa_cmnd / sizeof ( u32 ) ] ) ;
printk ( " + %d : dsa_cmnd = 0x%x " , hostdata - > dsa_cmnd ,
( u32 ) virt_to_bus ( cmd ) ) ;
/* XXX Maybe we should access cmd->host_scribble->result here. RGH */
if ( cmd ) {
printk ( " result = 0x%x, target = %d, lun = %d, cmd = " ,
cmd - > result , cmd - > device - > id , cmd - > device - > lun ) ;
2005-04-03 23:53:59 +04:00
__scsi_print_command ( cmd - > cmnd ) ;
2005-04-17 02:20:36 +04:00
} else
printk ( " \n " ) ;
printk ( " + %d : dsa_next = 0x%x \n " , hostdata - > dsa_next ,
dsa [ hostdata - > dsa_next / sizeof ( u32 ) ] ) ;
if ( cmd ) {
printk ( " scsi%d target %d : sxfer_sanity = 0x%x, scntl3_sanity = 0x%x \n "
" script : " ,
host - > host_no , cmd - > device - > id ,
hostdata - > sync [ cmd - > device - > id ] . sxfer_sanity ,
hostdata - > sync [ cmd - > device - > id ] . scntl3_sanity ) ;
for ( i = 0 ; i < ( sizeof ( hostdata - > sync [ cmd - > device - > id ] . script ) / 4 ) ; + + i )
printk ( " 0x%x " , hostdata - > sync [ cmd - > device - > id ] . script [ i ] ) ;
printk ( " \n " ) ;
print_progress ( cmd ) ;
}
}
/*
* Function : void print_queues ( Scsi_Host * host )
*
* Purpose : print the contents of the NCR issue and reconnect queues
*
* Inputs : host - SCSI host we are interested in
*
*/
static void
print_queues ( struct Scsi_Host * host ) {
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
u32 * dsa , * next_dsa ;
volatile u32 * ncrcurrent ;
int left ;
Scsi_Cmnd * cmd , * next_cmd ;
unsigned long flags ;
printk ( " scsi%d : issue queue \n " , host - > host_no ) ;
for ( left = host - > can_queue , cmd = ( Scsi_Cmnd * ) hostdata - > issue_queue ;
left > = 0 & & cmd ;
cmd = next_cmd ) {
next_cmd = ( Scsi_Cmnd * ) cmd - > SCp . ptr ;
local_irq_save ( flags ) ;
if ( cmd - > host_scribble ) {
if ( check_address ( ( unsigned long ) ( cmd - > host_scribble ) ,
sizeof ( cmd - > host_scribble ) ) = = - 1 )
printk ( " scsi%d: scsi pid %ld bad pointer to NCR53c7x0_cmd \n " ,
host - > host_no , cmd - > pid ) ;
/* print_dsa does sanity check on address, no need to check */
else
print_dsa ( host , ( ( struct NCR53c7x0_cmd * ) cmd - > host_scribble )
- > dsa , " " ) ;
} else
printk ( " scsi%d : scsi pid %ld for target %d lun %d has no NCR53c7x0_cmd \n " ,
host - > host_no , cmd - > pid , cmd - > device - > id , cmd - > device - > lun ) ;
local_irq_restore ( flags ) ;
}
if ( left < = 0 ) {
printk ( " scsi%d : loop detected in issue queue \n " ,
host - > host_no ) ;
}
/*
* Traverse the NCR reconnect and start DSA structures , printing out
* each element until we hit the end or detect a loop . Currently ,
* the reconnect structure is a linked list ; and the start structure
* is an array . Eventually , the reconnect structure will become a
* list as well , since this simplifies the code .
*/
printk ( " scsi%d : schedule dsa array : \n " , host - > host_no ) ;
for ( left = host - > can_queue , ncrcurrent = hostdata - > schedule ;
left > 0 ; ncrcurrent + = 2 , - - left )
if ( ncrcurrent [ 0 ] ! = hostdata - > NOP_insn )
/* FIXME : convert pointer to dsa_begin to pointer to dsa. */
print_dsa ( host , bus_to_virt ( ncrcurrent [ 1 ] -
( hostdata - > E_dsa_code_begin -
hostdata - > E_dsa_code_template ) ) , " " ) ;
printk ( " scsi%d : end schedule dsa array \n " , host - > host_no ) ;
printk ( " scsi%d : reconnect_dsa_head : \n " , host - > host_no ) ;
for ( left = host - > can_queue ,
dsa = bus_to_virt ( hostdata - > reconnect_dsa_head ) ;
left > = 0 & & dsa ;
dsa = next_dsa ) {
local_irq_save ( flags ) ;
if ( check_address ( ( unsigned long ) dsa , sizeof ( dsa ) ) = = - 1 ) {
printk ( " scsi%d: bad DSA pointer 0x%p " , host - > host_no ,
dsa ) ;
next_dsa = NULL ;
}
else
{
next_dsa = bus_to_virt ( dsa [ hostdata - > dsa_next / sizeof ( u32 ) ] ) ;
print_dsa ( host , dsa , " " ) ;
}
local_irq_restore ( flags ) ;
}
printk ( " scsi%d : end reconnect_dsa_head \n " , host - > host_no ) ;
if ( left < 0 )
printk ( " scsi%d: possible loop in ncr reconnect list \n " ,
host - > host_no ) ;
}
static void
print_lots ( struct Scsi_Host * host ) {
NCR53c7x0_local_declare ( ) ;
struct NCR53c7x0_hostdata * hostdata =
( struct NCR53c7x0_hostdata * ) host - > hostdata [ 0 ] ;
u32 * dsp_next , * dsp , * dsa , dbc_dcmd ;
unsigned char dcmd , sbcl ;
int i , size ;
NCR53c7x0_local_setup ( host ) ;
if ( ( dsp_next = bus_to_virt ( NCR53c7x0_read32 ( DSP_REG ) ) ) ) {
dbc_dcmd = NCR53c7x0_read32 ( DBC_REG ) ;
dcmd = ( dbc_dcmd & 0xff000000 ) > > 24 ;
dsp = dsp_next - NCR53c7x0_insn_size ( dcmd ) ;
dsa = bus_to_virt ( NCR53c7x0_read32 ( DSA_REG ) ) ;
sbcl = NCR53c7x0_read8 ( SBCL_REG ) ;
/*
* For the 53 c710 , the following will report value 0 for SCNTL3
* and STEST0 - we don ' t have these registers .
*/
printk ( " scsi%d : DCMD|DBC=0x%x, DNAD=0x%x (virt 0x%p) \n "
" DSA=0x%lx (virt 0x%p) \n "
" DSPS=0x%x, TEMP=0x%x (virt 0x%p), DMODE=0x%x \n "
" SXFER=0x%x, SCNTL3=0x%x \n "
" %s%s%sphase=%s, %d bytes in SCSI FIFO \n "
" SCRATCH=0x%x, saved2_dsa=0x%0lx \n " ,
host - > host_no , dbc_dcmd , NCR53c7x0_read32 ( DNAD_REG ) ,
bus_to_virt ( NCR53c7x0_read32 ( DNAD_REG ) ) ,
virt_to_bus ( dsa ) , dsa ,
NCR53c7x0_read32 ( DSPS_REG ) , NCR53c7x0_read32 ( TEMP_REG ) ,
bus_to_virt ( NCR53c7x0_read32 ( TEMP_REG ) ) ,
( int ) NCR53c7x0_read8 ( hostdata - > dmode ) ,
( int ) NCR53c7x0_read8 ( SXFER_REG ) ,
( ( hostdata - > chip / 100 ) = = 8 ) ?
( int ) NCR53c7x0_read8 ( SCNTL3_REG_800 ) : 0 ,
( sbcl & SBCL_BSY ) ? " BSY " : " " ,
( sbcl & SBCL_SEL ) ? " SEL " : " " ,
( sbcl & SBCL_REQ ) ? " REQ " : " " ,
sstat2_to_phase ( NCR53c7x0_read8 ( ( ( hostdata - > chip / 100 ) = = 8 ) ?
SSTAT1_REG : SSTAT2_REG ) ) ,
( NCR53c7x0_read8 ( ( hostdata - > chip / 100 ) = = 8 ?
SSTAT1_REG : SSTAT2_REG ) & SSTAT2_FF_MASK ) > > SSTAT2_FF_SHIFT ,
( ( hostdata - > chip / 100 ) = = 8 ) ? NCR53c7x0_read8 ( STEST0_REG_800 ) :
NCR53c7x0_read32 ( SCRATCHA_REG_800 ) ,
hostdata - > saved2_dsa ) ;
printk ( " scsi%d : DSP 0x%lx (virt 0x%p) -> \n " , host - > host_no ,
virt_to_bus ( dsp ) , dsp ) ;
for ( i = 6 ; i > 0 ; - - i , dsp + = size )
size = print_insn ( host , dsp , " " , 1 ) ;
if ( NCR53c7x0_read8 ( SCNTL1_REG ) & SCNTL1_CON ) {
if ( ( hostdata - > chip / 100 ) = = 8 )
printk ( " scsi%d : connected (SDID=0x%x, SSID=0x%x) \n " ,
host - > host_no , NCR53c7x0_read8 ( SDID_REG_800 ) ,
NCR53c7x0_read8 ( SSID_REG_800 ) ) ;
else
printk ( " scsi%d : connected (SDID=0x%x) \n " ,
host - > host_no , NCR53c7x0_read8 ( SDID_REG_700 ) ) ;
print_dsa ( host , dsa , " " ) ;
}
# if 1
print_queues ( host ) ;
# endif
}
}
/*
* Function : static int shutdown ( struct Scsi_Host * host )
*
* Purpose : does a clean ( we hope ) shutdown of the NCR SCSI
* chip . Use prior to dumping core , unloading the NCR driver ,
*
* Returns : 0 on success
*/
static int
shutdown ( struct Scsi_Host * host ) {
NCR53c7x0_local_declare ( ) ;
unsigned long flags ;
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
NCR53c7x0_local_setup ( host ) ;
local_irq_save ( flags ) ;
/* Get in a state where we can reset the SCSI bus */
ncr_halt ( host ) ;
ncr_scsi_reset ( host ) ;
hostdata - > soft_reset ( host ) ;
disable ( host ) ;
local_irq_restore ( flags ) ;
return 0 ;
}
/*
* Function : void ncr_scsi_reset ( struct Scsi_Host * host )
*
* Purpose : reset the SCSI bus .
*/
static void
ncr_scsi_reset ( struct Scsi_Host * host ) {
NCR53c7x0_local_declare ( ) ;
unsigned long flags ;
NCR53c7x0_local_setup ( host ) ;
local_irq_save ( flags ) ;
NCR53c7x0_write8 ( SCNTL1_REG , SCNTL1_RST ) ;
udelay ( 25 ) ; /* Minimum amount of time to assert RST */
NCR53c7x0_write8 ( SCNTL1_REG , 0 ) ;
local_irq_restore ( flags ) ;
}
/*
* Function : void hard_reset ( struct Scsi_Host * host )
*
*/
static void
hard_reset ( struct Scsi_Host * host ) {
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
unsigned long flags ;
local_irq_save ( flags ) ;
ncr_scsi_reset ( host ) ;
NCR53c7x0_driver_init ( host ) ;
if ( hostdata - > soft_reset )
hostdata - > soft_reset ( host ) ;
local_irq_restore ( flags ) ;
}
/*
* Function : Scsi_Cmnd * return_outstanding_commands ( struct Scsi_Host * host ,
* int free , int issue )
*
* Purpose : return a linked list ( using the SCp . buffer field as next ,
* so we don ' t perturb hostdata . We don ' t use a field of the
* NCR53c7x0_cmd structure since we may not have allocated one
* for the command causing the reset . ) of Scsi_Cmnd structures that
* had propagated below the Linux issue queue level . If free is set ,
* free the NCR53c7x0_cmd structures which are associated with
* the Scsi_Cmnd structures , and clean up any internal
* NCR lists that the commands were on . If issue is set ,
* also return commands in the issue queue .
*
* Returns : linked list of commands
*
* NOTE : the caller should insure that the NCR chip is halted
* if the free flag is set .
*/
static Scsi_Cmnd *
return_outstanding_commands ( struct Scsi_Host * host , int free , int issue ) {
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
struct NCR53c7x0_cmd * c ;
int i ;
u32 * ncrcurrent ;
Scsi_Cmnd * list = NULL , * tmp ;
for ( c = ( struct NCR53c7x0_cmd * ) hostdata - > running_list ; c ;
c = ( struct NCR53c7x0_cmd * ) c - > next ) {
if ( c - > cmd - > SCp . buffer ) {
printk ( " scsi%d : loop detected in running list! \n " , host - > host_no ) ;
break ;
} else {
printk ( " Duh? Bad things happening in the NCR driver \n " ) ;
break ;
}
c - > cmd - > SCp . buffer = ( struct scatterlist * ) list ;
list = c - > cmd ;
if ( free ) {
c - > next = hostdata - > free ;
hostdata - > free = c ;
}
}
if ( free ) {
for ( i = 0 , ncrcurrent = ( u32 * ) hostdata - > schedule ;
i < host - > can_queue ; + + i , ncrcurrent + = 2 ) {
ncrcurrent [ 0 ] = hostdata - > NOP_insn ;
ncrcurrent [ 1 ] = 0xdeadbeef ;
}
hostdata - > ncrcurrent = NULL ;
}
if ( issue ) {
for ( tmp = ( Scsi_Cmnd * ) hostdata - > issue_queue ; tmp ; tmp = tmp - > next ) {
if ( tmp - > SCp . buffer ) {
printk ( " scsi%d : loop detected in issue queue! \n " ,
host - > host_no ) ;
break ;
}
tmp - > SCp . buffer = ( struct scatterlist * ) list ;
list = tmp ;
}
if ( free )
hostdata - > issue_queue = NULL ;
}
return list ;
}
/*
* Function : static int disable ( struct Scsi_Host * host )
*
* Purpose : disables the given NCR host , causing all commands
* to return a driver error . Call this so we can unload the
* module during development and try again . Eventually ,
* we should be able to find clean workarounds for these
* problems .
*
* Inputs : host - hostadapter to twiddle
*
* Returns : 0 on success .
*/
static int
disable ( struct Scsi_Host * host ) {
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
unsigned long flags ;
Scsi_Cmnd * nuke_list , * tmp ;
local_irq_save ( flags ) ;
if ( hostdata - > state ! = STATE_HALTED )
ncr_halt ( host ) ;
nuke_list = return_outstanding_commands ( host , 1 /* free */ , 1 /* issue */ ) ;
hard_reset ( host ) ;
hostdata - > state = STATE_DISABLED ;
local_irq_restore ( flags ) ;
printk ( " scsi%d : nuking commands \n " , host - > host_no ) ;
for ( ; nuke_list ; nuke_list = tmp ) {
tmp = ( Scsi_Cmnd * ) nuke_list - > SCp . buffer ;
nuke_list - > result = DID_ERROR < < 16 ;
nuke_list - > scsi_done ( nuke_list ) ;
}
printk ( " scsi%d : done. \n " , host - > host_no ) ;
printk ( KERN_ALERT " scsi%d : disabled. Unload and reload \n " ,
host - > host_no ) ;
return 0 ;
}
/*
* Function : static int ncr_halt ( struct Scsi_Host * host )
*
* Purpose : halts the SCSI SCRIPTS ( tm ) processor on the NCR chip
*
* Inputs : host - SCSI chip to halt
*
* Returns : 0 on success
*/
static int
ncr_halt ( struct Scsi_Host * host ) {
NCR53c7x0_local_declare ( ) ;
unsigned long flags ;
unsigned char istat , tmp ;
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
int stage ;
NCR53c7x0_local_setup ( host ) ;
local_irq_save ( flags ) ;
/* Stage 0 : eat all interrupts
Stage 1 : set ABORT
Stage 2 : eat all but abort interrupts
Stage 3 : eat all interrupts
*/
for ( stage = 0 ; ; ) {
if ( stage = = 1 ) {
NCR53c7x0_write8 ( hostdata - > istat , ISTAT_ABRT ) ;
+ + stage ;
}
istat = NCR53c7x0_read8 ( hostdata - > istat ) ;
if ( istat & ISTAT_SIP ) {
tmp = NCR53c7x0_read8 ( SSTAT0_REG ) ;
} else if ( istat & ISTAT_DIP ) {
tmp = NCR53c7x0_read8 ( DSTAT_REG ) ;
if ( stage = = 2 ) {
if ( tmp & DSTAT_ABRT ) {
NCR53c7x0_write8 ( hostdata - > istat , 0 ) ;
+ + stage ;
} else {
printk ( KERN_ALERT " scsi%d : could not halt NCR chip \n " ,
host - > host_no ) ;
disable ( host ) ;
}
}
}
if ( ! ( istat & ( ISTAT_SIP | ISTAT_DIP ) ) ) {
if ( stage = = 0 )
+ + stage ;
else if ( stage = = 3 )
break ;
}
}
hostdata - > state = STATE_HALTED ;
local_irq_restore ( flags ) ;
#if 0
print_lots ( host ) ;
# endif
return 0 ;
}
/*
* Function : event_name ( int event )
*
* Purpose : map event enum into user - readable strings .
*/
static const char *
event_name ( int event ) {
switch ( event ) {
case EVENT_NONE : return " none " ;
case EVENT_ISSUE_QUEUE : return " to issue queue " ;
case EVENT_START_QUEUE : return " to start queue " ;
case EVENT_SELECT : return " selected " ;
case EVENT_DISCONNECT : return " disconnected " ;
case EVENT_RESELECT : return " reselected " ;
case EVENT_COMPLETE : return " completed " ;
case EVENT_IDLE : return " idle " ;
case EVENT_SELECT_FAILED : return " select failed " ;
case EVENT_BEFORE_SELECT : return " before select " ;
case EVENT_RESELECT_FAILED : return " reselect failed " ;
default : return " unknown " ;
}
}
/*
* Function : void dump_events ( struct Scsi_Host * host , count )
*
* Purpose : print last count events which have occurred .
*/
static void
dump_events ( struct Scsi_Host * host , int count ) {
struct NCR53c7x0_hostdata * hostdata = ( struct NCR53c7x0_hostdata * )
host - > hostdata [ 0 ] ;
struct NCR53c7x0_event event ;
int i ;
unsigned long flags ;
if ( hostdata - > events ) {
if ( count > hostdata - > event_size )
count = hostdata - > event_size ;
for ( i = hostdata - > event_index ; count > 0 ;
i = ( i ? i - 1 : hostdata - > event_size - 1 ) , - - count ) {
/*
* By copying the event we ' re currently examining with interrupts
* disabled , we can do multiple printk ( ) , etc . operations and
* still be guaranteed that they ' re happening on the same
* event structure .
*/
local_irq_save ( flags ) ;
#if 0
event = hostdata - > events [ i ] ;
# else
memcpy ( ( void * ) & event , ( void * ) & ( hostdata - > events [ i ] ) ,
sizeof ( event ) ) ;
# endif
local_irq_restore ( flags ) ;
printk ( " scsi%d : %s event %d at %ld secs %ld usecs target %d lun %d \n " ,
host - > host_no , event_name ( event . event ) , count ,
( long ) event . time . tv_sec , ( long ) event . time . tv_usec ,
event . target , event . lun ) ;
if ( event . dsa )
printk ( " event for dsa 0x%lx (virt 0x%p) \n " ,
virt_to_bus ( event . dsa ) , event . dsa ) ;
if ( event . pid ! = - 1 ) {
printk ( " event for pid %ld " , event . pid ) ;
2005-04-03 23:53:59 +04:00
__scsi_print_command ( event . cmnd ) ;
2005-04-17 02:20:36 +04:00
}
}
}
}
/*
* Function : check_address
*
* Purpose : Check to see if a possibly corrupt pointer will fault the
* kernel .
*
* Inputs : addr - address ; size - size of area
*
* Returns : 0 if area is OK , - 1 on error .
*
* NOTES : should be implemented in terms of vverify on kernels
* that have it .
*/
static int
check_address ( unsigned long addr , int size ) {
return ( virt_to_phys ( ( void * ) addr ) < PAGE_SIZE | | virt_to_phys ( ( void * ) ( addr + size ) ) > virt_to_phys ( high_memory ) ? - 1 : 0 ) ;
}
# ifdef MODULE
int
NCR53c7x0_release ( struct Scsi_Host * host ) {
struct NCR53c7x0_hostdata * hostdata =
( struct NCR53c7x0_hostdata * ) host - > hostdata [ 0 ] ;
struct NCR53c7x0_cmd * cmd , * tmp ;
shutdown ( host ) ;
if ( host - > irq ! = SCSI_IRQ_NONE )
{
int irq_count ;
struct Scsi_Host * tmp ;
for ( irq_count = 0 , tmp = first_host ; tmp ; tmp = tmp - > next )
if ( tmp - > hostt = = the_template & & tmp - > irq = = host - > irq )
+ + irq_count ;
if ( irq_count = = 1 )
free_irq ( host - > irq , NULL ) ;
}
if ( host - > dma_channel ! = DMA_NONE )
free_dma ( host - > dma_channel ) ;
if ( host - > io_port )
release_region ( host - > io_port , host - > n_io_port ) ;
for ( cmd = ( struct NCR53c7x0_cmd * ) hostdata - > free ; cmd ; cmd = tmp ,
- - hostdata - > num_cmds ) {
tmp = ( struct NCR53c7x0_cmd * ) cmd - > next ;
/*
* If we ' re going to loop , try to stop it to get a more accurate
* count of the leaked commands .
*/
cmd - > next = NULL ;
if ( cmd - > free )
cmd - > free ( ( void * ) cmd - > real , cmd - > size ) ;
}
if ( hostdata - > num_cmds )
printk ( " scsi%d : leaked %d NCR53c7x0_cmd structures \n " ,
host - > host_no , hostdata - > num_cmds ) ;
if ( hostdata - > events )
vfree ( ( void * ) hostdata - > events ) ;
/* XXX This assumes default cache mode to be IOMAP_FULL_CACHING, which
* XXX may be invalid ( CONFIG_060_WRITETHROUGH )
*/
kernel_set_cachemode ( ( void * ) hostdata , 8192 , IOMAP_FULL_CACHING ) ;
free_pages ( ( u32 ) hostdata , 1 ) ;
return 1 ;
}
# endif /* def MODULE */