/* aha152x.c -- Adaptec AHA-152x driver
 * Author: Jürgen E. Fischer, fischer@et-inf.fho-emden.de
 * Copyright 1993, 1994, 1995, 1996 Jürgen 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.18 1996/09/07 20:10:40 fischer Exp $
 *
 * $Log: aha152x.c,v $
 * Revision 1.18  1996/09/07 20:10:40  fischer
 * - fixed can_queue handling (multiple outstanding commands working again)
 *
 * Revision 1.17  1996/08/17 16:05:14  fischer
 * - biosparam improved
 * - interrupt verification
 * - updated documentation
 * - cleanups
 *
 * Revision 1.16  1996/06/09 00:04:56  root
 * - added configuration symbols for insmod (aha152x/aha152x1)
 *
 * Revision 1.15  1996/04/30 14:52:06  fischer
 * - proc info fixed
 * - support for extended translation for >1GB disks
 *
 * Revision 1.14  1996/01/17  15:11:20  fischer
 * - fixed lockup in MESSAGE IN phase after reconnection
 *
 * Revision 1.13  1996/01/09  02:15:53  fischer
 * - some cleanups
 * - moved request_irq behind controller initialization
 *   (to avoid spurious interrupts)
 *
 * Revision 1.12  1995/12/16  12:26:07  fischer
 * - barrier()s added
 * - configurable RESET delay added
 *
 * Revision 1.11  1995/12/06  21:18:35  fischer
 * - some minor updates
 *
 * Revision 1.10  1995/07/22  19:18:45  fischer
 * - support for 2 controllers
 * - started synchronous data transfers (not working yet)
 *
 * Revision 1.9  1995/03/18  09:20:24  root
 * - patches for PCMCIA and modules
 *
 * Revision 1.8  1995/01/21  22:07:19  root
 * - snarf_region => request_region
 * - aha152x_intr interface change
 *
 * Revision 1.7  1995/01/02  23:19:36  root
 * - updated COMMAND_SIZE to cmd_len
 * - changed sti() to restore_flags()
 * - fixed some #ifdef which generated warnings
 *
 * 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.


 CONFIGURATION ARGUMENTS:

 IOPORT        base io address                           (0x340/0x140)
 IRQ           interrupt level                           (9-12; default 11)
 SCSI_ID       scsi id of controller                     (0-7; default 7)
 RECONNECT     allow targets to disconnect from the bus  (0/1; default 1 [on])
 PARITY        enable parity checking                    (0/1; default 1 [on])
 SYNCHRONOUS   enable synchronous transfers              (0/1; default 0 [off])
 (NOT WORKING YET)
 DELAY:        bus reset delay                           (default 100)
 EXT_TRANS:    enable extended translation               (0/1: default 0 [off])
 (see NOTES below)

 COMPILE TIME CONFIGURATION (put into AHA152X in drivers/scsi/Makefile):

 -DAUTOCONF
 use configuration the controller reports (AHA-152x only)

 -DSKIP_BIOSTEST
 Don't test for BIOS signature (AHA-1510 or disabled BIOS)

 -DSETUP0="{ IOPORT, IRQ, SCSI_ID, RECONNECT, PARITY, SYNCHRONOUS, DELAY, EXT_TRANS }"
 override for the first controller 

 -DSETUP1="{ IOPORT, IRQ, SCSI_ID, RECONNECT, PARITY, SYNCHRONOUS, DELAY, EXT_TRANS }"
 override for the second controller


 LILO COMMAND LINE OPTIONS:

 aha152x=<IOPORT>[,<IRQ>[,<SCSI-ID>[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY> [,<EXT_TRANS]]]]]]]

 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 supported under normal
 operation.  If you think that you need other values: contact me.
 For two controllers use the aha152x statement twice.


 SYMBOLS FOR MODULE CONFIGURATION:

 aha152x=IOPORT,IRQ,SCSI_ID,RECONNECT,PARITY,SYNCHRONOUS,DELAY,EXT_TRANS
 configuration override of first controller


 aha152x1=IOPORT,IRQ,SCSI_ID,RECONNECT,PARITY,SYNCHRONOUS,DELAY,EXT_TRANS
 configuration override of second controller


 NOTES ON EXT_TRANS: 

 SCSI uses block numbers to address blocks/sectors on a device.
 The BIOS uses a cylinder/head/sector addressing scheme (C/H/S)
 scheme instead.  DOS expects a BIOS or driver that understands this
 C/H/S addressing.

 The number of cylinders/heads/sectors is called geometry and is required
 as base for requests in C/H/S addressing.  SCSI only knows about the
 total capacity of disks in blocks (sectors).

 Therefore the SCSI BIOS/DOS driver has to calculate a logical/virtual
 geometry just to be able to support that addressing scheme.  The geometry
 returned by the SCSI BIOS is a pure calculation and has nothing to
 do with the real/physical geometry of the disk (which is usually
 irrelevant anyway).

 Basically this has no impact at all on Linux, because it also uses block
 instead of C/H/S addressing.  Unfortunately C/H/S addressing is also used
 in the partition table and therefore every operating system has to know
 the right geometry to be able to interpret it.

 Moreover there are certain limitations to the C/H/S addressing scheme,
 namely the address space is limited to upto 255 heads, upto 63 sectors
 and a maximum of 1023 cylinders.

 The AHA-1522 BIOS calculates the geometry by fixing the number of heads
 to 64, the number of sectors to 32 and by calculating the number of
 cylinders by dividing the capacity reported by the disk by 64*32 (1 MB).
 This is considered to be the default translation.

 With respect to the limit of 1023 cylinders using C/H/S you can only
 address the first GB of your disk in the partition table.  Therefore
 BIOSes of some newer controllers based on the AIC-6260/6360 support
 extended translation.  This means that the BIOS uses 255 for heads,
 63 for sectors and then divides the capacity of the disk by 255*63
 (about 8 MB), as soon it sees a disk greater than 1 GB.  That results
 in a maximum of about 8 GB addressable diskspace in the partition table
 (but there are already bigger disks out there today).

 To make it even more complicated the translation mode might/might
 not be configurable in certain BIOS setups.

 This driver does some more or less failsafe guessing to get the
 geometry right in most cases:

 - for disks<1GB: use default translation (C/32/64)
 - for disks>1GB:
 - take current geometry from the partition table
 (using scsicam_bios_param and accept only `valid' geometries,
 ie. either (C/32/64) or (C/63/255)).  This can be extended
 translation even if it's not enabled in the driver.
 - if that fails, take extended translation if enabled by override,
 kernel or module parameter, otherwise take default translation and
 ask the user for verification.  This might on not yet partitioned
 disks or


 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.

 **************************************************************************/

#ifdef PCMCIA
#define MODULE
#endif

#include <linux/module.h>

#ifdef PCMCIA
#undef MODULE
#endif

#include <linux/sched.h>
#include <asm/io.h>
#include <linux/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 <linux/proc_fs.h>
#include <linux/interrupt.h>

#include "aha152x.h"
#include <linux/stat.h>

#include <scsi/scsicam.h>

struct proc_dir_entry proc_scsi_aha152x =
{
	PROC_SCSI_AHA152X, 7, "aha152x",
	S_IFDIR | S_IRUGO | S_IXUGO, 2
};

/* DEFINES */

/* For PCMCIA cards, always use AUTOCONF */
#if defined(PCMCIA) || defined(MODULE)
#if !defined(AUTOCONF)
#define AUTOCONF
#endif
#endif

#if !defined(AUTOCONF) && !defined(SETUP0)
#error define AUTOCONF or SETUP0
#endif

#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_SELECTION
#define DEBUG_PHASES
#define DEBUG_RESET
#define DEBUG_ABORT

#define DEBUG_DEFAULT (debug_reset|debug_abort)

#endif

/* END OF DEFINES */

extern long loops_per_sec;

#define DELAY_DEFAULT 100

/* some additional "phases" for getphase() */
#define P_BUSFREE  1
#define P_PARITY   2

/* possible irq range */
#ifdef PCMCIA
#define IRQ_MIN 0
#define IRQ_MAX 16
#else
#define IRQ_MIN 9
#define IRQ_MAX 12
#endif
#define IRQS    IRQ_MAX-IRQ_MIN+1

enum {
	not_issued = 0x0001,
	in_selection = 0x0002,
	disconnected = 0x0004,
	aborted = 0x0008,
	sent_ident = 0x0010,
	in_other = 0x0020,
	in_sync = 0x0040,
	sync_ok = 0x0080,
};

#if defined(MODULE)
#if defined(DEBUG_AHA152X)
int aha152x[] =
{0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0, DEBUG_DEFAULT};
int aha152x1[] =
{0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0, DEBUG_DEFAULT};
MODULE_PARM(aha152x, "1-9i");
MODULE_PARM(aha152x1, "1-9i");
#else
int aha152x[] =
{0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0};
int aha152x1[] =
{0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0};
MODULE_PARM(aha152x, "1-8i");
MODULE_PARM(aha152x1, "1-8i");
#endif
#endif

/* set by aha152x_setup according to the command line */
static int setup_count = 0;
static int registered_count = 0;
static struct aha152x_setup {
	int io_port;
	int irq;
	int scsiid;
	int reconnect;
	int parity;
	int synchronous;
	int delay;
	int ext_trans;
#ifdef DEBUG_AHA152X
	int debug;
#endif
	char *conf;
} setup[2];

static struct Scsi_Host *aha152x_host[IRQS];

