Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/scsi/pci2000.c b/drivers/scsi/pci2000.c
new file mode 100644
index 0000000..60ce1cc
--- /dev/null
+++ b/drivers/scsi/pci2000.c
@@ -0,0 +1,834 @@
+/****************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver for Linux.
+ *
+ * pci2000.c - Linux Host Driver for PCI-2000 IntelliCache SCSI Adapters
+ *
+ * Copyright (c) 1997-1999 Perceptive Solutions, Inc.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ *
+ * Technical updates and product information at:
+ *  http://www.psidisk.com
+ *
+ * Please send questions, comments, bug reports to:
+ *  tech@psidisk.com Technical Support
+ *
+ *
+ *	Revisions	1.10	Jan-21-1999
+ *		- Fixed sign on message to reflect proper controller name.
+ *		- Added support for RAID status monitoring and control.
+ *
+ *  Revisions	1.11	Mar-22-1999
+ *		- Fixed control timeout to not lock up the entire system if
+ *		  controller goes offline completely.
+ *
+ *	Revisions 1.12		Mar-26-1999
+ *		- Fixed spinlock and PCI configuration.
+ *
+ *	Revisions 1.20		Mar-27-2000
+ *		- Added support for dynamic DMA
+ *
+ ****************************************************************************/
+#define PCI2000_VERSION		"1.20"
+
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/spinlock.h>
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "pci2000.h"
+#include "psi_roy.h"
+
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+#define DEB(x) x
+#define STOP_HERE	{int st;for(st=0;st<100;st++){st=1;}}
+#else
+#define DEB(x)
+#define STOP_HERE
+#endif
+
+typedef struct
+	{
+	unsigned int	address;
+	unsigned int	length;
+	}	SCATGATH, *PSCATGATH;
+
+typedef struct
+	{
+	Scsi_Cmnd		*SCpnt;
+	PSCATGATH		 scatGath;
+	dma_addr_t		 scatGathDma;
+	UCHAR			*cdb;
+	dma_addr_t		 cdbDma; 
+	UCHAR			 tag;
+	}	DEV2000, *PDEV2000;
+
+typedef struct
+	{
+	ULONG			 basePort;
+	ULONG			 mb0;
+	ULONG			 mb1;
+	ULONG			 mb2;
+	ULONG			 mb3;
+	ULONG			 mb4;
+	ULONG			 cmd;
+	ULONG			 tag;
+	ULONG			 irqOwned;
+	struct pci_dev	*pdev;
+	DEV2000	 		 dev[MAX_BUS][MAX_UNITS];
+	}	ADAPTER2000, *PADAPTER2000;
+
+#define HOSTDATA(host) ((PADAPTER2000)&host->hostdata)
+#define consistentLen (MAX_BUS * MAX_UNITS * (16 * sizeof (SCATGATH) + MAX_COMMAND_SIZE))
+
+
+static struct	Scsi_Host 	   *PsiHost[MAXADAPTER] = {NULL,};  // One for each adapter
+static			int				NumAdapters = 0;
+/****************************************************************
+ *	Name:			WaitReady	:LOCAL
+ *
+ *	Description:	Wait for controller ready.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		TRUE on not ready.
+ *
+ ****************************************************************/
+static int WaitReady (PADAPTER2000 padapter)
+	{
+	ULONG	z;
+
+	for ( z = 0;  z < (TIMEOUT_COMMAND * 4);  z++ )
+		{
+		if ( !inb_p (padapter->cmd) )
+			return FALSE;
+		udelay (250);
+		};								
+	return TRUE;
+	}
+/****************************************************************
+ *	Name:			WaitReadyLong	:LOCAL
+ *
+ *	Description:	Wait for controller ready.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		TRUE on not ready.
+ *
+ ****************************************************************/
+static int WaitReadyLong (PADAPTER2000 padapter)
+	{
+	ULONG	z;
+
+	for ( z = 0;  z < (5000 * 4);  z++ )
+		{
+		if ( !inb_p (padapter->cmd) )
+			return FALSE;
+		udelay (250);
+		};								
+	return TRUE;
+	}
+/****************************************************************
+ *	Name:	OpDone	:LOCAL
+ *
+ *	Description:	Clean up operation and issue done to caller.
+ *
+ *	Parameters:		SCpnt	- Pointer to SCSI command structure.
+ *					status	- Caller status.
+ *
+ *	Returns:		Nothing.
+ *
+ ****************************************************************/
+static void OpDone (Scsi_Cmnd *SCpnt, ULONG status)
+	{
+	SCpnt->result = status;
+	SCpnt->scsi_done (SCpnt);
+	}
+/****************************************************************
+ *	Name:	Command		:LOCAL
+ *
+ *	Description:	Issue queued command to the PCI-2000.
+ *
+ *	Parameters:		padapter - Pointer to adapter information structure.
+ *					cmd		 - PCI-2000 command byte.
+ *
+ *	Returns:		Non-zero command tag if operation is accepted.
+ *
+ ****************************************************************/
+static UCHAR Command (PADAPTER2000 padapter, UCHAR cmd)
+	{
+	outb_p (cmd, padapter->cmd);
+	if ( WaitReady (padapter) )
+		return 0;
+
+	if ( inw_p (padapter->mb0) )
+		return 0;
+
+	return inb_p (padapter->mb1);
+	}
+/****************************************************************
+ *	Name:	BuildSgList		:LOCAL
+ *
+ *	Description:	Build the scatter gather list for controller.
+ *
+ *	Parameters:		SCpnt	 - Pointer to SCSI command structure.
+ *					padapter - Pointer to adapter information structure.
+ *					pdev	 - Pointer to adapter device structure.
+ *
+ *	Returns:		Non-zero in not scatter gather.
+ *
+ ****************************************************************/
+static int BuildSgList (Scsi_Cmnd *SCpnt, PADAPTER2000 padapter, PDEV2000 pdev)
+	{
+	int					 z;
+	int					 zc;
+	struct scatterlist	*sg;
+
+	if ( SCpnt->use_sg )
+		{
+		sg = (struct scatterlist *)SCpnt->request_buffer;
+		zc = pci_map_sg (padapter->pdev, sg, SCpnt->use_sg, scsi_to_pci_dma_dir (SCpnt->sc_data_direction));
+		for ( z = 0;  z < zc;  z++ )
+			{
+			pdev->scatGath[z].address = cpu_to_le32 (sg_dma_address (sg));
+			pdev->scatGath[z].length = cpu_to_le32 (sg_dma_len (sg++));
+			}
+		outl (pdev->scatGathDma, padapter->mb2);
+		outl ((zc << 24) | SCpnt->request_bufflen, padapter->mb3);
+		return FALSE;
+		}
+	if ( !SCpnt->request_bufflen)
+		{
+		outl (0, padapter->mb2);
+		outl (0, padapter->mb3);
+		return TRUE;
+		}
+	SCpnt->SCp.have_data_in = pci_map_single (padapter->pdev, SCpnt->request_buffer, SCpnt->request_bufflen, scsi_to_pci_dma_dir (SCpnt->sc_data_direction));
+	outl (SCpnt->SCp.have_data_in, padapter->mb2);
+	outl (SCpnt->request_bufflen, padapter->mb3);
+	return TRUE;
+	}
+/*********************************************************************
+ *	Name:	PsiRaidCmd
+ *
+ *	Description:	Execute a simple command.
+ *
+ *	Parameters:		padapter - Pointer to adapter control structure.
+ *					cmd		 - Roy command byte.
+ *
+ *	Returns:		Return error status.
+ *
+ ********************************************************************/
+static int PsiRaidCmd (PADAPTER2000 padapter, char cmd)
+	{
+	if ( WaitReady (padapter) )						// test for command register ready
+		return DID_TIME_OUT;
+	outb_p (cmd, padapter->cmd);					// issue command
+	if ( WaitReadyLong (padapter) )					// wait for adapter ready
+		return DID_TIME_OUT;
+	return DID_OK;
+	}
+/****************************************************************
+ *	Name:	Irq_Handler	:LOCAL
+ *
+ *	Description:	Interrupt handler.
+ *
+ *	Parameters:		irq		- Hardware IRQ number.
+ *					dev_id	-
+ *					regs	-
+ *
+ *	Returns:		TRUE if drive is not ready in time.
+ *
+ ****************************************************************/
+static irqreturn_t Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+	{
+	struct Scsi_Host   *shost = NULL;	// Pointer to host data block
+	PADAPTER2000		padapter;		// Pointer to adapter control structure
+	PDEV2000			pdev;
+	Scsi_Cmnd		   *SCpnt;
+	UCHAR				tag = 0;
+	UCHAR				tag0;
+	ULONG				error;
+	int					pun;
+	int					bus;
+	int					z;
+    unsigned long		flags;
+    int handled = 0;
+
+	DEB(printk ("\npci2000 received interrupt "));
+	for ( z = 0; z < NumAdapters;  z++ )										// scan for interrupt to process
+		{
+		if ( PsiHost[z]->irq == (UCHAR)(irq & 0xFF) )
+			{
+			tag = inb_p (HOSTDATA(PsiHost[z])->tag);
+			if (  tag )
+				{
+				shost = PsiHost[z];
+				break;
+				}
+			}
+		}
+
+	if ( !shost )
+		{
+		DEB (printk ("\npci2000: not my interrupt"));
+		goto out;
+		}
+
+    handled = 1;
+	spin_lock_irqsave(shost->host_lock, flags);
+	padapter = HOSTDATA(shost);
+
+	tag0 = tag & 0x7F;															// mask off the error bit
+	for ( bus = 0;  bus < MAX_BUS;  bus++ )										// scan the busses
+    	{
+		for ( pun = 0;  pun < MAX_UNITS;  pun++ )								// scan the targets
+    		{
+			pdev = &padapter->dev[bus][pun];
+			if ( !pdev->tag )
+    			continue;
+			if ( pdev->tag == tag0 )											// is this it?
+				{
+				pdev->tag = 0;
+				SCpnt = pdev->SCpnt;
+				goto unmapProceed;
+    			}
+			}
+    	}
+
+	outb_p (0xFF, padapter->tag);												// clear the op interrupt
+	outb_p (CMD_DONE, padapter->cmd);											// complete the op
+	goto irq_return;															// done, but, with what?
+
+unmapProceed:;
+	if ( !bus )
+		{
+		switch ( SCpnt->cmnd[0] )
+			{
+			case SCSIOP_TEST_UNIT_READY:
+				pci_unmap_single (padapter->pdev, SCpnt->SCp.have_data_in, sizeof (SCpnt->sense_buffer), PCI_DMA_FROMDEVICE);
+				goto irqProceed;
+			case SCSIOP_READ_CAPACITY:
+				pci_unmap_single (padapter->pdev, SCpnt->SCp.have_data_in, 8, PCI_DMA_FROMDEVICE);
+				goto irqProceed;
+			case SCSIOP_VERIFY:
+			case SCSIOP_START_STOP_UNIT:
+			case SCSIOP_MEDIUM_REMOVAL:
+				goto irqProceed;
+			}
+		}
+	if ( SCpnt->SCp.have_data_in )
+		pci_unmap_single (padapter->pdev, SCpnt->SCp.have_data_in, SCpnt->request_bufflen, scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+	else 
+		{
+		if ( SCpnt->use_sg )
+			pci_unmap_sg (padapter->pdev, (struct scatterlist *)SCpnt->request_buffer, SCpnt->use_sg, scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+		}
+
+irqProceed:;
+	if ( tag & ERR08_TAGGED )												// is there an error here?
+		{
+		if ( WaitReady (padapter) )
+			{
+			OpDone (SCpnt, DID_TIME_OUT << 16);
+			goto irq_return;
+			}
+
+		outb_p (tag0, padapter->mb0);										// get real error code
+		outb_p (CMD_ERROR, padapter->cmd);
+		if ( WaitReady (padapter) )											// wait for controller to suck up the op
+			{
+			OpDone (SCpnt, DID_TIME_OUT << 16);
+			goto irq_return;
+			}
+
+		error = inl (padapter->mb0);										// get error data
+		outb_p (0xFF, padapter->tag);										// clear the op interrupt
+		outb_p (CMD_DONE, padapter->cmd);									// complete the op
+
+		DEB (printk ("status: %lX ", error));
+		if ( error == 0x00020002 )											// is this error a check condition?
+			{
+			if ( bus )														// are we doint SCSI commands?
+				{
+				OpDone (SCpnt, (DID_OK << 16) | 2);
+				goto irq_return;
+				}
+			if ( *SCpnt->cmnd == SCSIOP_TEST_UNIT_READY )
+				OpDone (SCpnt, (DRIVER_SENSE << 24) | (DID_OK << 16) | 2);	// test caller we have sense data too
+			else
+				OpDone (SCpnt, DID_ERROR << 16);
+			goto irq_return;
+			}
+		OpDone (SCpnt, DID_ERROR << 16);
+		goto irq_return;
+		}
+
+	outb_p (0xFF, padapter->tag);											// clear the op interrupt
+	outb_p (CMD_DONE, padapter->cmd);										// complete the op
+	OpDone (SCpnt, DID_OK << 16);
+
+irq_return:
+    spin_unlock_irqrestore(shost->host_lock, flags);
+out:
+    return IRQ_RETVAL(handled);
+}
+/****************************************************************
+ *	Name:	Pci2000_QueueCommand
+ *
+ *	Description:	Process a queued command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					done  - Pointer to done function to call.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Pci2000_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+	{
+	UCHAR		   *cdb = (UCHAR *)SCpnt->cmnd;					// Pointer to SCSI CDB
+	PADAPTER2000	padapter = HOSTDATA(SCpnt->device->host);			// Pointer to adapter control structure
+	int				rc		 = -1;								// command return code
+	UCHAR			bus		 = SCpnt->device->channel;
+	UCHAR			pun		 = SCpnt->device->id;
+	UCHAR			lun		 = SCpnt->device->lun;
+	UCHAR			cmd;
+	PDEV2000		pdev	 = &padapter->dev[bus][pun];
+
+	if ( !done )
+		{
+		printk("pci2000_queuecommand: %02X: done can't be NULL\n", *cdb);
+		return 0;
+		}
+
+	SCpnt->scsi_done = done;
+	SCpnt->SCp.have_data_in = 0;
+	pdev->SCpnt = SCpnt;  									// Save this command data
+
+	if ( WaitReady (padapter) )
+		{
+		rc = DID_ERROR;
+		goto finished;
+		}
+
+	outw_p (pun | (lun << 8), padapter->mb0);
+
+	if ( bus )
+		{
+		DEB (if(*cdb) printk ("\nCDB: %X-  %X %X %X %X %X %X %X %X %X %X ", SCpnt->cmd_len, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9]));
+		DEB (if(*cdb) printk ("\ntimeout_per_command: %d, timeout_total: %d, timeout: %d, internal_timout: %d", SCpnt->timeout_per_command,
+							  SCpnt->timeout_total, SCpnt->timeout, SCpnt->internal_timeout));
+		outl (SCpnt->timeout_per_command, padapter->mb1);
+		outb_p (CMD_SCSI_TIMEOUT, padapter->cmd);
+		if ( WaitReady (padapter) )
+			{
+			rc = DID_ERROR;
+			goto finished;
+			}
+
+		outw_p (pun | (lun << 8), padapter->mb0);
+		outw_p (SCpnt->cmd_len << 8, padapter->mb0 + 2);
+		memcpy (pdev->cdb, cdb, MAX_COMMAND_SIZE);
+
+		outl (pdev->cdbDma, padapter->mb1);
+		if ( BuildSgList (SCpnt, padapter, pdev) )
+			cmd = CMD_SCSI_THRU;
+		else
+			cmd = CMD_SCSI_THRU_SG;
+		if ( (pdev->tag = Command (padapter, cmd)) == 0 )
+			rc = DID_TIME_OUT;
+		goto finished;
+		}
+	else
+		{
+		if ( lun )
+			{
+			rc = DID_BAD_TARGET;
+			goto finished;
+			}
+		}
+
+	switch ( *cdb )
+		{
+		case SCSIOP_INQUIRY:   					// inquiry CDB
+			if ( cdb[2] == SC_MY_RAID )
+				{
+				switch ( cdb[3] ) 
+					{
+					case MY_SCSI_REBUILD:
+						OpDone (SCpnt, PsiRaidCmd (padapter, CMD_RAID_REBUILD) << 16);
+						return 0;
+					case MY_SCSI_ALARMMUTE:
+						OpDone (SCpnt, PsiRaidCmd (padapter, CMD_RAID_MUTE) << 16);
+						return 0;
+					case MY_SCSI_DEMOFAIL:
+						OpDone (SCpnt, PsiRaidCmd (padapter, CMD_RAID_FAIL) << 16);
+						return 0;
+					default:
+						if ( SCpnt->use_sg )
+							{
+							rc = DID_ERROR;
+							goto finished;
+							}
+						else
+							{
+							SCpnt->SCp.have_data_in = pci_map_single (padapter->pdev, SCpnt->request_buffer, SCpnt->request_bufflen,
+													  scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+							outl (SCpnt->SCp.have_data_in, padapter->mb2);
+							}
+						outl (cdb[5], padapter->mb0);
+						outl (cdb[3], padapter->mb3);
+						cmd = CMD_DASD_RAID_RQ;
+						break;
+					}
+				break;
+				}
+			
+			if ( SCpnt->use_sg )
+				{
+				SCpnt->SCp.have_data_in = pci_map_single (padapter->pdev,
+									  ((struct scatterlist *)SCpnt->request_buffer)->address,
+									  SCpnt->request_bufflen,
+									  scsi_to_pci_dma_dir (SCpnt->sc_data_direction));
+				}
+			else
+				{
+				SCpnt->SCp.have_data_in = pci_map_single (padapter->pdev, SCpnt->request_buffer,
+									  SCpnt->request_bufflen,
+									  scsi_to_pci_dma_dir (SCpnt->sc_data_direction));
+				}
+			outl (SCpnt->SCp.have_data_in, padapter->mb2);
+			outl (SCpnt->request_bufflen, padapter->mb3);
+			cmd = CMD_DASD_SCSI_INQ;
+			break;
+
+		case SCSIOP_TEST_UNIT_READY:			// test unit ready CDB
+			SCpnt->SCp.have_data_in = pci_map_single (padapter->pdev, SCpnt->sense_buffer, sizeof (SCpnt->sense_buffer), PCI_DMA_FROMDEVICE);
+			outl (SCpnt->SCp.have_data_in, padapter->mb2);
+			outl (sizeof (SCpnt->sense_buffer), padapter->mb3);
+			cmd = CMD_TEST_READY;
+			break;
+
+		case SCSIOP_READ_CAPACITY:			  	// read capacity CDB
+			if ( SCpnt->use_sg )
+				{
+				SCpnt->SCp.have_data_in = pci_map_single (padapter->pdev, ((struct scatterlist *)(SCpnt->request_buffer))->address,
+										  8, PCI_DMA_FROMDEVICE);
+				}
+			else
+				SCpnt->SCp.have_data_in = pci_map_single (padapter->pdev, SCpnt->request_buffer, 8, PCI_DMA_FROMDEVICE);
+			outl (SCpnt->SCp.have_data_in, padapter->mb2);
+			outl (8, padapter->mb3);
+			cmd = CMD_DASD_CAP;
+			break;
+		case SCSIOP_VERIFY:						// verify CDB
+			outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+			outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+			cmd = CMD_READ_SG;
+			break;
+		case SCSIOP_READ:						// read10 CDB
+			outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+			outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+			if ( BuildSgList (SCpnt, padapter, pdev) )
+				cmd = CMD_READ;
+			else
+				cmd = CMD_READ_SG;
+			break;
+		case SCSIOP_READ6:						// read6  CDB
+			outw_p (cdb[4], padapter->mb0 + 2);
+			outl ((SCSI2LONG (&cdb[1])) & 0x001FFFFF, padapter->mb1);
+			if ( BuildSgList (SCpnt, padapter, pdev) )
+				cmd = CMD_READ;
+			else
+				cmd = CMD_READ_SG;
+			break;
+		case SCSIOP_WRITE:						// write10 CDB
+			outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+			outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+			if ( BuildSgList (SCpnt, padapter, pdev) )
+				cmd = CMD_WRITE;
+			else
+				cmd = CMD_WRITE_SG;
+			break;
+		case SCSIOP_WRITE6:						// write6  CDB
+			outw_p (cdb[4], padapter->mb0 + 2);
+			outl ((SCSI2LONG (&cdb[1])) & 0x001FFFFF, padapter->mb1);
+			if ( BuildSgList (SCpnt, padapter, pdev) )
+				cmd = CMD_WRITE;
+			else
+				cmd = CMD_WRITE_SG;
+			break;
+		case SCSIOP_START_STOP_UNIT:
+			cmd = CMD_EJECT_MEDIA;
+			break;
+		case SCSIOP_MEDIUM_REMOVAL:
+			switch ( cdb[4] )
+				{
+				case 0:
+					cmd = CMD_UNLOCK_DOOR;
+					break;
+				case 1:
+					cmd = CMD_LOCK_DOOR;
+					break;
+				default:
+					cmd = 0;
+					break;
+				}
+			if ( cmd )
+				break;
+		default:
+			DEB (printk ("pci2000_queuecommand: Unsupported command %02X\n", *cdb));
+			OpDone (SCpnt, DID_ERROR << 16);
+			return 0;
+		}
+
+	if ( (pdev->tag = Command (padapter, cmd)) == 0 )
+		rc = DID_TIME_OUT;
+finished:;
+	if ( rc != -1 )
+		OpDone (SCpnt, rc << 16);
+	return 0;
+	}
+/****************************************************************
+ *	Name:	Pci2000_Detect
+ *
+ *	Description:	Detect and initialize our boards.
+ *
+ *	Parameters:		tpnt - Pointer to SCSI host template structure.
+ *
+ *	Returns:		Number of adapters installed.
+ *
+ ****************************************************************/
+int Pci2000_Detect (Scsi_Host_Template *tpnt)
+	{
+	int					found = 0;
+	int					installed = 0;
+	struct Scsi_Host   *pshost;
+	PADAPTER2000	    padapter;
+	int					z, zz;
+	int					setirq;
+	struct pci_dev	   *pdev = NULL;
+	UCHAR			   *consistent;
+	dma_addr_t			consistentDma;
+
+	while ( (pdev = pci_find_device (VENDOR_PSI, DEVICE_ROY_1, pdev)) != NULL )
+		{
+		if (pci_enable_device(pdev))
+			continue;
+		pshost = scsi_register (tpnt, sizeof(ADAPTER2000));
+		if(pshost == NULL)
+			continue;
+		padapter = HOSTDATA(pshost);
+
+		padapter->basePort = pci_resource_start (pdev, 1);
+		DEB (printk ("\nBase Regs = %#04X", padapter->basePort));			// get the base I/O port address
+		padapter->mb0	= padapter->basePort + RTR_MAILBOX;		   			// get the 32 bit mail boxes
+		padapter->mb1	= padapter->basePort + RTR_MAILBOX + 4;
+		padapter->mb2	= padapter->basePort + RTR_MAILBOX + 8;
+		padapter->mb3	= padapter->basePort + RTR_MAILBOX + 12;
+		padapter->mb4	= padapter->basePort + RTR_MAILBOX + 16;
+		padapter->cmd	= padapter->basePort + RTR_LOCAL_DOORBELL;			// command register
+		padapter->tag	= padapter->basePort + RTR_PCI_DOORBELL;			// tag/response register
+		padapter->pdev = pdev;
+
+		if ( WaitReady (padapter) )
+			goto unregister;
+		outb_p (0x84, padapter->mb0);
+		outb_p (CMD_SPECIFY, padapter->cmd);
+		if ( WaitReady (padapter) )
+			goto unregister;
+
+		consistent = pci_alloc_consistent (pdev, consistentLen, &consistentDma);
+		if ( !consistent )
+			{
+			printk ("Unable to allocate DMA memory for PCI-2000 controller.\n");
+			goto unregister;
+			}
+		
+		scsi_set_device(pshost, &pdev->dev);
+		pshost->irq = pdev->irq;
+		setirq = 1;
+		padapter->irqOwned = 0;
+		for ( z = 0;  z < installed;  z++ )									// scan for shared interrupts
+			{
+			if ( PsiHost[z]->irq == pshost->irq )							// if shared then, don't posses
+				setirq = 0;
+			}
+		if ( setirq )												// if not shared, posses
+			{
+			if ( request_irq (pshost->irq, Irq_Handler, SA_SHIRQ, "pci2000", padapter) < 0 )
+				{
+				if ( request_irq (pshost->irq, Irq_Handler, SA_INTERRUPT | SA_SHIRQ, "pci2000", padapter) < 0 )
+					{
+					printk ("Unable to allocate IRQ for PCI-2000 controller.\n");
+					pci_free_consistent (pdev, consistentLen, consistent, consistentDma);
+					goto unregister;
+					}
+				}
+			padapter->irqOwned = pshost->irq;						// set IRQ as owned
+			}
+		PsiHost[installed]	= pshost;										// save SCSI_HOST pointer
+
+		pshost->io_port		= padapter->basePort;
+		pshost->n_io_port	= 0xFF;
+		pshost->unique_id	= padapter->basePort;
+		pshost->max_id		= 16;
+		pshost->max_channel	= 1;
+
+		for ( zz = 0;  zz < MAX_BUS;  zz++ )
+			for ( z = 0; z < MAX_UNITS;  z++ )
+				{
+				padapter->dev[zz][z].tag = 0;
+				padapter->dev[zz][z].scatGath = (PSCATGATH)consistent;
+				padapter->dev[zz][z].scatGathDma = consistentDma;
+				consistent += 16 * sizeof (SCATGATH);
+				consistentDma += 16 * sizeof (SCATGATH);
+				padapter->dev[zz][z].cdb = (UCHAR *)consistent;
+				padapter->dev[zz][z].cdbDma = consistentDma;
+				consistent += MAX_COMMAND_SIZE;
+				consistentDma += MAX_COMMAND_SIZE;
+				}
+			
+		printk("\nPSI-2000 Intelligent Storage SCSI CONTROLLER: at I/O = %lX  IRQ = %d\n", padapter->basePort, pshost->irq);
+		printk("Version %s, Compiled %s %s\n\n", PCI2000_VERSION,  __DATE__, __TIME__);
+		found++;
+		if ( ++installed < MAXADAPTER )
+			continue;
+		break;
+unregister:;
+		scsi_unregister (pshost);
+		found++;
+		}
+	NumAdapters = installed;
+	return installed;
+	}
+/****************************************************************
+ *	Name:	Pci2000_Abort
+ *
+ *	Description:	Process the Abort command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Allways snooze.
+ *
+ ****************************************************************/
+int Pci2000_Abort (Scsi_Cmnd *SCpnt)
+	{
+	DEB (printk ("pci2000_abort\n"));
+	return SCSI_ABORT_SNOOZE;
+	}
+/****************************************************************
+ *	Name:	Pci2000_Reset
+ *
+ *	Description:	Process the Reset command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					flags - Flags about the reset command
+ *
+ *	Returns:		No active command at this time, so this means
+ *					that each time we got some kind of response the
+ *					last time through.  Tell the mid-level code to
+ *					request sense information in order to decide what
+ *					to do next.
+ *
+ ****************************************************************/
+int Pci2000_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+	{
+	return SCSI_RESET_PUNT;
+	}
+/****************************************************************
+ *	Name:	Pci2000_Release
+ *
+ *	Description:	Release resources allocated for a single each adapter.
+ *
+ *	Parameters:		pshost - Pointer to SCSI command structure.
+ *
+ *	Returns:		zero.
+ *
+ ****************************************************************/
+int Pci2000_Release (struct Scsi_Host *pshost)
+	{
+    PADAPTER2000	padapter = HOSTDATA (pshost);
+
+	if ( padapter->irqOwned )
+		free_irq (pshost->irq, padapter);
+	pci_free_consistent (padapter->pdev, consistentLen, padapter->dev[0][0].scatGath, padapter->dev[0][0].scatGathDma);
+	release_region (pshost->io_port, pshost->n_io_port);
+    scsi_unregister(pshost);
+    return 0;
+	}
+
+/****************************************************************
+ *	Name:	Pci2000_BiosParam
+ *
+ *	Description:	Process the biosparam request from the SCSI manager to
+ *					return C/H/S data.
+ *
+ *	Parameters:		disk - Pointer to SCSI disk structure.
+ *					dev	 - Major/minor number from kernel.
+ *					geom - Pointer to integer array to place geometry data.
+ *
+ *	Returns:		zero.
+ *
+ ****************************************************************/
+int Pci2000_BiosParam (struct scsi_device *sdev, struct block_device *dev,
+		sector_t capacity, int geom[])
+	{
+	PADAPTER2000	    padapter;
+
+	padapter = HOSTDATA(sdev->host);
+
+	if ( WaitReady (padapter) )
+		return 0;
+	outb_p (sdev->id, padapter->mb0);
+	outb_p (CMD_GET_PARMS, padapter->cmd);
+	if ( WaitReady (padapter) )
+		return 0;
+
+	geom[0] = inb_p (padapter->mb2 + 3);
+	geom[1] = inb_p (padapter->mb2 + 2);
+	geom[2] = inw_p (padapter->mb2);
+	return 0;
+	}
+
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+static Scsi_Host_Template driver_template = {
+	.proc_name	= "pci2000",
+	.name		= "PCI-2000 SCSI Intelligent Disk Controller",
+	.detect		= Pci2000_Detect,
+	.release	= Pci2000_Release,
+	.queuecommand	= Pci2000_QueueCommand,
+	.abort		= Pci2000_Abort,
+	.reset		= Pci2000_Reset,
+	.bios_param	= Pci2000_BiosParam,
+	.can_queue	= 16,
+	.this_id	= -1,
+	.sg_tablesize	= 16,
+	.cmd_per_lun	= 1,
+	.use_clustering	= DISABLE_CLUSTERING,
+};
+#include "scsi_module.c"