diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
commit | 1513ff9b7899ab588401c89db0e99903dbf5f886 (patch) | |
tree | f69cc81a940a502ea23d664c3ffb2d215a479667 /drivers/scsi/aha152x.c |
Import of Linus's Linux 1.1.68
Diffstat (limited to 'drivers/scsi/aha152x.c')
-rw-r--r-- | drivers/scsi/aha152x.c | 2545 |
1 files changed, 2545 insertions, 0 deletions
diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c new file mode 100644 index 000000000..7ae1ee4a0 --- /dev/null +++ b/drivers/scsi/aha152x.c @@ -0,0 +1,2545 @@ +/* aha152x.c -- Adaptec AHA-152x driver + * Author: Juergen E. Fischer, fischer@server.et-inf.fho-emden.de + * Copyright 1993, 1994 Juergen E. Fischer + * + * + * This driver is based on + * fdomain.c -- Future Domain TMC-16x0 driver + * which is + * Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu) + * + + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + + * + * $Id: aha152x.c,v 1.6 1994/11/24 20:35:27 root Exp $ + * + + * $Log: aha152x.c,v $ + * Revision 1.6 1994/11/24 20:35:27 root + * - problem with odd number of bytes in fifo fixed + * + * Revision 1.5 1994/10/30 14:39:56 root + * - abort code fixed + * - debugging improved + * + * Revision 1.4 1994/09/12 11:33:01 root + * - irqaction to request_irq + * - abortion updated + * + * Revision 1.3 1994/08/04 13:53:05 root + * - updates for mid-level-driver changes + * - accept unexpected BUSFREE phase as error condition + * - parity check now configurable + * + * Revision 1.2 1994/07/03 12:56:36 root + * - cleaned up debugging code + * - more tweaking on reset delays + * - updated abort/reset code (pretty untested...) + * + * Revision 1.1 1994/05/28 21:18:49 root + * - update for mid-level interface change (abort-reset) + * - delays after resets adjusted for some slow devices + * + * Revision 1.0 1994/03/25 12:52:00 root + * - Fixed "more data than expected" problem + * - added new BIOS signatures + * + * Revision 0.102 1994/01/31 20:44:12 root + * - minor changes in insw/outsw handling + * + * Revision 0.101 1993/12/13 01:16:27 root + * - fixed STATUS phase (non-GOOD stati were dropped sometimes; + * fixes problems with CD-ROM sector size detection & media change) + * + * Revision 0.100 1993/12/10 16:58:47 root + * - fix for unsuccessful selections in case of non-continuous id assignments + * on the scsi bus. + * + * Revision 0.99 1993/10/24 16:19:59 root + * - fixed DATA IN (rare read errors gone) + * + * Revision 0.98 1993/10/17 12:54:44 root + * - fixed some recent fixes (shame on me) + * - moved initialization of scratch area to aha152x_queue + * + * Revision 0.97 1993/10/09 18:53:53 root + * - DATA IN fixed. Rarely left data in the fifo. + * + * Revision 0.96 1993/10/03 00:53:59 root + * - minor changes on DATA IN + * + * Revision 0.95 1993/09/24 10:36:01 root + * - change handling of MSGI after reselection + * - fixed sti/cli + * - minor changes + * + * Revision 0.94 1993/09/18 14:08:22 root + * - fixed bug in multiple outstanding command code + * - changed detection + * - support for kernel command line configuration + * - reset corrected + * - changed message handling + * + * Revision 0.93 1993/09/15 20:41:19 root + * - fixed bugs with multiple outstanding commands + * + * Revision 0.92 1993/09/13 02:46:33 root + * - multiple outstanding commands work (no problems with IBM drive) + * + * Revision 0.91 1993/09/12 20:51:46 root + * added multiple outstanding commands + * (some problem with this $%&? IBM device remain) + * + * Revision 0.9 1993/09/12 11:11:22 root + * - corrected auto-configuration + * - changed the auto-configuration (added some '#define's) + * - added support for dis-/reconnection + * + * Revision 0.8 1993/09/06 23:09:39 root + * - added support for the drive activity light + * - minor changes + * + * Revision 0.7 1993/09/05 14:30:15 root + * - improved phase detection + * - now using the new snarf_region code of 0.99pl13 + * + * Revision 0.6 1993/09/02 11:01:38 root + * first public release; added some signatures and biosparam() + * + * Revision 0.5 1993/08/30 10:23:30 root + * fixed timing problems with my IBM drive + * + * Revision 0.4 1993/08/29 14:06:52 root + * fixed some problems with timeouts due incomplete commands + * + * Revision 0.3 1993/08/28 15:55:03 root + * writing data works too. mounted and worked on a dos partition + * + * Revision 0.2 1993/08/27 22:42:07 root + * reading data works. Mounted a msdos partition. + * + * Revision 0.1 1993/08/25 13:38:30 root + * first "damn thing doesn't work" version + * + * Revision 0.0 1993/08/14 19:54:25 root + * empty function bodies; detect() works. + * + + ************************************************************************** + + + + DESCRIPTION: + + This is the Linux low-level SCSI driver for Adaptec AHA-1520/1522 + SCSI host adapters. + + + PER-DEFINE CONFIGURABLE OPTIONS: + + AUTOCONF : use configuration the controller reports (only 152x) + IRQ : override interrupt channel (9,10,11 or 12) (default 11) + SCSI_ID : override scsiid of AIC-6260 (0-7) (default 7) + RECONNECT : override target dis-/reconnection/multiple outstanding commands (default on) + PARITY : override parity check (default on) + SKIP_BIOSTEST : Don't test for BIOS signature (AHA-1510 or disabled BIOS) + PORTBASE : Force port base. Don't try to probe + + + LILO COMMAND LINE OPTIONS: + + aha152x=<PORTBASE>[,<IRQ>[,<SCSI-ID>[,<RECONNECT>[,<PARITY>]]]] + + The normal configuration can be overridden by specifying a command line. + When you do this, the BIOS test is skipped. Entered values have to be + valid (known). Don't use values that aren't support under normal operation. + If you think that you need other values: contact me. + + + REFERENCES USED: + + "AIC-6260 SCSI Chip Specification", Adaptec Corporation. + + "SCSI COMPUTER SYSTEM INTERFACE - 2 (SCSI-2)", X3T9.2/86-109 rev. 10h + + "Writing a SCSI device driver for Linux", Rik Faith (faith@cs.unc.edu) + + "Kernel Hacker's Guide", Michael K. Johnson (johnsonm@sunsite.unc.edu) + + "Adaptec 1520/1522 User's Guide", Adaptec Corporation. + + Michael K. Johnson (johnsonm@sunsite.unc.edu) + + Drew Eckhardt (drew@cs.colorado.edu) + + Eric Youngdale (ericy@cais.com) + + special thanks to Eric Youngdale for the free(!) supplying the + documentation on the chip. + + **************************************************************************/ + +#include <linux/sched.h> +#include <asm/io.h> +#include "../block/blk.h" +#include "scsi.h" +#include "sd.h" +#include "hosts.h" +#include "constants.h" +#include <asm/system.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/wait.h> +#include <linux/ioport.h> + +#include "aha152x.h" + +/* DEFINES */ + + +/* If auto configuration is disabled, IRQ, SCSI_ID and RECONNECT have to + be predefined */ +#if !defined(AUTOCONF) +#if !defined(IRQ) +#error undefined IRQ; define AUTOCONF or IRQ +#endif +#if !defined(SCSI_ID) +#error undefined SCSI_ID; define AUTOCONF or SCSI_ID +#endif +#if !defined(RECONNECT) +#error undefined RECONNECT; define AUTOCONF or RECONNECT +#endif +#if !defined(PARITY) +#error undefined PARITY; define AUTOCONF or PARITY +#endif +#endif + +/* I use this when I'm looking for weird bugs */ +#define DEBUG_TIMING + +#if defined(DEBUG_AHA152X) + +#undef SKIP_PORTS /* don't display ports */ + +#undef DEBUG_QUEUE /* debug queue() */ +#undef DEBUG_RESET /* debug reset() */ +#undef DEBUG_INTR /* debug intr() */ +#undef DEBUG_SELECTION /* debug selection part in intr() */ +#undef DEBUG_MSGO /* debug message out phase in intr() */ +#undef DEBUG_MSGI /* debug message in phase in intr() */ +#undef DEBUG_STATUS /* debug status phase in intr() */ +#undef DEBUG_CMD /* debug command phase in intr() */ +#undef DEBUG_DATAI /* debug data in phase in intr() */ +#undef DEBUG_DATAO /* debug data out phase in intr() */ +#undef DEBUG_ABORT /* debug abort() */ +#undef DEBUG_DONE /* debug done() */ +#undef DEBUG_BIOSPARAM /* debug biosparam() */ + +#undef DEBUG_RACE /* debug race conditions */ +#undef DEBUG_PHASES /* debug phases (useful to trace) */ +#undef DEBUG_QUEUES /* debug reselection */ + +/* recently used for debugging */ +#if 0 +#endif + +#define DEBUG_QUEUE +#define DEBUG_PHASES + +#endif + +#define DEBUG_RESET /* resets should be rare */ +#define DEBUG_ABORT /* aborts too */ + +#define DEBUG_DEFAULT (debug_reset|debug_abort) + +/* END OF DEFINES */ + +/* some additional "phases" for getphase() */ +#define P_BUSFREE 1 +#define P_PARITY 2 + +static int port_base = 0; +static int this_host = 0; +static int can_disconnect = 0; +static int can_doparity = 0; +static int commands = 0; + +#ifdef DEBUG_AHA152X +unsigned int aha152x_debug = DEBUG_DEFAULT; +#endif + +/* set by aha152x_setup according to the command line */ +static int setup_called = 0; +static int setup_portbase = 0; +static int setup_irq = 0; +static int setup_scsiid = 0; +static int setup_reconnect = 0; +static int setup_doparity = 0; + +#ifdef DEBUG_AHA152X +static int setup_debug = 0; +#endif + +static char *setup_str = (char *)NULL; + +enum { + not_issued = 0x01, + in_selection = 0x02, + disconnected = 0x04, + aborted = 0x08, + sent_ident = 0x10, + in_other = 0x20, +}; + +/* + * Command queues: + * issue_SC : commands that are queued to be issued + * current_SC : command that's currently using the bus + * disconnected_SC : commands that that have been disconnected + */ +static Scsi_Cmnd *issue_SC = NULL; +static Scsi_Cmnd *current_SC = NULL; +static Scsi_Cmnd *disconnected_SC = NULL; + +static int aborting=0, abortion_complete=0, abort_result; + +void aha152x_intr( int irqno ); +void aha152x_done( int error ); +void aha152x_setup( char *str, int *ints ); + +static void aha152x_reset_ports(void); +static void aha152x_panic(char *msg); + +static void disp_ports(void); +static void show_command(Scsi_Cmnd *ptr); +static void show_queues(void); +static void disp_enintr(void); + +#if defined(DEBUG_RACE) +static void enter_driver(const char *); +static void leave_driver(const char *); +#endif + +/* possible locations for the Adaptec BIOS */ +static void *addresses[] = +{ + (void *) 0xdc000, /* default first */ + (void *) 0xc8000, + (void *) 0xcc000, + (void *) 0xd0000, + (void *) 0xd4000, + (void *) 0xd8000, + (void *) 0xe0000, + (void *) 0xf0000, + (void *) 0xeb800, /* VTech Platinum SMP */ +}; +#define ADDRESS_COUNT (sizeof( addresses ) / sizeof( void * )) + +/* possible i/o addresses for the AIC-6260 */ +static unsigned short ports[] = +{ + 0x340, /* default first */ + 0x140 +}; +#define PORT_COUNT (sizeof( ports ) / sizeof( unsigned short )) + +/* possible interrupt channels */ +static unsigned short ints[] = { 9, 10, 11, 12 }; + +/* signatures for various AIC-6[23]60 based controllers. + The point in detecting signatures is to avoid useless + and maybe harmful probes on ports. I'm not sure that + all listed boards pass auto-configuration. For those + which fail the BIOS signature is obsolete, because + user intervention to supply the configuration is + needed anyway. */ +static struct signature { + char *signature; + int sig_offset; + int sig_length; +} signatures[] = +{ + { "Adaptec AHA-1520 BIOS", 0x102e, 21 }, /* Adaptec 152x */ + { "Adaptec ASW-B626 BIOS", 0x1029, 21 }, /* on-board controller */ + { "Adaptec BIOS: ASW-B626", 0x0f, 22 }, /* on-board controller */ + { "Adaptec ASW-B626 S2", 0x2e6c, 19 }, /* on-board controller */ + { "Adaptec BIOS:AIC-6360", 0xc, 21 }, /* on-board controller */ + { "ScsiPro SP-360 BIOS", 0x2873, 19 }, /* ScsiPro-Controller with AIC-6360 */ + { "GA-400 LOCAL BUS SCSI BIOS", 0x102e, 26 }, /* Gigabyte Local-Bus-SCSI */ +}; +#define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature )) + + +static void do_pause( unsigned amount ) /* Pause for amount*10 milliseconds */ +{ + unsigned long the_time = jiffies + amount; /* 0.01 seconds per jiffy */ + + while (jiffies < the_time) + ; +} + +/* + * queue services: + */ +static inline void append_SC( Scsi_Cmnd **SC, Scsi_Cmnd *new_SC) +{ + Scsi_Cmnd *end; + + new_SC->host_scribble = (unsigned char *) NULL; + if(!*SC) + *SC=new_SC; + else + { + for( end=*SC; + end->host_scribble; + end = (Scsi_Cmnd *) end->host_scribble ) + ; + end->host_scribble = (unsigned char *) new_SC; + } +} + +static inline Scsi_Cmnd *remove_first_SC( Scsi_Cmnd **SC ) +{ + Scsi_Cmnd *ptr; + + ptr=*SC; + if(ptr) + *SC= (Scsi_Cmnd *) (*SC)->host_scribble; + return ptr; +} + +static inline Scsi_Cmnd *remove_SC( Scsi_Cmnd **SC, int target, int lun ) +{ + Scsi_Cmnd *ptr, *prev; + + for( ptr=*SC, prev=NULL; + ptr && ((ptr->target!=target) || (ptr->lun!=lun)); + prev = ptr, ptr = (Scsi_Cmnd *) ptr->host_scribble ) + ; + + if(ptr) + if(prev) + prev->host_scribble = ptr->host_scribble; + else + *SC= (Scsi_Cmnd *) ptr->host_scribble; + return ptr; +} + +/* + * read inbound byte and wait for ACK to get low + */ +static void make_acklow(void) +{ + SETPORT( SXFRCTL0, CH1|SPIOEN ); + GETPORT(SCSIDAT); + SETPORT( SXFRCTL0, CH1 ); + + while( TESTHI( SCSISIG, ACKI ) ) + ; +} + +/* + * detect current phase more reliable: + * phase is valid, when the target asserts REQ after we've deasserted ACK. + * + * return value is a valid phase or an error code. + * + * errorcodes: + * P_BUSFREE BUS FREE phase detected + * P_PARITY parity error in DATA phase + */ +static int getphase(void) +{ + int phase, sstat1; + + while( 1 ) + { + do + { + while( !( ( sstat1 = GETPORT( SSTAT1 ) ) & (BUSFREE|SCSIRSTI|REQINIT ) ) ) + ; + if( sstat1 & BUSFREE ) + return P_BUSFREE; + if( sstat1 & SCSIRSTI ) + { + /* IBM drive responds with RSTI to RSTO */ + printk("aha152x: RESET IN\n"); + SETPORT( SSTAT1, SCSIRSTI ); + } + } + while( TESTHI( SCSISIG, ACKI ) || TESTLO( SSTAT1, REQINIT ) ); + + SETPORT( SSTAT1, CLRSCSIPERR ); + + phase = GETPORT( SCSISIG ) & P_MASK ; + + if( TESTHI( SSTAT1, SCSIPERR ) ) + { + if( (phase & (CDO|MSGO))==0 ) /* DATA phase */ + return P_PARITY; + + make_acklow(); + } + else + return phase; + } +} + +/* called from init/main.c */ +void aha152x_setup( char *str, int *ints) +{ + if(setup_called) + panic("aha152x: aha152x_setup called twice.\n"); + + setup_called=ints[0]; + setup_str=str; + + setup_portbase = ints[0] >= 1 ? ints[1] : 0x340; + setup_irq = ints[0] >= 2 ? ints[2] : 11; + setup_scsiid = ints[0] >= 3 ? ints[3] : 7; + setup_reconnect = ints[0] >= 4 ? ints[4] : 1; + setup_doparity = ints[0] >= 5 ? ints[5] : 1; +#ifdef DEBUG_AHA152X + setup_debug = ints[0] >= 6 ? ints[6] : DEBUG_DEFAULT; +#endif +} + +/* + Test, if port_base is valid. + */ +static int aha152x_porttest(int port_base) +{ + int i; + + if(check_region(port_base, TEST-SCSISEQ)) + return 0; + + SETPORT( DMACNTRL1, 0 ); /* reset stack pointer */ + for(i=0; i<16; i++) + SETPORT( STACK, i ); + + SETPORT( DMACNTRL1, 0 ); /* reset stack pointer */ + for(i=0; i<16 && GETPORT(STACK)==i; i++) + ; + + return(i==16); +} + +int aha152x_detect(Scsi_Host_Template * tpnt) +{ + int i, j, ok; + aha152x_config conf; + int interrupt_level; + + if(setup_called) + { + printk("aha152x: processing commandline: "); + +#ifdef DEBUG_AHA152X + if(setup_called>6) +#else + if(setup_called>5) +#endif + { + printk("\naha152x: %s\n", setup_str ); +#ifdef DEBUG_AHA152X + printk("aha152x: usage: aha152x=<PORTBASE>[,<IRQ>[,<SCSI ID>[,<RECONNECT>[,<PARITY>[,<DEBUG>]]]]]\n"); +#else + printk("aha152x: usage: aha152x=<PORTBASE>[,<IRQ>[,<SCSI ID>[,<RECONNECT>[,<PARITY>]]]]\n"); +#endif + panic("aha152x panics in line %d", __LINE__); + } + + port_base = setup_portbase; + interrupt_level = setup_irq; + this_host = setup_scsiid; + can_disconnect = setup_reconnect; + can_doparity = setup_doparity; +#ifdef DEBUG_AHA152X + aha152x_debug = setup_debug; +#endif + + for( i=0; i<PORT_COUNT && (port_base != ports[i]); i++) + ; + + if(i==PORT_COUNT) + { + printk("unknown portbase 0x%03x\n", port_base); + panic("aha152x panics in line %d", __LINE__); + } + + if(!aha152x_porttest(port_base)) + { + printk("portbase 0x%03x fails probe\n", port_base); + panic("aha152x panics in line %d", __LINE__); + } + + i=0; + while(ints[i] && (interrupt_level!=ints[i])) + i++; + if(!ints[i]) + { + printk("illegal IRQ %d\n", interrupt_level); + panic("aha152x panics in line %d", __LINE__); + } + + if( (this_host < 0) || (this_host > 7) ) + { + printk("illegal SCSI ID %d\n", this_host); + panic("aha152x panics in line %d", __LINE__); + } + + if( (can_disconnect < 0) || (can_disconnect > 1) ) + { + printk("reconnect %d should be 0 or 1\n", can_disconnect); + panic("aha152x panics in line %d", __LINE__); + } + + if( (can_doparity < 0) || (can_doparity > 1) ) + { + printk("parity %d should be 0 or 1\n", can_doparity); + panic("aha152x panics in line %d", __LINE__); + } + printk("ok\n"); + } + else + { +#if !defined(SKIP_BIOSTEST) + ok=0; + for( i=0; i < ADDRESS_COUNT && !ok; i++) + for( j=0; (j < SIGNATURE_COUNT) && !ok; j++) + ok=!memcmp((void *) addresses[i]+signatures[j].sig_offset, + (void *) signatures[j].signature, + (int) signatures[j].sig_length); + + if(!ok) + return 0; + + printk("aha152x: BIOS test: passed, "); +#else + printk("aha152x: "); +#endif /* !SKIP_BIOSTEST */ + +#if !defined(PORTBASE) + printk("porttest: "); + for( i=0; i<PORT_COUNT && !aha152x_porttest(ports[i]); i++) + ; + + if(i==PORT_COUNT) + { + printk("failed\n"); + return 0; + } + else + port_base=ports[i]; + printk("ok, "); +#else + port_base=PORTBASE; +#endif /* !PORTBASE */ + +#if defined(AUTOCONF) + + conf.cf_port = (GETPORT(PORTA)<<8) + GETPORT(PORTB); + + interrupt_level = ints[conf.cf_irq]; + this_host = conf.cf_id; + can_disconnect = conf.cf_tardisc; + can_doparity = !conf.cf_parity; + + printk("auto configuration: ok, "); + +#endif /* AUTOCONF */ + +#if defined(IRQ) + interrupt_level = IRQ; +#endif + +#if defined(SCSI_ID) + this_host = SCSI_ID; +#endif + +#if defined(RECONNECT) + can_disconnect=RECONNECT; +#endif + +#if defined(PARITY) + can_doparity=PARITY; +#endif + } + + printk("detection complete\n"); + + ok = request_irq(interrupt_level, aha152x_intr, SA_INTERRUPT, "aha152x"); + + if(ok<0) + { + if(ok == -EINVAL) + { + printk("aha152x: bad IRQ %d.\n", interrupt_level); + printk(" Contact author.\n"); + } + else + if( ok == -EBUSY) + printk( "aha152x: IRQ %d already in use. Configure another.\n", + interrupt_level); + else + { + printk( "\naha152x: Unexpected error code on requesting IRQ %d.\n", + interrupt_level); + printk(" Contact author.\n"); + } + panic("aha152x: driver needs an IRQ.\n"); + } + + SETPORT( SCSIID, this_host << 4 ); + tpnt->this_id=this_host; + + if(can_disconnect) + tpnt->can_queue=AHA152X_MAXQUEUE; + + /* RESET OUT */ + SETBITS(SCSISEQ, SCSIRSTO ); + do_pause(30); + CLRBITS(SCSISEQ, SCSIRSTO ); + do_pause(60); + + aha152x_reset(NULL); + + printk("aha152x: vital data: PORTBASE=0x%03x, IRQ=%d, SCSI ID=%d, reconnect=%s, parity=%s\n", + port_base, + interrupt_level, + this_host, + can_disconnect ? "enabled" : "disabled", + can_doparity ? "enabled" : "disabled"); + + snarf_region(port_base, TEST-SCSISEQ); /* Register */ + + /* not expecting any interrupts */ + SETPORT(SIMODE0, 0); + SETPORT(SIMODE1, 0); + + SETBITS( DMACNTRL0, INTEN); + return 1; +} + +/* + * Queue a command and setup interrupts for a free bus. + */ +int aha152x_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) +{ +#if defined(DEBUG_RACE) + enter_driver("queue"); +#else +#if defined(DEBUG_QUEUE) + if(aha152x_debug & debug_queue) + printk("aha152x: queue(), "); +#endif +#endif + +#if defined(DEBUG_QUEUE) + if(aha152x_debug & debug_queue) + { + printk( "SCpnt (target = %d lun = %d cmnd = ", + SCpnt->target, + SCpnt->lun); + print_command(SCpnt->cmnd); + printk( ", pieces = %d size = %u), ", + SCpnt->use_sg, + SCpnt->request_bufflen ); + disp_ports(); + } +#endif + + SCpnt->scsi_done = done; + + /* setup scratch area + SCp.ptr : buffer pointer + SCp.this_residual : buffer length + SCp.buffer : next buffer + SCp.buffers_residual : left buffers in list + SCp.phase : current state of the command */ + SCpnt->SCp.phase = not_issued; + if (SCpnt->use_sg) + { + SCpnt->SCp.buffer = (struct scatterlist *)SCpnt->request_buffer; + SCpnt->SCp.ptr = SCpnt->SCp.buffer->address; + SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; + SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1; + } + else + { + SCpnt->SCp.ptr = (char *)SCpnt->request_buffer; + SCpnt->SCp.this_residual = SCpnt->request_bufflen; + SCpnt->SCp.buffer = NULL; + SCpnt->SCp.buffers_residual = 0; + } + + SCpnt->SCp.Status = CHECK_CONDITION; + SCpnt->SCp.Message = 0; + SCpnt->SCp.have_data_in = 0; + SCpnt->SCp.sent_command = 0; + + /* Turn led on, when this is the first command. */ + cli(); + commands++; + if(commands==1) + SETPORT( PORTA, 1 ); + +#if defined(DEBUG_QUEUES) + if(aha152x_debug & debug_queues) + printk("i+ (%d), ", commands ); +#endif + append_SC( &issue_SC, SCpnt); + + /* Enable bus free interrupt, when we aren't currently on the bus */ + if(!current_SC) + { + SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 ); + SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0); + } + sti(); + +#if defined(DEBUG_RACE) + leave_driver("queue"); +#endif + + return 0; +} + +/* + * We only support command in interrupt-driven fashion + */ +int aha152x_command( Scsi_Cmnd *SCpnt ) +{ + printk( "aha152x: interrupt driven driver; use aha152x_queue()\n" ); + return -1; +} + +/* + * Abort a queued command + * (commands that are on the bus can't be aborted easily) + */ +int aha152x_abort( Scsi_Cmnd *SCpnt) +{ + Scsi_Cmnd *ptr, *prev; + + cli(); + +#if defined(DEBUG_ABORT) + if(aha152x_debug & debug_abort) + { + printk("aha152x: abort(), SCpnt=0x%08x, ", (unsigned int) SCpnt ); + + show_queues(); + } +#endif + + /* look for command in issue queue */ + for( ptr=issue_SC, prev=NULL; + ptr && ptr!=SCpnt; + prev=ptr, ptr=(Scsi_Cmnd *) ptr->host_scribble) + ; + + if(ptr) + { + /* dequeue */ + if(prev) + prev->host_scribble = ptr->host_scribble; + else + issue_SC = (Scsi_Cmnd *) ptr->host_scribble; + sti(); + + ptr->host_scribble = NULL; + ptr->result = DID_ABORT << 16; + ptr->done(ptr); + return SCSI_ABORT_SUCCESS; + } + + /* if the bus is busy or a command is currently processed, + we can't do anything more */ + if ( TESTLO(SSTAT1, BUSFREE) || (current_SC && current_SC!=SCpnt)) + { + /* fail abortion, if bus is busy */ + + if(!current_SC) + printk("bus busy w/o current command, "); + + sti(); + return SCSI_ABORT_BUSY; + } + + /* bus is free */ + + if(current_SC) + { + /* target entered bus free before COMMAND COMPLETE, nothing to abort */ + sti(); + current_SC->result = DID_ERROR << 16; + current_SC->done(current_SC); + current_SC = (Scsi_Cmnd *) NULL; + return SCSI_ABORT_SUCCESS; + } + + /* look for command in disconnected queue */ + for( ptr=disconnected_SC, prev=NULL; + ptr && ptr!=SCpnt; + prev=ptr, ptr=(Scsi_Cmnd *) ptr->host_scribble) + ; + + if(ptr) + if(!aborting) + { + /* dequeue */ + if(prev) + prev->host_scribble = ptr->host_scribble; + else + disconnected_SC = (Scsi_Cmnd *) ptr->host_scribble; + + /* set command current and initiate selection, + let the interrupt routine take care of the abortion */ + current_SC = ptr; + ptr->SCp.phase = in_selection|aborted; + SETPORT( SCSIID, (this_host << OID_) | current_SC->target ); + + /* enable interrupts for SELECTION OUT DONE and SELECTION TIME OUT */ + SETPORT( SIMODE0, ENSELDO | (disconnected_SC ? ENSELDI : 0) ); + SETPORT( SIMODE1, ENSELTIMO ); + + /* Enable SELECTION OUT sequence */ + SETBITS(SCSISEQ, ENSELO | ENAUTOATNO ); + + SETBITS( DMACNTRL0, INTEN ); + abort_result=SCSI_ABORT_SUCCESS; + aborting++; + abortion_complete=0; + sti(); + + /* sleep until the abortion is complete */ + while(!abortion_complete) + ; + aborting=0; + return abort_result; + } + else + { + /* we're already aborting a command */ + sti(); + return( SCSI_ABORT_BUSY ); + } + + /* command wasn't found */ + printk("command not found\n"); + sti(); + return SCSI_ABORT_NOT_RUNNING; +} + +/* + * Restore default values to the AIC-6260 registers and reset the fifos + */ +static void aha152x_reset_ports(void) +{ + /* disable interrupts */ + SETPORT(DMACNTRL0, RSTFIFO); + + SETPORT(SCSISEQ, 0); + + SETPORT(SXFRCTL1, 0); + SETPORT( SCSISIG, 0); + SETPORT(SCSIRATE, 0); + + /* clear all interrupt conditions */ + SETPORT(SSTAT0, 0x7f); + SETPORT(SSTAT1, 0xef); + + SETPORT(SSTAT4, SYNCERR|FWERR|FRERR); + + SETPORT(DMACNTRL0, 0); + SETPORT(DMACNTRL1, 0); + + SETPORT(BRSTCNTRL, 0xf1); + + /* clear SCSI fifo and transfer count */ + SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT); + SETPORT(SXFRCTL0, CH1); + + /* enable interrupts */ + SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 ); + SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0); +} + +/* + * Reset registers, reset a hanging bus and + * kill active and disconnected commands for target w/o soft reset + */ +int aha152x_reset(Scsi_Cmnd * __unused) +{ + Scsi_Cmnd *ptr, *prev, *next; + + aha152x_reset_ports(); + + /* Reset, if bus hangs */ + if( TESTLO( SSTAT1, BUSFREE ) ) + { + CLRBITS( DMACNTRL0, INTEN ); + +#if defined(DEBUG_RESET) + if(aha152x_debug & debug_reset) + { + printk("aha152x: reset(), bus not free: SCSI RESET OUT\n"); + show_queues(); + } +#endif + + if(current_SC && !current_SC->device->soft_reset) + { + current_SC->host_scribble = NULL; + current_SC->result = DID_RESET << 16; + current_SC->done(current_SC); + current_SC=NULL; + } + + cli(); + prev=NULL; ptr=disconnected_SC; + while(ptr) + { + if(!ptr->device->soft_reset) + { + if(prev) + prev->host_scribble = ptr->host_scribble; + else + disconnected_SC = (Scsi_Cmnd *) ptr->host_scribble; + + next = (Scsi_Cmnd *) ptr->host_scribble; + + ptr->host_scribble = NULL; + ptr->result = DID_RESET << 16; + ptr->done(ptr); + + ptr = next; + } + else + { + prev=ptr; + ptr = (Scsi_Cmnd *) ptr->host_scribble; + } + } + sti(); + +#if defined( DEBUG_RESET ) + if(aha152x_debug & debug_reset) + { + printk("commands on targets w/ soft-resets:\n"); + show_queues(); + } +#endif + + /* RESET OUT */ + SETPORT(SCSISEQ, SCSIRSTO); + do_pause(30); + SETPORT(SCSISEQ, 0); + do_pause(60); + + SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 ); + SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0); + + SETPORT( DMACNTRL0, INTEN ); + } + + return SCSI_RESET_SUCCESS; +} + +/* + * Return the "logical geometry" + */ +int aha152x_biosparam(Scsi_Disk * disk, int dev, int *info_array ) +{ + int size = disk->capacity; + +#if defined(DEBUG_BIOSPARAM) + if(aha152x_debug & debug_biosparam) + printk("aha152x_biosparam: dev=%x, size=%d, ", dev, size); +#endif + +/* I took this from other SCSI drivers, since it provides + the correct data for my devices. */ + info_array[0]=64; + info_array[1]=32; + info_array[2]=size>>11; + +#if defined(DEBUG_BIOSPARAM) + if(aha152x_debug & debug_biosparam) + { + printk("bios geometry: head=%d, sec=%d, cyl=%d\n", + info_array[0], info_array[1], info_array[2]); + printk("WARNING: check, if the bios geometry is correct.\n"); + } +#endif + + return 0; +} + +/* + * Internal done function + */ +void aha152x_done( int error ) +{ + Scsi_Cmnd *done_SC; + +#if defined(DEBUG_DONE) + if(aha152x_debug & debug_done) + { + printk("\naha152x: done(), "); + disp_ports(); + } +#endif + + if (current_SC) + { +#if defined(DEBUG_DONE) + if(aha152x_debug & debug_done) + printk("done(%x), ", error); +#endif + + cli(); + + done_SC = current_SC; + current_SC = NULL; + + /* turn led off, when no commands are in the driver */ + commands--; + if(!commands) + SETPORT( PORTA, 0 ); /* turn led off */ + +#if defined(DEBUG_QUEUES) + if(aha152x_debug & debug_queues) + printk("ok (%d), ", commands); +#endif + sti(); + + SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 ); + SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0); + +#if defined(DEBUG_PHASES) + if(aha152x_debug & debug_phases) + printk("BUS FREE loop, "); +#endif + while( TESTLO( SSTAT1, BUSFREE ) ) + ; +#if defined(DEBUG_PHASES) + if(aha152x_debug & debug_phases) + printk("BUS FREE\n"); +#endif + + done_SC->result = error; + if(done_SC->scsi_done) + { +#if defined(DEBUG_DONE) + if(aha152x_debug & debug_done) + printk("calling scsi_done, "); +#endif + done_SC->scsi_done( done_SC ); +#if defined(DEBUG_DONE) + if(aha152x_debug & debug_done) + printk("done returned, "); +#endif + } + else + panic( "aha152x: current_SC->scsi_done() == NULL" ); + } + else + aha152x_panic( "done() called outside of command" ); +} + +/* + * Interrupts handler (main routine of the driver) + */ +void aha152x_intr( int irqno ) +{ + int done=0, phase; + +#if defined(DEBUG_RACE) + enter_driver("intr"); +#else +#if defined(DEBUG_INTR) + if(aha152x_debug & debug_intr) + printk("\naha152x: intr(), "); +#endif +#endif + + /* no more interrupts from the controller, while we busy. + INTEN has to be restored, when we're ready to leave + intr(). To avoid race conditions we have to return + immediately afterwards. */ + CLRBITS( DMACNTRL0, INTEN); + sti(); + + /* disconnected target is trying to reconnect. + Only possible, if we have disconnected nexuses and + nothing is occupying the bus. + */ + if( TESTHI( SSTAT0, SELDI ) && + disconnected_SC && + ( !current_SC || ( current_SC->SCp.phase & in_selection ) ) + ) + { + int identify_msg, target, i; + + /* Avoid conflicts when a target reconnects + while we are trying to connect to another. */ + if(current_SC) + { +#if defined(DEBUG_QUEUES) + if(aha152x_debug & debug_queues) + printk("i+, "); +#endif + cli(); + append_SC( &issue_SC, current_SC); + current_SC=NULL; + sti(); + } + + /* disable sequences */ + SETPORT( SCSISEQ, 0 ); + SETPORT( SSTAT0, CLRSELDI ); + SETPORT( SSTAT1, CLRBUSFREE ); + +#if defined(DEBUG_QUEUES) || defined(DEBUG_PHASES) + if(aha152x_debug & (debug_queues|debug_phases)) + printk("reselected, "); +#endif + + i = GETPORT(SELID) & ~(1 << this_host); + target=0; + if(i) + for( ; (i & 1)==0; target++, i>>=1) + ; + else + aha152x_panic("reconnecting target unknown"); + +#if defined(DEBUG_QUEUES) + if(aha152x_debug & debug_queues) + printk("SELID=%02x, target=%d, ", GETPORT(SELID), target ); +#endif + SETPORT( SCSIID, (this_host << OID_) | target ); + SETPORT( SCSISEQ, ENRESELI ); + + if(TESTLO( SSTAT0, SELDI )) + aha152x_panic("RESELI failed"); + + SETPORT( SCSISIG, P_MSGI ); + + /* Get identify message */ + if((i=getphase())!=P_MSGI) + { + printk("target doesn't enter MSGI to identify (phase=%02x)\n", i); + aha152x_panic("unknown lun"); + } + SETPORT( SCSISEQ, 0 ); + + SETPORT( SXFRCTL0, CH1); + + identify_msg = GETPORT(SCSIBUS); + + if(!(identify_msg & IDENTIFY_BASE)) + { + printk("target=%d, inbound message (%02x) != IDENTIFY\n", + target, identify_msg); + aha152x_panic("unknown lun"); + } + + make_acklow(); + getphase(); + +#if defined(DEBUG_QUEUES) + if(aha152x_debug & debug_queues) + printk("identify=%02x, lun=%d, ", identify_msg, identify_msg & 0x3f ); +#endif + + cli(); +#if defined(DEBUG_QUEUES) + if(aha152x_debug & debug_queues) + printk("d-, "); +#endif + current_SC = remove_SC( &disconnected_SC, + target, + identify_msg & 0x3f ); + + if(!current_SC) + { + printk("lun=%d, ", identify_msg & 0x3f ); + aha152x_panic("no disconnected command for that lun"); + } + + current_SC->SCp.phase &= ~disconnected; + sti(); + + SETPORT( SIMODE0, 0 ); + SETPORT( SIMODE1, ENPHASEMIS|ENBUSFREE ); +#if defined(DEBUG_RACE) + leave_driver("(reselected) intr"); +#endif + SETBITS( DMACNTRL0, INTEN); + return; + } + + /* Check, if we aren't busy with a command */ + if(!current_SC) + { + /* bus is free to issue a queued command */ + if(TESTHI( SSTAT1, BUSFREE) && issue_SC) + { + cli(); +#if defined(DEBUG_QUEUES) + if(aha152x_debug & debug_queues) + printk("i-, "); +#endif + current_SC = remove_first_SC( &issue_SC ); + sti(); + +#if defined(DEBUG_INTR) || defined(DEBUG_SELECTION) || defined(DEBUG_PHASES) + if(aha152x_debug & (debug_intr|debug_selection|debug_phases)) + printk("issuing command, "); +#endif + current_SC->SCp.phase = in_selection; + + #if defined(DEBUG_INTR) || defined(DEBUG_SELECTION) || defined(DEBUG_PHASES) + if(aha152x_debug & (debug_intr|debug_selection|debug_phases)) + printk("selecting %d, ", current_SC->target); + #endif + SETPORT( SCSIID, (this_host << OID_) | current_SC->target ); + + /* Enable interrupts for SELECTION OUT DONE and SELECTION OUT INITIATED */ + SETPORT( SXFRCTL1, can_doparity ? (ENSPCHK|ENSTIMER) : ENSTIMER); + + /* enable interrupts for SELECTION OUT DONE and SELECTION TIME OUT */ + SETPORT( SIMODE0, ENSELDO | (disconnected_SC ? ENSELDI : 0) ); + SETPORT( SIMODE1, ENSELTIMO ); + + /* Enable SELECTION OUT sequence */ + SETBITS(SCSISEQ, ENSELO | ENAUTOATNO ); + + #if defined(DEBUG_RACE) + leave_driver("(selecting) intr"); + #endif + SETBITS( DMACNTRL0, INTEN ); + return; + } + + /* No command we are busy with and no new to issue */ + printk("aha152x: ignoring spurious interrupt, nothing to do\n"); + return; + } + + /* the bus is busy with something */ + +#if defined(DEBUG_INTR) + if(aha152x_debug & debug_intr) + disp_ports(); +#endif + + /* we are waiting for the result of a selection attempt */ + if(current_SC->SCp.phase & in_selection) + { + if( TESTLO( SSTAT1, SELTO ) ) + /* no timeout */ + if( TESTHI( SSTAT0, SELDO ) ) + { + /* clear BUS FREE interrupt */ + SETPORT( SSTAT1, CLRBUSFREE); + + /* Disable SELECTION OUT sequence */ + CLRBITS(SCSISEQ, ENSELO|ENAUTOATNO ); + + /* Disable SELECTION OUT DONE interrupt */ + CLRBITS(SIMODE0, ENSELDO); + CLRBITS(SIMODE1, ENSELTIMO); + + if( TESTLO(SSTAT0, SELDO) ) + { + printk("aha152x: passing bus free condition\n"); + +#if defined(DEBUG_RACE) + leave_driver("(passing bus free) intr"); +#endif + SETBITS( DMACNTRL0, INTEN); + + if(current_SC->SCp.phase & aborted) + { + abort_result=SCSI_ABORT_ERROR; + abortion_complete++; + } + + aha152x_done( DID_NO_CONNECT << 16 ); + return; + } +#if defined(DEBUG_SELECTION) || defined(DEBUG_PHASES) + if(aha152x_debug & (debug_selection|debug_phases)) + printk("SELDO (SELID=%x), ", GETPORT(SELID)); +#endif + + /* selection was done */ + SETPORT( SSTAT0, CLRSELDO ); + +#if defined(DEBUG_ABORT) + if((aha152x_debug & debug_abort) && (current_SC->SCp.phase & aborted)) + printk("(ABORT) target selected, "); +#endif + + current_SC->SCp.phase &= ~in_selection; + current_SC->SCp.phase |= in_other; + +#if defined(DEBUG_RACE) + leave_driver("(SELDO) intr"); +#endif + + SETPORT( SCSISIG, P_MSGO ); + + SETPORT( SIMODE0, 0 ); + SETPORT( SIMODE1, ENREQINIT|ENBUSFREE ); + SETBITS( DMACNTRL0, INTEN); + return; + } + else + aha152x_panic("neither timeout nor selection\007"); + else + { +#if defined(DEBUG_SELECTION) || defined(DEBUG_PHASES) + if(aha152x_debug & (debug_selection|debug_phases)) + printk("SELTO, "); +#endif + /* end selection attempt */ + CLRBITS(SCSISEQ, ENSELO|ENAUTOATNO ); + + /* timeout */ + SETPORT( SSTAT1, CLRSELTIMO ); + + SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 ); + SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0); + SETBITS( DMACNTRL0, INTEN ); +#if defined(DEBUG_RACE) + leave_driver("(SELTO) intr"); +#endif + + if(current_SC->SCp.phase & aborted) + { +#if defined(DEBUG_ABORT) + if(aha152x_debug & debug_abort) + printk("(ABORT) selection timeout, "); +#endif + abort_result=SCSI_ABORT_ERROR; + abortion_complete++; + } + + if( TESTLO( SSTAT0, SELINGO ) ) + /* ARBITRATION not won */ + aha152x_done( DID_BUS_BUSY << 16 ); + else + /* ARBITRATION won, but SELECTION failed */ + aha152x_done( DID_NO_CONNECT << 16 ); + return; + } + } + + /* enable interrupt, when target leaves current phase */ + phase = getphase(); + if(!(phase & ~P_MASK)) /* "real" phase */ + SETPORT(SCSISIG, phase); + SETPORT(SSTAT1, CLRPHASECHG); + current_SC->SCp.phase = + (current_SC->SCp.phase & ~((P_MASK|1)<<16)) | (phase << 16 ); + + /* information transfer phase */ + switch( phase ) + { + case P_MSGO: /* MESSAGE OUT */ + { + unsigned char message; + +#if defined(DEBUG_INTR) || defined(DEBUG_MSGO) || defined(DEBUG_PHASES) + if(aha152x_debug & (debug_intr|debug_msgo|debug_phases)) + printk("MESSAGE OUT, "); +#endif + + if( current_SC->SCp.phase & aborted ) + { +#if defined(DEBUG_MSGO) || defined(DEBUG_ABORT) + if(aha152x_debug & (debug_msgo|debug_abort)) + printk("ABORT, "); +#endif + message=ABORT; + } + else + /* If we didn't identify yet, do it. Otherwise there's nothing to do, + but reject (probably we got an message before, that we have to + reject (SDTR, WDTR, etc.) */ + if( !(current_SC->SCp.phase & sent_ident)) + { + message=IDENTIFY(can_disconnect,current_SC->lun); +#if defined(DEBUG_MSGO) + if(aha152x_debug & debug_msgo) + printk("IDENTIFY (reconnect=%s;lun=%d), ", + can_disconnect ? "enabled" : "disabled", current_SC->lun); +#endif + } + else + { + message=MESSAGE_REJECT; +#if defined(DEBUG_MSGO) + if(aha152x_debug & debug_msgo) + printk("REJECT, "); +#endif + } + + CLRBITS( SXFRCTL0, ENDMA); + + SETPORT( SIMODE0, 0 ); + SETPORT( SIMODE1, ENPHASEMIS|ENREQINIT|ENBUSFREE ); + + /* wait for data latch to become ready or a phase change */ + while( TESTLO( DMASTAT, INTSTAT ) ) + ; + + if( TESTHI( SSTAT1, PHASEMIS ) ) + aha152x_panic("unable to send message"); + + /* Leave MESSAGE OUT after transfer */ + SETPORT( SSTAT1, CLRATNO); + + SETPORT( SCSIDAT, message ); + + make_acklow(); + getphase(); + + if(message==IDENTIFY(can_disconnect,current_SC->lun)) + current_SC->SCp.phase |= sent_ident; + + if(message==ABORT) + { + /* revive abort(); abort() enables interrupts */ + abort_result=SCSI_ABORT_SUCCESS; + abortion_complete++; + + current_SC->SCp.phase = (current_SC->SCp.phase & ~(P_MASK<<16)); + + /* exit */ + SETBITS( DMACNTRL0, INTEN ); +#if defined(DEBUG_RACE) + leave_driver("(ABORT) intr"); +#endif + aha152x_done(DID_ABORT<<16); + return; + } + } + break; + + case P_CMD: /* COMMAND phase */ +#if defined(DEBUG_INTR) || defined(DEBUG_CMD) || defined(DEBUG_PHASES) + if(aha152x_debug & (debug_intr|debug_cmd|debug_phases)) + printk("COMMAND, "); +#endif + if( !(current_SC->SCp.sent_command) ) + { + if(GETPORT(FIFOSTAT) || GETPORT(SSTAT2) & (SFULL|SFCNT)) + printk("aha152x: P_CMD: %d(%d) bytes left in FIFO, resetting\n", + GETPORT(FIFOSTAT), GETPORT(SSTAT2) & (SFULL|SFCNT)); + + /* reset fifo and enable writes */ + SETPORT(DMACNTRL0, WRITE_READ|RSTFIFO); + SETPORT(DMACNTRL0, ENDMA|WRITE_READ); + + /* clear transfer count and scsi fifo */ + SETPORT(SXFRCTL0, CH1|CLRSTCNT|CLRCH1 ); + SETPORT(SXFRCTL0, SCSIEN|DMAEN|CH1); + + /* missing phase raises INTSTAT */ + SETPORT( SIMODE0, 0 ); + SETPORT( SIMODE1, ENPHASEMIS|ENBUSFREE ); + +#if defined(DEBUG_CMD) + if(aha152x_debug & debug_cmd) + printk("waiting, "); +#endif + /* wait for FIFO to get empty */ + while( TESTLO ( DMASTAT, DFIFOEMP|INTSTAT ) ) + ; + + if( TESTHI( SSTAT1, PHASEMIS ) ) + aha152x_panic("target left COMMAND phase"); + +#if defined(DEBUG_CMD) + if(aha152x_debug & debug_cmd) + { + printk("DFIFOEMP, outsw (%d words), ", + current_SC->cmd_len >>1 ); + disp_ports(); + } +#endif + + outsw( DATAPORT, + ¤t_SC->cmnd, + current_SC->cmd_len >>1 ); + +#if defined(DEBUG_CMD) + if(aha152x_debug & debug_cmd) + { + printk("FCNT=%d, STCNT=%d, ", GETPORT(FIFOSTAT), GETSTCNT() ); + disp_ports(); + } +#endif + + /* wait for SCSI FIFO to get empty. + very important to send complete commands. */ + while( TESTLO ( SSTAT2, SEMPTY ) ) + ; + + CLRBITS(SXFRCTL0, SCSIEN|DMAEN); + /* transfer can be considered ended, when SCSIEN reads back zero */ + while( TESTHI( SXFRCTL0, SCSIEN ) ) + ; + + CLRBITS(DMACNTRL0, ENDMA); + +#if defined(DEBUG_CMD) || defined(DEBUG_INTR) + if(debug_cmd & debug_intr) + printk("sent %d/%d command bytes, ", GETSTCNT(), + current_SC->cmd_len); +#endif + + } + else + aha152x_panic("Nothing to sent while in COMMAND OUT"); + break; + + case P_MSGI: /* MESSAGE IN phase */ +#if defined(DEBUG_INTR) || defined(DEBUG_MSGI) || defined(DEBUG_PHASES) + if(aha152x_debug & (debug_intr|debug_msgi|debug_phases)) + printk("MESSAGE IN, "); +#endif + SETPORT( SXFRCTL0, CH1); + + SETPORT( SIMODE0, 0); + SETPORT( SIMODE1, ENBUSFREE); + + while( phase == P_MSGI ) + { + current_SC->SCp.Message = GETPORT( SCSIBUS ); + switch(current_SC->SCp.Message) + { + case DISCONNECT: +#if defined(DEBUG_MSGI) || defined(DEBUG_PHASES) + if(aha152x_debug & (debug_msgi|debug_phases)) + printk("target disconnected, "); +#endif + current_SC->SCp.Message = 0; + current_SC->SCp.phase |= disconnected; + if(!can_disconnect) + aha152x_panic("target was not allowed to disconnect"); + break; + + case COMMAND_COMPLETE: +#if defined(DEBUG_MSGI) || defined(DEBUG_PHASES) + if(aha152x_debug & (debug_msgi|debug_phases)) + printk("inbound message ( COMMAND COMPLETE ), "); +#endif + done++; + break; + + case MESSAGE_REJECT: +#if defined(DEBUG_MSGI) + if(aha152x_debug & debug_msgi) + printk("inbound message ( MESSAGE REJECT ), "); +#endif + break; + + case SAVE_POINTERS: +#if defined(DEBUG_MSGI) + if(aha152x_debug & debug_msgi) + printk("inbound message ( SAVE DATA POINTERS ), "); +#endif + break; + + case EXTENDED_MESSAGE: + { + int i, code; + +#if defined(DEBUG_MSGI) + if(aha152x_debug & debug_msgi) + printk("inbound message ( EXTENDED MESSAGE ), "); +#endif + make_acklow(); + if(getphase()!=P_MSGI) + break; + + i=GETPORT(SCSIBUS); + +#if defined(DEBUG_MSGI) + if(aha152x_debug & debug_msgi) + printk("length (%d), code ( ", i); +#endif + + make_acklow(); + if(getphase()!=P_MSGI) + break; + + code = GETPORT(SCSIBUS); + + switch( code ) + { + case 0x00: +#if defined(DEBUG_MSGI) + if(aha152x_debug & debug_msgi) + printk("MODIFY DATA POINTER "); +#endif + SETPORT(SCSISIG, P_MSGI|ATNO); + break; + case 0x01: +#if defined(DEBUG_MSGI) + if(aha152x_debug & debug_msgi) + printk("SYNCHRONOUS DATA TRANSFER REQUEST "); +#endif + SETPORT(SCSISIG, P_MSGI|ATNO); + break; + case 0x02: +#if defined(DEBUG_MSGI) + if(aha152x_debug & debug_msgi) + printk("EXTENDED IDENTIFY "); +#endif + break; + case 0x03: +#if defined(DEBUG_MSGI) + if(aha152x_debug & debug_msgi) + printk("WIDE DATA TRANSFER REQUEST "); +#endif + SETPORT(SCSISIG, P_MSGI|ATNO); + break; + default: +#if defined(DEBUG_MSGI) + if(aha152x_debug & debug_msgi) + if( code & 0x80 ) + printk("reserved (%d) ", code ); + else + printk("vendor specific (%d) ", code); +#endif + SETPORT(SCSISIG, P_MSGI|ATNO); + break; + } +#if defined(DEBUG_MSGI) + if(aha152x_debug & debug_msgi) + printk(" ), data ( "); +#endif + while( --i && (make_acklow(), getphase()==P_MSGI)) + { +#if defined(DEBUG_MSGI) + if(aha152x_debug & debug_msgi) + printk("%x ", GETPORT(SCSIBUS) ); +#else + GETPORT(SCSIBUS); +#endif + } +#if defined(DEBUG_MSGI) + if(aha152x_debug & debug_msgi) + printk(" ), "); +#endif + /* We reject all extended messages. To do this + we just enter MSGO by asserting ATN. Since + we have already identified a REJECT message + will be sent. */ + SETPORT(SCSISIG, P_MSGI|ATNO); + } + break; + + default: + printk("unsupported inbound message %x, ", current_SC->SCp.Message); + break; + + } + + make_acklow(); + phase=getphase(); + } + + /* clear SCSI fifo on BUSFREE */ + if(phase==P_BUSFREE) + SETPORT(SXFRCTL0, CH1|CLRCH1); + + if(current_SC->SCp.phase & disconnected) + { + cli(); +#if defined(DEBUG_QUEUES) + if(aha152x_debug & debug_queues) + printk("d+, "); +#endif + append_SC( &disconnected_SC, current_SC); + current_SC = NULL; + sti(); + + SETBITS( SCSISEQ, ENRESELI ); + + SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 ); + SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0); + + SETBITS( DMACNTRL0, INTEN ); + return; + } + break; + + case P_STATUS: /* STATUS IN phase */ +#if defined(DEBUG_STATUS) || defined(DEBUG_INTR) || defined(DEBUG_PHASES) + if(aha152x_debug & (debug_status|debug_intr|debug_phases)) + printk("STATUS, "); +#endif + SETPORT( SXFRCTL0, CH1); + + SETPORT( SIMODE0, 0 ); + SETPORT( SIMODE1, ENREQINIT|ENBUSFREE ); + + if( TESTHI( SSTAT1, PHASEMIS ) ) + printk("aha152x: passing STATUS phase"); + + current_SC->SCp.Status = GETPORT( SCSIBUS ); + make_acklow(); + getphase(); + +#if defined(DEBUG_STATUS) + if(aha152x_debug & debug_status) + { + printk("inbound status "); + print_status( current_SC->SCp.Status ); + printk(", "); + } +#endif + break; + + case P_DATAI: /* DATA IN phase */ + { + int fifodata, data_count, done; + +#if defined(DEBUG_DATAI) || defined(DEBUG_INTR) || defined(DEBUG_PHASES) + if(aha152x_debug & (debug_datai|debug_intr|debug_phases)) + printk("DATA IN, "); +#endif + + if(GETPORT(FIFOSTAT) || GETPORT(SSTAT2) & (SFULL|SFCNT)) + printk("aha152x: P_DATAI: %d(%d) bytes left in FIFO, resetting\n", + GETPORT(FIFOSTAT), GETPORT(SSTAT2) & (SFULL|SFCNT)); + + /* reset host fifo */ + SETPORT(DMACNTRL0, RSTFIFO); + SETPORT(DMACNTRL0, RSTFIFO|ENDMA); + + SETPORT(SXFRCTL0, CH1|SCSIEN|DMAEN ); + + SETPORT( SIMODE0, 0 ); + SETPORT( SIMODE1, ENPHASEMIS|ENBUSFREE ); + + /* done is set when the FIFO is empty after the target left DATA IN */ + done=0; + + /* while the target stays in DATA to transfer data */ + while ( !done ) + { +#if defined(DEBUG_DATAI) + if(aha152x_debug & debug_datai) + printk("expecting data, "); +#endif + /* wait for PHASEMIS or full FIFO */ + while( TESTLO ( DMASTAT, DFIFOFULL|INTSTAT ) ) + ; + + if( TESTHI( DMASTAT, DFIFOFULL ) ) + fifodata=GETPORT(FIFOSTAT); + else + { + /* wait for SCSI fifo to get empty */ + while( TESTLO( SSTAT2, SEMPTY ) ) + ; + + /* rest of data in FIFO */ + fifodata=GETPORT(FIFOSTAT); +#if defined(DEBUG_DATAI) + if(aha152x_debug & debug_datai) + printk("last transfer, "); +#endif + done=1; + } + +#if defined(DEBUG_DATAI) + if(aha152x_debug & debug_datai) + printk("fifodata=%d, ", fifodata); +#endif + + while( fifodata && current_SC->SCp.this_residual ) + { + data_count=fifodata; + + /* limit data transfer to size of first sg buffer */ + if (data_count > current_SC->SCp.this_residual) + data_count = current_SC->SCp.this_residual; + + fifodata -= data_count; + +#if defined(DEBUG_DATAI) + if(aha152x_debug & debug_datai) + printk("data_count=%d, ", data_count); +#endif + + if(data_count&1) + { + /* get a single byte in byte mode */ + SETBITS(DMACNTRL0, _8BIT ); + *current_SC->SCp.ptr++ = GETPORT( DATAPORT ); + current_SC->SCp.this_residual--; + } + if(data_count>1) + { + CLRBITS(DMACNTRL0, _8BIT ); + data_count >>= 1; /* Number of words */ + insw( DATAPORT, current_SC->SCp.ptr, data_count ); +#if defined(DEBUG_DATAI) + if(aha152x_debug & debug_datai) +/* show what comes with the last transfer */ + if(done) + { + int i; + unsigned char *data; + + printk("data on last transfer (%d bytes: ", + 2*data_count); + data = (unsigned char *) current_SC->SCp.ptr; + for( i=0; i<2*data_count; i++) + printk("%2x ", *data++); + printk("), "); + } +#endif + current_SC->SCp.ptr += 2 * data_count; + current_SC->SCp.this_residual -= 2 * data_count; + } + + /* if this buffer is full and there are more buffers left */ + if (!current_SC->SCp.this_residual && + current_SC->SCp.buffers_residual) + { + /* advance to next buffer */ + current_SC->SCp.buffers_residual--; + current_SC->SCp.buffer++; + current_SC->SCp.ptr = + current_SC->SCp.buffer->address; + current_SC->SCp.this_residual = + current_SC->SCp.buffer->length; + } + } + + /* + * Fifo should be empty + */ + if(fifodata>0) + { + printk("aha152x: more data than expected (%d bytes)\n", + GETPORT(FIFOSTAT)); + SETBITS(DMACNTRL0, _8BIT ); + printk("aha152x: data ( "); + while(fifodata--) + printk("%2x ", GETPORT( DATAPORT )); + printk(")\n"); + } + +#if defined(DEBUG_DATAI) + if(aha152x_debug & debug_datai) + if(!fifodata) + printk("fifo empty, "); + else + printk("something left in fifo, "); +#endif + } + +#if defined(DEBUG_DATAI) + if((aha152x_debug & debug_datai) && (current_SC->SCp.buffers_residual || current_SC->SCp.this_residual)) + printk("left buffers (buffers=%d, bytes=%d), ", + current_SC->SCp.buffers_residual, + current_SC->SCp.this_residual); +#endif + /* transfer can be considered ended, when SCSIEN reads back zero */ + CLRBITS(SXFRCTL0, SCSIEN|DMAEN); + while( TESTHI( SXFRCTL0, SCSIEN ) ) + ; + CLRBITS(DMACNTRL0, ENDMA ); + +#if defined(DEBUG_DATAI) || defined(DEBUG_INTR) + if(aha152x_debug & (debug_datai|debug_intr)) + printk("got %d bytes, ", GETSTCNT()); +#endif + + current_SC->SCp.have_data_in++; + } + break; + + case P_DATAO: /* DATA OUT phase */ + { + int data_count; + +#if defined(DEBUG_DATAO) || defined(DEBUG_INTR) || defined(DEBUG_PHASES) + if(aha152x_debug & (debug_datao|debug_intr|debug_phases)) + printk("DATA OUT, "); +#endif +#if defined(DEBUG_DATAO) + if(aha152x_debug & debug_datao) + printk("got data to send (bytes=%d, buffers=%d), ", + current_SC->SCp.this_residual, + current_SC->SCp.buffers_residual ); +#endif + + if(GETPORT(FIFOSTAT) || GETPORT(SSTAT2) & (SFULL|SFCNT) ) + { + printk("%d(%d) left in FIFO, ", GETPORT(FIFOSTAT), GETPORT(SSTAT2) & (SFULL|SFCNT) ); + aha152x_panic("FIFO should be empty"); + } + + SETPORT(DMACNTRL0, WRITE_READ|RSTFIFO); + SETPORT(DMACNTRL0, ENDMA|WRITE_READ); + + SETPORT(SXFRCTL0, CH1|CLRSTCNT|CLRCH1 ); + SETPORT(SXFRCTL0, SCSIEN|DMAEN|CH1); + + SETPORT( SIMODE0, 0 ); + SETPORT( SIMODE1, ENPHASEMIS|ENBUSFREE ); + + /* while current buffer is not empty or + there are more buffers to transfer */ + while( TESTLO( SSTAT1, PHASEMIS ) && + (current_SC->SCp.this_residual || + current_SC->SCp.buffers_residual) ) + { +#if defined(DEBUG_DATAO) + if(aha152x_debug & debug_datao) + printk("sending data (left: bytes=%d, buffers=%d), waiting, ", + current_SC->SCp.this_residual, + current_SC->SCp.buffers_residual); +#endif + /* transfer rest of buffer, but max. 128 byte */ + data_count = current_SC->SCp.this_residual > 128 ? + 128 : current_SC->SCp.this_residual ; + +#if defined(DEBUG_DATAO) + if(aha152x_debug & debug_datao) + printk("data_count=%d, ", data_count); +#endif + + if(data_count&1) + { + /* put a single byte in byte mode */ + SETBITS(DMACNTRL0, _8BIT ); + SETPORT(DATAPORT, *current_SC->SCp.ptr++); + current_SC->SCp.this_residual--; + } + if(data_count>1) + { + CLRBITS(DMACNTRL0, _8BIT ); + data_count >>= 1; /* Number of words */ + outsw( DATAPORT, current_SC->SCp.ptr, data_count ); + current_SC->SCp.ptr += 2 * data_count; + current_SC->SCp.this_residual -= 2 * data_count; + } + + /* wait for FIFO to get empty */ + while( TESTLO ( DMASTAT, DFIFOEMP|INTSTAT ) ) + ; + +#if defined(DEBUG_DATAO) + if(aha152x_debug & debug_datao) + printk("fifo (%d bytes), transfered (%d bytes), ", + GETPORT(FIFOSTAT), GETSTCNT() ); +#endif + + /* if this buffer is empty and there are more buffers left */ + if ( TESTLO( SSTAT1, PHASEMIS ) && + !current_SC->SCp.this_residual && + current_SC->SCp.buffers_residual) + { + /* advance to next buffer */ + current_SC->SCp.buffers_residual--; + current_SC->SCp.buffer++; + current_SC->SCp.ptr = + current_SC->SCp.buffer->address; + current_SC->SCp.this_residual = + current_SC->SCp.buffer->length; + } + } + + if ( current_SC->SCp.this_residual || + current_SC->SCp.buffers_residual ) + { + /* target leaves DATA OUT for an other phase + (perhaps disconnect) */ + + /* data in fifos has to be resend */ + data_count = GETPORT(SSTAT2) & (SFULL|SFCNT); + + data_count += GETPORT(FIFOSTAT) ; + current_SC->SCp.ptr -= data_count; + current_SC->SCp.this_residual += data_count; +#if defined(DEBUG_DATAO) + if(aha152x_debug & debug_datao) + printk("left data (bytes=%d, buffers=%d), fifos (bytes=%d), transfer incomplete, resetting fifo, ", + current_SC->SCp.this_residual, + current_SC->SCp.buffers_residual, + data_count ); +#endif + SETPORT(DMACNTRL0, WRITE_READ|RSTFIFO); + CLRBITS(SXFRCTL0, SCSIEN|DMAEN ); + CLRBITS(DMACNTRL0, ENDMA); + } + else + { +#if defined(DEBUG_DATAO) + if(aha152x_debug & debug_datao) + printk("waiting for SCSI fifo to get empty, "); +#endif + /* wait for SCSI fifo to get empty */ + while( TESTLO( SSTAT2, SEMPTY ) ) + ; +#if defined(DEBUG_DATAO) + if(aha152x_debug & debug_datao) + printk("ok, left data (bytes=%d, buffers=%d) ", + current_SC->SCp.this_residual, + current_SC->SCp.buffers_residual); +#endif + CLRBITS(SXFRCTL0, SCSIEN|DMAEN); + + /* transfer can be considered ended, when SCSIEN reads back zero */ + while( TESTHI( SXFRCTL0, SCSIEN ) ) + ; + + CLRBITS(DMACNTRL0, ENDMA); + } + +#if defined(DEBUG_DATAO) || defined(DEBUG_INTR) + if(aha152x_debug & (debug_datao|debug_intr)) + printk("sent %d data bytes, ", GETSTCNT() ); +#endif + } + break; + + case P_BUSFREE: /* BUSFREE */ +#if defined(DEBUG_RACE) + leave_driver("(BUSFREE) intr"); +#endif +#if defined(DEBUG_PHASES) + if(aha152x_debug & debug_phases) + printk("unexpected BUS FREE, "); +#endif + current_SC->SCp.phase = (current_SC->SCp.phase & ~(P_MASK<<16)); + + aha152x_done( DID_ERROR << 16 ); /* Don't know any better */ + return; + break; + + case P_PARITY: /* parity error in DATA phase */ +#if defined(DEBUG_RACE) + leave_driver("(DID_PARITY) intr"); +#endif + printk("PARITY error in DATA phase, "); + + current_SC->SCp.phase = (current_SC->SCp.phase & ~(P_MASK<<16)); + + SETBITS( DMACNTRL0, INTEN ); + aha152x_done( DID_PARITY << 16 ); + return; + break; + + default: + printk("aha152x: unexpected phase\n"); + break; + } + + if(done) + { +#if defined(DEBUG_INTR) + if(aha152x_debug & debug_intr) + printk("command done.\n"); +#endif +#if defined(DEBUG_RACE) + leave_driver("(done) intr"); +#endif + + SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 ); + SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0); + SETPORT(SCSISEQ, disconnected_SC ? ENRESELI : 0 ); + + SETBITS( DMACNTRL0, INTEN ); + + aha152x_done( (current_SC->SCp.Status & 0xff) + | ( (current_SC->SCp.Message & 0xff) << 8) + | ( DID_OK << 16) ); + +#if defined(DEBUG_RACE) + printk("done returned (DID_OK: Status=%x; Message=%x).\n", + current_SC->SCp.Status, current_SC->SCp.Message); +#endif + return; + } + + if(current_SC) + current_SC->SCp.phase |= 1<<16 ; + + SETPORT( SIMODE0, 0 ); + SETPORT( SIMODE1, ENPHASEMIS|ENBUSFREE ); +#if defined(DEBUG_INTR) + if(aha152x_debug & debug_intr) + disp_enintr(); +#endif +#if defined(DEBUG_RACE) + leave_driver("(PHASEEND) intr"); +#endif + + SETBITS( DMACNTRL0, INTEN); + return; +} + +/* + * Dump the current driver status and panic... + */ +static void aha152x_panic(char *msg) +{ + printk("\naha152x_panic: %s\n", msg); + show_queues(); + panic("aha152x panic"); +} + +/* + * Display registers of AIC-6260 + */ +static void disp_ports(void) +{ +#ifdef DEBUG_AHA152X + int s; + + if(aha152x_debug & debug_skipports) + return; + + printk("\n%s: ", current_SC ? "on bus" : "waiting"); + + s=GETPORT(SCSISEQ); + printk("SCSISEQ ( "); + if( s & TEMODEO ) printk("TARGET MODE "); + if( s & ENSELO ) printk("SELO "); + if( s & ENSELI ) printk("SELI "); + if( s & ENRESELI ) printk("RESELI "); + if( s & ENAUTOATNO ) printk("AUTOATNO "); + if( s & ENAUTOATNI ) printk("AUTOATNI "); + if( s & ENAUTOATNP ) printk("AUTOATNP "); + if( s & SCSIRSTO ) printk("SCSIRSTO "); + printk(");"); + + printk(" SCSISIG ( "); + s=GETPORT(SCSISIG); + switch(s & P_MASK) + { + case P_DATAO: + printk("DATA OUT"); + break; + case P_DATAI: + printk("DATA IN"); + break; + case P_CMD: + printk("COMMAND"); + break; + case P_STATUS: + printk("STATUS"); + break; + case P_MSGO: + printk("MESSAGE OUT"); + break; + case P_MSGI: + printk("MESSAGE IN"); + break; + default: + printk("*illegal*"); + break; + } + + printk(" ); "); + + printk("INTSTAT ( %s ); ", TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo"); + + printk("SSTAT ( "); + s=GETPORT(SSTAT0); + if( s & TARGET ) printk("TARGET "); + if( s & SELDO ) printk("SELDO "); + if( s & SELDI ) printk("SELDI "); + if( s & SELINGO ) printk("SELINGO "); + if( s & SWRAP ) printk("SWRAP "); + if( s & SDONE ) printk("SDONE "); + if( s & SPIORDY ) printk("SPIORDY "); + if( s & DMADONE ) printk("DMADONE "); + + s=GETPORT(SSTAT1); + if( s & SELTO ) printk("SELTO "); + if( s & ATNTARG ) printk("ATNTARG "); + if( s & SCSIRSTI ) printk("SCSIRSTI "); + if( s & PHASEMIS ) printk("PHASEMIS "); + if( s & BUSFREE ) printk("BUSFREE "); + if( s & SCSIPERR ) printk("SCSIPERR "); + if( s & PHASECHG ) printk("PHASECHG "); + if( s & REQINIT ) printk("REQINIT "); + printk("); "); + + + printk("SSTAT ( "); + + s=GETPORT(SSTAT0) & GETPORT(SIMODE0); + + if( s & TARGET ) printk("TARGET "); + if( s & SELDO ) printk("SELDO "); + if( s & SELDI ) printk("SELDI "); + if( s & SELINGO ) printk("SELINGO "); + if( s & SWRAP ) printk("SWRAP "); + if( s & SDONE ) printk("SDONE "); + if( s & SPIORDY ) printk("SPIORDY "); + if( s & DMADONE ) printk("DMADONE "); + + s=GETPORT(SSTAT1) & GETPORT(SIMODE1); + + if( s & SELTO ) printk("SELTO "); + if( s & ATNTARG ) printk("ATNTARG "); + if( s & SCSIRSTI ) printk("SCSIRSTI "); + if( s & PHASEMIS ) printk("PHASEMIS "); + if( s & BUSFREE ) printk("BUSFREE "); + if( s & SCSIPERR ) printk("SCSIPERR "); + if( s & PHASECHG ) printk("PHASECHG "); + if( s & REQINIT ) printk("REQINIT "); + printk("); "); + + printk("SXFRCTL0 ( "); + + s=GETPORT(SXFRCTL0); + if( s & SCSIEN ) printk("SCSIEN "); + if( s & DMAEN ) printk("DMAEN "); + if( s & CH1 ) printk("CH1 "); + if( s & CLRSTCNT ) printk("CLRSTCNT "); + if( s & SPIOEN ) printk("SPIOEN "); + if( s & CLRCH1 ) printk("CLRCH1 "); + printk("); "); + + printk("SIGNAL ( "); + + s=GETPORT(SCSISIG); + if( s & ATNI ) printk("ATNI "); + if( s & SELI ) printk("SELI "); + if( s & BSYI ) printk("BSYI "); + if( s & REQI ) printk("REQI "); + if( s & ACKI ) printk("ACKI "); + printk("); "); + + printk("SELID ( %02x ), ", GETPORT(SELID) ); + + printk("SSTAT2 ( "); + + s=GETPORT(SSTAT2); + if( s & SOFFSET) printk("SOFFSET "); + if( s & SEMPTY) printk("SEMPTY "); + if( s & SFULL) printk("SFULL "); + printk("); SFCNT ( %d ); ", s & (SFULL|SFCNT) ); + +#if 0 + printk("SSTAT4 ( "); + s=GETPORT(SSTAT4); + if( s & SYNCERR) printk("SYNCERR "); + if( s & FWERR) printk("FWERR "); + if( s & FRERR) printk("FRERR "); + printk("); "); +#endif + + printk("FCNT ( %d ); ", GETPORT(FIFOSTAT) ); + + printk("DMACNTRL0 ( "); + s=GETPORT(DMACNTRL0); + printk( "%s ", s & _8BIT ? "8BIT" : "16BIT" ); + printk( "%s ", s & DMA ? "DMA" : "PIO" ); + printk( "%s ", s & WRITE_READ ? "WRITE" : "READ" ); + if( s & ENDMA ) printk("ENDMA "); + if( s & INTEN ) printk("INTEN "); + if( s & RSTFIFO ) printk("RSTFIFO "); + if( s & SWINT ) printk("SWINT "); + printk("); "); + + +#if 0 + printk("DMACNTRL1 ( "); + + s=GETPORT(DMACNTRL1); + if( s & PWRDWN ) printk("PWRDN "); + printk("); "); + + + printk("STK ( %d ); ", s & 0xf); + + printk("DMASTAT ("); + s=GETPORT(DMASTAT); + if( s & ATDONE ) printk("ATDONE "); + if( s & WORDRDY ) printk("WORDRDY "); + if( s & DFIFOFULL ) printk("DFIFOFULL "); + if( s & DFIFOEMP ) printk("DFIFOEMP "); + printk(")"); + +#endif + + printk("\n"); +#endif +} + +/* + * display enabled interrupts + */ +static void disp_enintr(void) +{ + int s; + + printk("enabled interrupts ( "); + + s=GETPORT(SIMODE0); + if( s & ENSELDO ) printk("ENSELDO "); + if( s & ENSELDI ) printk("ENSELDI "); + if( s & ENSELINGO ) printk("ENSELINGO "); + if( s & ENSWRAP ) printk("ENSWRAP "); + if( s & ENSDONE ) printk("ENSDONE "); + if( s & ENSPIORDY ) printk("ENSPIORDY "); + if( s & ENDMADONE ) printk("ENDMADONE "); + + s=GETPORT(SIMODE1); + if( s & ENSELTIMO ) printk("ENSELTIMO "); + if( s & ENATNTARG ) printk("ENATNTARG "); + if( s & ENPHASEMIS ) printk("ENPHASEMIS "); + if( s & ENBUSFREE ) printk("ENBUSFREE "); + if( s & ENSCSIPERR ) printk("ENSCSIPERR "); + if( s & ENPHASECHG ) printk("ENPHASECHG "); + if( s & ENREQINIT ) printk("ENREQINIT "); + printk(")\n"); +} + +#if defined(DEBUG_RACE) + +static const char *should_leave; +static int in_driver=0; + +/* + * Only one routine can be in the driver at once. + */ +static void enter_driver(const char *func) +{ + cli(); + printk("aha152x: entering %s() (%x)\n", func, jiffies); + if(in_driver) + { + printk("%s should leave first.\n", should_leave); + panic("aha152x: already in driver\n"); + } + + in_driver++; + should_leave=func; + sti(); +} + +static void leave_driver(const char *func) +{ + cli(); + printk("\naha152x: leaving %s() (%x)\n", func, jiffies); + if(!in_driver) + { + printk("aha152x: %s already left.\n", should_leave); + panic("aha152x: %s already left driver.\n"); + } + + in_driver--; + should_leave=func; + sti(); +} +#endif + +/* + * Show the command data of a command + */ +static void show_command(Scsi_Cmnd *ptr) +{ + printk("0x%08x: target=%d; lun=%d; cmnd=( ", + (unsigned int) ptr, ptr->target, ptr->lun); + + print_command(ptr->cmnd); + + printk("); residual=%d; buffers=%d; phase |", + ptr->SCp.this_residual, ptr->SCp.buffers_residual); + + if( ptr->SCp.phase & not_issued ) printk("not issued|"); + if( ptr->SCp.phase & in_selection ) printk("in selection|"); + if( ptr->SCp.phase & disconnected ) printk("disconnected|"); + if( ptr->SCp.phase & aborted ) printk("aborted|"); + if( ptr->SCp.phase & sent_ident ) printk("send_ident|"); + if( ptr->SCp.phase & in_other ) + { + printk("; in other("); + switch( (ptr->SCp.phase >> 16) & P_MASK ) + { + case P_DATAO: + printk("DATA OUT"); + break; + case P_DATAI: + printk("DATA IN"); + break; + case P_CMD: + printk("COMMAND"); + break; + case P_STATUS: + printk("STATUS"); + break; + case P_MSGO: + printk("MESSAGE OUT"); + break; + case P_MSGI: + printk("MESSAGE IN"); + break; + default: + printk("*illegal*"); + break; + } + printk(")"); + if(ptr->SCp.phase & (1<<16)) + printk("; phaseend"); + } + printk("; next=0x%08x\n", (unsigned int) ptr->host_scribble); +} + +/* + * Dump the queued data + */ +static void show_queues(void) +{ + Scsi_Cmnd *ptr; + + cli(); + printk("QUEUE STATUS:\nissue_SC:\n"); + for(ptr=issue_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble ) + show_command(ptr); + + printk("current_SC:\n"); + if(current_SC) + show_command(current_SC); + else + printk("none\n"); + + printk("disconnected_SC:\n"); + for(ptr=disconnected_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble ) + show_command(ptr); + + disp_ports(); + disp_enintr(); + sti(); +} |