#define HOSTDATA(shpnt)   ((struct aha152x_hostdata *) &shpnt->hostdata)
#define CURRENT_SC        (HOSTDATA(shpnt)->current_SC)
#define ISSUE_SC          (HOSTDATA(shpnt)->issue_SC)
#define DISCONNECTED_SC   (HOSTDATA(shpnt)->disconnected_SC)
#define DELAY             (HOSTDATA(shpnt)->delay)
#define EXT_TRANS         (HOSTDATA(shpnt)->ext_trans)
#define SYNCRATE          (HOSTDATA(shpnt)->syncrate[CURRENT_SC->target])
#define MSG(i)            (HOSTDATA(shpnt)->message[i])
#define MSGLEN            (HOSTDATA(shpnt)->message_len)
#define ADDMSG(x)         (MSG(MSGLEN++)=x)

struct aha152x_hostdata {
	Scsi_Cmnd *issue_SC;
	Scsi_Cmnd *current_SC;
	Scsi_Cmnd *disconnected_SC;
	int aborting;
	int abortion_complete;
	int abort_result;
	int commands;

	int reconnect;
	int parity;
	int synchronous;
	int delay;
	int ext_trans;

	int swint;
	int service;

	unsigned char syncrate[8];

	unsigned char message[256];
	int message_len;

#ifdef DEBUG_AHA152X
	int debug;
#endif
};

static void aha152x_intr(int irq, void *dev_id, struct pt_regs *);
void aha152x_done(struct Scsi_Host *shpnt, int error);
void aha152x_setup(char *str, int *ints);
int aha152x_checksetup(struct aha152x_setup *setup);

static void aha152x_reset_ports(struct Scsi_Host *shpnt);
static void aha152x_panic(struct Scsi_Host *shpnt, char *msg);

static void disp_ports(struct Scsi_Host *shpnt);
static void show_command(Scsi_Cmnd * ptr);
static void show_queues(struct Scsi_Host *shpnt);
static void disp_enintr(struct Scsi_Host *shpnt);

#if defined(DEBUG_RACE)
static void enter_driver(const char *);
static void leave_driver(const char *);
#endif

/* possible i/o addresses for the AIC-6260 */
static unsigned short ports[] =
{
	0x340,			/* default first */
	0x140
};
#define PORT_COUNT (sizeof(ports) / sizeof(unsigned short))

#if !defined(SKIP_BIOSTEST)
/* possible locations for the Adaptec BIOS */
static unsigned int addresses[] =
{
	0xdc000,		/* default first */
	0xc8000,
	0xcc000,
	0xd0000,
	0xd4000,
	0xd8000,
	0xe0000,
	0xeb800,		/* VTech Platinum SMP */
	0xf0000,
};
#define ADDRESS_COUNT (sizeof(addresses) / sizeof(unsigned int))

/* 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.  May be an information whether or not the BIOS supports
   extended translation could be also useful here. */
static struct signature {
	unsigned char *signature;
	int sig_offset;
	int sig_length;
} signatures[] =

