58217f5900
WOO HOO!
2377 lines
63 KiB
C
2377 lines
63 KiB
C
/* aha152x.c -- Adaptec AHA-152x driver
|
|
* Author: Juergen E. Fischer, fischer@server.et-inf.fho-emden.de
|
|
* Copyright 1993 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 0.101 1993/12/13 01:16:27 root Exp $
|
|
*
|
|
|
|
* $Log: aha152x.c,v $
|
|
* 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
|
|
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>
|
|
|
|
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 value: 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 (eric@tantalus.nrl.navy.mil)
|
|
|
|
special thanks to Eric Youngdale for the free(!) supplying the
|
|
documentation on the chip.
|
|
|
|
**************************************************************************/
|
|
|
|
#include "aha152x.h"
|
|
|
|
#include <linux/sched.h>
|
|
#include <asm/io.h>
|
|
#include "../block/blk.h"
|
|
#include "scsi.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>
|
|
|
|
/* 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
|
|
#endif
|
|
|
|
/* I use this when I'm looking for weird bugs */
|
|
#define DEBUG_TIMING
|
|
|
|
#if defined(DEBUG)
|
|
|
|
#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
|
|
#define DEBUG_PHASES
|
|
#define DEBUG_DATAI
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#define DEBUG_RESET /* resets should be rare */
|
|
#define DEBUG_ABORT /* aborts too */
|
|
|
|
/* END OF DEFINES */
|
|
|
|
/* some additional "phases" for getphase() */
|
|
#define P_BUSFREE 1
|
|
#define P_PARITY 2
|
|
|
|
char *aha152x_id = "Adaptec 152x SCSI driver; $Revision: 0.101 $\n";
|
|
|
|
static int port_base = 0;
|
|
static int this_host = 0;
|
|
static int can_disconnect = 0;
|
|
static int commands = 0;
|
|
|
|
/* 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 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 struct wait_queue *abortion_complete;
|
|
static int 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,
|
|
};
|
|
#define ADDRESS_COUNT (sizeof( addresses ) / sizeof( unsigned ))
|
|
|
|
/* possible i/o adresses 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-6260 based controllers */
|
|
static struct signature {
|
|
char *signature;
|
|
int sig_offset;
|
|
int sig_length;
|
|
} signatures[] =
|
|
{
|
|
{
|
|
"Adaptec AHA-1520 BIOS\r\n\0\
|
|
Version 1.4 \r\n\0\
|
|
Copyright 1990 Adaptec, Inc.\r\n\
|
|
All Rights Reserved\r\n \r\n \r\n", 0x102e, 101
|
|
}, /* Adaptec 152x */
|
|
{
|
|
"Adaptec ASW-B626 BIOS\r\n\0\
|
|
Version 1.0 \r\n\0\
|
|
Copyright 1990 Adaptec, Inc.\r\n\
|
|
All Rights Reserved\r\n\0 \r\n \r\n", 0x1029, 102
|
|
}, /* on-board controller */
|
|
{ "Adaptec BIOS: ASW-B626", 0x0F, 22}, /* on-board controller */
|
|
{ "Adaptec ASW-B626 S2 BIOS", 0x2e6c, 24}, /* on-board controller */
|
|
};
|
|
#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;
|
|
|
|
if(ints[0] != 4)
|
|
return;
|
|
|
|
setup_portbase = ints[1];
|
|
setup_irq = ints[2];
|
|
setup_scsiid = ints[3];
|
|
setup_reconnect = ints[4];
|
|
}
|
|
|
|
/*
|
|
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(int hostno)
|
|
{
|
|
int i, j, ok;
|
|
aha152x_config conf;
|
|
struct sigaction sa;
|
|
int interrupt_level;
|
|
|
|
#if defined(DEBUG_RACE)
|
|
enter_driver("detect");
|
|
#endif
|
|
|
|
printk("aha152x: Probing: ");
|
|
|
|
if(setup_called)
|
|
{
|
|
printk("processing commandline: ");
|
|
|
|
if(setup_called!=4)
|
|
{
|
|
printk("\naha152x: %s\n", setup_str );
|
|
printk("aha152x: usage: aha152x=<PORTBASE>,<IRQ>,<SCSI ID>,<RECONNECT>\n");
|
|
panic("aha152x panics in line %d", __LINE__);
|
|
}
|
|
|
|
port_base = setup_portbase;
|
|
interrupt_level = setup_irq;
|
|
this_host = setup_scsiid;
|
|
can_disconnect = setup_reconnect;
|
|
|
|
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__);
|
|
}
|
|
printk("ok, ");
|
|
}
|
|
else
|
|
{
|
|
#if !defined(SKIP_BIOSTEST)
|
|
printk("BIOS test: ");
|
|
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)
|
|
{
|
|
#if defined(DEBUG_RACE)
|
|
leave_driver("(1) detect");
|
|
#endif
|
|
printk("failed\n");
|
|
return 0;
|
|
}
|
|
printk("ok, ");
|
|
#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");
|
|
#if defined(DEBUG_RACE)
|
|
leave_driver("(2) detect");
|
|
#endif
|
|
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;
|
|
|
|
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
|
|
}
|
|
|
|
printk("detection complete\n");
|
|
|
|
sa.sa_handler = aha152x_intr;
|
|
sa.sa_flags = SA_INTERRUPT;
|
|
sa.sa_mask = 0;
|
|
sa.sa_restorer = NULL;
|
|
|
|
ok = irqaction( interrupt_level, &sa);
|
|
|
|
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 );
|
|
scsi_hosts[hostno].this_id=this_host;
|
|
|
|
if(can_disconnect)
|
|
scsi_hosts[hostno].can_queue=AHA152X_MAXQUEUE;
|
|
|
|
/* RESET OUT */
|
|
SETBITS(SCSISEQ, SCSIRSTO );
|
|
do_pause(5);
|
|
CLRBITS(SCSISEQ, SCSIRSTO );
|
|
do_pause(10);
|
|
|
|
aha152x_reset(NULL);
|
|
|
|
printk("aha152x: vital data: PORTBASE=0x%03x, IRQ=%d, SCSI ID=%d, reconnect=%s, parity=enabled\n",
|
|
port_base, interrupt_level, this_host, can_disconnect ? "enabled" : "disabled" );
|
|
|
|
snarf_region(port_base, TEST-SCSISEQ); /* Register */
|
|
|
|
/* not expecting any interrupts */
|
|
SETPORT(SIMODE0, 0);
|
|
SETPORT(SIMODE1, 0);
|
|
|
|
#if defined(DEBUG_RACE)
|
|
leave_driver("(3) detect");
|
|
#endif
|
|
|
|
SETBITS( DMACNTRL0, INTEN);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* return the name of the thing
|
|
*/
|
|
const char *aha152x_info(void)
|
|
{
|
|
#if defined(DEBUG_RACE)
|
|
enter_driver("info");
|
|
leave_driver("info");
|
|
#else
|
|
#if defined(DEBUG_INFO)
|
|
printk("\naha152x: info()\n");
|
|
#endif
|
|
#endif
|
|
return(aha152x_id);
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
printk("aha152x: queue(), ");
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#if defined(DEBUG_QUEUE)
|
|
printk( "SCpnt (target = %d lun = %d cmnd = 0x%02x pieces = %d size = %u), ",
|
|
SCpnt->target,
|
|
SCpnt->lun,
|
|
*(unsigned char *)SCpnt->cmnd,
|
|
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)
|
|
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();
|
|
|
|
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, int code )
|
|
{
|
|
Scsi_Cmnd *ptr, *prev;
|
|
|
|
cli();
|
|
|
|
#if defined(DEBUG_ABORT)
|
|
printk("aha152x: abort(), SCpnt=0x%08x, ", (unsigned long) SCpnt );
|
|
#endif
|
|
|
|
show_queues();
|
|
|
|
/* 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 = (code ? code : DID_ABORT ) << 16;
|
|
ptr->done(ptr);
|
|
return 0;
|
|
}
|
|
|
|
/* Fail abortion, if we're on the bus */
|
|
if (current_SC)
|
|
{
|
|
sti();
|
|
return -1;
|
|
}
|
|
|
|
/* 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 && TESTLO(SSTAT1, BUSFREE) )
|
|
printk("bus busy but no current command, ");
|
|
|
|
if(ptr && TESTHI(SSTAT1, BUSFREE) )
|
|
{
|
|
/* dequeue */
|
|
if(prev)
|
|
prev->host_scribble = ptr->host_scribble;
|
|
else
|
|
issue_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=0;
|
|
sti();
|
|
|
|
/* sleep until the abortion is complete */
|
|
sleep_on( &abortion_complete );
|
|
return abort_result;
|
|
}
|
|
else
|
|
printk("aha152x: bus busy but no current command\n");
|
|
|
|
/* command wasn't found */
|
|
sti();
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
int aha152x_reset(Scsi_Cmnd * __unused)
|
|
{
|
|
Scsi_Cmnd *ptr;
|
|
|
|
aha152x_reset_ports();
|
|
|
|
/* Reset, if bus hangs */
|
|
if( TESTLO( SSTAT1, BUSFREE ) )
|
|
{
|
|
CLRBITS( DMACNTRL0, INTEN );
|
|
|
|
#if defined( DEBUG_RESET )
|
|
printk("aha152x: reset(), bus not free: SCSI RESET OUT\n");
|
|
#endif
|
|
|
|
show_queues();
|
|
|
|
if(current_SC)
|
|
{
|
|
current_SC->host_scribble = NULL;
|
|
current_SC->result = DID_RESET << 16;
|
|
current_SC->done(current_SC);
|
|
current_SC=NULL;
|
|
}
|
|
|
|
while(disconnected_SC)
|
|
{
|
|
ptr = disconnected_SC;
|
|
disconnected_SC = (Scsi_Cmnd *) ptr->host_scribble;
|
|
ptr->host_scribble = NULL;
|
|
ptr->result = DID_RESET << 16;
|
|
ptr->done(ptr);
|
|
}
|
|
|
|
/* RESET OUT */
|
|
SETPORT(SCSISEQ, SCSIRSTO);
|
|
do_pause(5);
|
|
SETPORT(SCSISEQ, 0);
|
|
do_pause(10);
|
|
|
|
SETPORT(SIMODE0, 0 );
|
|
SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0);
|
|
|
|
SETPORT( DMACNTRL0, INTEN );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Return the "logical geometry"
|
|
*/
|
|
int aha152x_biosparam( int size, int dev, int *info_array )
|
|
{
|
|
#if defined(DEBUG_RACE)
|
|
enter_driver("biosparam");
|
|
#else
|
|
#if defined(DEBUG_BIOSPARAM)
|
|
printk("\naha152x: biosparam(), ");
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(DEBUG_BIOSPARAM)
|
|
printk("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)
|
|
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
|
|
|
|
#if defined(DEBUG_RACE)
|
|
leave_driver("biosparam");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Internal done function
|
|
*/
|
|
void aha152x_done( int error )
|
|
{
|
|
Scsi_Cmnd *done_SC;
|
|
|
|
#if defined(DEBUG_DONE)
|
|
printk("\naha152x: done(), ");
|
|
disp_ports();
|
|
#endif
|
|
|
|
if (current_SC)
|
|
{
|
|
#if defined(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)
|
|
printk("ok (%d), ", commands);
|
|
#endif
|
|
sti();
|
|
|
|
SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 );
|
|
SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0);
|
|
|
|
#if defined(DEBUG_PHASES)
|
|
printk("BUS FREE loop, ");
|
|
#endif
|
|
while( TESTLO( SSTAT1, BUSFREE ) )
|
|
;
|
|
#if defined(DEBUG_PHASES)
|
|
printk("BUS FREE\n");
|
|
#endif
|
|
|
|
done_SC->result = error;
|
|
if(done_SC->scsi_done)
|
|
{
|
|
#if defined(DEBUG_DONE)
|
|
printk("calling scsi_done, ");
|
|
#endif
|
|
done_SC->scsi_done( done_SC );
|
|
#if defined(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)
|
|
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 occuping 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)
|
|
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)
|
|
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)
|
|
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)
|
|
printk("identify=%02x, lun=%d, ", identify_msg, identify_msg & 0x3f );
|
|
#endif
|
|
|
|
cli();
|
|
#if defined(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 );
|
|
#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)
|
|
printk("i-, ");
|
|
#endif
|
|
current_SC = remove_first_SC( &issue_SC );
|
|
sti();
|
|
|
|
#if defined(DEBUG_INTR) || defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
|
|
printk("issueing command, ");
|
|
#endif
|
|
current_SC->SCp.phase = in_selection;
|
|
|
|
#if defined(DEBUG_INTR) || defined(DEBUG_SELECTION) || defined(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, ENSPCHK|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)
|
|
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=1;
|
|
wake_up( &abortion_complete );
|
|
}
|
|
|
|
aha152x_done( DID_NO_CONNECT << 16 );
|
|
return;
|
|
}
|
|
#if defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
|
|
printk("SELDO (SELID=%x), ", GETPORT(SELID));
|
|
#endif
|
|
|
|
/* selection was done */
|
|
SETPORT( SSTAT0, CLRSELDO );
|
|
|
|
#if defined(DEBUG_ABORT)
|
|
if(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 );
|
|
SETBITS( DMACNTRL0, INTEN);
|
|
return;
|
|
}
|
|
else
|
|
aha152x_panic("neither timeout nor selection\007");
|
|
else
|
|
{
|
|
#if defined(DEBUG_SELECTION) || defined(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)
|
|
printk("(ABORT) selection timeout, ");
|
|
#endif
|
|
abort_result=1;
|
|
wake_up( &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)
|
|
printk("MESSAGE OUT, ");
|
|
#endif
|
|
|
|
if( current_SC->SCp.phase & aborted )
|
|
{
|
|
#if defined(DEBUG_MSGO) || defined(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)
|
|
printk("IDENTIFY (reconnect=%s;lun=%d), ",
|
|
can_disconnect ? "enabled" : "disabled", current_SC->lun);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
message=MESSAGE_REJECT;
|
|
#if defined(DEBUG_MSGO)
|
|
printk("REJECT, ");
|
|
#endif
|
|
}
|
|
|
|
CLRBITS( SXFRCTL0, ENDMA);
|
|
|
|
SETPORT( SIMODE0, 0 );
|
|
SETPORT( SIMODE1, ENPHASEMIS|ENREQINIT );
|
|
|
|
/* 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=0;
|
|
wake_up( &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)
|
|
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 );
|
|
|
|
#if defined(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)
|
|
printk("DFIFOEMP, outsw (%d words), ",
|
|
COMMAND_SIZE(current_SC->cmnd[0])>>1);
|
|
disp_ports();
|
|
#endif
|
|
|
|
outsw( DATAPORT,
|
|
¤t_SC->cmnd,
|
|
COMMAND_SIZE(current_SC->cmnd[0])>>1 );
|
|
|
|
#if defined(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)
|
|
printk("sent %d/%d command bytes, ", GETSTCNT(),
|
|
COMMAND_SIZE(current_SC->cmnd[0]));
|
|
#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)
|
|
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)
|
|
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)
|
|
printk("inbound message ( COMMAND COMPLETE ), ");
|
|
#endif
|
|
done++;
|
|
break;
|
|
|
|
case MESSAGE_REJECT:
|
|
#if defined(DEBUG_MSGI) || defined(DEBUG_TIMING)
|
|
printk("inbound message ( MESSAGE REJECT ), ");
|
|
#endif
|
|
break;
|
|
|
|
case SAVE_POINTERS:
|
|
#if defined(DEBUG_MSGI)
|
|
printk("inbound message ( SAVE DATA POINTERS ), ");
|
|
#endif
|
|
break;
|
|
|
|
case EXTENDED_MESSAGE:
|
|
{
|
|
int i, code;
|
|
|
|
#if defined(DEBUG_MSGI)
|
|
printk("inbound message ( EXTENDED MESSAGE ), ");
|
|
#endif
|
|
make_acklow();
|
|
if(getphase()!=P_MSGI)
|
|
break;
|
|
|
|
i=GETPORT(SCSIBUS);
|
|
|
|
#if defined(DEBUG_MSGI)
|
|
printk("length (%d), ", i);
|
|
#endif
|
|
|
|
#if defined(DEBUG_MSGI)
|
|
printk("code ( ");
|
|
#endif
|
|
|
|
make_acklow();
|
|
if(getphase()!=P_MSGI)
|
|
break;
|
|
|
|
code = GETPORT(SCSIBUS);
|
|
|
|
switch( code )
|
|
{
|
|
case 0x00:
|
|
#if defined(DEBUG_MSGI)
|
|
printk("MODIFY DATA POINTER ");
|
|
#endif
|
|
SETPORT(SCSISIG, P_MSGI|ATNO);
|
|
break;
|
|
case 0x01:
|
|
#if defined(DEBUG_MSGI)
|
|
printk("SYNCHRONOUS DATA TRANSFER REQUEST ");
|
|
#endif
|
|
SETPORT(SCSISIG, P_MSGI|ATNO);
|
|
break;
|
|
case 0x02:
|
|
#if defined(DEBUG_MSGI)
|
|
printk("EXTENDED IDENTIFY ");
|
|
#endif
|
|
break;
|
|
case 0x03:
|
|
#if defined(DEBUG_MSGI)
|
|
printk("WIDE DATA TRANSFER REQUEST ");
|
|
#endif
|
|
SETPORT(SCSISIG, P_MSGI|ATNO);
|
|
break;
|
|
default:
|
|
#if defined(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)
|
|
printk(" ), data ( ");
|
|
#endif
|
|
while( --i && (make_acklow(), getphase()==P_MSGI))
|
|
{
|
|
#if defined(DEBUG_MSGI)
|
|
printk("%x ", GETPORT(SCSIBUS) );
|
|
#else
|
|
GETPORT(SCSIBUS);
|
|
#endif
|
|
}
|
|
#if defined(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)
|
|
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)
|
|
printk("STATUS, ");
|
|
#endif
|
|
SETPORT( SXFRCTL0, CH1);
|
|
|
|
SETPORT( SIMODE0, 0 );
|
|
SETPORT( SIMODE1, ENREQINIT );
|
|
|
|
if( TESTHI( SSTAT1, PHASEMIS ) )
|
|
printk("aha152x: passing STATUS phase");
|
|
|
|
current_SC->SCp.Status = GETPORT( SCSIBUS );
|
|
make_acklow();
|
|
getphase();
|
|
|
|
#if defined(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)
|
|
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)
|
|
printk("expecting data, ");
|
|
#endif
|
|
/* wait for PHASEMIS or full FIFO */
|
|
while( TESTLO ( DMASTAT, DFIFOFULL|INTSTAT ) )
|
|
;
|
|
|
|
if( TESTHI( DMASTAT, DFIFOFULL ) )
|
|
fifodata=132;
|
|
else
|
|
{
|
|
/* wait for SCSI fifo to get empty */
|
|
while( TESTLO( SSTAT2, SEMPTY ) )
|
|
;
|
|
|
|
/* rest of data in FIFO */
|
|
fifodata=GETPORT(FIFOSTAT);
|
|
#if defined(DEBUG_DATAI)
|
|
printk("last transfer, ");
|
|
#endif
|
|
done=1;
|
|
}
|
|
|
|
#if defined(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)
|
|
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--;
|
|
}
|
|
else
|
|
{
|
|
CLRBITS(DMACNTRL0, _8BIT );
|
|
data_count >>= 1; /* Number of words */
|
|
insw( DATAPORT, current_SC->SCp.ptr, data_count );
|
|
#if defined(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;
|
|
}
|
|
}
|
|
|
|
/* rare (but possible) status bytes (probably also DISCONNECT
|
|
messages) get transfered in the data phase, so I assume 1
|
|
additional byte is ok */
|
|
if(fifodata>1)
|
|
{
|
|
printk("aha152x: more data than expected (%d bytes)\n",
|
|
GETPORT(FIFOSTAT));
|
|
}
|
|
|
|
#if defined(DEBUG_DATAI)
|
|
if(!fifodata)
|
|
printk("fifo empty, ");
|
|
else
|
|
printk("something left in fifo, ");
|
|
#endif
|
|
}
|
|
|
|
#if defined(DEBUG_DATAI)
|
|
if(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)
|
|
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)
|
|
printk("DATA OUT, ");
|
|
#endif
|
|
#if defined(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 );
|
|
|
|
/* 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)
|
|
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)
|
|
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--;
|
|
}
|
|
else
|
|
{
|
|
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)
|
|
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)
|
|
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)
|
|
printk("waiting for SCSI fifo to get empty, ");
|
|
#endif
|
|
/* wait for SCSI fifo to get empty */
|
|
while( TESTLO( SSTAT2, SEMPTY ) )
|
|
;
|
|
#if defined(DEBUG_DATAO)
|
|
printk("ok, ");
|
|
#endif
|
|
|
|
#if defined(DEBUG_DATAO)
|
|
printk("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)
|
|
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)
|
|
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)
|
|
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 );
|
|
#if defined(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)
|
|
{
|
|
#if !defined(SKIP_PORTS)
|
|
int s;
|
|
|
|
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)
|
|
{
|
|
int i;
|
|
|
|
printk("0x%08x: target=%d; lun=%d; cmnd=( ",
|
|
(unsigned long) ptr, ptr->target, ptr->lun);
|
|
|
|
for(i=0; i<COMMAND_SIZE(ptr->cmnd[0]); i++)
|
|
printk("%02x ", ptr->cmnd[i]);
|
|
|
|
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 long) 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();
|
|
}
|