mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	Make it explicit that the SCSI host template is not modified. Signed-off-by: Bart Van Assche <bvanassche@acm.org> Link: https://lore.kernel.org/r/20230322195515.1267197-19-bvanassche@acm.org Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
		
			
				
	
	
		
			3434 lines
		
	
	
	
		
			83 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3434 lines
		
	
	
	
		
			83 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
						|
/* aha152x.c -- Adaptec AHA-152x driver
 | 
						|
 * Author: Jürgen E. Fischer, fischer@norbit.de
 | 
						|
 * Copyright 1993-2004 Jürgen E. Fischer
 | 
						|
 *
 | 
						|
 * $Id: aha152x.c,v 2.7 2004/01/24 11:42:59 fischer Exp $
 | 
						|
 *
 | 
						|
 * $Log: aha152x.c,v $
 | 
						|
 * Revision 2.7  2004/01/24 11:42:59  fischer
 | 
						|
 * - gather code that is not used by PCMCIA at the end
 | 
						|
 * - move request_region for !PCMCIA case to detection
 | 
						|
 * - migration to new scsi host api (remove legacy code)
 | 
						|
 * - free host scribble before scsi_done
 | 
						|
 * - fix error handling
 | 
						|
 * - one isapnp device added to id_table
 | 
						|
 *
 | 
						|
 * Revision 2.6  2003/10/30 20:52:47  fischer
 | 
						|
 * - interfaces changes for kernel 2.6
 | 
						|
 * - aha152x_probe_one introduced for pcmcia stub
 | 
						|
 * - fixed pnpdev handling
 | 
						|
 * - instead of allocation a new one, reuse command for request sense after check condition and reset
 | 
						|
 * - fixes race in is_complete
 | 
						|
 *
 | 
						|
 * Revision 2.5  2002/04/14 11:24:53  fischer
 | 
						|
 * - isapnp support
 | 
						|
 * - abort fixed
 | 
						|
 * - 2.5 support
 | 
						|
 *
 | 
						|
 * Revision 2.4  2000/12/16 12:53:56  fischer
 | 
						|
 * - allow REQUEST SENSE to be queued
 | 
						|
 * - handle shared PCI interrupts
 | 
						|
 *
 | 
						|
 * Revision 2.3  2000/11/04 16:40:26  fischer
 | 
						|
 * - handle data overruns
 | 
						|
 * - extend timeout for data phases
 | 
						|
 *
 | 
						|
 * Revision 2.2  2000/08/08 19:54:53  fischer
 | 
						|
 * - minor changes
 | 
						|
 *
 | 
						|
 * Revision 2.1  2000/05/17 16:23:17  fischer
 | 
						|
 * - signature update
 | 
						|
 * - fix for data out w/o scatter gather
 | 
						|
 *
 | 
						|
 * Revision 2.0  1999/12/25 15:07:32  fischer
 | 
						|
 * - interrupt routine completly reworked
 | 
						|
 * - basic support for new eh code
 | 
						|
 *
 | 
						|
 * Revision 1.21  1999/11/10 23:46:36  fischer
 | 
						|
 * - default to synchronous operation
 | 
						|
 * - synchronous negotiation fixed
 | 
						|
 * - added timeout to loops
 | 
						|
 * - debugging output can be controlled through procfs
 | 
						|
 *
 | 
						|
 * Revision 1.20  1999/11/07 18:37:31  fischer
 | 
						|
 * - synchronous operation works
 | 
						|
 * - resid support for sg driver
 | 
						|
 *
 | 
						|
 * Revision 1.19  1999/11/02 22:39:59  fischer
 | 
						|
 * - moved leading comments to README.aha152x
 | 
						|
 * - new additional module parameters
 | 
						|
 * - updates for 2.3
 | 
						|
 * - support for the Tripace TC1550 controller
 | 
						|
 * - interrupt handling changed
 | 
						|
 *
 | 
						|
 * 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.
 | 
						|
 *
 | 
						|
 **************************************************************************
 | 
						|
 | 
						|
 see Documentation/scsi/aha152x.rst for configuration details
 | 
						|
 | 
						|
 **************************************************************************/
 | 
						|
 | 
						|
#include <linux/module.h>
 | 
						|
#include <asm/irq.h>
 | 
						|
#include <linux/io.h>
 | 
						|
#include <linux/blkdev.h>
 | 
						|
#include <linux/completion.h>
 | 
						|
#include <linux/errno.h>
 | 
						|
#include <linux/string.h>
 | 
						|
#include <linux/wait.h>
 | 
						|
#include <linux/ioport.h>
 | 
						|
#include <linux/delay.h>
 | 
						|
#include <linux/proc_fs.h>
 | 
						|
#include <linux/interrupt.h>
 | 
						|
#include <linux/init.h>
 | 
						|
#include <linux/kernel.h>
 | 
						|
#include <linux/isapnp.h>
 | 
						|
#include <linux/spinlock.h>
 | 
						|
#include <linux/workqueue.h>
 | 
						|
#include <linux/list.h>
 | 
						|
#include <linux/slab.h>
 | 
						|
 | 
						|
#include <scsi/scsi.h>
 | 
						|
#include <scsi/scsi_cmnd.h>
 | 
						|
#include <scsi/scsi_dbg.h>
 | 
						|
#include <scsi/scsi_device.h>
 | 
						|
#include <scsi/scsi_eh.h>
 | 
						|
#include <scsi/scsi_host.h>
 | 
						|
#include <scsi/scsi_tcq.h>
 | 
						|
#include <scsi/scsi_transport_spi.h>
 | 
						|
#include <scsi/scsicam.h>
 | 
						|
#include "aha152x.h"
 | 
						|
 | 
						|
static LIST_HEAD(aha152x_host_list);
 | 
						|
 | 
						|
 | 
						|
/* DEFINES */
 | 
						|
 | 
						|
/* For PCMCIA cards, always use AUTOCONF */
 | 
						|
#if defined(AHA152X_PCMCIA) || defined(MODULE)
 | 
						|
#if !defined(AUTOCONF)
 | 
						|
#define AUTOCONF
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
#if !defined(AUTOCONF) && !defined(SETUP0)
 | 
						|
#error define AUTOCONF or SETUP0
 | 
						|
#endif
 | 
						|
 | 
						|
#define	DO_LOCK(flags)		spin_lock_irqsave(&QLOCK,flags)
 | 
						|
#define	DO_UNLOCK(flags)	spin_unlock_irqrestore(&QLOCK,flags)
 | 
						|
 | 
						|
#define LEAD		"(scsi%d:%d:%d) "
 | 
						|
#define INFO_LEAD	KERN_INFO	LEAD
 | 
						|
#define CMDINFO(cmd) \
 | 
						|
			(cmd) ? ((cmd)->device->host->host_no) : -1, \
 | 
						|
                        (cmd) ? ((cmd)->device->id & 0x0f) : -1, \
 | 
						|
			(cmd) ? ((u8)(cmd)->device->lun & 0x07) : -1
 | 
						|
 | 
						|
static inline void
 | 
						|
CMD_INC_RESID(struct scsi_cmnd *cmd, int inc)
 | 
						|
{
 | 
						|
	scsi_set_resid(cmd, scsi_get_resid(cmd) + inc);
 | 
						|
}
 | 
						|
 | 
						|
#define DELAY_DEFAULT 1000
 | 
						|
 | 
						|
#if defined(AHA152X_PCMCIA)
 | 
						|
#define IRQ_MIN 0
 | 
						|
#define IRQ_MAX 16
 | 
						|
#else
 | 
						|
#define IRQ_MIN 9
 | 
						|
#if defined(__PPC)
 | 
						|
#define IRQ_MAX (nr_irqs-1)
 | 
						|
#else
 | 
						|
#define IRQ_MAX 12
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
enum {
 | 
						|
	not_issued	= 0x0001,	/* command not yet issued */
 | 
						|
	selecting	= 0x0002,	/* target is being selected */
 | 
						|
	identified	= 0x0004,	/* IDENTIFY was sent */
 | 
						|
	disconnected	= 0x0008,	/* target disconnected */
 | 
						|
	completed	= 0x0010,	/* target sent COMMAND COMPLETE */
 | 
						|
	aborted		= 0x0020,	/* ABORT was sent */
 | 
						|
	resetted	= 0x0040,	/* BUS DEVICE RESET was sent */
 | 
						|
	spiordy		= 0x0080,	/* waiting for SPIORDY to raise */
 | 
						|
	syncneg		= 0x0100,	/* synchronous negotiation in progress */
 | 
						|
	aborting	= 0x0200,	/* ABORT is pending */
 | 
						|
	resetting	= 0x0400,	/* BUS DEVICE RESET is pending */
 | 
						|
	check_condition = 0x0800,	/* requesting sense after CHECK CONDITION */
 | 
						|
};
 | 
						|
 | 
						|
struct aha152x_cmd_priv {
 | 
						|
	char *ptr;
 | 
						|
	int this_residual;
 | 
						|
	struct scatterlist *buffer;
 | 
						|
	int status;
 | 
						|
	int message;
 | 
						|
	int sent_command;
 | 
						|
	int phase;
 | 
						|
};
 | 
						|
 | 
						|
static struct aha152x_cmd_priv *aha152x_priv(struct scsi_cmnd *cmd)
 | 
						|
{
 | 
						|
	return scsi_cmd_priv(cmd);
 | 
						|
}
 | 
						|
 | 
						|
MODULE_AUTHOR("Jürgen Fischer");
 | 
						|
MODULE_DESCRIPTION(AHA152X_REVID);
 | 
						|
MODULE_LICENSE("GPL");
 | 
						|
 | 
						|
#if !defined(AHA152X_PCMCIA)
 | 
						|
#if defined(MODULE)
 | 
						|
static int io[] = {0, 0};
 | 
						|
module_param_hw_array(io, int, ioport, NULL, 0);
 | 
						|
MODULE_PARM_DESC(io,"base io address of controller");
 | 
						|
 | 
						|
static int irq[] = {0, 0};
 | 
						|
module_param_hw_array(irq, int, irq, NULL, 0);
 | 
						|
MODULE_PARM_DESC(irq,"interrupt for controller");
 | 
						|
 | 
						|
static int scsiid[] = {7, 7};
 | 
						|
module_param_array(scsiid, int, NULL, 0);
 | 
						|
MODULE_PARM_DESC(scsiid,"scsi id of controller");
 | 
						|
 | 
						|
static int reconnect[] = {1, 1};
 | 
						|
module_param_array(reconnect, int, NULL, 0);
 | 
						|
MODULE_PARM_DESC(reconnect,"allow targets to disconnect");
 | 
						|
 | 
						|
static int parity[] = {1, 1};
 | 
						|
module_param_array(parity, int, NULL, 0);
 | 
						|
MODULE_PARM_DESC(parity,"use scsi parity");
 | 
						|
 | 
						|
static int sync[] = {1, 1};
 | 
						|
module_param_array(sync, int, NULL, 0);
 | 
						|
MODULE_PARM_DESC(sync,"use synchronous transfers");
 | 
						|
 | 
						|
static int delay[] = {DELAY_DEFAULT, DELAY_DEFAULT};
 | 
						|
module_param_array(delay, int, NULL, 0);
 | 
						|
MODULE_PARM_DESC(delay,"scsi reset delay");
 | 
						|
 | 
						|
static int exttrans[] = {0, 0};
 | 
						|
module_param_array(exttrans, int, NULL, 0);
 | 
						|
MODULE_PARM_DESC(exttrans,"use extended translation");
 | 
						|
 | 
						|
static int aha152x[] = {0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0};
 | 
						|
module_param_array(aha152x, int, NULL, 0);
 | 
						|
MODULE_PARM_DESC(aha152x, "parameters for first controller");
 | 
						|
 | 
						|
static int aha152x1[] = {0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0};
 | 
						|
module_param_array(aha152x1, int, NULL, 0);
 | 
						|
MODULE_PARM_DESC(aha152x1, "parameters for second controller");
 | 
						|
#endif /* MODULE */
 | 
						|
 | 
						|
#ifdef __ISAPNP__
 | 
						|
static struct isapnp_device_id id_table[] = {
 | 
						|
	{ ISAPNP_ANY_ID, ISAPNP_ANY_ID,	ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1502), 0 },
 | 
						|
	{ ISAPNP_ANY_ID, ISAPNP_ANY_ID,	ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1505), 0 },
 | 
						|
	{ ISAPNP_ANY_ID, ISAPNP_ANY_ID,	ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1510), 0 },
 | 
						|
	{ ISAPNP_ANY_ID, ISAPNP_ANY_ID,	ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1515), 0 },
 | 
						|
	{ ISAPNP_ANY_ID, ISAPNP_ANY_ID,	ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1520), 0 },
 | 
						|
	{ ISAPNP_ANY_ID, ISAPNP_ANY_ID,	ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x2015), 0 },
 | 
						|
	{ ISAPNP_ANY_ID, ISAPNP_ANY_ID,	ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1522), 0 },
 | 
						|
	{ ISAPNP_ANY_ID, ISAPNP_ANY_ID,	ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x2215), 0 },
 | 
						|
	{ ISAPNP_ANY_ID, ISAPNP_ANY_ID,	ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1530), 0 },
 | 
						|
	{ ISAPNP_ANY_ID, ISAPNP_ANY_ID,	ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x3015), 0 },
 | 
						|
	{ ISAPNP_ANY_ID, ISAPNP_ANY_ID,	ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1532), 0 },
 | 
						|
	{ ISAPNP_ANY_ID, ISAPNP_ANY_ID,	ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x3215), 0 },
 | 
						|
	{ ISAPNP_ANY_ID, ISAPNP_ANY_ID,	ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x6360), 0 },
 | 
						|
	{ ISAPNP_DEVICE_SINGLE_END, }
 | 
						|
};
 | 
						|
MODULE_DEVICE_TABLE(isapnp, id_table);
 | 
						|
#endif /* ISAPNP */
 | 
						|
 | 
						|
#endif /* !AHA152X_PCMCIA */
 | 
						|
 | 
						|
static const struct scsi_host_template aha152x_driver_template;
 | 
						|
 | 
						|
/*
 | 
						|
 * internal states of the host
 | 
						|
 *
 | 
						|
 */
 | 
						|