{
	{
		"Adaptec AHA-1520 BIOS", 0x102e, 21
	},			/* Adaptec 152x */
	{
		"Adaptec AHA-1520B", 0x0b, 17
	},			/* Adaptec 152x rev B */
	{
		"Adaptec AHA-1520B", 0x26, 17
	},                      /* Iomega Jaz Jet ISA (AIC6370Q) */
	{
		"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  */
	{
		"GA-400 LOCAL BUS SCSI BIOS", 0x102e, 26
	},			/* Gigabyte Local-Bus-SCSI */
	{
		"Adaptec BIOS:AVA-282X", 0xc, 21
	},			/* Adaptec 282x */
	{
		"Adaptec IBM Dock II SCSI", 0x2edd, 24
	},			/* IBM Thinkpad Dock II */
	{
		"Adaptec BIOS:AHA-1532P", 0x1c, 22
	},			/* IBM Thinkpad Dock II SCSI */
	{
		"DTC3520A Host Adapter BIOS", 0x318a, 26
	},			/* DTC 3520A ISA SCSI */
};

#define SIGNATURE_COUNT (sizeof(signatures) / sizeof(struct signature))
#endif


static void do_pause(unsigned amount)
{				/* Pause for amount*10 milliseconds */
	unsigned long the_time = jiffies + amount;	/* 0.01 seconds per jiffy */

	while (time_before(jiffies, the_time))
		barrier();
}

/*
 *  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(struct Scsi_Host *shpnt)
{
	SETPORT(SXFRCTL0, CH1 | SPIOEN);
	GETPORT(SCSIDAT);
	SETPORT(SXFRCTL0, CH1);

	while (TESTHI(SCSISIG, ACKI))
		barrier();
}

/*
 * 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(struct Scsi_Host *shpnt)
{
	int phase, sstat1;

	while (1) {
		do {
			while (!((sstat1 = GETPORT(SSTAT1)) & (BUSFREE | SCSIRSTI | REQINIT)))
				barrier();
			if (sstat1 & BUSFREE)
				return P_BUSFREE;
			if (sstat1 & SCSIRSTI) {
				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(shpnt);
		} else
			return phase;
	}
}

/* called from init/main.c */
void aha152x_setup(char *str, int *ints)
{
	if (setup_count > 2)
		panic("aha152x: you can only configure up to two controllers\n");

	setup[setup_count].conf = str;
	setup[setup_count].io_port = ints[0] >= 1 ? ints[1] : 0x340;
	setup[setup_count].irq = ints[0] >= 2 ? ints[2] : 11;
	setup[setup_count].scsiid = ints[0] >= 3 ? ints[3] : 7;
	setup[setup_count].reconnect = ints[0] >= 4 ? ints[4] : 1;
	setup[setup_count].parity = ints[0] >= 5 ? ints[5] : 1;
	setup[setup_count].synchronous = ints[0] >= 6 ? ints[6] : 0 /* FIXME: 1 */ ;
	setup[setup_count].delay = ints[0] >= 7 ? ints[7] : DELAY_DEFAULT;
	setup[setup_count].ext_trans = ints[0] >= 8 ? ints[8] : 0;
#ifdef DEBUG_AHA152X
	setup[setup_count].debug = ints[0] >= 9 ? ints[9] : DEBUG_DEFAULT;
	if (ints[0] > 9) {
		printk("aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>"
		       "[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<EXT_TRANS>[,<DEBUG>]]]]]]]]\n");
#else
	if (ints[0] > 8) {
		printk("aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>"
		       "[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<EXT_TRANS>]]]]]]]\n");
#endif
	} else
		setup_count++;
}

/*
 * Test, if port_base is valid.
 */
static int aha152x_porttest(int io_port)
{
	int i;

	if (check_region(io_port, IO_RANGE))
		return 0;

	SETPORT(io_port + O_DMACNTRL1, 0);	/* reset stack pointer */
	for (i = 0; i < 16; i++)
		SETPORT(io_port + O_STACK, i);

	SETPORT(io_port + O_DMACNTRL1, 0);	/* reset stack pointer */
	for (i = 0; i < 16 && GETPORT(io_port + O_STACK) == i; i++);

	return (i == 16);
}

int aha152x_checksetup(struct aha152x_setup *setup)
{
	int i;

#ifndef PCMCIA
	for (i = 0; i < PORT_COUNT && (setup->io_port != ports[i]); i++);

	if (i == PORT_COUNT)
		return 0;
#endif

	if (!aha152x_porttest(setup->io_port))
		return 0;

	if ((setup->irq < IRQ_MIN) || (setup->irq > IRQ_MAX))
		return 0;

	if ((setup->scsiid < 0) || (setup->scsiid > 7))
		return 0;

	if ((setup->reconnect < 0) || (setup->reconnect > 1))
		return 0;

	if ((setup->parity < 0) || (setup->parity > 1))
		return 0;

	if ((setup->synchronous < 0) || (setup->synchronous > 1))
		return 0;

	if ((setup->ext_trans < 0) || (setup->ext_trans > 1))
		return 0;


	return 1;
}

void aha152x_swintr(int irqno, void *dev_id, struct pt_regs *regs)
{
	struct Scsi_Host *shpnt = aha152x_host[irqno - IRQ_MIN];

	if (!shpnt)
		panic("aha152x: catched software interrupt for unknown controller.\n");

	HOSTDATA(shpnt)->swint++;
}


int aha152x_detect(Scsi_Host_Template * tpnt)
{
	int i, j, ok;
#if defined(AUTOCONF)
	aha152x_config conf;
#endif

	tpnt->proc_dir = &proc_scsi_aha152x;

	for (i = 0; i < IRQS; i++)
		aha152x_host[i] = (struct Scsi_Host *) NULL;

	if (setup_count) {
		printk("aha152x: processing commandline: ");

		for (i = 0; i < setup_count; i++)
			if (!aha152x_checksetup(&setup[i])) {
				printk("\naha152x: %s\n", setup[i].conf);
				printk("aha152x: invalid line (controller=%d)\n", i + 1);
			}
		printk("ok\n");
	}
#ifdef SETUP0
	if (setup_count < 2) {
		struct aha152x_setup override = SETUP0;

		if (setup_count == 0 || (override.io_port != setup[0].io_port))
			if (!aha152x_checksetup(&override)) {
				printk("\naha152x: invalid override SETUP0={0x%x,%d,%d,%d,%d,%d,%d,%d}\n",
				       override.io_port,
				       override.irq,
				       override.scsiid,
				       override.reconnect,
				       override.parity,
				       override.synchronous,
				       override.delay,
				       override.ext_trans);
			} else
				setup[setup_count++] = override;
	}
#endif

#ifdef SETUP1
	if (setup_count < 2) {
		struct aha152x_setup override = SETUP1;

		if (setup_count == 0 || (override.io_port != setup[0].io_port))
			if (!aha152x_checksetup(&override)) {
				printk("\naha152x: invalid override SETUP1={0x%x,%d,%d,%d,%d,%d,%d,%d}\n",
				       override.io_port,
				       override.irq,
				       override.scsiid,
				       override.reconnect,
				       override.parity,
				       override.synchronous,
				       override.delay,
				       override.ext_trans);
			} else
				setup[setup_count++] = override;
	}
#endif

#if defined(MODULE)
	if (setup_count < 2 && aha152x[0] != 0) {
		setup[setup_count].conf = "";
		setup[setup_count].io_port = aha152x[0];
		setup[setup_count].irq = aha152x[1];
		setup[setup_count].scsiid = aha152x[2];
		setup[setup_count].reconnect = aha152x[3];
		setup[setup_count].parity = aha152x[4];
		setup[setup_count].synchronous = aha152x[5];
		setup[setup_count].delay = aha152x[6];
		setup[setup_count].ext_trans = aha152x[7];
#ifdef DEBUG_AHA152X
		setup[setup_count].debug = aha152x[8];
#endif
		if (aha152x_checksetup(&setup[setup_count]))
			setup_count++;
		else
			printk("\naha152x: invalid module argument aha152x=0x%x,%d,%d,%d,%d,%d,%d,%d\n",
			       setup[setup_count].io_port,
			       setup[setup_count].irq,
			       setup[setup_count].scsiid,
			       setup[setup_count].reconnect,
			       setup[setup_count].parity,
			       setup[setup_count].synchronous,
			       setup[setup_count].delay,
			       setup[setup_count].ext_trans);
	}
	if (setup_count < 2 && aha152x1[0] != 0) {
		setup[setup_count].conf = "";
		setup[setup_count].io_port = aha152x1[0];
		setup[setup_count].irq = aha152x1[1];
		setup[setup_count].scsiid = aha152x1[2];
		setup[setup_count].reconnect = aha152x1[3];
		setup[setup_count].parity = aha152x1[4];
		setup[setup_count].synchronous = aha152x1[5];
		setup[setup_count].delay = aha152x1[6];
		setup[setup_count].ext_trans = aha152x1[7];
#ifdef DEBUG_AHA152X
		setup[setup_count].debug = aha152x1[8];
#endif
		if (aha152x_checksetup(&setup[setup_count]))
			setup_count++;
		else
			printk("\naha152x: invalid module argument aha152x1=0x%x,%d,%d,%d,%d,%d,%d,%d\n",
			       setup[setup_count].io_port,
			       setup[setup_count].irq,
			       setup[setup_count].scsiid,
			       setup[setup_count].reconnect,
			       setup[setup_count].parity,
			       setup[setup_count].synchronous,
			       setup[setup_count].delay,
			       setup[setup_count].ext_trans);
	}
#endif

#if defined(AUTOCONF)
	if (setup_count < 2) {
#if !defined(SKIP_BIOSTEST)
		ok = 0;
		for (i = 0; i < ADDRESS_COUNT && !ok; i++)
			for (j = 0; (j < SIGNATURE_COUNT) && !ok; j++)
				ok = check_signature(addresses[i] + signatures[j].sig_offset,
						     signatures[j].signature, signatures[j].sig_length);

		if (!ok && setup_count == 0)
			return 0;

		printk("aha152x: BIOS test: passed, ");
#else
		printk("aha152x: ");
#endif				/* !SKIP_BIOSTEST */

		ok = 0;
		for (i = 0; i < PORT_COUNT && setup_count < 2; i++) {
			if ((setup_count == 1) && (setup[0].io_port == ports[i]))
				continue;

			if (aha152x_porttest(ports[i])) {
				ok++;
				setup[setup_count].io_port = ports[i];

				conf.cf_port =
				    (GETPORT(ports[i] + O_PORTA) << 8) + GETPORT(ports[i] + O_PORTB);

				setup[setup_count].irq = IRQ_MIN + conf.cf_irq;
				setup[setup_count].scsiid = conf.cf_id;
				setup[setup_count].reconnect = conf.cf_tardisc;
				setup[setup_count].parity = !conf.cf_parity;
				setup[setup_count].synchronous = 0 /* FIXME: conf.cf_syncneg */ ;
				setup[setup_count].delay = DELAY_DEFAULT;
				setup[setup_count].ext_trans = 0;
#ifdef DEBUG_AHA152X
				setup[setup_count].debug = DEBUG_DEFAULT;
#endif
				setup_count++;
			}
		}

		if (ok)
			printk("auto configuration: ok, ");
	}
#endif

	printk("detected %d controller(s)\n", setup_count);

	for (i = 0; i < setup_count; i++) {
		struct Scsi_Host *shpnt;
		unsigned long int the_time;

		shpnt = aha152x_host[setup[i].irq - IRQ_MIN] =
		    scsi_register(tpnt, sizeof(struct aha152x_hostdata));
		registered_count++;

		shpnt->io_port = setup[i].io_port;
		shpnt->n_io_port = IO_RANGE;
		shpnt->irq = setup[i].irq;

		ISSUE_SC = (Scsi_Cmnd *) NULL;
		CURRENT_SC = (Scsi_Cmnd *) NULL;
		DISCONNECTED_SC = (Scsi_Cmnd *) NULL;

		HOSTDATA(shpnt)->reconnect = setup[i].reconnect;
		HOSTDATA(shpnt)->parity = setup[i].parity;
		HOSTDATA(shpnt)->synchronous = setup[i].synchronous;
		HOSTDATA(shpnt)->delay = setup[i].delay;
		HOSTDATA(shpnt)->ext_trans = setup[i].ext_trans;
#ifdef DEBUG_AHA152X
		HOSTDATA(shpnt)->debug = setup[i].debug;
#endif

		HOSTDATA(shpnt)->aborting = 0;
		HOSTDATA(shpnt)->abortion_complete = 0;
		HOSTDATA(shpnt)->abort_result = 0;
		HOSTDATA(shpnt)->commands = 0;

		HOSTDATA(shpnt)->message_len = 0;

		for (j = 0; j < 8; j++)
			HOSTDATA(shpnt)->syncrate[j] = 0;

		SETPORT(SCSIID, setup[i].scsiid << 4);
		shpnt->this_id = setup[i].scsiid;

		if (setup[i].reconnect)
			shpnt->can_queue = AHA152X_MAXQUEUE;

		/* RESET OUT */
		SETBITS(SCSISEQ, SCSIRSTO);
		do_pause(30);
		CLRBITS(SCSISEQ, SCSIRSTO);
		do_pause(setup[i].delay);

		aha152x_reset_ports(shpnt);

		printk("aha152x%d: vital data: PORTBASE=0x%03lx, IRQ=%d, SCSI ID=%d,"
		       " reconnect=%s, parity=%s, synchronous=%s, delay=%d, extended translation=%s\n",
		       i,
		       shpnt->io_port,
		       shpnt->irq,
		       shpnt->this_id,
		     HOSTDATA(shpnt)->reconnect ? "enabled" : "disabled",
		       HOSTDATA(shpnt)->parity ? "enabled" : "disabled",
		   HOSTDATA(shpnt)->synchronous ? "enabled" : "disabled",
		       HOSTDATA(shpnt)->delay,
		    HOSTDATA(shpnt)->ext_trans ? "enabled" : "disabled");

		request_region(shpnt->io_port, IO_RANGE, "aha152x");	/* Register */

		/* not expecting any interrupts */
		SETPORT(SIMODE0, 0);
		SETPORT(SIMODE1, 0);

		SETBITS(DMACNTRL0, INTEN);

		ok = request_irq(shpnt->irq, aha152x_swintr, SA_INTERRUPT, "aha152x", shpnt);
		if (ok < 0) {
			if (ok == -EINVAL)
				printk("aha152x%d: bad IRQ %d.\n", i, shpnt->irq);
			else if (ok == -EBUSY)
				printk("aha152x%d: IRQ %d already in use.\n", i, shpnt->irq);
			else
				printk("\naha152x%d: Unexpected error code %d on requesting IRQ %d.\n", i, ok, shpnt->irq);
			printk("aha152x: driver needs an IRQ.\n");

			scsi_unregister(shpnt);
			registered_count--;
			release_region(shpnt->io_port, IO_RANGE);
			shpnt = aha152x_host[shpnt->irq - IRQ_MIN] = 0;
			continue;
		}
		HOSTDATA(shpnt)->swint = 0;

		printk("aha152x: trying software interrupt, ");
		SETBITS(DMACNTRL0, SWINT);

		the_time = jiffies + 100;
		while (!HOSTDATA(shpnt)->swint && time_before(jiffies, the_time))
			barrier();

		free_irq(shpnt->irq, shpnt);

		if (!HOSTDATA(shpnt)->swint) {
			if (TESTHI(DMASTAT, INTSTAT)) {
				printk("lost.\n");
			} else {
				printk("failed.\n");
			}

			printk("aha152x: IRQ %d possibly wrong.  Please verify.\n", shpnt->irq);

			scsi_unregister(shpnt);
			registered_count--;
			release_region(shpnt->io_port, IO_RANGE);
			shpnt = aha152x_host[shpnt->irq - IRQ_MIN] = 0;
			continue;
		}
		printk("ok.\n");

		CLRBITS(DMACNTRL0, SWINT);

		/* clear interrupts */
		SETPORT(SSTAT0, 0x7f);
		SETPORT(SSTAT1, 0xef);

		if (request_irq(shpnt->irq, aha152x_intr, SA_INTERRUPT, "aha152x", shpnt) < 0) {
			printk("aha152x: failed to reassign interrupt.\n");
		}
	}

	return (registered_count > 0);
}


int aha152x_release(struct Scsi_Host *shpnt)
{
	if (shpnt->irq)
		free_irq(shpnt->irq, shpnt);
	if (shpnt->io_port)
		release_region(shpnt->io_port, IO_RANGE);

	return 0;
}

/* 
 *  Queue a command and setup interrupts for a free bus.
 */
int aha152x_queue(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
{
	struct Scsi_Host *shpnt = SCpnt->host;
	unsigned long flags;

#if defined(DEBUG_RACE)
	enter_driver("queue");
#else
#if defined(DEBUG_QUEUE)
	if (HOSTDATA(shpnt)->debug & debug_queue)
		printk("aha152x: queue(), ");
#endif
#endif

#if defined(DEBUG_QUEUE)
	if (HOSTDATA(shpnt)->debug & debug_queue) {
		printk("SCpnt (target = %d lun = %d cmnd = ",
		       SCpnt->target, SCpnt->lun);
		print_command(SCpnt->cmnd);
		printk(", cmd_len=%d, pieces = %d size = %u), ",
		  SCpnt->cmd_len, SCpnt->use_sg, SCpnt->request_bufflen);
		disp_ports(shpnt);
	}
#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. */
	save_flags(flags);
	cli();
	HOSTDATA(shpnt)->commands++;
	if (HOSTDATA(shpnt)->commands == 1)
		SETPORT(PORTA, 1);

#if defined(DEBUG_QUEUES)
	if (HOSTDATA(shpnt)->debug & debug_queues)
		printk("i+ (%d), ", HOSTDATA(shpnt)->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);
	}
	restore_flags(flags);

#if defined(DEBUG_RACE)
	leave_driver("queue");
#endif

	return 0;
}

/*
 *  We only support commands 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)
{
	struct Scsi_Host *shpnt = SCpnt->host;
	unsigned long flags;
	Scsi_Cmnd *ptr, *prev;

	save_flags(flags);
	cli();

#if defined(DEBUG_ABORT)
	if (HOSTDATA(shpnt)->debug & debug_abort) {
		printk("aha152x: abort(), SCpnt=0x%08x, ", (unsigned int) SCpnt);
		show_queues(shpnt);
	}
#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;

		HOSTDATA(shpnt)->commands--;

		restore_flags(flags);

		ptr->host_scribble = NULL;
		ptr->result = DID_ABORT << 16;
		spin_lock_irqsave(&io_request_lock, flags);
		ptr->scsi_done(ptr);
		spin_unlock_irqrestore(&io_request_lock, flags);

		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, ");

		restore_flags(flags);

		return SCSI_ABORT_BUSY;
	}
	/* bus is free */

	if (CURRENT_SC) {
		HOSTDATA(shpnt)->commands--;

		/* target entered bus free before COMMAND COMPLETE, nothing to abort */
		restore_flags(flags);
		spin_lock_irqsave(&io_request_lock, flags);
		CURRENT_SC->result = DID_ERROR << 16;
		CURRENT_SC->scsi_done(CURRENT_SC);
		CURRENT_SC = (Scsi_Cmnd *) NULL;
		spin_unlock_irqrestore(&io_request_lock, flags);

		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) {
		/* command wasn't found */
		printk("command not found\n");
		restore_flags(flags);

		return SCSI_ABORT_NOT_RUNNING;
	}
	if (!HOSTDATA(shpnt)->aborting) {
		/* dequeue */
		if (prev)
			prev->host_scribble = ptr->host_scribble;
		else
			DISCONNECTED_SC = (Scsi_Cmnd *) ptr->host_scribble;

		HOSTDATA(shpnt)->commands--;

		/* 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, (shpnt->this_id << OID_) | CURRENT_SC->target);

		ADDMSG(ABORT);

		/* 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);
		HOSTDATA(shpnt)->abort_result = SCSI_ABORT_SUCCESS;
		HOSTDATA(shpnt)->aborting++;
		HOSTDATA(shpnt)->abortion_complete = 0;

		restore_flags(flags);

		/* sleep until the abortion is complete */
		while (!HOSTDATA(shpnt)->abortion_complete)
			barrier();
		HOSTDATA(shpnt)->aborting = 0;

		return HOSTDATA(shpnt)->abort_result;
	} else {
		/* we're already aborting a command */
		restore_flags(flags);

		return SCSI_ABORT_BUSY;
	}
}

/*
 *  Restore default values to the AIC-6260 registers and reset the fifos
 */
static void aha152x_reset_ports(struct Scsi_Host *shpnt)
{
	/* 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 * SCpnt, unsigned int unused)
{
	struct Scsi_Host *shpnt = SCpnt->host;
	unsigned long flags;
	Scsi_Cmnd *ptr, *prev, *next;

	aha152x_reset_ports(shpnt);

	/* Reset, if bus hangs */
	if (TESTLO(SSTAT1, BUSFREE)) {
		CLRBITS(DMACNTRL0, INTEN);

#if defined(DEBUG_RESET)
		if (HOSTDATA(shpnt)->debug & debug_reset) {
			printk("aha152x: reset(), bus not free: SCSI RESET OUT\n");
			show_queues(shpnt);
		}
#endif

		ptr = CURRENT_SC;
		if (ptr && !ptr->device->soft_reset) {
			ptr->host_scribble = NULL;
			ptr->result = DID_RESET << 16;
			ptr->scsi_done(CURRENT_SC);
			CURRENT_SC = NULL;
		}
		save_flags(flags);
		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->scsi_done(ptr);

				ptr = next;
			} else {
				prev = ptr;
				ptr = (Scsi_Cmnd *) ptr->host_scribble;
			}
		}
		restore_flags(flags);

#if defined(DEBUG_RESET)
		if (HOSTDATA(shpnt)->debug & debug_reset) {
			printk("commands on targets w/ soft-resets:\n");
			show_queues(shpnt);
		}
#endif

		/* RESET OUT */
		SETPORT(SCSISEQ, SCSIRSTO);
		do_pause(30);
		SETPORT(SCSISEQ, 0);
		do_pause(DELAY);

		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, kdev_t dev, int *info_array)
{
	struct Scsi_Host *shpnt = disk->device->host;

#if defined(DEBUG_BIOSPARAM)
	if (HOSTDATA(shpnt)->debug & debug_biosparam)
		printk("aha152x_biosparam: dev=%s, size=%d, ",
		       kdevname(dev), disk->capacity);
#endif

	/* try default translation */
	info_array[0] = 64;
	info_array[1] = 32;
	info_array[2] = disk->capacity / (64 * 32);

	/* for disks >1GB do some guessing */
	if (info_array[2] >= 1024) {
		int info[3];

		/* try to figure out the geometry from the partition table */
		if (scsicam_bios_param(disk, dev, info) < 0 ||
		    !((info[0] == 64 && info[1] == 32) || (info[0] == 255 && info[1] == 63))) {
			if (EXT_TRANS) {
				printk("aha152x: unable to verify geometry for disk with >1GB.\n"
				"         using extended translation.\n");
				info_array[0] = 255;
				info_array[1] = 63;
				info_array[2] = disk->capacity / (255 * 63);
			} else {
				printk("aha152x: unable to verify geometry for disk with >1GB.\n"
				       "         Using default translation. Please verify yourself.\n"
				       "         Perhaps you need to enable extended translation in the driver.\n"
				       "         See /usr/src/linux/drivers/scsi/aha152x.c for details.\n");
			}
		} else {
			info_array[0] = info[0];
			info_array[1] = info[1];
			info_array[2] = info[2];

			if (info[0] == 255 && !EXT_TRANS) {
				printk("aha152x: current partition table is using extended translation.\n"
				       "         using it also, although it's not explicty enabled.\n");
			}
		}
	}
#if defined(DEBUG_BIOSPARAM)
	if (HOSTDATA(shpnt)->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(struct Scsi_Host *shpnt, int error)
{
	unsigned long flags;
	Scsi_Cmnd *done_SC;

#if defined(DEBUG_DONE)
	if (HOSTDATA(shpnt)->debug & debug_done) {
		printk("\naha152x: done(), ");
		disp_ports(shpnt);
	}
#endif

	if (CURRENT_SC) {
#if defined(DEBUG_DONE)
		if (HOSTDATA(shpnt)->debug & debug_done)
			printk("done(%x), ", error);
#endif

		save_flags(flags);
		cli();

		done_SC = CURRENT_SC;
		CURRENT_SC = NULL;

		/* turn led off, when no commands are in the driver */
		HOSTDATA(shpnt)->commands--;
		if (!HOSTDATA(shpnt)->commands)
			SETPORT(PORTA, 0);	/* turn led off */

#if defined(DEBUG_QUEUES)
		if (HOSTDATA(shpnt)->debug & debug_queues)
			printk("ok (%d), ", HOSTDATA(shpnt)->commands);
#endif
		restore_flags(flags);

		SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
		SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);

#if 0
/* Why poll for the BUS FREE phase, when we have setup the interrupt!? */
#if defined(DEBUG_PHASES)
		if (HOSTDATA(shpnt)->debug & debug_phases)
			printk("BUS FREE loop, ");
#endif
		while (TESTLO(SSTAT1, BUSFREE))
			barrier();
#if defined(DEBUG_PHASES)
		if (HOSTDATA(shpnt)->debug & debug_phases)
			printk("BUS FREE\n");
#endif
#endif

		done_SC->result = error;
		if (done_SC->scsi_done) {
#if defined(DEBUG_DONE)
			if (HOSTDATA(shpnt)->debug & debug_done)
				printk("calling scsi_done, ");
#endif
			spin_lock_irqsave(&io_request_lock, flags);
			done_SC->scsi_done(done_SC);
			spin_unlock_irqrestore(&io_request_lock, flags);
#if defined(DEBUG_DONE)
			if (HOSTDATA(shpnt)->debug & debug_done)
				printk("done returned, ");
#endif
		} else
			panic("aha152x: current_SC->scsi_done() == NULL");
	} else
		aha152x_panic(shpnt, "done() called outside of command");
}


static void aha152x_complete(struct Scsi_Host *);

static struct tq_struct aha152x_tq;

/*
 *    Run service completions on the card with interrupts enabled.
 */

static void aha152x_run(void)
{
	int i;
	for (i = 0; i < IRQS; i++) {
		struct Scsi_Host *shpnt = aha152x_host[i];
		if (shpnt && HOSTDATA(shpnt)->service) {
			HOSTDATA(shpnt)->service = 0;
			aha152x_complete(shpnt);
		}
	}
}

/*
 *    Interrupts handler (main routine of the driver)
 */

static void aha152x_intr(int irqno, void *dev_id, struct pt_regs *regs)
{
	struct Scsi_Host *shpnt = aha152x_host[irqno - IRQ_MIN];

#if defined(DEBUG_RACE)
	enter_driver("intr");
#else
#if defined(DEBUG_INTR)
	if (HOSTDATA(shpnt)->debug & debug_intr)
		printk("\naha152x: intr(), ");
#endif
#endif

	if (!shpnt)
		panic("aha152x: catched interrupt for unknown controller.\n");

	/* no more interrupts from the controller, while we're busy.
	   INTEN is restored by the BH handler */

	CLRBITS(DMACNTRL0, INTEN);

	/* Poke the BH handler */

	HOSTDATA(shpnt)->service = 1;
	aha152x_tq.routine = (void *) aha152x_run;
	queue_task(&aha152x_tq, &tq_immediate);
	mark_bh(IMMEDIATE_BH);
}

static void aha152x_complete(struct Scsi_Host *shpnt)
{
	unsigned int flags;
	int done = 0, phase;

	/* 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 (HOSTDATA(shpnt)->debug & debug_queues)
				printk("i+, ");
#endif
			save_flags(flags);
			cli();
			append_SC(&ISSUE_SC, CURRENT_SC);
			CURRENT_SC = NULL;
			restore_flags(flags);
		}
		/* disable sequences */
		SETPORT(SCSISEQ, 0);
		SETPORT(SSTAT0, CLRSELDI);
		SETPORT(SSTAT1, CLRBUSFREE);

#if defined(DEBUG_QUEUES) || defined(DEBUG_PHASES)
		if (HOSTDATA(shpnt)->debug & (debug_queues | debug_phases))
			printk("reselected, ");
#endif

		i = GETPORT(SELID) & ~(1 << shpnt->this_id);
		target = 0;

		if (i == 0)
			aha152x_panic(shpnt, "reconnecting target unknown");

		for (; (i & 1) == 0; target++, i >>= 1);

#if defined(DEBUG_QUEUES)
		if (HOSTDATA(shpnt)->debug & debug_queues)
			printk("SELID=%02x, target=%d, ", GETPORT(SELID), target);
#endif
		SETPORT(SCSIID, (shpnt->this_id << OID_) | target);
		SETPORT(SCSISEQ, ENRESELI);

		if (TESTLO(SSTAT0, SELDI))
			aha152x_panic(shpnt, "RESELI failed");

		SETPORT(SCSIRATE, HOSTDATA(shpnt)->syncrate[target] & 0x7f);

		SETPORT(SCSISIG, P_MSGI);

		/* Get identify message */
		if ((i = getphase(shpnt)) != P_MSGI) {
			printk("target doesn't enter MSGI to identify (phase=%02x)\n", i);
			aha152x_panic(shpnt, "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(shpnt, "unknown lun");
		}
#if defined(DEBUG_QUEUES)
		if (HOSTDATA(shpnt)->debug & debug_queues)
			printk("identify=%02x, lun=%d, ", identify_msg, identify_msg & 0x3f);
#endif

		save_flags(flags);
		cli();

#if defined(DEBUG_QUEUES)
		if (HOSTDATA(shpnt)->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(shpnt, "no disconnected command for that lun");
		}
		CURRENT_SC->SCp.phase &= ~disconnected;
		restore_flags(flags);

		make_acklow(shpnt);
		if (getphase(shpnt) != P_MSGI) {
			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) {
			save_flags(flags);
			cli();
#if defined(DEBUG_QUEUES)
			if (HOSTDATA(shpnt)->debug & debug_queues)
				printk("i-, ");
#endif
			CURRENT_SC = remove_first_SC(&ISSUE_SC);
			restore_flags(flags);

#if defined(DEBUG_INTR) || defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
			if (HOSTDATA(shpnt)->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 (HOSTDATA(shpnt)->debug & (debug_intr | debug_selection | debug_phases))
				printk("selecting %d, ", CURRENT_SC->target);
#endif
			SETPORT(SCSIID, (shpnt->this_id << OID_) | CURRENT_SC->target);

			/* Enable interrupts for SELECTION OUT DONE and SELECTION OUT INITIATED */
			SETPORT(SXFRCTL1, HOSTDATA(shpnt)->parity ? (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);

		} else {
			/* No command we are busy with and no new to issue */
			printk("aha152x: ignoring spurious interrupt, nothing to do\n");
			if (TESTHI(DMACNTRL0, SWINT)) {
				printk("aha152x: SWINT is set!  Why?\n");
				CLRBITS(DMACNTRL0, SWINT);
			}
			show_queues(shpnt);
		}

#if defined(DEBUG_RACE)
		leave_driver("(selecting) intr");
#endif
		SETBITS(DMACNTRL0, INTEN);
		return;
	}
	/* the bus is busy with something */

#if defined(DEBUG_INTR)
	if (HOSTDATA(shpnt)->debug & debug_intr)
		disp_ports(shpnt);
#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) {
						HOSTDATA(shpnt)->abort_result = SCSI_ABORT_ERROR;
						HOSTDATA(shpnt)->abortion_complete++;
					}
					aha152x_done(shpnt, DID_NO_CONNECT << 16);

					return;
				}
#if defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
				if (HOSTDATA(shpnt)->debug & (debug_selection | debug_phases))
					printk("SELDO (SELID=%x), ", GETPORT(SELID));
#endif

				/* selection was done */
				SETPORT(SSTAT0, CLRSELDO);

#if defined(DEBUG_ABORT)
				if ((HOSTDATA(shpnt)->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;

				ADDMSG(IDENTIFY(HOSTDATA(shpnt)->reconnect, CURRENT_SC->lun));

				if (!(SYNCRATE & 0x80) && HOSTDATA(shpnt)->synchronous) {
					ADDMSG(EXTENDED_MESSAGE);
					ADDMSG(3);
					ADDMSG(EXTENDED_SDTR);
					ADDMSG(50);
					ADDMSG(8);

					printk("outbound SDTR: ");
					print_msg(&MSG(MSGLEN - 5));

					SYNCRATE = 0x80;
					CURRENT_SC->SCp.phase |= in_sync;
				}
#if defined(DEBUG_RACE)
				leave_driver("(SELDO) intr");
#endif
				SETPORT(SCSIRATE, SYNCRATE & 0x7f);

				SETPORT(SCSISIG, P_MSGO);

				SETPORT(SIMODE0, 0);
				SETPORT(SIMODE1, ENREQINIT | ENBUSFREE);
				SETBITS(DMACNTRL0, INTEN);

				return;
			} else
				aha152x_panic(shpnt, "neither timeout nor selection\007");
		} else {
#if defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
			if (HOSTDATA(shpnt)->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 (HOSTDATA(shpnt)->debug & debug_abort)
					printk("(ABORT) selection timeout, ");
#endif
				HOSTDATA(shpnt)->abort_result = SCSI_ABORT_ERROR;
				HOSTDATA(shpnt)->abortion_complete++;
			}
			if (TESTLO(SSTAT0, SELINGO))
				/* ARBITRATION not won */
				aha152x_done(shpnt, DID_BUS_BUSY << 16);
			else
				/* ARBITRATION won, but SELECTION failed */
				aha152x_done(shpnt, DID_NO_CONNECT << 16);

			return;
		}
	}
	/* enable interrupt, when target leaves current phase */
	phase = getphase(shpnt);
	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 */
		{
			int i, identify = 0, abort = 0;

#if defined(DEBUG_INTR) || defined(DEBUG_MSGO) || defined(DEBUG_PHASES)
			if (HOSTDATA(shpnt)->debug & (debug_intr | debug_msgo | debug_phases))
				printk("MESSAGE OUT, ");
#endif
			if (MSGLEN == 0) {
				ADDMSG(MESSAGE_REJECT);
#if defined(DEBUG_MSGO)
				if (HOSTDATA(shpnt)->debug & debug_msgo)
					printk("unexpected MESSAGE OUT phase; rejecting, ");
#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))
				barrier();

#if defined(DEBUG_MSGO)
			if (HOSTDATA(shpnt)->debug & debug_msgo) {
				int i;

				printk("messages (");
				for (i = 0; i < MSGLEN; i += print_msg(&MSG(i)), printk(" "));
				printk("), ");
			}
#endif

			for (i = 0; i < MSGLEN && TESTLO(SSTAT1, PHASEMIS); i++) {
#if defined(DEBUG_MSGO)
				if (HOSTDATA(shpnt)->debug & debug_msgo)
					printk("%x ", MSG(i));
#endif
				if (i == MSGLEN - 1) {
					/* Leave MESSAGE OUT after transfer */
					SETPORT(SSTAT1, CLRATNO);
				}
				SETPORT(SCSIDAT, MSG(i));

				make_acklow(shpnt);
				getphase(shpnt);

				if (MSG(i) == IDENTIFY(HOSTDATA(shpnt)->reconnect, CURRENT_SC->lun))
					identify++;

				if (MSG(i) == ABORT)
					abort++;

			}

			MSGLEN = 0;

			if (identify)
				CURRENT_SC->SCp.phase |= sent_ident;

			if (abort) {
				/* revive abort(); abort() enables interrupts */
				HOSTDATA(shpnt)->abort_result = SCSI_ABORT_SUCCESS;
				HOSTDATA(shpnt)->abortion_complete++;

				CURRENT_SC->SCp.phase &= ~(P_MASK << 16);

				/* exit */
				SETBITS(DMACNTRL0, INTEN);
#if defined(DEBUG_RACE)
				leave_driver("(ABORT) intr");
#endif
				aha152x_done(shpnt, DID_ABORT << 16);

				return;
			}
		}
		break;

	case P_CMD:		/* COMMAND phase */
#if defined(DEBUG_INTR) || defined(DEBUG_CMD) || defined(DEBUG_PHASES)
		if (HOSTDATA(shpnt)->debug & (debug_intr | debug_cmd | debug_phases))
			printk("COMMAND, ");
#endif
		if (!(CURRENT_SC->SCp.sent_command)) {
			int i;

			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))
				barrier();

			for (i = 0; i < CURRENT_SC->cmd_len && TESTLO(SSTAT1, PHASEMIS); i++) {
				SETPORT(SCSIDAT, CURRENT_SC->cmnd[i]);

				make_acklow(shpnt);
				getphase(shpnt);
			}

			if (i < CURRENT_SC->cmd_len && TESTHI(SSTAT1, PHASEMIS))
				aha152x_panic(shpnt, "target left COMMAND");

			CURRENT_SC->SCp.sent_command++;
		} else
			aha152x_panic(shpnt, "Nothing to send while in COMMAND");
		break;

	case P_MSGI:		/* MESSAGE IN phase */
		{
			int start_sync = 0;

#if defined(DEBUG_INTR) || defined(DEBUG_MSGI) || defined(DEBUG_PHASES)
			if (HOSTDATA(shpnt)->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(SCSIDAT);
				switch (CURRENT_SC->SCp.Message) {
				case DISCONNECT:
#if defined(DEBUG_MSGI) || defined(DEBUG_PHASES)
					if (HOSTDATA(shpnt)->debug & (debug_msgi | debug_phases))
						printk("target disconnected, ");
#endif
					CURRENT_SC->SCp.Message = 0;
					CURRENT_SC->SCp.phase |= disconnected;
					if (!HOSTDATA(shpnt)->reconnect)
						aha152x_panic(shpnt, "target was not allowed to disconnect");

					break;

				case COMMAND_COMPLETE:
#if defined(DEBUG_MSGI) || defined(DEBUG_PHASES)
					if (HOSTDATA(shpnt)->debug & (debug_msgi | debug_phases))
						printk("inbound message (COMMAND COMPLETE), ");
#endif
					done++;
					break;

				case MESSAGE_REJECT:
					if (CURRENT_SC->SCp.phase & in_sync) {
						CURRENT_SC->SCp.phase &= ~in_sync;
						SYNCRATE = 0x80;
						printk("synchronous rejected, ");
					} else
						printk("inbound message (MESSAGE REJECT), ");
#if defined(DEBUG_MSGI)
					if (HOSTDATA(shpnt)->debug & debug_msgi)
						printk("inbound message (MESSAGE REJECT), ");
#endif
					break;

				case SAVE_POINTERS:
#if defined(DEBUG_MSGI)
					if (HOSTDATA(shpnt)->debug & debug_msgi)
						printk("inbound message (SAVE DATA POINTERS), ");
#endif
					break;

				case RESTORE_POINTERS:
#if defined(DEBUG_MSGI)
					if (HOSTDATA(shpnt)->debug & debug_msgi)
						printk("inbound message (RESTORE DATA POINTERS), ");
#endif
					break;

				case EXTENDED_MESSAGE:
					{
						char buffer[16];
						int i;

#if defined(DEBUG_MSGI)
						if (HOSTDATA(shpnt)->debug & debug_msgi)
							printk("inbound message (EXTENDED MESSAGE), ");
#endif
						make_acklow(shpnt);
						if (getphase(shpnt) != P_MSGI)
							break;

						buffer[0] = EXTENDED_MESSAGE;
						buffer[1] = GETPORT(SCSIDAT);

						for (i = 0; i < buffer[1] &&
						     (make_acklow(shpnt), getphase(shpnt) == P_MSGI); i++)
							buffer[2 + i] = GETPORT(SCSIDAT);

#if defined(DEBUG_MSGI)
						if (HOSTDATA(shpnt)->debug & debug_msgi)
							print_msg(buffer);
#endif

						switch (buffer[2]) {
						case EXTENDED_SDTR:
							{
								long ticks;

								if (buffer[1] != 3)
									aha152x_panic(shpnt, "SDTR message length != 3");

								if (!HOSTDATA(shpnt)->synchronous)
									break;

								printk("inbound SDTR: ");
								print_msg(buffer);

								ticks = (buffer[3] * 4 + 49) / 50;

								if (CURRENT_SC->SCp.phase & in_sync) {
									/* we initiated SDTR */
									if (ticks > 9 || buffer[4] < 1 || buffer[4] > 8)
										aha152x_panic(shpnt, "received SDTR invalid");

									SYNCRATE |= ((ticks - 2) << 4) + buffer[4];
								} else if (ticks <= 9 && buffer[4] >= 1) {
									if (buffer[4] > 8)
										buffer[4] = 8;

									ADDMSG(EXTENDED_MESSAGE);
									ADDMSG(3);
									ADDMSG(EXTENDED_SDTR);
									if (ticks < 4) {
										ticks = 4;
										ADDMSG(50);
									} else
										ADDMSG(buffer[3]);

									ADDMSG(buffer[4]);

									printk("outbound SDTR: ");
									print_msg(&MSG(MSGLEN - 5));

									CURRENT_SC->SCp.phase |= in_sync;

									SYNCRATE |= ((ticks - 2) << 4) + buffer[4];

									start_sync++;
								} else {
									/* requested SDTR is too slow, do it asynchronously */
									ADDMSG(MESSAGE_REJECT);
									SYNCRATE = 0;
								}

								SETPORT(SCSIRATE, SYNCRATE & 0x7f);
							}
							break;

						case EXTENDED_MODIFY_DATA_POINTER:
						case EXTENDED_EXTENDED_IDENTIFY:
						case EXTENDED_WDTR:
						default:
							ADDMSG(MESSAGE_REJECT);
							break;
						}
					}
					break;

				default:
					printk("unsupported inbound message %x, ", CURRENT_SC->SCp.Message);
					break;

				}

				make_acklow(shpnt);
				phase = getphase(shpnt);
			}

			if (start_sync)
				CURRENT_SC->SCp.phase |= in_sync;
			else
				CURRENT_SC->SCp.phase &= ~in_sync;

			if (MSGLEN > 0)
				SETPORT(SCSISIG, P_MSGI | ATNO);

			/* clear SCSI fifo on BUSFREE */
			if (phase == P_BUSFREE)
				SETPORT(SXFRCTL0, CH1 | CLRCH1);

			if (CURRENT_SC->SCp.phase & disconnected) {
				save_flags(flags);
				cli();
#if defined(DEBUG_QUEUES)
				if (HOSTDATA(shpnt)->debug & debug_queues)
					printk("d+, ");
#endif
				append_SC(&DISCONNECTED_SC, CURRENT_SC);
				CURRENT_SC->SCp.phase |= 1 << 16;
				CURRENT_SC = NULL;
				restore_flags(flags);

				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 (HOSTDATA(shpnt)->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(shpnt);
		getphase(shpnt);

#if defined(DEBUG_STATUS)
		if (HOSTDATA(shpnt)->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 (HOSTDATA(shpnt)->debug & (debug_datai | debug_intr | debug_phases))
				printk("DATA IN, ");
#endif

#if 0
			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));
#endif

			/* 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 (HOSTDATA(shpnt)->debug & debug_datai)
					printk("expecting data, ");
#endif
				/* wait for PHASEMIS or full FIFO */
				while (TESTLO(DMASTAT, DFIFOFULL | INTSTAT))
					barrier();

#if defined(DEBUG_DATAI)
				if (HOSTDATA(shpnt)->debug & debug_datai)
					printk("ok, ");
#endif

				if (TESTHI(DMASTAT, DFIFOFULL))
					fifodata = GETPORT(FIFOSTAT);
				else {
					/* wait for SCSI fifo to get empty */
					while (TESTLO(SSTAT2, SEMPTY))
						barrier();

					/* rest of data in FIFO */
					fifodata = GETPORT(FIFOSTAT);
#if defined(DEBUG_DATAI)
					if (HOSTDATA(shpnt)->debug & debug_datai)
						printk("last transfer, ");
#endif
					done = 1;
				}

#if defined(DEBUG_DATAI)
				if (HOSTDATA(shpnt)->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 (HOSTDATA(shpnt)->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 (HOSTDATA(shpnt)->debug & debug_datai)
							/* show what comes with the last transfer */
							if (done) {
#if 0
								int i;
								unsigned char *data;
#endif

								printk("data on last transfer (%d bytes) ",
								       2 * data_count);
#if 0
								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
							}
#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 (HOSTDATA(shpnt)->debug & debug_datai)
					if (!fifodata)
						printk("fifo empty, ");
					else
						printk("something left in fifo, ");
#endif
			}

#if defined(DEBUG_DATAI)
			if ((HOSTDATA(shpnt)->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))
				barrier();
			CLRBITS(DMACNTRL0, ENDMA);

#if defined(DEBUG_DATAI) || defined(DEBUG_INTR)
			if (HOSTDATA(shpnt)->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 (HOSTDATA(shpnt)->debug & (debug_datao | debug_intr | debug_phases))
				printk("DATA OUT, ");
#endif
#if defined(DEBUG_DATAO)
			if (HOSTDATA(shpnt)->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(shpnt, "FIFO should be empty");
			}
			SETPORT(SXFRCTL0, CH1 | CLRSTCNT | CLRCH1);
			SETPORT(SXFRCTL0, SCSIEN | DMAEN | CH1);

			SETPORT(DMACNTRL0, WRITE_READ | RSTFIFO);
			SETPORT(DMACNTRL0, ENDMA | WRITE_READ);

			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 (HOSTDATA(shpnt)->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 (HOSTDATA(shpnt)->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))
					barrier();

#if defined(DEBUG_DATAO)
				if (HOSTDATA(shpnt)->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 (HOSTDATA(shpnt)->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 (HOSTDATA(shpnt)->debug & debug_datao)
					printk("waiting for SCSI fifo to get empty, ");
#endif
				/* wait for SCSI fifo to get empty */
				while (TESTLO(SSTAT2, SEMPTY))
					barrier();
#if defined(DEBUG_DATAO)
				if (HOSTDATA(shpnt)->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))
					barrier();

				CLRBITS(DMACNTRL0, ENDMA);
			}

#if defined(DEBUG_DATAO) || defined(DEBUG_INTR)
			if (HOSTDATA(shpnt)->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 (HOSTDATA(shpnt)->debug & debug_phases)
			printk("unexpected BUS FREE, ");
#endif
		CURRENT_SC->SCp.phase &= ~(P_MASK << 16);

		aha152x_done(shpnt, 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 &= ~(P_MASK << 16);

		SETBITS(DMACNTRL0, INTEN);
		aha152x_done(shpnt, DID_PARITY << 16);
		return;
		break;

	default:
		printk("aha152x: unexpected phase\n");
		break;
	}

	if (done) {
#if defined(DEBUG_INTR)
		if (HOSTDATA(shpnt)->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(shpnt,
			     (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 (HOSTDATA(shpnt)->debug & debug_intr)
		disp_enintr(shpnt);
#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(struct Scsi_Host *shpnt, char *msg)
{
	printk("\naha152x: %s\n", msg);
	show_queues(shpnt);
	panic("aha152x panic");
}

/*
 * Display registers of AIC-6260
 */
static void disp_ports(struct Scsi_Host *shpnt)
{
#ifdef DEBUG_AHA152X
	int s;

#ifdef SKIP_PORTS
	if (HOSTDATA(shpnt)->debug & debug_skipports)
		return;
#endif

	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));

	s = GETPORT(SSTAT3);
	printk("SCSICNT (%d), OFFCNT(%d), ", (s & 0xf0) >> 4, s & 0x0f);

	printk("SSTAT4 (");
	s = GETPORT(SSTAT4);
	if (s & SYNCERR)
		printk("SYNCERR ");
	if (s & FWERR)
		printk("FWERR ");
	if (s & FRERR)
		printk("FRERR ");
	printk("); ");

	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("); ");

	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(")");

	printk("\n");
#endif
}

/*
 * display enabled interrupts
 */
static void disp_enintr(struct Scsi_Host *shpnt)
{
	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)
{
	unsigned long flags;

	save_flags(flags);
	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;
	restore_flags(flags);
}

static void leave_driver(const char *func)
{
	unsigned long flags;

	save_flags(flags);
	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;
	restore_flags(flags);
}
#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(struct Scsi_Host *shpnt)
{
	unsigned long flags;
	Scsi_Cmnd *ptr;

	save_flags(flags);
	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(shpnt);
	disp_enintr(shpnt);
	restore_flags(flags);
}

int aha152x_set_info(char *buffer, int length, struct Scsi_Host *shpnt)
{
	return (-ENOSYS);	/* Currently this is a no-op */
}

#undef SPRINTF
#define SPRINTF(args...) pos += sprintf(pos, ## args)

static int get_command(char *pos, Scsi_Cmnd * ptr)
{
	char *start = pos;
	int i;

	SPRINTF("0x%08x: target=%d; lun=%d; cmnd=( ",
		(unsigned int) ptr, ptr->target, ptr->lun);

	for (i = 0; i < COMMAND_SIZE(ptr->cmnd[0]); i++)
		SPRINTF("0x%02x ", ptr->cmnd[i]);

	SPRINTF("); residual=%d; buffers=%d; phase |",
		ptr->SCp.this_residual, ptr->SCp.buffers_residual);

	if (ptr->SCp.phase & not_issued)
		SPRINTF("not issued|");
	if (ptr->SCp.phase & in_selection)
		SPRINTF("in selection|");
	if (ptr->SCp.phase & disconnected)
		SPRINTF("disconnected|");
	if (ptr->SCp.phase & aborted)
		SPRINTF("aborted|");
	if (ptr->SCp.phase & sent_ident)
		SPRINTF("send_ident|");
	if (ptr->SCp.phase & in_other) {
		SPRINTF("; in other(");
		switch ((ptr->SCp.phase >> 16) & P_MASK) {
		case P_DATAO:
			SPRINTF("DATA OUT");
			break;
		case P_DATAI:
			SPRINTF("DATA IN");
			break;
		case P_CMD:
			SPRINTF("COMMAND");
			break;
		case P_STATUS:
			SPRINTF("STATUS");
			break;
		case P_MSGO:
			SPRINTF("MESSAGE OUT");
			break;
		case P_MSGI:
			SPRINTF("MESSAGE IN");
			break;
		default:
			SPRINTF("*illegal*");
			break;
		}
		SPRINTF(")");
		if (ptr->SCp.phase & (1 << 16))
			SPRINTF("; phaseend");
	}
	SPRINTF("; next=0x%08x\n", (unsigned int) ptr->host_scribble);

	return (pos - start);
}

static int get_ports(struct Scsi_Host *shpnt, char *pos)
{
	char *start = pos;
	int s;

#ifdef SKIP_PORTS
	if (HOSTDATA(shpnt)->debug & debug_skipports)
		return;
#endif

	SPRINTF("\n%s: ", CURRENT_SC ? "on bus" : "waiting");

	s = GETPORT(SCSISEQ);
	SPRINTF("SCSISEQ (");
	if (s & TEMODEO)
		SPRINTF("TARGET MODE ");
	if (s & ENSELO)
		SPRINTF("SELO ");
	if (s & ENSELI)
		SPRINTF("SELI ");
	if (s & ENRESELI)
		SPRINTF("RESELI ");
	if (s & ENAUTOATNO)
		SPRINTF("AUTOATNO ");
	if (s & ENAUTOATNI)
		SPRINTF("AUTOATNI ");
	if (s & ENAUTOATNP)
		SPRINTF("AUTOATNP ");
	if (s & SCSIRSTO)
		SPRINTF("SCSIRSTO ");
	SPRINTF(");");

	SPRINTF(" SCSISIG (");
	s = GETPORT(SCSISIG);
	switch (s & P_MASK) {
	case P_DATAO:
		SPRINTF("DATA OUT");
		break;
	case P_DATAI:
		SPRINTF("DATA IN");
		break;
	case P_CMD:
		SPRINTF("COMMAND");
		break;
	case P_STATUS:
		SPRINTF("STATUS");
		break;
	case P_MSGO:
		SPRINTF("MESSAGE OUT");
		break;
	case P_MSGI:
		SPRINTF("MESSAGE IN");
		break;
	default:
		SPRINTF("*illegal*");
		break;
	}

	SPRINTF("); ");

	SPRINTF("INTSTAT (%s); ", TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo");

	SPRINTF("SSTAT (");
	s = GETPORT(SSTAT0);
	if (s & TARGET)
		SPRINTF("TARGET ");
	if (s & SELDO)
		SPRINTF("SELDO ");
	if (s & SELDI)
		SPRINTF("SELDI ");
	if (s & SELINGO)
		SPRINTF("SELINGO ");
	if (s & SWRAP)
		SPRINTF("SWRAP ");
	if (s & SDONE)
		SPRINTF("SDONE ");
	if (s & SPIORDY)
		SPRINTF("SPIORDY ");
	if (s & DMADONE)
		SPRINTF("DMADONE ");

	s = GETPORT(SSTAT1);
	if (s & SELTO)
		SPRINTF("SELTO ");
	if (s & ATNTARG)
		SPRINTF("ATNTARG ");
	if (s & SCSIRSTI)
		SPRINTF("SCSIRSTI ");
	if (s & PHASEMIS)
		SPRINTF("PHASEMIS ");
	if (s & BUSFREE)
		SPRINTF("BUSFREE ");
	if (s & SCSIPERR)
		SPRINTF("SCSIPERR ");
	if (s & PHASECHG)
		SPRINTF("PHASECHG ");
	if (s & REQINIT)
		SPRINTF("REQINIT ");
	SPRINTF("); ");


	SPRINTF("SSTAT (");

	s = GETPORT(SSTAT0) & GETPORT(SIMODE0);

	if (s & TARGET)
		SPRINTF("TARGET ");
	if (s & SELDO)
		SPRINTF("SELDO ");
	if (s & SELDI)
		SPRINTF("SELDI ");
	if (s & SELINGO)
		SPRINTF("SELINGO ");
	if (s & SWRAP)
		SPRINTF("SWRAP ");
	if (s & SDONE)
		SPRINTF("SDONE ");
	if (s & SPIORDY)
		SPRINTF("SPIORDY ");
	if (s & DMADONE)
		SPRINTF("DMADONE ");

	s = GETPORT(SSTAT1) & GETPORT(SIMODE1);

	if (s & SELTO)
		SPRINTF("SELTO ");
	if (s & ATNTARG)
		SPRINTF("ATNTARG ");
	if (s & SCSIRSTI)
		SPRINTF("SCSIRSTI ");
	if (s & PHASEMIS)
		SPRINTF("PHASEMIS ");
	if (s & BUSFREE)
		SPRINTF("BUSFREE ");
	if (s & SCSIPERR)
		SPRINTF("SCSIPERR ");
	if (s & PHASECHG)
		SPRINTF("PHASECHG ");
	if (s & REQINIT)
		SPRINTF("REQINIT ");
	SPRINTF("); ");

	SPRINTF("SXFRCTL0 (");

	s = GETPORT(SXFRCTL0);
	if (s & SCSIEN)
		SPRINTF("SCSIEN ");
	if (s & DMAEN)
		SPRINTF("DMAEN ");
	if (s & CH1)
		SPRINTF("CH1 ");
	if (s & CLRSTCNT)
		SPRINTF("CLRSTCNT ");
	if (s & SPIOEN)
		SPRINTF("SPIOEN ");
	if (s & CLRCH1)
		SPRINTF("CLRCH1 ");
	SPRINTF("); ");

	SPRINTF("SIGNAL (");

	s = GETPORT(SCSISIG);
	if (s & ATNI)
		SPRINTF("ATNI ");
	if (s & SELI)
		SPRINTF("SELI ");
	if (s & BSYI)
		SPRINTF("BSYI ");
	if (s & REQI)
		SPRINTF("REQI ");
	if (s & ACKI)
		SPRINTF("ACKI ");
	SPRINTF("); ");

	SPRINTF("SELID (%02x), ", GETPORT(SELID));

	SPRINTF("SSTAT2 (");

	s = GETPORT(SSTAT2);
	if (s & SOFFSET)
		SPRINTF("SOFFSET ");
	if (s & SEMPTY)
		SPRINTF("SEMPTY ");
	if (s & SFULL)
		SPRINTF("SFULL ");
	SPRINTF("); SFCNT (%d); ", s & (SFULL | SFCNT));

	s = GETPORT(SSTAT3);
	SPRINTF("SCSICNT (%d), OFFCNT(%d), ", (s & 0xf0) >> 4, s & 0x0f);

	SPRINTF("SSTAT4 (");
	s = GETPORT(SSTAT4);
	if (s & SYNCERR)
		SPRINTF("SYNCERR ");
	if (s & FWERR)
		SPRINTF("FWERR ");
	if (s & FRERR)
		SPRINTF("FRERR ");
	SPRINTF("); ");

	SPRINTF("DMACNTRL0 (");
	s = GETPORT(DMACNTRL0);
	SPRINTF("%s ", s & _8BIT ? "8BIT" : "16BIT");
	SPRINTF("%s ", s & DMA ? "DMA" : "PIO");
	SPRINTF("%s ", s & WRITE_READ ? "WRITE" : "READ");
	if (s & ENDMA)
		SPRINTF("ENDMA ");
	if (s & INTEN)
		SPRINTF("INTEN ");
	if (s & RSTFIFO)
		SPRINTF("RSTFIFO ");
	if (s & SWINT)
		SPRINTF("SWINT ");
	SPRINTF("); ");

	SPRINTF("DMASTAT (");
	s = GETPORT(DMASTAT);
	if (s & ATDONE)
		SPRINTF("ATDONE ");
	if (s & WORDRDY)
		SPRINTF("WORDRDY ");
	if (s & DFIFOFULL)
		SPRINTF("DFIFOFULL ");
	if (s & DFIFOEMP)
		SPRINTF("DFIFOEMP ");
	SPRINTF(")\n\n");

	SPRINTF("enabled interrupts (");

	s = GETPORT(SIMODE0);
	if (s & ENSELDO)
		SPRINTF("ENSELDO ");
	if (s & ENSELDI)
		SPRINTF("ENSELDI ");
	if (s & ENSELINGO)
		SPRINTF("ENSELINGO ");
	if (s & ENSWRAP)
		SPRINTF("ENSWRAP ");
	if (s & ENSDONE)
		SPRINTF("ENSDONE ");
	if (s & ENSPIORDY)
		SPRINTF("ENSPIORDY ");
	if (s & ENDMADONE)
		SPRINTF("ENDMADONE ");

	s = GETPORT(SIMODE1);
	if (s & ENSELTIMO)
		SPRINTF("ENSELTIMO ");
	if (s & ENATNTARG)
		SPRINTF("ENATNTARG ");
	if (s & ENPHASEMIS)
		SPRINTF("ENPHASEMIS ");
	if (s & ENBUSFREE)
		SPRINTF("ENBUSFREE ");
	if (s & ENSCSIPERR)
		SPRINTF("ENSCSIPERR ");
	if (s & ENPHASECHG)
		SPRINTF("ENPHASECHG ");
	if (s & ENREQINIT)
		SPRINTF("ENREQINIT ");
	SPRINTF(")\n");

	return (pos - start);
}

#undef SPRINTF
#define SPRINTF(args...) do { if(pos < buffer + length) pos += sprintf(pos, ## args); } while(0)

int aha152x_proc_info(char *buffer, char **start,
		      off_t offset, int length, int hostno, int inout)
{
	int i;
	char *pos = buffer;
	struct Scsi_Host *shpnt;
	unsigned long flags;
	Scsi_Cmnd *ptr;

	for (i = 0, shpnt = (struct Scsi_Host *) NULL; i < IRQS; i++)
		if (aha152x_host[i] && aha152x_host[i]->host_no == hostno)
			shpnt = aha152x_host[i];

	if (!shpnt)
		return (-ESRCH);

	if (inout)		/* Has data been written to the file ? */
		return (aha152x_set_info(buffer, length, shpnt));

	SPRINTF(AHA152X_REVID "\n");

	save_flags(flags);
	cli();

	SPRINTF("ioports 0x%04lx to 0x%04lx\n",
		shpnt->io_port, shpnt->io_port + shpnt->n_io_port - 1);
	SPRINTF("interrupt 0x%02x\n", shpnt->irq);
	SPRINTF("disconnection/reconnection %s\n",
		HOSTDATA(shpnt)->reconnect ? "enabled" : "disabled");
	SPRINTF("parity checking %s\n",
		HOSTDATA(shpnt)->parity ? "enabled" : "disabled");
	SPRINTF("synchronous transfers %s\n",
		HOSTDATA(shpnt)->synchronous ? "enabled" : "disabled");
	SPRINTF("%d commands currently queued\n", HOSTDATA(shpnt)->commands);

	if (HOSTDATA(shpnt)->synchronous) {
#if 0
		SPRINTF("synchronously operating targets (tick=%ld ns):\n",
			250000000 / loops_per_sec);
		for (i = 0; i < 8; i++)
			if (HOSTDATA(shpnt)->syncrate[i] & 0x7f)
				SPRINTF("target %d: period %dT/%ldns; req/ack offset %d\n",
					i,
					(((HOSTDATA(shpnt)->syncrate[i] & 0x70) >> 4) + 2),
					(((HOSTDATA(shpnt)->syncrate[i] & 0x70) >> 4) + 2) *
					250000000 / loops_per_sec,
				    HOSTDATA(shpnt)->syncrate[i] & 0x0f);
#else
		SPRINTF("synchronously operating targets (tick=50 ns):\n");
		for (i = 0; i < 8; i++)
			if (HOSTDATA(shpnt)->syncrate[i] & 0x7f)
				SPRINTF("target %d: period %dT/%dns; req/ack offset %d\n",
					i,
					(((HOSTDATA(shpnt)->syncrate[i] & 0x70) >> 4) + 2),
					(((HOSTDATA(shpnt)->syncrate[i] & 0x70) >> 4) + 2) * 50,
				    HOSTDATA(shpnt)->syncrate[i] & 0x0f);
#endif
	}
#ifdef DEBUG_AHA152X
#define PDEBUG(flags,txt) if(HOSTDATA(shpnt)->debug & flags) SPRINTF("(%s) ", txt);

	SPRINTF("enabled debugging options: ");

	PDEBUG(debug_skipports, "skip ports");
	PDEBUG(debug_queue, "queue");
	PDEBUG(debug_intr, "interrupt");
	PDEBUG(debug_selection, "selection");
	PDEBUG(debug_msgo, "message out");
	PDEBUG(debug_msgi, "message in");
	PDEBUG(debug_status, "status");
	PDEBUG(debug_cmd, "command");
	PDEBUG(debug_datai, "data in");
	PDEBUG(debug_datao, "data out");
	PDEBUG(debug_abort, "abort");
	PDEBUG(debug_done, "done");
	PDEBUG(debug_biosparam, "bios parameters");
	PDEBUG(debug_phases, "phases");
	PDEBUG(debug_queues, "queues");
	PDEBUG(debug_reset, "reset");

	SPRINTF("\n");
#endif

	SPRINTF("\nqueue status:\n");
	if (ISSUE_SC) {
		SPRINTF("not yet issued commands:\n");
		for (ptr = ISSUE_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
			pos += get_command(pos, ptr);
	} else
		SPRINTF("no not yet issued commands\n");

	if (CURRENT_SC) {
		SPRINTF("current command:\n");
		pos += get_command(pos, CURRENT_SC);
	} else
		SPRINTF("no current command\n");

	if (DISCONNECTED_SC) {
		SPRINTF("disconnected commands:\n");
		for (ptr = DISCONNECTED_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
			pos += get_command(pos, ptr);
	} else
		SPRINTF("no disconnected commands\n");

	restore_flags(flags);

	pos += get_ports(shpnt, pos);

	*start = buffer + offset;
	if (pos - buffer < offset)
		return 0;
	else if (pos - buffer - offset < length)
		return pos - buffer - offset;
	else
		return length;
}

#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
Scsi_Host_Template driver_template = AHA152X;

#include "scsi_module.c"
#endif