enum aha152x_state {
 | 
						|
	idle=0,
 | 
						|
	unknown,
 | 
						|
	seldo,
 | 
						|
	seldi,
 | 
						|
	selto,
 | 
						|
	busfree,
 | 
						|
	msgo,
 | 
						|
	cmd,
 | 
						|
	msgi,
 | 
						|
	status,
 | 
						|
	datai,
 | 
						|
	datao,
 | 
						|
	parerr,
 | 
						|
	rsti,
 | 
						|
	maxstate
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * current state information of the host
 | 
						|
 *
 | 
						|
 */
 | 
						|
struct aha152x_hostdata {
 | 
						|
	struct scsi_cmnd *issue_SC;
 | 
						|
		/* pending commands to issue */
 | 
						|
 | 
						|
	struct scsi_cmnd *current_SC;
 | 
						|
		/* current command on the bus */
 | 
						|
 | 
						|
	struct scsi_cmnd *disconnected_SC;
 | 
						|
		/* commands that disconnected */
 | 
						|
 | 
						|
	struct scsi_cmnd *done_SC;
 | 
						|
		/* command that was completed */
 | 
						|
 | 
						|
	spinlock_t lock;
 | 
						|
		/* host lock */
 | 
						|
 | 
						|
#if defined(AHA152X_STAT)
 | 
						|
	int	      total_commands;
 | 
						|
	int	      disconnections;
 | 
						|
	int	      busfree_without_any_action;
 | 
						|
	int	      busfree_without_old_command;
 | 
						|
	int	      busfree_without_new_command;
 | 
						|
	int	      busfree_without_done_command;
 | 
						|
	int	      busfree_with_check_condition;
 | 
						|
	int	      count[maxstate];
 | 
						|
	int	      count_trans[maxstate];
 | 
						|
	unsigned long time[maxstate];
 | 
						|
#endif
 | 
						|
 | 
						|
	int commands;		/* current number of commands */
 | 
						|
 | 
						|
	int reconnect;		/* disconnection allowed */
 | 
						|
	int parity;		/* parity checking enabled */
 | 
						|
	int synchronous;	/* synchronous transferes enabled */
 | 
						|
	int delay;		/* reset out delay */
 | 
						|
	int ext_trans;		/* extended translation enabled */
 | 
						|
 | 
						|
	int swint;		/* software-interrupt was fired during detect() */
 | 
						|
	int service;		/* bh needs to be run */
 | 
						|
	int in_intr;		/* bh is running */
 | 
						|
 | 
						|
	/* current state,
 | 
						|
	   previous state,
 | 
						|
	   last state different from current state */
 | 
						|
	enum aha152x_state state, prevstate, laststate;
 | 
						|
 | 
						|
	int target;
 | 
						|
		/* reconnecting target */
 | 
						|
 | 
						|
	unsigned char syncrate[8];
 | 
						|
		/* current synchronous transfer agreements */
 | 
						|
 | 
						|
	unsigned char syncneg[8];
 | 
						|
		/* 0: no negotiation;
 | 
						|
		 * 1: negotiation in progress;
 | 
						|
		 * 2: negotiation completed
 | 
						|
		 */
 | 
						|
 | 
						|
	int cmd_i;
 | 
						|
		/* number of sent bytes of current command */
 | 
						|
 | 
						|
	int msgi_len;
 | 
						|
		/* number of received message bytes */
 | 
						|
	unsigned char msgi[256];
 | 
						|
		/* received message bytes */
 | 
						|
 | 
						|
	int msgo_i, msgo_len;
 | 
						|
		/* number of sent bytes and length of current messages */
 | 
						|
	unsigned char msgo[256];
 | 
						|
		/* pending messages */
 | 
						|
 | 
						|
	int data_len;
 | 
						|
		/* number of sent/received bytes in dataphase */
 | 
						|
 | 
						|
	unsigned long io_port0;
 | 
						|
	unsigned long io_port1;
 | 
						|
 | 
						|
#ifdef __ISAPNP__
 | 
						|
	struct pnp_dev *pnpdev;
 | 
						|
#endif
 | 
						|
	struct list_head host_list;
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * host specific command extension
 | 
						|
 *
 | 
						|
 */
 | 
						|
struct aha152x_scdata {
 | 
						|
	struct scsi_cmnd *next;	/* next sc in queue */
 | 
						|
	struct completion *done;/* semaphore to block on */
 | 
						|
	struct scsi_eh_save ses;
 | 
						|
};
 | 
						|
 | 
						|
/* access macros for hostdata */
 | 
						|
 | 
						|
#define HOSTDATA(shpnt)		((struct aha152x_hostdata *) &shpnt->hostdata)
 | 
						|
 | 
						|
#define HOSTNO			((shpnt)->host_no)
 | 
						|
 | 
						|
#define CURRENT_SC		(HOSTDATA(shpnt)->current_SC)
 | 
						|
#define DONE_SC			(HOSTDATA(shpnt)->done_SC)
 | 
						|
#define ISSUE_SC		(HOSTDATA(shpnt)->issue_SC)
 | 
						|
#define DISCONNECTED_SC		(HOSTDATA(shpnt)->disconnected_SC)
 | 
						|
#define QLOCK			(HOSTDATA(shpnt)->lock)
 | 
						|
#define QLOCKER			(HOSTDATA(shpnt)->locker)
 | 
						|
#define QLOCKERL		(HOSTDATA(shpnt)->lockerl)
 | 
						|
 | 
						|
#define STATE			(HOSTDATA(shpnt)->state)
 | 
						|
#define PREVSTATE		(HOSTDATA(shpnt)->prevstate)
 | 
						|
#define LASTSTATE		(HOSTDATA(shpnt)->laststate)
 | 
						|
 | 
						|
#define RECONN_TARGET		(HOSTDATA(shpnt)->target)
 | 
						|
 | 
						|
#define CMD_I			(HOSTDATA(shpnt)->cmd_i)
 | 
						|
 | 
						|
#define MSGO(i)			(HOSTDATA(shpnt)->msgo[i])
 | 
						|
#define MSGO_I			(HOSTDATA(shpnt)->msgo_i)
 | 
						|
#define MSGOLEN			(HOSTDATA(shpnt)->msgo_len)
 | 
						|
#define ADDMSGO(x)		(MSGOLEN<256 ? (void)(MSGO(MSGOLEN++)=x) : aha152x_error(shpnt,"MSGO overflow"))
 | 
						|
 | 
						|
#define MSGI(i)			(HOSTDATA(shpnt)->msgi[i])
 | 
						|
#define MSGILEN			(HOSTDATA(shpnt)->msgi_len)
 | 
						|
#define ADDMSGI(x)		(MSGILEN<256 ? (void)(MSGI(MSGILEN++)=x) : aha152x_error(shpnt,"MSGI overflow"))
 | 
						|
 | 
						|
#define DATA_LEN		(HOSTDATA(shpnt)->data_len)
 | 
						|
 | 
						|
#define SYNCRATE		(HOSTDATA(shpnt)->syncrate[CURRENT_SC->device->id])
 | 
						|
#define SYNCNEG			(HOSTDATA(shpnt)->syncneg[CURRENT_SC->device->id])
 | 
						|
 | 
						|
#define DELAY			(HOSTDATA(shpnt)->delay)
 | 
						|
#define EXT_TRANS		(HOSTDATA(shpnt)->ext_trans)
 | 
						|
#define TC1550			(HOSTDATA(shpnt)->tc1550)
 | 
						|
#define RECONNECT		(HOSTDATA(shpnt)->reconnect)
 | 
						|
#define PARITY			(HOSTDATA(shpnt)->parity)
 | 
						|
#define SYNCHRONOUS		(HOSTDATA(shpnt)->synchronous)
 | 
						|
 | 
						|
#define HOSTIOPORT0		(HOSTDATA(shpnt)->io_port0)
 | 
						|
#define HOSTIOPORT1		(HOSTDATA(shpnt)->io_port1)
 | 
						|
 | 
						|
#define SCDATA(SCpnt)		((struct aha152x_scdata *) (SCpnt)->host_scribble)
 | 
						|
#define SCNEXT(SCpnt)		SCDATA(SCpnt)->next
 | 
						|
#define SCSEM(SCpnt)		SCDATA(SCpnt)->done
 | 
						|
 | 
						|
#define SG_ADDRESS(buffer)	((char *) sg_virt((buffer)))
 | 
						|
 | 
						|
/* state handling */
 | 
						|
static void seldi_run(struct Scsi_Host *shpnt);
 | 
						|
static void seldo_run(struct Scsi_Host *shpnt);
 | 
						|
static void selto_run(struct Scsi_Host *shpnt);
 | 
						|
static void busfree_run(struct Scsi_Host *shpnt);
 | 
						|
 | 
						|
static void msgo_init(struct Scsi_Host *shpnt);
 | 
						|
static void msgo_run(struct Scsi_Host *shpnt);
 | 
						|
static void msgo_end(struct Scsi_Host *shpnt);
 | 
						|
 | 
						|
static void cmd_init(struct Scsi_Host *shpnt);
 | 
						|
static void cmd_run(struct Scsi_Host *shpnt);
 | 
						|
static void cmd_end(struct Scsi_Host *shpnt);
 | 
						|
 | 
						|
static void datai_init(struct Scsi_Host *shpnt);
 | 
						|
static void datai_run(struct Scsi_Host *shpnt);
 | 
						|
static void datai_end(struct Scsi_Host *shpnt);
 | 
						|
 | 
						|
static void datao_init(struct Scsi_Host *shpnt);
 | 
						|
static void datao_run(struct Scsi_Host *shpnt);
 | 
						|
static void datao_end(struct Scsi_Host *shpnt);
 | 
						|
 | 
						|
static void status_run(struct Scsi_Host *shpnt);
 | 
						|
 | 
						|
static void msgi_run(struct Scsi_Host *shpnt);
 | 
						|
static void msgi_end(struct Scsi_Host *shpnt);
 | 
						|
 | 
						|
static void parerr_run(struct Scsi_Host *shpnt);
 | 
						|
static void rsti_run(struct Scsi_Host *shpnt);
 | 
						|
 | 
						|
static void is_complete(struct Scsi_Host *shpnt);
 | 
						|
 | 
						|
/*
 | 
						|
 * driver states
 | 
						|
 *
 | 
						|
 */
 | 
						|
static struct {
 | 
						|
	char		*name;
 | 
						|
	void		(*init)(struct Scsi_Host *);
 | 
						|
	void		(*run)(struct Scsi_Host *);
 | 
						|
	void		(*end)(struct Scsi_Host *);
 | 
						|
	int		spio;
 | 
						|
} states[] = {
 | 
						|
	{ "idle",	NULL,		NULL,		NULL,		0},
 | 
						|
	{ "unknown",	NULL,		NULL,		NULL,		0},
 | 
						|
	{ "seldo",	NULL,		seldo_run,	NULL,		0},
 | 
						|
	{ "seldi",	NULL,		seldi_run,	NULL,		0},
 | 
						|
	{ "selto",	NULL,		selto_run,	NULL,		0},
 | 
						|
	{ "busfree",	NULL,		busfree_run,	NULL,		0},
 | 
						|
	{ "msgo",	msgo_init,	msgo_run,	msgo_end,	1},
 | 
						|
	{ "cmd",	cmd_init,	cmd_run,	cmd_end,	1},
 | 
						|
	{ "msgi",	NULL,		msgi_run,	msgi_end,	1},
 | 
						|
	{ "status",	NULL,		status_run,	NULL,		1},
 | 
						|
	{ "datai",	datai_init,	datai_run,	datai_end,	0},
 | 
						|
	{ "datao",	datao_init,	datao_run,	datao_end,	0},
 | 
						|
	{ "parerr",	NULL,		parerr_run,	NULL,		0},
 | 
						|
	{ "rsti",	NULL,		rsti_run,	NULL,		0},
 | 
						|
};
 | 
						|
 | 
						|
/* setup & interrupt */
 | 
						|
static irqreturn_t intr(int irq, void *dev_id);
 | 
						|
static void reset_ports(struct Scsi_Host *shpnt);
 | 
						|
static void aha152x_error(struct Scsi_Host *shpnt, char *msg);
 | 
						|
static void done(struct Scsi_Host *shpnt, unsigned char status_byte,
 | 
						|
		 unsigned char host_byte);
 | 
						|
 | 
						|
/* diagnostics */
 | 
						|
static void show_command(struct scsi_cmnd * ptr);
 | 
						|
static void show_queues(struct Scsi_Host *shpnt);
 | 
						|
static void disp_enintr(struct Scsi_Host *shpnt);
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 *  queue services:
 | 
						|
 *
 | 
						|
 */
 | 
						|
static inline void append_SC(struct scsi_cmnd **SC, struct scsi_cmnd *new_SC)
 | 
						|
{
 | 
						|
	struct scsi_cmnd *end;
 | 
						|
 | 
						|
	SCNEXT(new_SC) = NULL;
 | 
						|
	if (!*SC)
 | 
						|
		*SC = new_SC;
 | 
						|
	else {
 | 
						|
		for (end = *SC; SCNEXT(end); end = SCNEXT(end))
 | 
						|
			;
 | 
						|
		SCNEXT(end) = new_SC;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static inline struct scsi_cmnd *remove_first_SC(struct scsi_cmnd ** SC)
 | 
						|
{
 | 
						|
	struct scsi_cmnd *ptr;
 | 
						|
 | 
						|
	ptr = *SC;
 | 
						|
	if (ptr) {
 | 
						|
		*SC = SCNEXT(*SC);
 | 
						|
		SCNEXT(ptr)=NULL;
 | 
						|
	}
 | 
						|
	return ptr;
 | 
						|
}
 | 
						|
 | 
						|
static inline struct scsi_cmnd *remove_lun_SC(struct scsi_cmnd ** SC,
 | 
						|
					      int target, int lun)
 | 
						|
{
 | 
						|
	struct scsi_cmnd *ptr, *prev;
 | 
						|
 | 
						|
	for (ptr = *SC, prev = NULL;
 | 
						|
	     ptr && ((ptr->device->id != target) || (ptr->device->lun != lun));
 | 
						|
	     prev = ptr, ptr = SCNEXT(ptr))
 | 
						|
	     ;
 | 
						|
 | 
						|
	if (ptr) {
 | 
						|
		if (prev)
 | 
						|
			SCNEXT(prev) = SCNEXT(ptr);
 | 
						|
		else
 | 
						|
			*SC = SCNEXT(ptr);
 | 
						|
 | 
						|
		SCNEXT(ptr)=NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	return ptr;
 | 
						|
}
 | 
						|
 | 
						|
static inline struct scsi_cmnd *remove_SC(struct scsi_cmnd **SC,
 | 
						|
					  struct scsi_cmnd *SCp)
 | 
						|
{
 | 
						|
	struct scsi_cmnd *ptr, *prev;
 | 
						|
 | 
						|
	for (ptr = *SC, prev = NULL;
 | 
						|
	     ptr && SCp!=ptr;
 | 
						|
	     prev = ptr, ptr = SCNEXT(ptr))
 | 
						|
	     ;
 | 
						|
 | 
						|
	if (ptr) {
 | 
						|
		if (prev)
 | 
						|
			SCNEXT(prev) = SCNEXT(ptr);
 | 
						|
		else
 | 
						|
			*SC = SCNEXT(ptr);
 | 
						|
 | 
						|
		SCNEXT(ptr)=NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	return ptr;
 | 
						|
}
 | 
						|
 | 
						|
static irqreturn_t swintr(int irqno, void *dev_id)
 | 
						|
{
 | 
						|
	struct Scsi_Host *shpnt = dev_id;
 | 
						|
 | 
						|
	HOSTDATA(shpnt)->swint++;
 | 
						|
 | 
						|
	SETPORT(DMACNTRL0, INTEN);
 | 
						|
	return IRQ_HANDLED;
 | 
						|
}
 | 
						|
 | 
						|
struct Scsi_Host *aha152x_probe_one(struct aha152x_setup *setup)
 | 
						|
{
 | 
						|
	struct Scsi_Host *shpnt;
 | 
						|
 | 
						|
	shpnt = scsi_host_alloc(&aha152x_driver_template, sizeof(struct aha152x_hostdata));
 | 
						|
	if (!shpnt) {
 | 
						|
		printk(KERN_ERR "aha152x: scsi_host_alloc failed\n");
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	memset(HOSTDATA(shpnt), 0, sizeof *HOSTDATA(shpnt));
 | 
						|
	INIT_LIST_HEAD(&HOSTDATA(shpnt)->host_list);
 | 
						|
 | 
						|
	/* need to have host registered before triggering any interrupt */
 | 
						|
	list_add_tail(&HOSTDATA(shpnt)->host_list, &aha152x_host_list);
 | 
						|
 | 
						|
	shpnt->io_port   = setup->io_port;
 | 
						|
	shpnt->n_io_port = IO_RANGE;
 | 
						|
	shpnt->irq       = setup->irq;
 | 
						|
 | 
						|
	if (!setup->tc1550) {
 | 
						|
		HOSTIOPORT0 = setup->io_port;
 | 
						|
		HOSTIOPORT1 = setup->io_port;
 | 
						|
	} else {
 | 
						|
		HOSTIOPORT0 = setup->io_port+0x10;
 | 
						|
		HOSTIOPORT1 = setup->io_port-0x10;
 | 
						|
	}
 | 
						|
 | 
						|
	spin_lock_init(&QLOCK);
 | 
						|
	RECONNECT   = setup->reconnect;
 | 
						|
	SYNCHRONOUS = setup->synchronous;
 | 
						|
	PARITY      = setup->parity;
 | 
						|
	DELAY       = setup->delay;
 | 
						|
	EXT_TRANS   = setup->ext_trans;
 | 
						|
 | 
						|
	SETPORT(SCSIID, setup->scsiid << 4);
 | 
						|
	shpnt->this_id = setup->scsiid;
 | 
						|
 | 
						|
	if (setup->reconnect)
 | 
						|
		shpnt->can_queue = AHA152X_MAXQUEUE;
 | 
						|
 | 
						|
	/* RESET OUT */
 | 
						|
	printk("aha152x: resetting bus...\n");
 | 
						|
	SETPORT(SCSISEQ, SCSIRSTO);
 | 
						|
	mdelay(256);
 | 
						|
	SETPORT(SCSISEQ, 0);
 | 
						|
	mdelay(DELAY);
 | 
						|
 | 
						|
	reset_ports(shpnt);
 | 
						|
 | 
						|
	printk(KERN_INFO
 | 
						|
	       "aha152x%d%s: "
 | 
						|
	       "vital data: rev=%x, "
 | 
						|
	       "io=0x%03lx (0x%03lx/0x%03lx), "
 | 
						|
	       "irq=%d, "
 | 
						|
	       "scsiid=%d, "
 | 
						|
	       "reconnect=%s, "
 | 
						|
	       "parity=%s, "
 | 
						|
	       "synchronous=%s, "
 | 
						|
	       "delay=%d, "
 | 
						|
	       "extended translation=%s\n",
 | 
						|
	       shpnt->host_no, setup->tc1550 ? " (tc1550 mode)" : "",
 | 
						|
	       GETPORT(REV) & 0x7,
 | 
						|
	       shpnt->io_port, HOSTIOPORT0, HOSTIOPORT1,
 | 
						|
	       shpnt->irq,
 | 
						|
	       shpnt->this_id,
 | 
						|
	       RECONNECT ? "enabled" : "disabled",
 | 
						|
	       PARITY ? "enabled" : "disabled",
 | 
						|
	       SYNCHRONOUS ? "enabled" : "disabled",
 | 
						|
	       DELAY,
 | 
						|
	       EXT_TRANS ? "enabled" : "disabled");
 | 
						|
 | 
						|
	/* not expecting any interrupts */
 | 
						|
	SETPORT(SIMODE0, 0);
 | 
						|
	SETPORT(SIMODE1, 0);
 | 
						|
 | 
						|
	if (request_irq(shpnt->irq, swintr, IRQF_SHARED, "aha152x", shpnt)) {
 | 
						|
		printk(KERN_ERR "aha152x%d: irq %d busy.\n", shpnt->host_no, shpnt->irq);
 | 
						|
		goto out_host_put;
 | 
						|
	}
 | 
						|
 | 
						|
	HOSTDATA(shpnt)->swint = 0;
 | 
						|
 | 
						|
	printk(KERN_INFO "aha152x%d: trying software interrupt, ", shpnt->host_no);
 | 
						|
 | 
						|
	mb();
 | 
						|
	SETPORT(DMACNTRL0, SWINT|INTEN);
 | 
						|
	mdelay(1000);
 | 
						|
	free_irq(shpnt->irq, shpnt);
 | 
						|
 | 
						|
	if (!HOSTDATA(shpnt)->swint) {
 | 
						|
		if (TESTHI(DMASTAT, INTSTAT)) {
 | 
						|
			printk("lost.\n");
 | 
						|
		} else {
 | 
						|
			printk("failed.\n");
 | 
						|
		}
 | 
						|
 | 
						|
		SETPORT(DMACNTRL0, INTEN);
 | 
						|
 | 
						|
		printk(KERN_ERR "aha152x%d: irq %d possibly wrong.  "
 | 
						|
				"Please verify.\n", shpnt->host_no, shpnt->irq);
 | 
						|
		goto out_host_put;
 | 
						|
	}
 | 
						|
	printk("ok.\n");
 | 
						|
 | 
						|
 | 
						|
	/* clear interrupts */
 | 
						|
	SETPORT(SSTAT0, 0x7f);
 | 
						|
	SETPORT(SSTAT1, 0xef);
 | 
						|
 | 
						|
	if (request_irq(shpnt->irq, intr, IRQF_SHARED, "aha152x", shpnt)) {
 | 
						|
		printk(KERN_ERR "aha152x%d: failed to reassign irq %d.\n", shpnt->host_no, shpnt->irq);
 | 
						|
		goto out_host_put;
 | 
						|
	}
 | 
						|
 | 
						|
	if( scsi_add_host(shpnt, NULL) ) {
 | 
						|
		free_irq(shpnt->irq, shpnt);
 | 
						|
		printk(KERN_ERR "aha152x%d: failed to add host.\n", shpnt->host_no);
 | 
						|
		goto out_host_put;
 | 
						|
	}
 | 
						|
 | 
						|
	scsi_scan_host(shpnt);
 | 
						|
 | 
						|
	return shpnt;
 | 
						|
 | 
						|
out_host_put:
 | 
						|
	list_del(&HOSTDATA(shpnt)->host_list);
 | 
						|
	scsi_host_put(shpnt);
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
void aha152x_release(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	if (!shpnt)
 | 
						|
		return;
 | 
						|
 | 
						|
	scsi_remove_host(shpnt);
 | 
						|
	if (shpnt->irq)
 | 
						|
		free_irq(shpnt->irq, shpnt);
 | 
						|
 | 
						|
#if !defined(AHA152X_PCMCIA)
 | 
						|
	if (shpnt->io_port)
 | 
						|
		release_region(shpnt->io_port, IO_RANGE);
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef __ISAPNP__
 | 
						|
	if (HOSTDATA(shpnt)->pnpdev)
 | 
						|
		pnp_device_detach(HOSTDATA(shpnt)->pnpdev);
 | 
						|
#endif
 | 
						|
 | 
						|
	list_del(&HOSTDATA(shpnt)->host_list);
 | 
						|
	scsi_host_put(shpnt);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * setup controller to generate interrupts depending
 | 
						|
 * on current state (lock has to be acquired)
 | 
						|
 *
 | 
						|
 */
 | 
						|
static int setup_expected_interrupts(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	if(CURRENT_SC) {
 | 
						|
		struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
 | 
						|
 | 
						|
		acp->phase |= 1 << 16;
 | 
						|
 | 
						|
		if (acp->phase & selecting) {
 | 
						|
			SETPORT(SSTAT1, SELTO);
 | 
						|
			SETPORT(SIMODE0, ENSELDO | (DISCONNECTED_SC ? ENSELDI : 0));
 | 
						|
			SETPORT(SIMODE1, ENSELTIMO);
 | 
						|
		} else {
 | 
						|
			SETPORT(SIMODE0, (acp->phase & spiordy) ? ENSPIORDY : 0);
 | 
						|
			SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE);
 | 
						|
		}
 | 
						|
	} else if(STATE==seldi) {
 | 
						|
		SETPORT(SIMODE0, 0);
 | 
						|
		SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE);
 | 
						|
	} else {
 | 
						|
		SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
 | 
						|
		SETPORT(SIMODE1, ENSCSIRST | ( (ISSUE_SC||DONE_SC) ? ENBUSFREE : 0));
 | 
						|
	}
 | 
						|
 | 
						|
	if(!HOSTDATA(shpnt)->in_intr)
 | 
						|
		SETBITS(DMACNTRL0, INTEN);
 | 
						|
 | 
						|
	return TESTHI(DMASTAT, INTSTAT);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 *  Queue a command and setup interrupts for a free bus.
 | 
						|
 */
 | 
						|
static int aha152x_internal_queue(struct scsi_cmnd *SCpnt,
 | 
						|
				  struct completion *complete, int phase)
 | 
						|
{
 | 
						|
	struct aha152x_cmd_priv *acp = aha152x_priv(SCpnt);
 | 
						|
	struct Scsi_Host *shpnt = SCpnt->device->host;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	acp->phase        = not_issued | phase;
 | 
						|
	acp->status       = 0x1; /* Illegal status by SCSI standard */
 | 
						|
	acp->message      = 0;
 | 
						|
	acp->sent_command = 0;
 | 
						|
 | 
						|
	if (acp->phase & (resetting | check_condition)) {
 | 
						|
		if (!SCpnt->host_scribble || SCSEM(SCpnt) || SCNEXT(SCpnt)) {
 | 
						|
			scmd_printk(KERN_ERR, SCpnt, "cannot reuse command\n");
 | 
						|
			return FAILED;
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		SCpnt->host_scribble = kmalloc(sizeof(struct aha152x_scdata), GFP_ATOMIC);
 | 
						|
		if(!SCpnt->host_scribble) {
 | 
						|
			scmd_printk(KERN_ERR, SCpnt, "allocation failed\n");
 | 
						|
			return FAILED;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	SCNEXT(SCpnt)		= NULL;
 | 
						|
	SCSEM(SCpnt)		= complete;
 | 
						|
 | 
						|
	/* setup scratch area
 | 
						|
	   SCp.ptr              : buffer pointer
 | 
						|
	   SCp.this_residual    : buffer length
 | 
						|
	   SCp.buffer           : next buffer
 | 
						|
	   SCp.phase            : current state of the command */
 | 
						|
 | 
						|
	if ((phase & resetting) || !scsi_sglist(SCpnt)) {
 | 
						|
		acp->ptr           = NULL;
 | 
						|
		acp->this_residual = 0;
 | 
						|
		scsi_set_resid(SCpnt, 0);
 | 
						|
		acp->buffer        = NULL;
 | 
						|
	} else {
 | 
						|
		scsi_set_resid(SCpnt, scsi_bufflen(SCpnt));
 | 
						|
		acp->buffer        = scsi_sglist(SCpnt);
 | 
						|
		acp->ptr           = SG_ADDRESS(acp->buffer);
 | 
						|
		acp->this_residual = acp->buffer->length;
 | 
						|
	}
 | 
						|
 | 
						|
	DO_LOCK(flags);
 | 
						|
 | 
						|
#if defined(AHA152X_STAT)
 | 
						|
	HOSTDATA(shpnt)->total_commands++;
 | 
						|
#endif
 | 
						|
 | 
						|
	/* Turn led on, when this is the first command. */
 | 
						|
	HOSTDATA(shpnt)->commands++;
 | 
						|
	if (HOSTDATA(shpnt)->commands==1)
 | 
						|
		SETPORT(PORTA, 1);
 | 
						|
 | 
						|
	append_SC(&ISSUE_SC, SCpnt);
 | 
						|
 | 
						|
	if(!HOSTDATA(shpnt)->in_intr)
 | 
						|
		setup_expected_interrupts(shpnt);
 | 
						|
 | 
						|
	DO_UNLOCK(flags);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 *  queue a command
 | 
						|
 *
 | 
						|
 */
 | 
						|
static int aha152x_queue_lck(struct scsi_cmnd *SCpnt)
 | 
						|
{
 | 
						|
	return aha152x_internal_queue(SCpnt, NULL, 0);
 | 
						|
}
 | 
						|
 | 
						|
static DEF_SCSI_QCMD(aha152x_queue)
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void reset_done(struct scsi_cmnd *SCpnt)
 | 
						|
{
 | 
						|
	if(SCSEM(SCpnt)) {
 | 
						|
		complete(SCSEM(SCpnt));
 | 
						|
	} else {
 | 
						|
		printk(KERN_ERR "aha152x: reset_done w/o completion\n");
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void aha152x_scsi_done(struct scsi_cmnd *SCpnt)
 | 
						|
{
 | 
						|
	if (aha152x_priv(SCpnt)->phase & resetting)
 | 
						|
		reset_done(SCpnt);
 | 
						|
	else
 | 
						|
		scsi_done(SCpnt);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 *  Abort a command
 | 
						|
 *
 | 
						|
 */
 | 
						|
static int aha152x_abort(struct scsi_cmnd *SCpnt)
 | 
						|
{
 | 
						|
	struct Scsi_Host *shpnt = SCpnt->device->host;
 | 
						|
	struct scsi_cmnd *ptr;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	DO_LOCK(flags);
 | 
						|
 | 
						|
	ptr=remove_SC(&ISSUE_SC, SCpnt);
 | 
						|
 | 
						|
	if(ptr) {
 | 
						|
		HOSTDATA(shpnt)->commands--;
 | 
						|
		if (!HOSTDATA(shpnt)->commands)
 | 
						|
			SETPORT(PORTA, 0);
 | 
						|
		DO_UNLOCK(flags);
 | 
						|
 | 
						|
		kfree(SCpnt->host_scribble);
 | 
						|
		SCpnt->host_scribble=NULL;
 | 
						|
 | 
						|
		return SUCCESS;
 | 
						|
	}
 | 
						|
 | 
						|
	DO_UNLOCK(flags);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * FIXME:
 | 
						|
	 * for current command: queue ABORT for message out and raise ATN
 | 
						|
	 * for disconnected command: pseudo SC with ABORT message or ABORT on reselection?
 | 
						|
	 *
 | 
						|
	 */
 | 
						|
 | 
						|
	scmd_printk(KERN_ERR, SCpnt,
 | 
						|
		    "cannot abort running or disconnected command\n");
 | 
						|
 | 
						|
	return FAILED;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Reset a device
 | 
						|
 *
 | 
						|
 */
 | 
						|
static int aha152x_device_reset(struct scsi_cmnd * SCpnt)
 | 
						|
{
 | 
						|
	struct Scsi_Host *shpnt = SCpnt->device->host;
 | 
						|
	DECLARE_COMPLETION(done);
 | 
						|
	int ret, issued, disconnected;
 | 
						|
	unsigned char old_cmd_len = SCpnt->cmd_len;
 | 
						|
	unsigned long flags;
 | 
						|
	unsigned long timeleft;
 | 
						|
 | 
						|
	if(CURRENT_SC==SCpnt) {
 | 
						|
		scmd_printk(KERN_ERR, SCpnt, "cannot reset current device\n");
 | 
						|
		return FAILED;
 | 
						|
	}
 | 
						|
 | 
						|
	DO_LOCK(flags);
 | 
						|
	issued       = remove_SC(&ISSUE_SC, SCpnt) == NULL;
 | 
						|
	disconnected = issued && remove_SC(&DISCONNECTED_SC, SCpnt);
 | 
						|
	DO_UNLOCK(flags);
 | 
						|
 | 
						|
	SCpnt->cmd_len         = 0;
 | 
						|
 | 
						|
	aha152x_internal_queue(SCpnt, &done, resetting);
 | 
						|
 | 
						|
	timeleft = wait_for_completion_timeout(&done, 100*HZ);
 | 
						|
	if (!timeleft) {
 | 
						|
		/* remove command from issue queue */
 | 
						|
		DO_LOCK(flags);
 | 
						|
		remove_SC(&ISSUE_SC, SCpnt);
 | 
						|
		DO_UNLOCK(flags);
 | 
						|
	}
 | 
						|
 | 
						|
	SCpnt->cmd_len         = old_cmd_len;
 | 
						|
 | 
						|
	DO_LOCK(flags);
 | 
						|
 | 
						|
	if (aha152x_priv(SCpnt)->phase & resetted) {
 | 
						|
		HOSTDATA(shpnt)->commands--;
 | 
						|
		if (!HOSTDATA(shpnt)->commands)
 | 
						|
			SETPORT(PORTA, 0);
 | 
						|
		kfree(SCpnt->host_scribble);
 | 
						|
		SCpnt->host_scribble=NULL;
 | 
						|
 | 
						|
		ret = SUCCESS;
 | 
						|
	} else {
 | 
						|
		/* requeue */
 | 
						|
		if(!issued) {
 | 
						|
			append_SC(&ISSUE_SC, SCpnt);
 | 
						|
		} else if(disconnected) {
 | 
						|
			append_SC(&DISCONNECTED_SC, SCpnt);
 | 
						|
		}
 | 
						|
 | 
						|
		ret = FAILED;
 | 
						|
	}
 | 
						|
 | 
						|
	DO_UNLOCK(flags);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static void free_hard_reset_SCs(struct Scsi_Host *shpnt,
 | 
						|
				struct scsi_cmnd **SCs)
 | 
						|
{
 | 
						|
	struct scsi_cmnd *ptr;
 | 
						|
 | 
						|
	ptr=*SCs;
 | 
						|
	while(ptr) {
 | 
						|
		struct scsi_cmnd *next;
 | 
						|
 | 
						|
		if(SCDATA(ptr)) {
 | 
						|
			next = SCNEXT(ptr);
 | 
						|
		} else {
 | 
						|
			scmd_printk(KERN_DEBUG, ptr,
 | 
						|
				    "queue corrupted at %p\n", ptr);
 | 
						|
			next = NULL;
 | 
						|
		}
 | 
						|
 | 
						|
		if (!ptr->device->soft_reset) {
 | 
						|
			remove_SC(SCs, ptr);
 | 
						|
			HOSTDATA(shpnt)->commands--;
 | 
						|
			kfree(ptr->host_scribble);
 | 
						|
			ptr->host_scribble=NULL;
 | 
						|
		}
 | 
						|
 | 
						|
		ptr = next;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Reset the bus
 | 
						|
 *
 | 
						|
 * AIC-6260 has a hard reset (MRST signal), but apparently
 | 
						|
 * one cannot trigger it via software. So live with
 | 
						|
 * a soft reset; no-one seemed to have cared.
 | 
						|
 */
 | 
						|
static int aha152x_bus_reset_host(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	DO_LOCK(flags);
 | 
						|
 | 
						|
	free_hard_reset_SCs(shpnt, &ISSUE_SC);
 | 
						|
	free_hard_reset_SCs(shpnt, &DISCONNECTED_SC);
 | 
						|
 | 
						|
	SETPORT(SCSISEQ, SCSIRSTO);
 | 
						|
	mdelay(256);
 | 
						|
	SETPORT(SCSISEQ, 0);
 | 
						|
	mdelay(DELAY);
 | 
						|
 | 
						|
	setup_expected_interrupts(shpnt);
 | 
						|
	if(HOSTDATA(shpnt)->commands==0)
 | 
						|
		SETPORT(PORTA, 0);
 | 
						|
 | 
						|
	DO_UNLOCK(flags);
 | 
						|
 | 
						|
	return SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Reset the bus
 | 
						|
 *
 | 
						|
 */
 | 
						|
static int aha152x_bus_reset(struct scsi_cmnd *SCpnt)
 | 
						|
{
 | 
						|
	return aha152x_bus_reset_host(SCpnt->device->host);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 *  Restore default values to the AIC-6260 registers and reset the fifos
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void reset_ports(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	/* disable interrupts */
 | 
						|
	SETPORT(DMACNTRL0, RSTFIFO);
 | 
						|
 | 
						|
	SETPORT(SCSISEQ, 0);
 | 
						|
 | 
						|
	SETPORT(SXFRCTL1, 0);
 | 
						|
	SETPORT(SCSISIG, 0);
 | 
						|
	SETRATE(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 fifos and transfer count */
 | 
						|
	SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT);
 | 
						|
	SETPORT(SXFRCTL0, CH1);
 | 
						|
 | 
						|
	DO_LOCK(flags);
 | 
						|
	setup_expected_interrupts(shpnt);
 | 
						|
	DO_UNLOCK(flags);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Reset the host (bus and controller)
 | 
						|
 *
 | 
						|
 */
 | 
						|
int aha152x_host_reset_host(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	aha152x_bus_reset_host(shpnt);
 | 
						|
	reset_ports(shpnt);
 | 
						|
 | 
						|
	return SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Return the "logical geometry"
 | 
						|
 *
 | 
						|
 */
 | 
						|
static int aha152x_biosparam(struct scsi_device *sdev, struct block_device *bdev,
 | 
						|
		sector_t capacity, int *info_array)
 | 
						|
{
 | 
						|
	struct Scsi_Host *shpnt = sdev->host;
 | 
						|
 | 
						|
	/* try default translation */
 | 
						|
	info_array[0] = 64;
 | 
						|
	info_array[1] = 32;
 | 
						|
	info_array[2] = (unsigned long)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(bdev, capacity, info) < 0 ||
 | 
						|
		    !((info[0] == 64 && info[1] == 32) || (info[0] == 255 && info[1] == 63))) {
 | 
						|
			if (EXT_TRANS) {
 | 
						|
				printk(KERN_NOTICE
 | 
						|
				       "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] = (unsigned long)capacity / (255 * 63);
 | 
						|
			} else {
 | 
						|
				printk(KERN_NOTICE
 | 
						|
				       "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 Documentation/scsi/aha152x.rst 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(KERN_NOTICE
 | 
						|
				       "aha152x: current partition table is using extended translation.\n"
 | 
						|
				       "         using it also, although it's not explicitly enabled.\n");
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 *  Internal done function
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void done(struct Scsi_Host *shpnt, unsigned char status_byte,
 | 
						|
		 unsigned char host_byte)
 | 
						|
{
 | 
						|
	if (CURRENT_SC) {
 | 
						|
		if(DONE_SC)
 | 
						|
			scmd_printk(KERN_ERR, CURRENT_SC,
 | 
						|
				    "there's already a completed command %p "
 | 
						|
				    "- will cause abort\n", DONE_SC);
 | 
						|
 | 
						|
		DONE_SC = CURRENT_SC;
 | 
						|
		CURRENT_SC = NULL;
 | 
						|
		set_status_byte(DONE_SC, status_byte);
 | 
						|
		set_host_byte(DONE_SC, host_byte);
 | 
						|
	} else
 | 
						|
		printk(KERN_ERR "aha152x: done() called outside of command\n");
 | 
						|
}
 | 
						|
 | 
						|
static struct work_struct aha152x_tq;
 | 
						|
 | 
						|
/*
 | 
						|
 * Run service completions on the card with interrupts enabled.
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void run(struct work_struct *work)
 | 
						|
{
 | 
						|
	struct aha152x_hostdata *hd;
 | 
						|
 | 
						|
	list_for_each_entry(hd, &aha152x_host_list, host_list) {
 | 
						|
		struct Scsi_Host *shost = container_of((void *)hd, struct Scsi_Host, hostdata);
 | 
						|
 | 
						|
		is_complete(shost);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Interrupt handler
 | 
						|
 *
 | 
						|
 */
 | 
						|
static irqreturn_t intr(int irqno, void *dev_id)
 | 
						|
{
 | 
						|
	struct Scsi_Host *shpnt = dev_id;
 | 
						|
	unsigned long flags;
 | 
						|
	unsigned char rev, dmacntrl0;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Read a couple of registers that are known to not be all 1's. If
 | 
						|
	 * we read all 1's (-1), that means that either:
 | 
						|
	 *
 | 
						|
	 * a. The host adapter chip has gone bad, and we cannot control it,
 | 
						|
	 *	OR
 | 
						|
	 * b. The host adapter is a PCMCIA card that has been ejected
 | 
						|
	 *
 | 
						|
	 * In either case, we cannot do anything with the host adapter at
 | 
						|
	 * this point in time. So just ignore the interrupt and return.
 | 
						|
	 * In the latter case, the interrupt might actually be meant for
 | 
						|
	 * someone else sharing this IRQ, and that driver will handle it.
 | 
						|
	 */
 | 
						|
	rev = GETPORT(REV);
 | 
						|
	dmacntrl0 = GETPORT(DMACNTRL0);
 | 
						|
	if ((rev == 0xFF) && (dmacntrl0 == 0xFF))
 | 
						|
		return IRQ_NONE;
 | 
						|
 | 
						|
	if( TESTLO(DMASTAT, INTSTAT) )
 | 
						|
		return IRQ_NONE;
 | 
						|
 | 
						|
	/* no more interrupts from the controller, while we're busy.
 | 
						|
	   INTEN is restored by the BH handler */
 | 
						|
	CLRBITS(DMACNTRL0, INTEN);
 | 
						|
 | 
						|
	DO_LOCK(flags);
 | 
						|
	if( HOSTDATA(shpnt)->service==0 ) {
 | 
						|
		HOSTDATA(shpnt)->service=1;
 | 
						|
 | 
						|
		/* Poke the BH handler */
 | 
						|
		INIT_WORK(&aha152x_tq, run);
 | 
						|
		schedule_work(&aha152x_tq);
 | 
						|
	}
 | 
						|
	DO_UNLOCK(flags);
 | 
						|
 | 
						|
	return IRQ_HANDLED;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * busfree phase
 | 
						|
 * - handle completition/disconnection/error of current command
 | 
						|
 * - start selection for next command (if any)
 | 
						|
 */
 | 
						|
static void busfree_run(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	unsigned long flags;
 | 
						|
#if defined(AHA152X_STAT)
 | 
						|
	int action=0;
 | 
						|
#endif
 | 
						|
 | 
						|
	SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT);
 | 
						|
	SETPORT(SXFRCTL0, CH1);
 | 
						|
 | 
						|
	SETPORT(SSTAT1, CLRBUSFREE);
 | 
						|
 | 
						|
	if(CURRENT_SC) {
 | 
						|
		struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
 | 
						|
 | 
						|
#if defined(AHA152X_STAT)
 | 
						|
		action++;
 | 
						|
#endif
 | 
						|
		acp->phase &= ~syncneg;
 | 
						|
 | 
						|
		if (acp->phase & completed) {
 | 
						|
			/* target sent COMMAND COMPLETE */
 | 
						|
			done(shpnt, acp->status, DID_OK);
 | 
						|
 | 
						|
		} else if (acp->phase & aborted) {
 | 
						|
			done(shpnt, acp->status, DID_ABORT);
 | 
						|
 | 
						|
		} else if (acp->phase & resetted) {
 | 
						|
			done(shpnt, acp->status, DID_RESET);
 | 
						|
 | 
						|
		} else if (acp->phase & disconnected) {
 | 
						|
			/* target sent DISCONNECT */
 | 
						|
#if defined(AHA152X_STAT)
 | 
						|
			HOSTDATA(shpnt)->disconnections++;
 | 
						|
#endif
 | 
						|
			append_SC(&DISCONNECTED_SC, CURRENT_SC);
 | 
						|
			acp->phase |= 1 << 16;
 | 
						|
			CURRENT_SC = NULL;
 | 
						|
 | 
						|
		} else {
 | 
						|
			done(shpnt, SAM_STAT_GOOD, DID_ERROR);
 | 
						|
		}
 | 
						|
#if defined(AHA152X_STAT)
 | 
						|
	} else {
 | 
						|
		HOSTDATA(shpnt)->busfree_without_old_command++;
 | 
						|
#endif
 | 
						|
	}
 | 
						|
 | 
						|
	DO_LOCK(flags);
 | 
						|
 | 
						|
	if(DONE_SC) {
 | 
						|
#if defined(AHA152X_STAT)
 | 
						|
		action++;
 | 
						|
#endif
 | 
						|
 | 
						|
		if (aha152x_priv(DONE_SC)->phase & check_condition) {
 | 
						|
			struct scsi_cmnd *cmd = HOSTDATA(shpnt)->done_SC;
 | 
						|
			struct aha152x_scdata *sc = SCDATA(cmd);
 | 
						|
 | 
						|
			scsi_eh_restore_cmnd(cmd, &sc->ses);
 | 
						|
 | 
						|
			aha152x_priv(cmd)->status = SAM_STAT_CHECK_CONDITION;
 | 
						|
 | 
						|
			HOSTDATA(shpnt)->commands--;
 | 
						|
			if (!HOSTDATA(shpnt)->commands)
 | 
						|
				SETPORT(PORTA, 0);	/* turn led off */
 | 
						|
		} else if (aha152x_priv(DONE_SC)->status == SAM_STAT_CHECK_CONDITION) {
 | 
						|
#if defined(AHA152X_STAT)
 | 
						|
			HOSTDATA(shpnt)->busfree_with_check_condition++;
 | 
						|
#endif
 | 
						|
 | 
						|
			if (!(aha152x_priv(DONE_SC)->phase & not_issued)) {
 | 
						|
				struct aha152x_scdata *sc;
 | 
						|
				struct scsi_cmnd *ptr = DONE_SC;
 | 
						|
				DONE_SC=NULL;
 | 
						|
 | 
						|
				sc = SCDATA(ptr);
 | 
						|
				/* It was allocated in aha152x_internal_queue? */
 | 
						|
				BUG_ON(!sc);
 | 
						|
				scsi_eh_prep_cmnd(ptr, &sc->ses, NULL, 0, ~0);
 | 
						|
 | 
						|
				DO_UNLOCK(flags);
 | 
						|
				aha152x_internal_queue(ptr, NULL, check_condition);
 | 
						|
				DO_LOCK(flags);
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (DONE_SC) {
 | 
						|
			struct scsi_cmnd *ptr = DONE_SC;
 | 
						|
			DONE_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 (!(aha152x_priv(ptr)->phase & resetting)) {
 | 
						|
				kfree(ptr->host_scribble);
 | 
						|
				ptr->host_scribble=NULL;
 | 
						|
			}
 | 
						|
 | 
						|
			DO_UNLOCK(flags);
 | 
						|
			aha152x_scsi_done(ptr);
 | 
						|
			DO_LOCK(flags);
 | 
						|
		}
 | 
						|
 | 
						|
		DONE_SC=NULL;
 | 
						|
#if defined(AHA152X_STAT)
 | 
						|
	} else {
 | 
						|
		HOSTDATA(shpnt)->busfree_without_done_command++;
 | 
						|
#endif
 | 
						|
	}
 | 
						|
 | 
						|
	if(ISSUE_SC)
 | 
						|
		CURRENT_SC = remove_first_SC(&ISSUE_SC);
 | 
						|
 | 
						|
	DO_UNLOCK(flags);
 | 
						|
 | 
						|
	if(CURRENT_SC) {
 | 
						|
		struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
 | 
						|
 | 
						|
#if defined(AHA152X_STAT)
 | 
						|
		action++;
 | 
						|
#endif
 | 
						|
		acp->phase |= selecting;
 | 
						|
 | 
						|
		/* clear selection timeout */
 | 
						|
		SETPORT(SSTAT1, SELTO);
 | 
						|
 | 
						|
		SETPORT(SCSIID, (shpnt->this_id << OID_) | CURRENT_SC->device->id);
 | 
						|
		SETPORT(SXFRCTL1, (PARITY ? ENSPCHK : 0 ) | ENSTIMER);
 | 
						|
		SETPORT(SCSISEQ, ENSELO | ENAUTOATNO | (DISCONNECTED_SC ? ENRESELI : 0));
 | 
						|
	} else {
 | 
						|
#if defined(AHA152X_STAT)
 | 
						|
		HOSTDATA(shpnt)->busfree_without_new_command++;
 | 
						|
#endif
 | 
						|
		SETPORT(SCSISEQ, DISCONNECTED_SC ? ENRESELI : 0);
 | 
						|
	}
 | 
						|
 | 
						|
#if defined(AHA152X_STAT)
 | 
						|
	if(!action)
 | 
						|
		HOSTDATA(shpnt)->busfree_without_any_action++;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Selection done (OUT)
 | 
						|
 * - queue IDENTIFY message and SDTR to selected target for message out
 | 
						|
 *   (ATN asserted automagically via ENAUTOATNO in busfree())
 | 
						|
 */
 | 
						|
static void seldo_run(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
 | 
						|
 | 
						|
	SETPORT(SCSISIG, 0);
 | 
						|
	SETPORT(SSTAT1, CLRBUSFREE);
 | 
						|
	SETPORT(SSTAT1, CLRPHASECHG);
 | 
						|
 | 
						|
	acp->phase &= ~(selecting | not_issued);
 | 
						|
 | 
						|
	SETPORT(SCSISEQ, 0);
 | 
						|
 | 
						|
	if (TESTLO(SSTAT0, SELDO)) {
 | 
						|
		scmd_printk(KERN_ERR, CURRENT_SC,
 | 
						|
			    "aha152x: passing bus free condition\n");
 | 
						|
		done(shpnt, SAM_STAT_GOOD, DID_NO_CONNECT);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	SETPORT(SSTAT0, CLRSELDO);
 | 
						|
 | 
						|
	ADDMSGO(IDENTIFY(RECONNECT, CURRENT_SC->device->lun));
 | 
						|
 | 
						|
	if (acp->phase & aborting) {
 | 
						|
		ADDMSGO(ABORT);
 | 
						|
	} else if (acp->phase & resetting) {
 | 
						|
		ADDMSGO(BUS_DEVICE_RESET);
 | 
						|
	} else if (SYNCNEG==0 && SYNCHRONOUS) {
 | 
						|
		acp->phase |= syncneg;
 | 
						|
		MSGOLEN += spi_populate_sync_msg(&MSGO(MSGOLEN), 50, 8);
 | 
						|
		SYNCNEG=1;		/* negotiation in progress */
 | 
						|
	}
 | 
						|
 | 
						|
	SETRATE(SYNCRATE);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Selection timeout
 | 
						|
 * - return command to mid-level with failure cause
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void selto_run(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	struct aha152x_cmd_priv *acp;
 | 
						|
 | 
						|
	SETPORT(SCSISEQ, 0);
 | 
						|
	SETPORT(SSTAT1, CLRSELTIMO);
 | 
						|
 | 
						|
	if (!CURRENT_SC)
 | 
						|
		return;
 | 
						|
 | 
						|
	acp = aha152x_priv(CURRENT_SC);
 | 
						|
	acp->phase &= ~selecting;
 | 
						|
 | 
						|
	if (acp->phase & aborted)
 | 
						|
		done(shpnt, SAM_STAT_GOOD, DID_ABORT);
 | 
						|
	else if (TESTLO(SSTAT0, SELINGO))
 | 
						|
		done(shpnt, SAM_STAT_GOOD, DID_BUS_BUSY);
 | 
						|
	else
 | 
						|
		/* ARBITRATION won, but SELECTION failed */
 | 
						|
		done(shpnt, SAM_STAT_GOOD, DID_NO_CONNECT);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Selection in done
 | 
						|
 * - put current command back to issue queue
 | 
						|
 *   (reconnection of a disconnected nexus instead
 | 
						|
 *    of successful selection out)
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void seldi_run(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	int selid;
 | 
						|
	int target;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	SETPORT(SCSISIG, 0);
 | 
						|
	SETPORT(SSTAT0, CLRSELDI);
 | 
						|
	SETPORT(SSTAT1, CLRBUSFREE);
 | 
						|
	SETPORT(SSTAT1, CLRPHASECHG);
 | 
						|
 | 
						|
	if(CURRENT_SC) {
 | 
						|
		struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
 | 
						|
 | 
						|
		if (!(acp->phase & not_issued))
 | 
						|
			scmd_printk(KERN_ERR, CURRENT_SC,
 | 
						|
				    "command should not have been issued yet\n");
 | 
						|
 | 
						|
		DO_LOCK(flags);
 | 
						|
		append_SC(&ISSUE_SC, CURRENT_SC);
 | 
						|
		DO_UNLOCK(flags);
 | 
						|
 | 
						|
		CURRENT_SC = NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!DISCONNECTED_SC)
 | 
						|
		return;
 | 
						|
 | 
						|
	RECONN_TARGET=-1;
 | 
						|
 | 
						|
	selid = GETPORT(SELID) & ~(1 << shpnt->this_id);
 | 
						|
 | 
						|
	if (selid==0) {
 | 
						|
		shost_printk(KERN_INFO, shpnt,
 | 
						|
			     "target id unknown (%02x)\n", selid);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	for(target=7; !(selid & (1 << target)); target--)
 | 
						|
		;
 | 
						|
 | 
						|
	if(selid & ~(1 << target)) {
 | 
						|
		shost_printk(KERN_INFO, shpnt,
 | 
						|
			     "multiple targets reconnected (%02x)\n", selid);
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	SETPORT(SCSIID, (shpnt->this_id << OID_) | target);
 | 
						|
	SETPORT(SCSISEQ, 0);
 | 
						|
 | 
						|
	SETRATE(HOSTDATA(shpnt)->syncrate[target]);
 | 
						|
 | 
						|
	RECONN_TARGET=target;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * message in phase
 | 
						|
 * - handle initial message after reconnection to identify
 | 
						|
 *   reconnecting nexus
 | 
						|
 * - queue command on DISCONNECTED_SC on DISCONNECT message
 | 
						|
 * - set completed flag on COMMAND COMPLETE
 | 
						|
 *   (other completition code moved to busfree_run)
 | 
						|
 * - handle response to SDTR
 | 
						|
 * - clear synchronous transfer agreements on BUS RESET
 | 
						|
 *
 | 
						|
 * FIXME: what about SAVE POINTERS, RESTORE POINTERS?
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void msgi_run(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	for(;;) {
 | 
						|
		struct aha152x_cmd_priv *acp;
 | 
						|
		int sstat1 = GETPORT(SSTAT1);
 | 
						|
 | 
						|
		if(sstat1 & (PHASECHG|PHASEMIS|BUSFREE) || !(sstat1 & REQINIT))
 | 
						|
			return;
 | 
						|
 | 
						|
		if (TESTLO(SSTAT0, SPIORDY))
 | 
						|
			return;
 | 
						|
 | 
						|
		ADDMSGI(GETPORT(SCSIDAT));
 | 
						|
 | 
						|
		if(!CURRENT_SC) {
 | 
						|
			if(LASTSTATE!=seldi) {
 | 
						|
				shost_printk(KERN_ERR, shpnt,
 | 
						|
					     "message in w/o current command"
 | 
						|
					     " not after reselection\n");
 | 
						|
			}
 | 
						|
 | 
						|
			/*
 | 
						|
			 * Handle reselection
 | 
						|
			 */
 | 
						|
			if(!(MSGI(0) & IDENTIFY_BASE)) {
 | 
						|
				shost_printk(KERN_ERR, shpnt,
 | 
						|
					     "target didn't identify after reselection\n");
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			CURRENT_SC = remove_lun_SC(&DISCONNECTED_SC, RECONN_TARGET, MSGI(0) & 0x3f);
 | 
						|
 | 
						|
			if (!CURRENT_SC) {
 | 
						|
				show_queues(shpnt);
 | 
						|
				shost_printk(KERN_ERR, shpnt,
 | 
						|
					     "no disconnected command"
 | 
						|
					     " for target %d/%d\n",
 | 
						|
					     RECONN_TARGET, MSGI(0) & 0x3f);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			acp = aha152x_priv(CURRENT_SC);
 | 
						|
			acp->message = MSGI(0);
 | 
						|
			acp->phase &= ~disconnected;
 | 
						|
 | 
						|
			MSGILEN=0;
 | 
						|
 | 
						|
			/* next message if any */
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		acp = aha152x_priv(CURRENT_SC);
 | 
						|
		acp->message = MSGI(0);
 | 
						|
 | 
						|
		switch (MSGI(0)) {
 | 
						|
		case DISCONNECT:
 | 
						|
			if (!RECONNECT)
 | 
						|
				scmd_printk(KERN_WARNING, CURRENT_SC,
 | 
						|
					    "target was not allowed to disconnect\n");
 | 
						|
 | 
						|
			acp->phase |= disconnected;
 | 
						|
			break;
 | 
						|
 | 
						|
		case COMMAND_COMPLETE:
 | 
						|
			acp->phase |= completed;
 | 
						|
			break;
 | 
						|
 | 
						|
		case MESSAGE_REJECT:
 | 
						|
			if (SYNCNEG==1) {
 | 
						|
				scmd_printk(KERN_INFO, CURRENT_SC,
 | 
						|
					    "Synchronous Data Transfer Request"
 | 
						|
					    " was rejected\n");
 | 
						|
				SYNCNEG=2;	/* negotiation completed */
 | 
						|
			} else
 | 
						|
				scmd_printk(KERN_INFO, CURRENT_SC,
 | 
						|
					    "inbound message (MESSAGE REJECT)\n");
 | 
						|
			break;
 | 
						|
 | 
						|
		case SAVE_POINTERS:
 | 
						|
			break;
 | 
						|
 | 
						|
		case RESTORE_POINTERS:
 | 
						|
			break;
 | 
						|
 | 
						|
		case EXTENDED_MESSAGE:
 | 
						|
			if(MSGILEN<2 || MSGILEN<MSGI(1)+2) {
 | 
						|
				/* not yet completed */
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			switch (MSGI(2)) {
 | 
						|
			case EXTENDED_SDTR:
 | 
						|
				{
 | 
						|
					long ticks;
 | 
						|
 | 
						|
					if (MSGI(1) != 3) {
 | 
						|
						scmd_printk(KERN_ERR, CURRENT_SC,
 | 
						|
							    "SDTR message length!=3\n");
 | 
						|
						break;
 | 
						|
					}
 | 
						|
 | 
						|
					if (!HOSTDATA(shpnt)->synchronous)
 | 
						|
						break;
 | 
						|
 | 
						|
					printk(INFO_LEAD, CMDINFO(CURRENT_SC));
 | 
						|
					spi_print_msg(&MSGI(0));
 | 
						|
					printk("\n");
 | 
						|
 | 
						|
					ticks = (MSGI(3) * 4 + 49) / 50;
 | 
						|
 | 
						|
					if (syncneg) {
 | 
						|
						/* negotiation in progress */
 | 
						|
						if (ticks > 9 || MSGI(4) < 1 || MSGI(4) > 8) {
 | 
						|
							ADDMSGO(MESSAGE_REJECT);
 | 
						|
							scmd_printk(KERN_INFO,
 | 
						|
								    CURRENT_SC,
 | 
						|
								    "received Synchronous Data Transfer Request invalid - rejected\n");
 | 
						|
							break;
 | 
						|
						}
 | 
						|
 | 
						|
						SYNCRATE |= ((ticks - 2) << 4) + MSGI(4);
 | 
						|
					} else if (ticks <= 9 && MSGI(4) >= 1) {
 | 
						|
						ADDMSGO(EXTENDED_MESSAGE);
 | 
						|
						ADDMSGO(3);
 | 
						|
						ADDMSGO(EXTENDED_SDTR);
 | 
						|
						if (ticks < 4) {
 | 
						|
							ticks = 4;
 | 
						|
							ADDMSGO(50);
 | 
						|
						} else
 | 
						|
							ADDMSGO(MSGI(3));
 | 
						|
 | 
						|
						if (MSGI(4) > 8)
 | 
						|
							MSGI(4) = 8;
 | 
						|
 | 
						|
						ADDMSGO(MSGI(4));
 | 
						|
 | 
						|
						SYNCRATE |= ((ticks - 2) << 4) + MSGI(4);
 | 
						|
					} else {
 | 
						|
						/* requested SDTR is too slow, do it asynchronously */
 | 
						|
						scmd_printk(KERN_INFO,
 | 
						|
							    CURRENT_SC,
 | 
						|
							    "Synchronous Data Transfer Request too slow - Rejecting\n");
 | 
						|
						ADDMSGO(MESSAGE_REJECT);
 | 
						|
					}
 | 
						|
 | 
						|
					/* negotiation completed */
 | 
						|
					SYNCNEG=2;
 | 
						|
					SETRATE(SYNCRATE);
 | 
						|
				}
 | 
						|
				break;
 | 
						|
 | 
						|
			case BUS_DEVICE_RESET:
 | 
						|
				{
 | 
						|
					int i;
 | 
						|
 | 
						|
					for(i=0; i<8; i++) {
 | 
						|
						HOSTDATA(shpnt)->syncrate[i]=0;
 | 
						|
						HOSTDATA(shpnt)->syncneg[i]=0;
 | 
						|
					}
 | 
						|
 | 
						|
				}
 | 
						|
				break;
 | 
						|
 | 
						|
			case EXTENDED_MODIFY_DATA_POINTER:
 | 
						|
			case EXTENDED_EXTENDED_IDENTIFY:
 | 
						|
			case EXTENDED_WDTR:
 | 
						|
			default:
 | 
						|
				ADDMSGO(MESSAGE_REJECT);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		MSGILEN=0;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void msgi_end(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	if(MSGILEN>0)
 | 
						|
		scmd_printk(KERN_WARNING, CURRENT_SC,
 | 
						|
			    "target left before message completed (%d)\n",
 | 
						|
			    MSGILEN);
 | 
						|
 | 
						|
	if (MSGOLEN > 0 && !(GETPORT(SSTAT1) & BUSFREE))
 | 
						|
		SETPORT(SCSISIG, P_MSGI | SIG_ATNO);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * message out phase
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void msgo_init(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	if(MSGOLEN==0) {
 | 
						|
		if ((aha152x_priv(CURRENT_SC)->phase & syncneg) &&
 | 
						|
		    SYNCNEG == 2 && SYNCRATE == 0) {
 | 
						|
			ADDMSGO(IDENTIFY(RECONNECT, CURRENT_SC->device->lun));
 | 
						|
		} else {
 | 
						|
			scmd_printk(KERN_INFO, CURRENT_SC,
 | 
						|
				    "unexpected MESSAGE OUT phase; rejecting\n");
 | 
						|
			ADDMSGO(MESSAGE_REJECT);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * message out phase
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void msgo_run(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
 | 
						|
 | 
						|
	while(MSGO_I<MSGOLEN) {
 | 
						|
		if (TESTLO(SSTAT0, SPIORDY))
 | 
						|
			return;
 | 
						|
 | 
						|
		if (MSGO_I==MSGOLEN-1) {
 | 
						|
			/* Leave MESSAGE OUT after transfer */
 | 
						|
			SETPORT(SSTAT1, CLRATNO);
 | 
						|
		}
 | 
						|
 | 
						|
 | 
						|
		if (MSGO(MSGO_I) & IDENTIFY_BASE)
 | 
						|
			acp->phase |= identified;
 | 
						|
 | 
						|
		if (MSGO(MSGO_I)==ABORT)
 | 
						|
			acp->phase |= aborted;
 | 
						|
 | 
						|
		if (MSGO(MSGO_I)==BUS_DEVICE_RESET)
 | 
						|
			acp->phase |= resetted;
 | 
						|
 | 
						|
		SETPORT(SCSIDAT, MSGO(MSGO_I++));
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void msgo_end(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	if(MSGO_I<MSGOLEN) {
 | 
						|
		scmd_printk(KERN_ERR, CURRENT_SC,
 | 
						|
			    "message sent incompletely (%d/%d)\n",
 | 
						|
			    MSGO_I, MSGOLEN);
 | 
						|
		if(SYNCNEG==1) {
 | 
						|
			scmd_printk(KERN_INFO, CURRENT_SC,
 | 
						|
				    "Synchronous Data Transfer Request was rejected\n");
 | 
						|
			SYNCNEG=2;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	MSGO_I  = 0;
 | 
						|
	MSGOLEN = 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * command phase
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void cmd_init(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	if (aha152x_priv(CURRENT_SC)->sent_command) {
 | 
						|
		scmd_printk(KERN_ERR, CURRENT_SC,
 | 
						|
			    "command already sent\n");
 | 
						|
		done(shpnt, SAM_STAT_GOOD, DID_ERROR);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	CMD_I=0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * command phase
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void cmd_run(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	while(CMD_I<CURRENT_SC->cmd_len) {
 | 
						|
		if (TESTLO(SSTAT0, SPIORDY))
 | 
						|
			return;
 | 
						|
 | 
						|
		SETPORT(SCSIDAT, CURRENT_SC->cmnd[CMD_I++]);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void cmd_end(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	if(CMD_I<CURRENT_SC->cmd_len)
 | 
						|
		scmd_printk(KERN_ERR, CURRENT_SC,
 | 
						|
			    "command sent incompletely (%d/%d)\n",
 | 
						|
			    CMD_I, CURRENT_SC->cmd_len);
 | 
						|
	else
 | 
						|
		aha152x_priv(CURRENT_SC)->sent_command++;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * status phase
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void status_run(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	if (TESTLO(SSTAT0, SPIORDY))
 | 
						|
		return;
 | 
						|
 | 
						|
	aha152x_priv(CURRENT_SC)->status = GETPORT(SCSIDAT);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * data in phase
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void datai_init(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	SETPORT(DMACNTRL0, RSTFIFO);
 | 
						|
	SETPORT(DMACNTRL0, RSTFIFO|ENDMA);
 | 
						|
 | 
						|
	SETPORT(SXFRCTL0, CH1|CLRSTCNT);
 | 
						|
	SETPORT(SXFRCTL0, CH1|SCSIEN|DMAEN);
 | 
						|
 | 
						|
	SETPORT(SIMODE0, 0);
 | 
						|
	SETPORT(SIMODE1, ENSCSIPERR | ENSCSIRST | ENPHASEMIS | ENBUSFREE);
 | 
						|
 | 
						|
	DATA_LEN=0;
 | 
						|
}
 | 
						|
 | 
						|
static void datai_run(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	struct aha152x_cmd_priv *acp;
 | 
						|
	unsigned long the_time;
 | 
						|
	int fifodata, data_count;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * loop while the phase persists or the fifos are not empty
 | 
						|
	 *
 | 
						|
	 */
 | 
						|
	while(TESTLO(DMASTAT, INTSTAT) || TESTLO(DMASTAT, DFIFOEMP) || TESTLO(SSTAT2, SEMPTY)) {
 | 
						|
		/* FIXME: maybe this should be done by setting up
 | 
						|
		 * STCNT to trigger ENSWRAP interrupt, instead of
 | 
						|
		 * polling for DFIFOFULL
 | 
						|
		 */
 | 
						|
		the_time=jiffies + 100*HZ;
 | 
						|
		while(TESTLO(DMASTAT, DFIFOFULL|INTSTAT) && time_before(jiffies,the_time))
 | 
						|
			barrier();
 | 
						|
 | 
						|
		if(TESTLO(DMASTAT, DFIFOFULL|INTSTAT)) {
 | 
						|
			scmd_printk(KERN_ERR, CURRENT_SC, "datai timeout\n");
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		if(TESTHI(DMASTAT, DFIFOFULL)) {
 | 
						|
			fifodata = 128;
 | 
						|
		} else {
 | 
						|
			the_time=jiffies + 100*HZ;
 | 
						|
			while(TESTLO(SSTAT2, SEMPTY) && time_before(jiffies,the_time))
 | 
						|
				barrier();
 | 
						|
 | 
						|
			if(TESTLO(SSTAT2, SEMPTY)) {
 | 
						|
				scmd_printk(KERN_ERR, CURRENT_SC,
 | 
						|
					    "datai sempty timeout");
 | 
						|
				break;
 | 
						|
			}
 | 
						|
 | 
						|
			fifodata = GETPORT(FIFOSTAT);
 | 
						|
		}
 | 
						|
 | 
						|
		acp = aha152x_priv(CURRENT_SC);
 | 
						|
		if (acp->this_residual > 0) {
 | 
						|
			while (fifodata > 0 && acp->this_residual > 0) {
 | 
						|
				data_count = fifodata > acp->this_residual ?
 | 
						|
						acp->this_residual : fifodata;
 | 
						|
				fifodata -= data_count;
 | 
						|
 | 
						|
				if (data_count & 1) {
 | 
						|
					SETPORT(DMACNTRL0, ENDMA|_8BIT);
 | 
						|
					*acp->ptr++ = GETPORT(DATAPORT);
 | 
						|
					acp->this_residual--;
 | 
						|
					DATA_LEN++;
 | 
						|
					SETPORT(DMACNTRL0, ENDMA);
 | 
						|
				}
 | 
						|
 | 
						|
				if (data_count > 1) {
 | 
						|
					data_count >>= 1;
 | 
						|
					insw(DATAPORT, acp->ptr, data_count);
 | 
						|
					acp->ptr += 2 * data_count;
 | 
						|
					acp->this_residual -= 2 * data_count;
 | 
						|
					DATA_LEN += 2 * data_count;
 | 
						|
				}
 | 
						|
 | 
						|
				if (acp->this_residual == 0 &&
 | 
						|
				    !sg_is_last(acp->buffer)) {
 | 
						|
					/* advance to next buffer */
 | 
						|
					acp->buffer = sg_next(acp->buffer);
 | 
						|
					acp->ptr = SG_ADDRESS(acp->buffer);
 | 
						|
					acp->this_residual = acp->buffer->length;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		} else if (fifodata > 0) {
 | 
						|
			scmd_printk(KERN_ERR, CURRENT_SC,
 | 
						|
				    "no buffers left for %d(%d) bytes"
 | 
						|
				    " (data overrun!?)\n",
 | 
						|
				    fifodata, GETPORT(FIFOSTAT));
 | 
						|
			SETPORT(DMACNTRL0, ENDMA|_8BIT);
 | 
						|
			while(fifodata>0) {
 | 
						|
				GETPORT(DATAPORT);
 | 
						|
				fifodata--;
 | 
						|
				DATA_LEN++;
 | 
						|
			}
 | 
						|
			SETPORT(DMACNTRL0, ENDMA|_8BIT);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if(TESTLO(DMASTAT, INTSTAT) ||
 | 
						|
	   TESTLO(DMASTAT, DFIFOEMP) ||
 | 
						|
	   TESTLO(SSTAT2, SEMPTY) ||
 | 
						|
	   GETPORT(FIFOSTAT)>0) {
 | 
						|
		/*
 | 
						|
		 * something went wrong, if there's something left in the fifos
 | 
						|
		 * or the phase didn't change
 | 
						|
		 */
 | 
						|
		scmd_printk(KERN_ERR, CURRENT_SC,
 | 
						|
			    "fifos should be empty and phase should have changed\n");
 | 
						|
	}
 | 
						|
 | 
						|
	if(DATA_LEN!=GETSTCNT()) {
 | 
						|
		scmd_printk(KERN_ERR, CURRENT_SC,
 | 
						|
			    "manual transfer count differs from automatic "
 | 
						|
			    "(count=%d;stcnt=%d;diff=%d;fifostat=%d)",
 | 
						|
			    DATA_LEN, GETSTCNT(), GETSTCNT()-DATA_LEN,
 | 
						|
			    GETPORT(FIFOSTAT));
 | 
						|
		mdelay(10000);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void datai_end(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	CMD_INC_RESID(CURRENT_SC, -GETSTCNT());
 | 
						|
 | 
						|
	SETPORT(SXFRCTL0, CH1|CLRSTCNT);
 | 
						|
	SETPORT(DMACNTRL0, 0);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * data out phase
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void datao_init(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	SETPORT(DMACNTRL0, WRITE_READ | RSTFIFO);
 | 
						|
	SETPORT(DMACNTRL0, WRITE_READ | ENDMA);
 | 
						|
 | 
						|
	SETPORT(SXFRCTL0, CH1|CLRSTCNT);
 | 
						|
	SETPORT(SXFRCTL0, CH1|SCSIEN|DMAEN);
 | 
						|
 | 
						|
	SETPORT(SIMODE0, 0);
 | 
						|
	SETPORT(SIMODE1, ENSCSIPERR | ENSCSIRST | ENPHASEMIS | ENBUSFREE );
 | 
						|
 | 
						|
	DATA_LEN = scsi_get_resid(CURRENT_SC);
 | 
						|
}
 | 
						|
 | 
						|
static void datao_run(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
 | 
						|
	unsigned long the_time;
 | 
						|
	int data_count;
 | 
						|
 | 
						|
	/* until phase changes or all data sent */
 | 
						|
	while (TESTLO(DMASTAT, INTSTAT) && acp->this_residual > 0) {
 | 
						|
		data_count = 128;
 | 
						|
		if (data_count > acp->this_residual)
 | 
						|
			data_count = acp->this_residual;
 | 
						|
 | 
						|
		if(TESTLO(DMASTAT, DFIFOEMP)) {
 | 
						|
			scmd_printk(KERN_ERR, CURRENT_SC,
 | 
						|
				    "datao fifo not empty (%d)",
 | 
						|
				    GETPORT(FIFOSTAT));
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		if(data_count & 1) {
 | 
						|
			SETPORT(DMACNTRL0,WRITE_READ|ENDMA|_8BIT);
 | 
						|
			SETPORT(DATAPORT, *acp->ptr++);
 | 
						|
			acp->this_residual--;
 | 
						|
			CMD_INC_RESID(CURRENT_SC, -1);
 | 
						|
			SETPORT(DMACNTRL0,WRITE_READ|ENDMA);
 | 
						|
		}
 | 
						|
 | 
						|
		if(data_count > 1) {
 | 
						|
			data_count >>= 1;
 | 
						|
			outsw(DATAPORT, acp->ptr, data_count);
 | 
						|
			acp->ptr += 2 * data_count;
 | 
						|
			acp->this_residual -= 2 * data_count;
 | 
						|
			CMD_INC_RESID(CURRENT_SC, -2 * data_count);
 | 
						|
		}
 | 
						|
 | 
						|
		if (acp->this_residual == 0 && !sg_is_last(acp->buffer)) {
 | 
						|
			/* advance to next buffer */
 | 
						|
			acp->buffer = sg_next(acp->buffer);
 | 
						|
			acp->ptr = SG_ADDRESS(acp->buffer);
 | 
						|
			acp->this_residual = acp->buffer->length;
 | 
						|
		}
 | 
						|
 | 
						|
		the_time=jiffies + 100*HZ;
 | 
						|
		while(TESTLO(DMASTAT, DFIFOEMP|INTSTAT) && time_before(jiffies,the_time))
 | 
						|
			barrier();
 | 
						|
 | 
						|
		if(TESTLO(DMASTAT, DFIFOEMP|INTSTAT)) {
 | 
						|
			scmd_printk(KERN_ERR, CURRENT_SC, "dataout timeout\n");
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void datao_end(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
 | 
						|
 | 
						|
	if(TESTLO(DMASTAT, DFIFOEMP)) {
 | 
						|
		u32 datao_cnt = GETSTCNT();
 | 
						|
		int datao_out = DATA_LEN - scsi_get_resid(CURRENT_SC);
 | 
						|
		int done;
 | 
						|
		struct scatterlist *sg = scsi_sglist(CURRENT_SC);
 | 
						|
 | 
						|
		CMD_INC_RESID(CURRENT_SC, datao_out - datao_cnt);
 | 
						|
 | 
						|
		done = scsi_bufflen(CURRENT_SC) - scsi_get_resid(CURRENT_SC);
 | 
						|
		/* Locate the first SG entry not yet sent */
 | 
						|
		while (done > 0 && !sg_is_last(sg)) {
 | 
						|
			if (done < sg->length)
 | 
						|
				break;
 | 
						|
			done -= sg->length;
 | 
						|
			sg = sg_next(sg);
 | 
						|
		}
 | 
						|
 | 
						|
		acp->buffer = sg;
 | 
						|
		acp->ptr = SG_ADDRESS(acp->buffer) + done;
 | 
						|
		acp->this_residual = acp->buffer->length - done;
 | 
						|
	}
 | 
						|
 | 
						|
	SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT);
 | 
						|
	SETPORT(SXFRCTL0, CH1);
 | 
						|
 | 
						|
	SETPORT(DMACNTRL0, 0);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * figure out what state we're in
 | 
						|
 *
 | 
						|
 */
 | 
						|
static int update_state(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	int dataphase=0;
 | 
						|
	unsigned int stat0 = GETPORT(SSTAT0);
 | 
						|
	unsigned int stat1 = GETPORT(SSTAT1);
 | 
						|
 | 
						|
	PREVSTATE = STATE;
 | 
						|
	STATE=unknown;
 | 
						|
 | 
						|
	if(stat1 & SCSIRSTI) {
 | 
						|
		STATE=rsti;
 | 
						|
		SETPORT(SCSISEQ,0);
 | 
						|
		SETPORT(SSTAT1,SCSIRSTI);
 | 
						|
	} else if (stat0 & SELDI && PREVSTATE == busfree) {
 | 
						|
		STATE=seldi;
 | 
						|
	} else if (stat0 & SELDO && CURRENT_SC &&
 | 
						|
		   (aha152x_priv(CURRENT_SC)->phase & selecting)) {
 | 
						|
		STATE=seldo;
 | 
						|
	} else if(stat1 & SELTO) {
 | 
						|
		STATE=selto;
 | 
						|
	} else if(stat1 & BUSFREE) {
 | 
						|
		STATE=busfree;
 | 
						|
		SETPORT(SSTAT1,BUSFREE);
 | 
						|
	} else if(stat1 & SCSIPERR) {
 | 
						|
		STATE=parerr;
 | 
						|
		SETPORT(SSTAT1,SCSIPERR);
 | 
						|
	} else if(stat1 & REQINIT) {
 | 
						|
		switch(GETPORT(SCSISIG) & P_MASK) {
 | 
						|
		case P_MSGI:	STATE=msgi;	break;
 | 
						|
		case P_MSGO:	STATE=msgo;	break;
 | 
						|
		case P_DATAO:	STATE=datao;	break;
 | 
						|
		case P_DATAI:	STATE=datai;	break;
 | 
						|
		case P_STATUS:	STATE=status;	break;
 | 
						|
		case P_CMD:	STATE=cmd;	break;
 | 
						|
		}
 | 
						|
		dataphase=1;
 | 
						|
	}
 | 
						|
 | 
						|
	if((stat0 & SELDI) && STATE!=seldi && !dataphase) {
 | 
						|
		scmd_printk(KERN_INFO, CURRENT_SC, "reselection missed?");
 | 
						|
	}
 | 
						|
 | 
						|
	if(STATE!=PREVSTATE) {
 | 
						|
		LASTSTATE=PREVSTATE;
 | 
						|
	}
 | 
						|
 | 
						|
	return dataphase;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * handle parity error
 | 
						|
 *
 | 
						|
 * FIXME: in which phase?
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void parerr_run(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	scmd_printk(KERN_ERR, CURRENT_SC, "parity error\n");
 | 
						|
	done(shpnt, SAM_STAT_GOOD, DID_PARITY);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * handle reset in
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void rsti_run(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	struct scsi_cmnd *ptr;
 | 
						|
 | 
						|
	shost_printk(KERN_NOTICE, shpnt, "scsi reset in\n");
 | 
						|
 | 
						|
	ptr=DISCONNECTED_SC;
 | 
						|
	while(ptr) {
 | 
						|
		struct scsi_cmnd *next = SCNEXT(ptr);
 | 
						|
 | 
						|
		if (!ptr->device->soft_reset) {
 | 
						|
			remove_SC(&DISCONNECTED_SC, ptr);
 | 
						|
 | 
						|
			kfree(ptr->host_scribble);
 | 
						|
			ptr->host_scribble=NULL;
 | 
						|
 | 
						|
			set_host_byte(ptr, DID_RESET);
 | 
						|
			aha152x_scsi_done(ptr);
 | 
						|
		}
 | 
						|
 | 
						|
		ptr = next;
 | 
						|
	}
 | 
						|
 | 
						|
	if(CURRENT_SC && !CURRENT_SC->device->soft_reset)
 | 
						|
		done(shpnt, SAM_STAT_GOOD, DID_RESET);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * bottom-half handler
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void is_complete(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	int dataphase;
 | 
						|
	unsigned long flags;
 | 
						|
	int pending;
 | 
						|
 | 
						|
	if(!shpnt)
 | 
						|
		return;
 | 
						|
 | 
						|
	DO_LOCK(flags);
 | 
						|
 | 
						|
	if( HOSTDATA(shpnt)->service==0 )  {
 | 
						|
		DO_UNLOCK(flags);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	HOSTDATA(shpnt)->service = 0;
 | 
						|
 | 
						|
	if(HOSTDATA(shpnt)->in_intr) {
 | 
						|
		DO_UNLOCK(flags);
 | 
						|
		/* aha152x_error never returns.. */
 | 
						|
		aha152x_error(shpnt, "bottom-half already running!?");
 | 
						|
	}
 | 
						|
	HOSTDATA(shpnt)->in_intr++;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * loop while there are interrupt conditions pending
 | 
						|
	 *
 | 
						|
	 */
 | 
						|
	do {
 | 
						|
		unsigned long start = jiffies;
 | 
						|
		DO_UNLOCK(flags);
 | 
						|
 | 
						|
		dataphase=update_state(shpnt);
 | 
						|
 | 
						|
		/*
 | 
						|
		 * end previous state
 | 
						|
		 *
 | 
						|
		 */
 | 
						|
		if(PREVSTATE!=STATE && states[PREVSTATE].end)
 | 
						|
			states[PREVSTATE].end(shpnt);
 | 
						|
 | 
						|
		/*
 | 
						|
		 * disable SPIO mode if previous phase used it
 | 
						|
		 * and this one doesn't
 | 
						|
		 *
 | 
						|
		 */
 | 
						|
		if(states[PREVSTATE].spio && !states[STATE].spio) {
 | 
						|
			SETPORT(SXFRCTL0, CH1);
 | 
						|
			SETPORT(DMACNTRL0, 0);
 | 
						|
			if(CURRENT_SC)
 | 
						|
				aha152x_priv(CURRENT_SC)->phase &= ~spiordy;
 | 
						|
		}
 | 
						|
 | 
						|
		/*
 | 
						|
		 * accept current dataphase phase
 | 
						|
		 *
 | 
						|
		 */
 | 
						|
		if(dataphase) {
 | 
						|
			SETPORT(SSTAT0, REQINIT);
 | 
						|
			SETPORT(SCSISIG, GETPORT(SCSISIG) & P_MASK);
 | 
						|
			SETPORT(SSTAT1, PHASECHG);
 | 
						|
		}
 | 
						|
 | 
						|
		/*
 | 
						|
		 * enable SPIO mode if previous didn't use it
 | 
						|
		 * and this one does
 | 
						|
		 *
 | 
						|
		 */
 | 
						|
		if(!states[PREVSTATE].spio && states[STATE].spio) {
 | 
						|
			SETPORT(DMACNTRL0, 0);
 | 
						|
			SETPORT(SXFRCTL0, CH1|SPIOEN);
 | 
						|
			if(CURRENT_SC)
 | 
						|
				aha152x_priv(CURRENT_SC)->phase |= spiordy;
 | 
						|
		}
 | 
						|
 | 
						|
		/*
 | 
						|
		 * initialize for new state
 | 
						|
		 *
 | 
						|
		 */
 | 
						|
		if(PREVSTATE!=STATE && states[STATE].init)
 | 
						|
			states[STATE].init(shpnt);
 | 
						|
 | 
						|
		/*
 | 
						|
		 * handle current state
 | 
						|
		 *
 | 
						|
		 */
 | 
						|
		if(states[STATE].run)
 | 
						|
			states[STATE].run(shpnt);
 | 
						|
		else
 | 
						|
			scmd_printk(KERN_ERR, CURRENT_SC,
 | 
						|
				    "unexpected state (%x)\n", STATE);
 | 
						|
 | 
						|
		/*
 | 
						|
		 * setup controller to interrupt on
 | 
						|
		 * the next expected condition and
 | 
						|
		 * loop if it's already there
 | 
						|
		 *
 | 
						|
		 */
 | 
						|
		DO_LOCK(flags);
 | 
						|
		pending=setup_expected_interrupts(shpnt);
 | 
						|
#if defined(AHA152X_STAT)
 | 
						|
		HOSTDATA(shpnt)->count[STATE]++;
 | 
						|
		if(PREVSTATE!=STATE)
 | 
						|
			HOSTDATA(shpnt)->count_trans[STATE]++;
 | 
						|
		HOSTDATA(shpnt)->time[STATE] += jiffies-start;
 | 
						|
#endif
 | 
						|
 | 
						|
	} while(pending);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * enable interrupts and leave bottom-half
 | 
						|
	 *
 | 
						|
	 */
 | 
						|
	HOSTDATA(shpnt)->in_intr--;
 | 
						|
	SETBITS(DMACNTRL0, INTEN);
 | 
						|
	DO_UNLOCK(flags);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Dump the current driver status and panic
 | 
						|
 */
 | 
						|
static void aha152x_error(struct Scsi_Host *shpnt, char *msg)
 | 
						|
{
 | 
						|
	shost_printk(KERN_EMERG, shpnt, "%s\n", msg);
 | 
						|
	show_queues(shpnt);
 | 
						|
	panic("aha152x panic\n");
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * display enabled interrupts
 | 
						|
 */
 | 
						|
static void disp_enintr(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	int s0, s1;
 | 
						|
 | 
						|
	s0 = GETPORT(SIMODE0);
 | 
						|
	s1 = GETPORT(SIMODE1);
 | 
						|
 | 
						|
	shost_printk(KERN_DEBUG, shpnt,
 | 
						|
		     "enabled interrupts (%s%s%s%s%s%s%s%s%s%s%s%s%s%s)\n",
 | 
						|
		     (s0 & ENSELDO) ? "ENSELDO " : "",
 | 
						|
		     (s0 & ENSELDI) ? "ENSELDI " : "",
 | 
						|
		     (s0 & ENSELINGO) ? "ENSELINGO " : "",
 | 
						|
		     (s0 & ENSWRAP) ? "ENSWRAP " : "",
 | 
						|
		     (s0 & ENSDONE) ? "ENSDONE " : "",
 | 
						|
		     (s0 & ENSPIORDY) ? "ENSPIORDY " : "",
 | 
						|
		     (s0 & ENDMADONE) ? "ENDMADONE " : "",
 | 
						|
		     (s1 & ENSELTIMO) ? "ENSELTIMO " : "",
 | 
						|
		     (s1 & ENATNTARG) ? "ENATNTARG " : "",
 | 
						|
		     (s1 & ENPHASEMIS) ? "ENPHASEMIS " : "",
 | 
						|
		     (s1 & ENBUSFREE) ? "ENBUSFREE " : "",
 | 
						|
		     (s1 & ENSCSIPERR) ? "ENSCSIPERR " : "",
 | 
						|
		     (s1 & ENPHASECHG) ? "ENPHASECHG " : "",
 | 
						|
		     (s1 & ENREQINIT) ? "ENREQINIT " : "");
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Show the command data of a command
 | 
						|
 */
 | 
						|
static void show_command(struct scsi_cmnd *ptr)
 | 
						|
{
 | 
						|
	const int phase = aha152x_priv(ptr)->phase;
 | 
						|
 | 
						|
	scsi_print_command(ptr);
 | 
						|
	scmd_printk(KERN_DEBUG, ptr,
 | 
						|
		    "request_bufflen=%d; resid=%d; "
 | 
						|
		    "phase |%s%s%s%s%s%s%s%s%s; next=0x%p",
 | 
						|
		    scsi_bufflen(ptr), scsi_get_resid(ptr),
 | 
						|
		    phase & not_issued ? "not issued|" : "",
 | 
						|
		    phase & selecting ? "selecting|" : "",
 | 
						|
		    phase & identified ? "identified|" : "",
 | 
						|
		    phase & disconnected ? "disconnected|" : "",
 | 
						|
		    phase & completed ? "completed|" : "",
 | 
						|
		    phase & spiordy ? "spiordy|" : "",
 | 
						|
		    phase & syncneg ? "syncneg|" : "",
 | 
						|
		    phase & aborted ? "aborted|" : "",
 | 
						|
		    phase & resetted ? "resetted|" : "",
 | 
						|
		    SCDATA(ptr) ? SCNEXT(ptr) : NULL);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Dump the queued data
 | 
						|
 */
 | 
						|
static void show_queues(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	struct scsi_cmnd *ptr;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	DO_LOCK(flags);
 | 
						|
	printk(KERN_DEBUG "\nqueue status:\nissue_SC:\n");
 | 
						|
	for (ptr = ISSUE_SC; ptr; ptr = SCNEXT(ptr))
 | 
						|
		show_command(ptr);
 | 
						|
	DO_UNLOCK(flags);
 | 
						|
 | 
						|
	printk(KERN_DEBUG "current_SC:\n");
 | 
						|
	if (CURRENT_SC)
 | 
						|
		show_command(CURRENT_SC);
 | 
						|
	else
 | 
						|
		printk(KERN_DEBUG "none\n");
 | 
						|
 | 
						|
	printk(KERN_DEBUG "disconnected_SC:\n");
 | 
						|
	for (ptr = DISCONNECTED_SC; ptr; ptr = SCDATA(ptr) ? SCNEXT(ptr) : NULL)
 | 
						|
		show_command(ptr);
 | 
						|
 | 
						|
	disp_enintr(shpnt);
 | 
						|
}
 | 
						|
 | 
						|
static void get_command(struct seq_file *m, struct scsi_cmnd * ptr)
 | 
						|
{
 | 
						|
	struct aha152x_cmd_priv *acp = aha152x_priv(ptr);
 | 
						|
	const int phase = acp->phase;
 | 
						|
	int i;
 | 
						|
 | 
						|
	seq_printf(m, "%p: target=%d; lun=%d; cmnd=( ",
 | 
						|
		ptr, ptr->device->id, (u8)ptr->device->lun);
 | 
						|
 | 
						|
	for (i = 0; i < COMMAND_SIZE(ptr->cmnd[0]); i++)
 | 
						|
		seq_printf(m, "0x%02x ", ptr->cmnd[i]);
 | 
						|
 | 
						|
	seq_printf(m, "); resid=%d; residual=%d; buffers=%d; phase |",
 | 
						|
		scsi_get_resid(ptr), acp->this_residual,
 | 
						|
		sg_nents(acp->buffer) - 1);
 | 
						|
 | 
						|
	if (phase & not_issued)
 | 
						|
		seq_puts(m, "not issued|");
 | 
						|
	if (phase & selecting)
 | 
						|
		seq_puts(m, "selecting|");
 | 
						|
	if (phase & disconnected)
 | 
						|
		seq_puts(m, "disconnected|");
 | 
						|
	if (phase & aborted)
 | 
						|
		seq_puts(m, "aborted|");
 | 
						|
	if (phase & identified)
 | 
						|
		seq_puts(m, "identified|");
 | 
						|
	if (phase & completed)
 | 
						|
		seq_puts(m, "completed|");
 | 
						|
	if (phase & spiordy)
 | 
						|
		seq_puts(m, "spiordy|");
 | 
						|
	if (phase & syncneg)
 | 
						|
		seq_puts(m, "syncneg|");
 | 
						|
	seq_printf(m, "; next=0x%p\n", SCNEXT(ptr));
 | 
						|
}
 | 
						|
 | 
						|
static void get_ports(struct seq_file *m, struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	int s;
 | 
						|
 | 
						|
	seq_printf(m, "\n%s: %s(%s) ", CURRENT_SC ? "on bus" : "waiting", states[STATE].name, states[PREVSTATE].name);
 | 
						|
 | 
						|
	s = GETPORT(SCSISEQ);
 | 
						|
	seq_puts(m, "SCSISEQ( ");
 | 
						|
	if (s & TEMODEO)
 | 
						|
		seq_puts(m, "TARGET MODE ");
 | 
						|
	if (s & ENSELO)
 | 
						|
		seq_puts(m, "SELO ");
 | 
						|
	if (s & ENSELI)
 | 
						|
		seq_puts(m, "SELI ");
 | 
						|
	if (s & ENRESELI)
 | 
						|
		seq_puts(m, "RESELI ");
 | 
						|
	if (s & ENAUTOATNO)
 | 
						|
		seq_puts(m, "AUTOATNO ");
 | 
						|
	if (s & ENAUTOATNI)
 | 
						|
		seq_puts(m, "AUTOATNI ");
 | 
						|
	if (s & ENAUTOATNP)
 | 
						|
		seq_puts(m, "AUTOATNP ");
 | 
						|
	if (s & SCSIRSTO)
 | 
						|
		seq_puts(m, "SCSIRSTO ");
 | 
						|
	seq_puts(m, ");");
 | 
						|
 | 
						|
	seq_puts(m, " SCSISIG(");
 | 
						|
	s = GETPORT(SCSISIG);
 | 
						|
	switch (s & P_MASK) {
 | 
						|
	case P_DATAO:
 | 
						|
		seq_puts(m, "DATA OUT");
 | 
						|
		break;
 | 
						|
	case P_DATAI:
 | 
						|
		seq_puts(m, "DATA IN");
 | 
						|
		break;
 | 
						|
	case P_CMD:
 | 
						|
		seq_puts(m, "COMMAND");
 | 
						|
		break;
 | 
						|
	case P_STATUS:
 | 
						|
		seq_puts(m, "STATUS");
 | 
						|
		break;
 | 
						|
	case P_MSGO:
 | 
						|
		seq_puts(m, "MESSAGE OUT");
 | 
						|
		break;
 | 
						|
	case P_MSGI:
 | 
						|
		seq_puts(m, "MESSAGE IN");
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		seq_puts(m, "*invalid*");
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	seq_puts(m, "); ");
 | 
						|
 | 
						|
	seq_printf(m, "INTSTAT (%s); ", TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo");
 | 
						|
 | 
						|
	seq_puts(m, "SSTAT( ");
 | 
						|
	s = GETPORT(SSTAT0);
 | 
						|
	if (s & TARGET)
 | 
						|
		seq_puts(m, "TARGET ");
 | 
						|
	if (s & SELDO)
 | 
						|
		seq_puts(m, "SELDO ");
 | 
						|
	if (s & SELDI)
 | 
						|
		seq_puts(m, "SELDI ");
 | 
						|
	if (s & SELINGO)
 | 
						|
		seq_puts(m, "SELINGO ");
 | 
						|
	if (s & SWRAP)
 | 
						|
		seq_puts(m, "SWRAP ");
 | 
						|
	if (s & SDONE)
 | 
						|
		seq_puts(m, "SDONE ");
 | 
						|
	if (s & SPIORDY)
 | 
						|
		seq_puts(m, "SPIORDY ");
 | 
						|
	if (s & DMADONE)
 | 
						|
		seq_puts(m, "DMADONE ");
 | 
						|
 | 
						|
	s = GETPORT(SSTAT1);
 | 
						|
	if (s & SELTO)
 | 
						|
		seq_puts(m, "SELTO ");
 | 
						|
	if (s & ATNTARG)
 | 
						|
		seq_puts(m, "ATNTARG ");
 | 
						|
	if (s & SCSIRSTI)
 | 
						|
		seq_puts(m, "SCSIRSTI ");
 | 
						|
	if (s & PHASEMIS)
 | 
						|
		seq_puts(m, "PHASEMIS ");
 | 
						|
	if (s & BUSFREE)
 | 
						|
		seq_puts(m, "BUSFREE ");
 | 
						|
	if (s & SCSIPERR)
 | 
						|
		seq_puts(m, "SCSIPERR ");
 | 
						|
	if (s & PHASECHG)
 | 
						|
		seq_puts(m, "PHASECHG ");
 | 
						|
	if (s & REQINIT)
 | 
						|
		seq_puts(m, "REQINIT ");
 | 
						|
	seq_puts(m, "); ");
 | 
						|
 | 
						|
 | 
						|
	seq_puts(m, "SSTAT( ");
 | 
						|
 | 
						|
	s = GETPORT(SSTAT0) & GETPORT(SIMODE0);
 | 
						|
 | 
						|
	if (s & TARGET)
 | 
						|
		seq_puts(m, "TARGET ");
 | 
						|
	if (s & SELDO)
 | 
						|
		seq_puts(m, "SELDO ");
 | 
						|
	if (s & SELDI)
 | 
						|
		seq_puts(m, "SELDI ");
 | 
						|
	if (s & SELINGO)
 | 
						|
		seq_puts(m, "SELINGO ");
 | 
						|
	if (s & SWRAP)
 | 
						|
		seq_puts(m, "SWRAP ");
 | 
						|
	if (s & SDONE)
 | 
						|
		seq_puts(m, "SDONE ");
 | 
						|
	if (s & SPIORDY)
 | 
						|
		seq_puts(m, "SPIORDY ");
 | 
						|
	if (s & DMADONE)
 | 
						|
		seq_puts(m, "DMADONE ");
 | 
						|
 | 
						|
	s = GETPORT(SSTAT1) & GETPORT(SIMODE1);
 | 
						|
 | 
						|
	if (s & SELTO)
 | 
						|
		seq_puts(m, "SELTO ");
 | 
						|
	if (s & ATNTARG)
 | 
						|
		seq_puts(m, "ATNTARG ");
 | 
						|
	if (s & SCSIRSTI)
 | 
						|
		seq_puts(m, "SCSIRSTI ");
 | 
						|
	if (s & PHASEMIS)
 | 
						|
		seq_puts(m, "PHASEMIS ");
 | 
						|
	if (s & BUSFREE)
 | 
						|
		seq_puts(m, "BUSFREE ");
 | 
						|
	if (s & SCSIPERR)
 | 
						|
		seq_puts(m, "SCSIPERR ");
 | 
						|
	if (s & PHASECHG)
 | 
						|
		seq_puts(m, "PHASECHG ");
 | 
						|
	if (s & REQINIT)
 | 
						|
		seq_puts(m, "REQINIT ");
 | 
						|
	seq_puts(m, "); ");
 | 
						|
 | 
						|
	seq_puts(m, "SXFRCTL0( ");
 | 
						|
 | 
						|
	s = GETPORT(SXFRCTL0);
 | 
						|
	if (s & SCSIEN)
 | 
						|
		seq_puts(m, "SCSIEN ");
 | 
						|
	if (s & DMAEN)
 | 
						|
		seq_puts(m, "DMAEN ");
 | 
						|
	if (s & CH1)
 | 
						|
		seq_puts(m, "CH1 ");
 | 
						|
	if (s & CLRSTCNT)
 | 
						|
		seq_puts(m, "CLRSTCNT ");
 | 
						|
	if (s & SPIOEN)
 | 
						|
		seq_puts(m, "SPIOEN ");
 | 
						|
	if (s & CLRCH1)
 | 
						|
		seq_puts(m, "CLRCH1 ");
 | 
						|
	seq_puts(m, "); ");
 | 
						|
 | 
						|
	seq_puts(m, "SIGNAL( ");
 | 
						|
 | 
						|
	s = GETPORT(SCSISIG);
 | 
						|
	if (s & SIG_ATNI)
 | 
						|
		seq_puts(m, "ATNI ");
 | 
						|
	if (s & SIG_SELI)
 | 
						|
		seq_puts(m, "SELI ");
 | 
						|
	if (s & SIG_BSYI)
 | 
						|
		seq_puts(m, "BSYI ");
 | 
						|
	if (s & SIG_REQI)
 | 
						|
		seq_puts(m, "REQI ");
 | 
						|
	if (s & SIG_ACKI)
 | 
						|
		seq_puts(m, "ACKI ");
 | 
						|
	seq_puts(m, "); ");
 | 
						|
 | 
						|
	seq_printf(m, "SELID(%02x), ", GETPORT(SELID));
 | 
						|
 | 
						|
	seq_printf(m, "STCNT(%d), ", GETSTCNT());
 | 
						|
 | 
						|
	seq_puts(m, "SSTAT2( ");
 | 
						|
 | 
						|
	s = GETPORT(SSTAT2);
 | 
						|
	if (s & SOFFSET)
 | 
						|
		seq_puts(m, "SOFFSET ");
 | 
						|
	if (s & SEMPTY)
 | 
						|
		seq_puts(m, "SEMPTY ");
 | 
						|
	if (s & SFULL)
 | 
						|
		seq_puts(m, "SFULL ");
 | 
						|
	seq_printf(m, "); SFCNT (%d); ", s & (SFULL | SFCNT));
 | 
						|
 | 
						|
	s = GETPORT(SSTAT3);
 | 
						|
	seq_printf(m, "SCSICNT (%d), OFFCNT(%d), ", (s & 0xf0) >> 4, s & 0x0f);
 | 
						|
 | 
						|
	seq_puts(m, "SSTAT4( ");
 | 
						|
	s = GETPORT(SSTAT4);
 | 
						|
	if (s & SYNCERR)
 | 
						|
		seq_puts(m, "SYNCERR ");
 | 
						|
	if (s & FWERR)
 | 
						|
		seq_puts(m, "FWERR ");
 | 
						|
	if (s & FRERR)
 | 
						|
		seq_puts(m, "FRERR ");
 | 
						|
	seq_puts(m, "); ");
 | 
						|
 | 
						|
	seq_puts(m, "DMACNTRL0( ");
 | 
						|
	s = GETPORT(DMACNTRL0);
 | 
						|
	seq_printf(m, "%s ", s & _8BIT ? "8BIT" : "16BIT");
 | 
						|
	seq_printf(m, "%s ", s & DMA ? "DMA" : "PIO");
 | 
						|
	seq_printf(m, "%s ", s & WRITE_READ ? "WRITE" : "READ");
 | 
						|
	if (s & ENDMA)
 | 
						|
		seq_puts(m, "ENDMA ");
 | 
						|
	if (s & INTEN)
 | 
						|
		seq_puts(m, "INTEN ");
 | 
						|
	if (s & RSTFIFO)
 | 
						|
		seq_puts(m, "RSTFIFO ");
 | 
						|
	if (s & SWINT)
 | 
						|
		seq_puts(m, "SWINT ");
 | 
						|
	seq_puts(m, "); ");
 | 
						|
 | 
						|
	seq_puts(m, "DMASTAT( ");
 | 
						|
	s = GETPORT(DMASTAT);
 | 
						|
	if (s & ATDONE)
 | 
						|
		seq_puts(m, "ATDONE ");
 | 
						|
	if (s & WORDRDY)
 | 
						|
		seq_puts(m, "WORDRDY ");
 | 
						|
	if (s & DFIFOFULL)
 | 
						|
		seq_puts(m, "DFIFOFULL ");
 | 
						|
	if (s & DFIFOEMP)
 | 
						|
		seq_puts(m, "DFIFOEMP ");
 | 
						|
	seq_puts(m, ")\n");
 | 
						|
 | 
						|
	seq_puts(m, "enabled interrupts( ");
 | 
						|
 | 
						|
	s = GETPORT(SIMODE0);
 | 
						|
	if (s & ENSELDO)
 | 
						|
		seq_puts(m, "ENSELDO ");
 | 
						|
	if (s & ENSELDI)
 | 
						|
		seq_puts(m, "ENSELDI ");
 | 
						|
	if (s & ENSELINGO)
 | 
						|
		seq_puts(m, "ENSELINGO ");
 | 
						|
	if (s & ENSWRAP)
 | 
						|
		seq_puts(m, "ENSWRAP ");
 | 
						|
	if (s & ENSDONE)
 | 
						|
		seq_puts(m, "ENSDONE ");
 | 
						|
	if (s & ENSPIORDY)
 | 
						|
		seq_puts(m, "ENSPIORDY ");
 | 
						|
	if (s & ENDMADONE)
 | 
						|
		seq_puts(m, "ENDMADONE ");
 | 
						|
 | 
						|
	s = GETPORT(SIMODE1);
 | 
						|
	if (s & ENSELTIMO)
 | 
						|
		seq_puts(m, "ENSELTIMO ");
 | 
						|
	if (s & ENATNTARG)
 | 
						|
		seq_puts(m, "ENATNTARG ");
 | 
						|
	if (s & ENPHASEMIS)
 | 
						|
		seq_puts(m, "ENPHASEMIS ");
 | 
						|
	if (s & ENBUSFREE)
 | 
						|
		seq_puts(m, "ENBUSFREE ");
 | 
						|
	if (s & ENSCSIPERR)
 | 
						|
		seq_puts(m, "ENSCSIPERR ");
 | 
						|
	if (s & ENPHASECHG)
 | 
						|
		seq_puts(m, "ENPHASECHG ");
 | 
						|
	if (s & ENREQINIT)
 | 
						|
		seq_puts(m, "ENREQINIT ");
 | 
						|
	seq_puts(m, ")\n");
 | 
						|
}
 | 
						|
 | 
						|
static int aha152x_set_info(struct Scsi_Host *shpnt, char *buffer, int length)
 | 
						|
{
 | 
						|
	if(!shpnt || !buffer || length<8 || strncmp("aha152x ", buffer, 8)!=0)
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
#if defined(AHA152X_STAT)
 | 
						|
	if(length>13 && strncmp("reset", buffer+8, 5)==0) {
 | 
						|
		int i;
 | 
						|
 | 
						|
		HOSTDATA(shpnt)->total_commands=0;
 | 
						|
		HOSTDATA(shpnt)->disconnections=0;
 | 
						|
		HOSTDATA(shpnt)->busfree_without_any_action=0;
 | 
						|
		HOSTDATA(shpnt)->busfree_without_old_command=0;
 | 
						|
		HOSTDATA(shpnt)->busfree_without_new_command=0;
 | 
						|
		HOSTDATA(shpnt)->busfree_without_done_command=0;
 | 
						|
		HOSTDATA(shpnt)->busfree_with_check_condition=0;
 | 
						|
		for (i = idle; i<maxstate; i++) {
 | 
						|
			HOSTDATA(shpnt)->count[i]=0;
 | 
						|
			HOSTDATA(shpnt)->count_trans[i]=0;
 | 
						|
			HOSTDATA(shpnt)->time[i]=0;
 | 
						|
		}
 | 
						|
 | 
						|
		shost_printk(KERN_INFO, shpnt, "aha152x: stats reset.\n");
 | 
						|
 | 
						|
	} else
 | 
						|
#endif
 | 
						|
	{
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	return length;
 | 
						|
}
 | 
						|
 | 
						|
static int aha152x_show_info(struct seq_file *m, struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	struct scsi_cmnd *ptr;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	seq_puts(m, AHA152X_REVID "\n");
 | 
						|
 | 
						|
	seq_printf(m, "ioports 0x%04lx to 0x%04lx\n",
 | 
						|
		shpnt->io_port, shpnt->io_port + shpnt->n_io_port - 1);
 | 
						|
	seq_printf(m, "interrupt 0x%02x\n", shpnt->irq);
 | 
						|
	seq_printf(m, "disconnection/reconnection %s\n",
 | 
						|
		RECONNECT ? "enabled" : "disabled");
 | 
						|
	seq_printf(m, "parity checking %s\n",
 | 
						|
		PARITY ? "enabled" : "disabled");
 | 
						|
	seq_printf(m, "synchronous transfers %s\n",
 | 
						|
		SYNCHRONOUS ? "enabled" : "disabled");
 | 
						|
	seq_printf(m, "%d commands currently queued\n", HOSTDATA(shpnt)->commands);
 | 
						|
 | 
						|
	if(SYNCHRONOUS) {
 | 
						|
		seq_puts(m, "synchronously operating targets (tick=50 ns):\n");
 | 
						|
		for (i = 0; i < 8; i++)
 | 
						|
			if (HOSTDATA(shpnt)->syncrate[i] & 0x7f)
 | 
						|
				seq_printf(m, "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);
 | 
						|
	}
 | 
						|
	seq_puts(m, "\nqueue status:\n");
 | 
						|
	DO_LOCK(flags);
 | 
						|
	if (ISSUE_SC) {
 | 
						|
		seq_puts(m, "not yet issued commands:\n");
 | 
						|
		for (ptr = ISSUE_SC; ptr; ptr = SCNEXT(ptr))
 | 
						|
			get_command(m, ptr);
 | 
						|
	} else
 | 
						|
		seq_puts(m, "no not yet issued commands\n");
 | 
						|
	DO_UNLOCK(flags);
 | 
						|
 | 
						|
	if (CURRENT_SC) {
 | 
						|
		seq_puts(m, "current command:\n");
 | 
						|
		get_command(m, CURRENT_SC);
 | 
						|
	} else
 | 
						|
		seq_puts(m, "no current command\n");
 | 
						|
 | 
						|
	if (DISCONNECTED_SC) {
 | 
						|
		seq_puts(m, "disconnected commands:\n");
 | 
						|
		for (ptr = DISCONNECTED_SC; ptr; ptr = SCNEXT(ptr))
 | 
						|
			get_command(m, ptr);
 | 
						|
	} else
 | 
						|
		seq_puts(m, "no disconnected commands\n");
 | 
						|
 | 
						|
	get_ports(m, shpnt);
 | 
						|
 | 
						|
#if defined(AHA152X_STAT)
 | 
						|
	seq_printf(m, "statistics:\n"
 | 
						|
		"total commands:               %d\n"
 | 
						|
		"disconnections:               %d\n"
 | 
						|
		"busfree with check condition: %d\n"
 | 
						|
		"busfree without old command:  %d\n"
 | 
						|
		"busfree without new command:  %d\n"
 | 
						|
		"busfree without done command: %d\n"
 | 
						|
		"busfree without any action:   %d\n"
 | 
						|
		"state      "
 | 
						|
		"transitions  "
 | 
						|
		"count        "
 | 
						|
		"time\n",
 | 
						|
		HOSTDATA(shpnt)->total_commands,
 | 
						|
		HOSTDATA(shpnt)->disconnections,
 | 
						|
		HOSTDATA(shpnt)->busfree_with_check_condition,
 | 
						|
		HOSTDATA(shpnt)->busfree_without_old_command,
 | 
						|
		HOSTDATA(shpnt)->busfree_without_new_command,
 | 
						|
		HOSTDATA(shpnt)->busfree_without_done_command,
 | 
						|
		HOSTDATA(shpnt)->busfree_without_any_action);
 | 
						|
	for(i=0; i<maxstate; i++) {
 | 
						|
		seq_printf(m, "%-10s %-12d %-12d %-12ld\n",
 | 
						|
			states[i].name,
 | 
						|
			HOSTDATA(shpnt)->count_trans[i],
 | 
						|
			HOSTDATA(shpnt)->count[i],
 | 
						|
			HOSTDATA(shpnt)->time[i]);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int aha152x_adjust_queue(struct scsi_device *device)
 | 
						|
{
 | 
						|
	blk_queue_bounce_limit(device->request_queue, BLK_BOUNCE_HIGH);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static const struct scsi_host_template aha152x_driver_template = {
 | 
						|
	.module				= THIS_MODULE,
 | 
						|
	.name				= AHA152X_REVID,
 | 
						|
	.proc_name			= "aha152x",
 | 
						|
	.show_info			= aha152x_show_info,
 | 
						|
	.write_info			= aha152x_set_info,
 | 
						|
	.queuecommand			= aha152x_queue,
 | 
						|
	.eh_abort_handler		= aha152x_abort,
 | 
						|
	.eh_device_reset_handler	= aha152x_device_reset,
 | 
						|
	.eh_bus_reset_handler		= aha152x_bus_reset,
 | 
						|
	.bios_param			= aha152x_biosparam,
 | 
						|
	.can_queue			= 1,
 | 
						|
	.this_id			= 7,
 | 
						|
	.sg_tablesize			= SG_ALL,
 | 
						|
	.dma_boundary			= PAGE_SIZE - 1,
 | 
						|
	.slave_alloc			= aha152x_adjust_queue,
 | 
						|
	.cmd_size			= sizeof(struct aha152x_cmd_priv),
 | 
						|
};
 | 
						|
 | 
						|
#if !defined(AHA152X_PCMCIA)
 | 
						|
static int setup_count;
 | 
						|
static struct aha152x_setup setup[2];
 | 
						|
 | 
						|
/* possible i/o addresses for the AIC-6260; default first */
 | 
						|
static unsigned short ports[] = { 0x340, 0x140 };
 | 
						|
 | 
						|
#if !defined(SKIP_BIOSTEST)
 | 
						|
/* possible locations for the Adaptec BIOS; defaults first */
 | 
						|
static unsigned int addresses[] =
 | 
						|
{
 | 
						|
	0xdc000,		/* default first */
 | 
						|
	0xc8000,
 | 
						|
	0xcc000,
 | 
						|
	0xd0000,
 | 
						|
	0xd4000,
 | 
						|
	0xd8000,
 | 
						|
	0xe0000,
 | 
						|
	0xeb800,		/* VTech Platinum SMP */
 | 
						|
	0xf0000,
 | 
						|
};
 | 
						|
 | 
						|
/* 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",		0x000b, 17 },
 | 
						|
		/* Adaptec 152x rev B */
 | 
						|
	{ "Adaptec AHA-1520B",		0x0026, 17 },
 | 
						|
		/* Iomega Jaz Jet ISA (AIC6370Q) */
 | 
						|
	{ "Adaptec ASW-B626 BIOS",	0x1029, 21 },
 | 
						|
		/* on-board controller */
 | 
						|
	{ "Adaptec BIOS: ASW-B626",	0x000f, 22 },
 | 
						|
		/* on-board controller */
 | 
						|
	{ "Adaptec ASW-B626 S2",	0x2e6c, 19 },
 | 
						|
		/* on-board controller */
 | 
						|
	{ "Adaptec BIOS:AIC-6360",	0x000c, 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",	0x000c, 21 },
 | 
						|
		/* Adaptec 282x */
 | 
						|
	{ "Adaptec IBM Dock II SCSI",   0x2edd, 24 },
 | 
						|
		/* IBM Thinkpad Dock II */
 | 
						|
	{ "Adaptec BIOS:AHA-1532P",     0x001c, 22 },
 | 
						|
		/* IBM Thinkpad Dock II SCSI */
 | 
						|
	{ "DTC3520A Host Adapter BIOS", 0x318a, 26 },
 | 
						|
		/* DTC 3520A ISA SCSI */
 | 
						|
};
 | 
						|
#endif /* !SKIP_BIOSTEST */
 | 
						|
 | 
						|
/*
 | 
						|
 * Test, if port_base is valid.
 | 
						|
 *
 | 
						|
 */
 | 
						|
static int aha152x_porttest(int io_port)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
	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);
 | 
						|
}
 | 
						|
 | 
						|
static int tc1550_porttest(int io_port)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
	SETPORT(io_port + O_TC_DMACNTRL1, 0);	/* reset stack pointer */
 | 
						|
	for (i = 0; i < 16; i++)
 | 
						|
		SETPORT(io_port + O_STACK, i);
 | 
						|
 | 
						|
	SETPORT(io_port + O_TC_DMACNTRL1, 0);	/* reset stack pointer */
 | 
						|
	for (i = 0; i < 16 && GETPORT(io_port + O_TC_STACK) == i; i++)
 | 
						|
		;
 | 
						|
 | 
						|
	return (i == 16);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int checksetup(struct aha152x_setup *setup)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	for (i = 0; i < ARRAY_SIZE(ports) && (setup->io_port != ports[i]); i++)
 | 
						|
		;
 | 
						|
 | 
						|
	if (i == ARRAY_SIZE(ports))
 | 
						|
		return 0;
 | 
						|
 | 
						|
	if (!request_region(setup->io_port, IO_RANGE, "aha152x")) {
 | 
						|
		printk(KERN_ERR "aha152x: io port 0x%x busy.\n", setup->io_port);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	if( aha152x_porttest(setup->io_port) ) {
 | 
						|
		setup->tc1550=0;
 | 
						|
	} else if( tc1550_porttest(setup->io_port) ) {
 | 
						|
		setup->tc1550=1;
 | 
						|
	} else {
 | 
						|
		release_region(setup->io_port, IO_RANGE);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	release_region(setup->io_port, IO_RANGE);
 | 
						|
 | 
						|
	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;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int __init aha152x_init(void)
 | 
						|
{
 | 
						|
	int i, j, ok;
 | 
						|
#if defined(AUTOCONF)
 | 
						|
	aha152x_config conf;
 | 
						|
#endif
 | 
						|
#ifdef __ISAPNP__
 | 
						|
	struct pnp_dev *dev=NULL, *pnpdev[2] = {NULL, NULL};
 | 
						|
#endif
 | 
						|
 | 
						|
	if ( setup_count ) {
 | 
						|
		printk(KERN_INFO "aha152x: processing commandline: ");
 | 
						|
 | 
						|
		for (i = 0; i<setup_count; i++) {
 | 
						|
			if (!checksetup(&setup[i])) {
 | 
						|
				printk(KERN_ERR "\naha152x: %s\n", setup[i].conf);
 | 
						|
				printk(KERN_ERR "aha152x: invalid line\n");
 | 
						|
			}
 | 
						|
		}
 | 
						|
		printk("ok\n");
 | 
						|
	}
 | 
						|
 | 
						|
#if defined(SETUP0)
 | 
						|
	if (setup_count < ARRAY_SIZE(setup)) {
 | 
						|
		struct aha152x_setup override = SETUP0;
 | 
						|
 | 
						|
		if (setup_count == 0 || (override.io_port != setup[0].io_port)) {
 | 
						|
			if (!checksetup(&override)) {
 | 
						|
				printk(KERN_ERR "\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
 | 
						|
 | 
						|
#if defined(SETUP1)
 | 
						|
	if (setup_count < ARRAY_SIZE(setup)) {
 | 
						|
		struct aha152x_setup override = SETUP1;
 | 
						|
 | 
						|
		if (setup_count == 0 || (override.io_port != setup[0].io_port)) {
 | 
						|
			if (!checksetup(&override)) {
 | 
						|
				printk(KERN_ERR "\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<ARRAY_SIZE(setup) && (aha152x[0]!=0 || io[0]!=0 || irq[0]!=0)) {
 | 
						|
		if(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];
 | 
						|
		} else if (io[0] != 0 || irq[0] != 0) {
 | 
						|
			if(io[0]!=0)  setup[setup_count].io_port = io[0];
 | 
						|
			if(irq[0]!=0) setup[setup_count].irq     = irq[0];
 | 
						|
 | 
						|
			setup[setup_count].scsiid      = scsiid[0];
 | 
						|
			setup[setup_count].reconnect   = reconnect[0];
 | 
						|
			setup[setup_count].parity      = parity[0];
 | 
						|
			setup[setup_count].synchronous = sync[0];
 | 
						|
			setup[setup_count].delay       = delay[0];
 | 
						|
			setup[setup_count].ext_trans   = exttrans[0];
 | 
						|
		}
 | 
						|
 | 
						|
		if (checksetup(&setup[setup_count]))
 | 
						|
			setup_count++;
 | 
						|
		else
 | 
						|
			printk(KERN_ERR "aha152x: invalid module params io=0x%x, irq=%d,scsiid=%d,reconnect=%d,parity=%d,sync=%d,delay=%d,exttrans=%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<ARRAY_SIZE(setup) && (aha152x1[0]!=0 || io[1]!=0 || irq[1]!=0)) {
 | 
						|
		if(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];
 | 
						|
		} else if (io[1] != 0 || irq[1] != 0) {
 | 
						|
			if(io[1]!=0)  setup[setup_count].io_port = io[1];
 | 
						|
			if(irq[1]!=0) setup[setup_count].irq     = irq[1];
 | 
						|
 | 
						|
			setup[setup_count].scsiid      = scsiid[1];
 | 
						|
			setup[setup_count].reconnect   = reconnect[1];
 | 
						|
			setup[setup_count].parity      = parity[1];
 | 
						|
			setup[setup_count].synchronous = sync[1];
 | 
						|
			setup[setup_count].delay       = delay[1];
 | 
						|
			setup[setup_count].ext_trans   = exttrans[1];
 | 
						|
		}
 | 
						|
		if (checksetup(&setup[setup_count]))
 | 
						|
			setup_count++;
 | 
						|
		else
 | 
						|
			printk(KERN_ERR "aha152x: invalid module params io=0x%x, irq=%d,scsiid=%d,reconnect=%d,parity=%d,sync=%d,delay=%d,exttrans=%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
 | 
						|
 | 
						|
#ifdef __ISAPNP__
 | 
						|
	for(i=0; setup_count<ARRAY_SIZE(setup) && id_table[i].vendor; i++) {
 | 
						|
		while ( setup_count<ARRAY_SIZE(setup) &&
 | 
						|
			(dev=pnp_find_dev(NULL, id_table[i].vendor, id_table[i].function, dev)) ) {
 | 
						|
			if (pnp_device_attach(dev) < 0)
 | 
						|
				continue;
 | 
						|
 | 
						|
			if (pnp_activate_dev(dev) < 0) {
 | 
						|
				pnp_device_detach(dev);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			if (!pnp_port_valid(dev, 0)) {
 | 
						|
				pnp_device_detach(dev);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			if (setup_count==1 && pnp_port_start(dev, 0)==setup[0].io_port) {
 | 
						|
				pnp_device_detach(dev);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			setup[setup_count].io_port     = pnp_port_start(dev, 0);
 | 
						|
			setup[setup_count].irq         = pnp_irq(dev, 0);
 | 
						|
			setup[setup_count].scsiid      = 7;
 | 
						|
			setup[setup_count].reconnect   = 1;
 | 
						|
			setup[setup_count].parity      = 1;
 | 
						|
			setup[setup_count].synchronous = 1;
 | 
						|
			setup[setup_count].delay       = DELAY_DEFAULT;
 | 
						|
			setup[setup_count].ext_trans   = 0;
 | 
						|
#if defined(__ISAPNP__)
 | 
						|
			pnpdev[setup_count]            = dev;
 | 
						|
#endif
 | 
						|
			printk (KERN_INFO
 | 
						|
				"aha152x: found ISAPnP adapter at io=0x%03x, irq=%d\n",
 | 
						|
				setup[setup_count].io_port, setup[setup_count].irq);
 | 
						|
			setup_count++;
 | 
						|
		}
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(AUTOCONF)
 | 
						|
	if (setup_count<ARRAY_SIZE(setup)) {
 | 
						|
#if !defined(SKIP_BIOSTEST)
 | 
						|
		ok = 0;
 | 
						|
		for (i = 0; i < ARRAY_SIZE(addresses) && !ok; i++) {
 | 
						|
			void __iomem *p = ioremap(addresses[i], 0x4000);
 | 
						|
			if (!p)
 | 
						|
				continue;
 | 
						|
			for (j = 0; j<ARRAY_SIZE(signatures) && !ok; j++)
 | 
						|
				ok = check_signature(p + signatures[j].sig_offset,
 | 
						|
								signatures[j].signature, signatures[j].sig_length);
 | 
						|
			iounmap(p);
 | 
						|
		}
 | 
						|
		if (!ok && setup_count == 0)
 | 
						|
			return -ENODEV;
 | 
						|
 | 
						|
		printk(KERN_INFO "aha152x: BIOS test: passed, ");
 | 
						|
#else
 | 
						|
		printk(KERN_INFO "aha152x: ");
 | 
						|
#endif				/* !SKIP_BIOSTEST */
 | 
						|
 | 
						|
		ok = 0;
 | 
						|
		for (i = 0; i < ARRAY_SIZE(ports) && setup_count < 2; i++) {
 | 
						|
			if ((setup_count == 1) && (setup[0].io_port == ports[i]))
 | 
						|
				continue;
 | 
						|
 | 
						|
			if (!request_region(ports[i], IO_RANGE, "aha152x")) {
 | 
						|
				printk(KERN_ERR "aha152x: io port 0x%x busy.\n", ports[i]);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			if (aha152x_porttest(ports[i])) {
 | 
						|
				setup[setup_count].tc1550  = 0;
 | 
						|
 | 
						|
				conf.cf_port =
 | 
						|
				    (GETPORT(ports[i] + O_PORTA) << 8) + GETPORT(ports[i] + O_PORTB);
 | 
						|
			} else if (tc1550_porttest(ports[i])) {
 | 
						|
				setup[setup_count].tc1550  = 1;
 | 
						|
 | 
						|
				conf.cf_port =
 | 
						|
				    (GETPORT(ports[i] + O_TC_PORTA) << 8) + GETPORT(ports[i] + O_TC_PORTB);
 | 
						|
			} else {
 | 
						|
				release_region(ports[i], IO_RANGE);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			release_region(ports[i], IO_RANGE);
 | 
						|
 | 
						|
			ok++;
 | 
						|
			setup[setup_count].io_port = ports[i];
 | 
						|
			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 = conf.cf_syncneg;
 | 
						|
			setup[setup_count].delay = DELAY_DEFAULT;
 | 
						|
			setup[setup_count].ext_trans = 0;
 | 
						|
			setup_count++;
 | 
						|
 | 
						|
		}
 | 
						|
 | 
						|
		if (ok)
 | 
						|
			printk("auto configuration: ok, ");
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	printk("%d controller(s) configured\n", setup_count);
 | 
						|
 | 
						|
	for (i=0; i<setup_count; i++) {
 | 
						|
		if ( request_region(setup[i].io_port, IO_RANGE, "aha152x") ) {
 | 
						|
			struct Scsi_Host *shpnt = aha152x_probe_one(&setup[i]);
 | 
						|
 | 
						|
			if( !shpnt ) {
 | 
						|
				release_region(setup[i].io_port, IO_RANGE);
 | 
						|
#if defined(__ISAPNP__)
 | 
						|
			} else if( pnpdev[i] ) {
 | 
						|
				HOSTDATA(shpnt)->pnpdev=pnpdev[i];
 | 
						|
				pnpdev[i]=NULL;
 | 
						|
#endif
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			printk(KERN_ERR "aha152x: io port 0x%x busy.\n", setup[i].io_port);
 | 
						|
		}
 | 
						|
 | 
						|
#if defined(__ISAPNP__)
 | 
						|
		if( pnpdev[i] )
 | 
						|
			pnp_device_detach(pnpdev[i]);
 | 
						|
#endif
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void __exit aha152x_exit(void)
 | 
						|
{
 | 
						|
	struct aha152x_hostdata *hd, *tmp;
 | 
						|
 | 
						|
	list_for_each_entry_safe(hd, tmp, &aha152x_host_list, host_list) {
 | 
						|
		struct Scsi_Host *shost = container_of((void *)hd, struct Scsi_Host, hostdata);
 | 
						|
 | 
						|
		aha152x_release(shost);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
module_init(aha152x_init);
 | 
						|
module_exit(aha152x_exit);
 | 
						|
 | 
						|
#if !defined(MODULE)
 | 
						|
static int __init aha152x_setup(char *str)
 | 
						|
{
 | 
						|
	int ints[10];
 | 
						|
 | 
						|
	get_options(str, ARRAY_SIZE(ints), ints);
 | 
						|
 | 
						|
	if(setup_count>=ARRAY_SIZE(setup)) {
 | 
						|
		printk(KERN_ERR "aha152x: you can only configure up to two controllers\n");
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	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] : 1;
 | 
						|
	setup[setup_count].delay       = ints[0] >= 7 ? ints[7] : DELAY_DEFAULT;
 | 
						|
	setup[setup_count].ext_trans   = ints[0] >= 8 ? ints[8] : 0;
 | 
						|
	if (ints[0] > 8)
 | 
						|
		printk(KERN_NOTICE "aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>"
 | 
						|
		       "[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<EXT_TRANS>]]]]]]]\n");
 | 
						|
	else
 | 
						|
		setup_count++;
 | 
						|
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
__setup("aha152x=", aha152x_setup);
 | 
						|
#endif
 | 
						|
 | 
						|
#endif /* !AHA152X_PCMCIA */
 |