[4640] in linux-scsi channel archive

home help back first fref pref prev next nref lref last post

Re: AMI MegaRAID rel 0.92 beta

daemon@ATHENA.MIT.EDU (Jeffrey Jones)
Wed Sep 2 19:47:02 1998

From: Jeffrey Jones <jeffreyj@ami.com>
To: "'linux-kernel@vger.rutgers.edu'" <linux-kernel@vger.rutgers.edu>,
        "'linux-raid@vger.rutgers.edu'" <linux-raid@vger.rutgers.edu>,
        "'linux-scsi@vger.rutgers.edu'" <linux-scsi@vger.rutgers.edu>
Date: 	Wed, 2 Sep 1998 18:55:54 -0400 

Well, unfortunately I was overruled in my descision to put the beta driver
on our ftp site.  We have
a policy of only putting fully released and supported drivers there.  I
apologize for the inconvenience,
We will be releasing a final version of this officially very soon, and then
I let Linus or Alan or whomever
look over the code and suggest changes, so that it can hopefully be added to
an upcoming kernel
release.

For now, here is the latest 2.0 patch, supporting multiple processors...

Jeff L Jones (jeffreyj@ami.com)
RAID SW Dev, American Megatrends Inc.
-

patch -p0 -s -d /usr/src << '_EOF_'
diff -urN linux/drivers/scsi/Config.in
linux-2.0.32-megaraid/drivers/scsi/Config.in
--- linux/drivers/scsi/Config.in	Mon Sep 15 12:41:28 1997
+++ linux-2.0.32-megaraid/drivers/scsi/Config.in	Fri Aug 28 17:28:07
1998
@@ -32,6 +32,7 @@
 dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
 dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI
 dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974
$CONFIG_SCSI
+dep_tristate 'AMI MegaRAID support' CONFIG_SCSI_MEGARAID $CONFIG_SCSI
 dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI
 if [ "$CONFIG_SCSI_BUSLOGIC" != "n" ]; then
     bool '  Omit FlashPoint support' CONFIG_SCSI_OMIT_FLASHPOINT
diff -urN linux/drivers/scsi/Makefile
linux-2.0.32-megaraid/drivers/scsi/Makefile
--- linux/drivers/scsi/Makefile	Thu Aug 14 13:31:20 1997
+++ linux-2.0.32-megaraid/drivers/scsi/Makefile	Fri Aug 28 17:28:07 1998
@@ -363,6 +363,13 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_MEGARAID),y)
+L_OBJS += megaraid.o
+else
+  ifeq ($(CONFIG_SCSI_MEGARAID),m)
+  M_OBJS += megaraid.o
+  endif
+endif
 
 ifeq ($(CONFIG_BLK_DEV_IDESCSI),y)
 L_OBJS += ide-scsi.o
@@ -400,6 +407,9 @@
 
 g_NCR5380.o: g_NCR5380.c
 	$(CC) $(CFLAGS)
-DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0,
BOARD_NCR53C400}};" -c g_NCR5380.c
+
+megaraid.o: megaraid.c
+	$(CC) $(CFLAGS) -c megaraid.c
 
 scsi_mod.o: $(MX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o \
 		scsicam.o scsi_proc.o
diff -urN linux/drivers/scsi/hosts.c
linux-2.0.32-megaraid/drivers/scsi/hosts.c
--- linux/drivers/scsi/hosts.c	Thu Aug 14 13:31:20 1997
+++ linux-2.0.32-megaraid/drivers/scsi/hosts.c	Fri Aug 28 17:28:07 1998
@@ -161,6 +161,10 @@
 #include "AM53C974.h"
 #endif
 
+#ifdef CONFIG_SCSI_MEGARAID
+#include "megaraid.h"
+#endif
+
 #ifdef CONFIG_SCSI_PPA
 #include "ppa.h"
 #endif
@@ -311,6 +315,9 @@
 #endif
 #ifdef CONFIG_SCSI_AM53C974
     AM53C974,
+#endif
+#ifdef CONFIG_SCSI_MEGARAID
+    MEGARAID,
 #endif
 #ifdef CONFIG_SCSI_PPA
     PPA,
diff -urN linux/drivers/scsi/megaraid.c
linux-2.0.32-megaraid/drivers/scsi/megaraid.c
--- linux/drivers/scsi/megaraid.c	Wed Dec 31 19:00:00 1969
+++ linux-2.0.32-megaraid/drivers/scsi/megaraid.c	Mon Aug 31 10:57:03
1998
@@ -0,0 +1,1324 @@
+/*===================================================================
+ *
+ *                    Linux MegaRAID device driver
+ * 
+ * Copyright 1998 American Megatrends Inc.
+ *
+ * Version : 0.92
+ * 
+ * Description: Linux device driver for AMI MegaRAID controller
+ *
+ * History:
+ *
+ * Version 0.90:
+ *     Works and has been tested with the MegaRAID 428 controller, and
+ *     the MegaRAID 438 controller.  Probably works with the 466 also,
+ *     but not tested.
+ *
+ * Version 0.91:
+ *     Aligned mailbox area on 16-byte boundry.
+ *     Added schedule() at the end to properly clean up.
+ *     Made improvements for conformity to linux driver standards.
+ *
+ * Version 0.92:
+ *     Added support for 2.1 kernels.
+ *         Reads from pci_dev struct, so it's not dependent on pcibios.
+ *         Added some missing virt_to_bus() translations.
+ *     Added support for SMP.
+ *         Changed global cli()'s to spinlocks for 2.1, and simulated
+ *          spinlocks for 2.0.
+ *     Removed setting of SA_INTERRUPT flag when requesting Irq.
+ *
+ * BUGS:
+ *     Tested with 2.1.90, but unfortunately there is a bug in pci.c which
+ *     fails to detect our controller.  Does work with 2.1.118--don't know
+ *     which kernel in between it was fixed in.
+ *     With SMP enabled under 2.1.118 with more than one processor, gets an
+ *     error message "scsi_end_request: buffer-list destroyed" under heavy
+ *     IO, but doesn't seem to affect operation, or data integrity.  The
+ *     message doesn't occur without SMP enabled, or with one proccessor
with
+ *     SMP enabled, or under any combination under 2.0 kernels.
+ *
+ *===================================================================*/
+#define QISR 1
+
+#define CRLFSTR "\n"
+
+#define MULTIQ 1
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+char kernel_version[] = UTS_RELEASE;
+
+/* originally ported by Dell Corporation; updated, released, and maintained
by
+   American Megatrends */
+MODULE_AUTHOR("American Megatrends Inc."); 
+MODULE_DESCRIPTION("AMI MegaRAID driver");    
+#endif
+#endif
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/blk.h>
+#include <linux/wait.h>
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/malloc.h>	/* for kmalloc() */
+#include <linux/config.h>	/* for CONFIG_PCI */
+#if LINUX_VERSION_CODE < 0x20100
+#include <linux/bios32.h>
+#else
+#include <asm/spinlock.h>
+#endif
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "sd.h"
+#include "scsi.h"
+#include "hosts.h"
+
+#include "megaraid.h"
+
+//================================================================
+//
+//                          #Defines
+//
+//================================================================
+
+#if LINUX_VERSION_CODE < 0x020100
+#define ioremap vremap
+#define iounmap vfree
+
+/* simulate spin locks */
+typedef struct {volatile char lock;} spinlock_t;
+#define spin_lock_init(x) { (x)->lock = 0;}
+#define spin_lock_irqsave(x,flags) { while ((x)->lock) barrier();\
+                                        (x)->lock=1; save_flags(flags);\
+                                        cli();}
+#define spin_unlock_irqrestore(x,flags) { (x)->lock=0;
restore_flags(flags);}
+
+#endif
+
+#if LINUX_VERSION_CODE >= 0x020100
+#define queue_task_irq(a,b)     queue_task(a,b)
+#define queue_task_irq_off(a,b) queue_task(a,b)
+#endif
+
+#define MAX_SERBUF 160
+#define COM_BASE 0x2f8
+
+#define ENQUEUE(obj,type,list,next) \
+{ type **node; long cpuflag; \
+  spin_lock_irqsave(&mega_lock,cpuflag);\
+  for(node=&(list); *node; node=(type **)&(*node)->##next); \
+  (*node) = obj; \
+  (*node)->##next = NULL; \
+  spin_unlock_irqrestore(&mega_lock,cpuflag);\
+};
+
+#define DEQUEUE(obj,type,list,next) \
+{ long cpuflag; \
+  spin_lock_irqsave(&mega_lock,cpuflag);\
+  if ((obj=list) != NULL) {\
+    list = (type *)(list)->##next; \
+  } \
+  spin_unlock_irqrestore(&mega_lock,cpuflag);\
+};
+
+u_long RDINDOOR(mega_host_config *megaCfg)
+{
+  return readl(megaCfg->base + 0x20);
+}
+
+void WRINDOOR(mega_host_config *megaCfg, u_long value)
+{
+  writel(value,megaCfg->base+0x20);
+}
+
+u_long RDOUTDOOR(mega_host_config *megaCfg)
+{
+  return readl(megaCfg->base+0x2C);
+}
+
+void WROUTDOOR(mega_host_config *megaCfg, u_long value)
+{
+  writel(value,megaCfg->base+0x2C);
+}
+
+//================================================================
+//
+//                    Function prototypes
+//
+//================================================================
+static int  MegaIssueCmd(mega_host_config *megaCfg,
+			 u_char *mboxData,
+			 mega_scb *scb,
+			 int intr);
+static int  build_sglist(mega_host_config *megaCfg, mega_scb *scb, 
+			 u_long *buffer, u_long *length);
+
+static void mega_runque(void *);
+static void mega_rundoneq(void);
+static void mega_cmd_done(mega_host_config *,mega_scb *, int);
+
+/* set SERDEBUG to 1 to enable serial debugging */
+#define SERDEBUG 0
+#if SERDEBUG
+static void ser_init(void);
+static void ser_puts(char *str);
+static void ser_putc(char c);
+static int  ser_printk(const char *fmt, ...);
+#endif
+
+//================================================================
+//
+//                    Global variables
+//
+//================================================================
+static int               numCtlrs = 0;
+static mega_host_config *megaCtlrs[4] = { 0 };
+
+/* Change this to 0 if you want to see the raw drives */
+static int use_raid   = 1;
+
+/* Queue of pending/completed SCBs */
+static mega_scb  *qPending   = NULL;
+static Scsi_Cmnd *qCompleted = NULL;
+
+volatile static spinlock_t mega_lock;
+static struct tq_struct runq = {0,0,mega_runque,NULL};
+
+struct proc_dir_entry proc_scsi_megaraid = {
+  PROC_SCSI_MEGARAID, 8, "megaraid",
+  S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+#if SERDEBUG
+static char strbuf[MAX_SERBUF+1];
+
+static void ser_init()
+{
+    unsigned port=COM_BASE;
+
+    outb(0x80,port+3);
+    outb(0,port+1);
+    /* 9600 Baud, if 19200: outb(6,port) */
+    outb(12, port);
+    outb(3,port+3);
+    outb(0,port+1);
+}
+
+static void ser_puts(char *str)
+{
+    char *ptr;
+
+    ser_init();
+    for (ptr=str;*ptr;++ptr)
+        ser_putc(*ptr);
+}
+
+static void ser_putc(char c)
+{
+    unsigned port=COM_BASE;
+
+    while ((inb(port+5) & 0x20)==0);
+    outb(c,port);
+    if (c==0x0a)
+    {
+        while ((inb(port+5) & 0x20)==0);
+        outb(0x0d,port);
+    }
+}
+
+static int ser_printk(const char *fmt, ...)
+{
+    va_list args;
+    int i;
+    long flags;
+
+    spin_lock_irqsave(mega_lock,flags);
+    va_start(args,fmt);
+    i = vsprintf(strbuf,fmt,args);
+    ser_puts(strbuf);
+    va_end(args);
+    spin_unlock_irqrestore(&mega_lock,flags);
+
+    return i;
+}
+
+#define TRACE(a)    { ser_printk a;}
+
+#else
+#define TRACE(A)
+#endif
+
+void callDone(Scsi_Cmnd *SCpnt)
+{
+  if (SCpnt->result) {
+    TRACE(("*** %.08lx %.02x <%d.%d.%d> = %x\n", SCpnt->serial_number, 
+	   SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, SCpnt->lun, 
+	   SCpnt->result));
+  }
+  SCpnt->scsi_done(SCpnt);
+}
+
+/*-------------------------------------------------------------------------
+ *
+ *                      Local functions
+ *
+
*-------------------------------------------------------------------------*/
+
+//================================================
+// Initialize SCB structures
+//================================================
+static void initSCB(mega_host_config *megaCfg)
+{
+  int idx;
+
+  for(idx=0; idx<megaCfg->max_cmds; idx++) {
+    megaCfg->scbList[idx].idx    = -1;
+    megaCfg->scbList[idx].flag   = 0;
+    megaCfg->scbList[idx].sgList = NULL;
+    megaCfg->scbList[idx].SCpnt  = NULL;
+  }
+}
+
+//===========================
+// Allocate a SCB structure
+//===========================
+static mega_scb *allocateSCB(mega_host_config *megaCfg,Scsi_Cmnd *SCpnt)
+{
+  int        idx;
+  long       flags;
+
+  spin_lock_irqsave(&mega_lock,flags);
+  for(idx=0; idx<megaCfg->max_cmds; idx++) {
+    if (megaCfg->scbList[idx].idx < 0) {
+
+      /* Set Index and SCB pointer */ 
+      megaCfg->scbList[idx].flag  = 0;
+      megaCfg->scbList[idx].idx   = idx;
+      megaCfg->scbList[idx].SCpnt = SCpnt;
+      megaCfg->scbList[idx].next  = NULL;
+      spin_unlock_irqrestore(&mega_lock,flags);
+
+      if (megaCfg->scbList[idx].sgList == NULL) {
+	megaCfg->scbList[idx].sgList =
+
kmalloc(sizeof(mega_sglist)*MAX_SGLIST,GFP_ATOMIC|GFP_DMA);
+      }
+
+      return &megaCfg->scbList[idx];
+    }
+  }
+  spin_unlock_irqrestore(&mega_lock,flags);
+
+  printk("Megaraid: Could not allocate free SCB!!!\n");
+  
+  return NULL;
+}
+
+//=======================
+// Free a SCB structure
+//=======================
+static void freeSCB(mega_scb *scb)
+{
+  long flags;
+
+  spin_lock_irqsave(&mega_lock,flags);
+  scb->flag  = 0;
+  scb->idx   = -1;
+  scb->next  = NULL;
+  scb->SCpnt = NULL;
+  spin_unlock_irqrestore(&mega_lock,flags);
+}
+
+/* Run through the list of completed requests */
+static void mega_rundoneq()
+{
+  mega_host_config *megaCfg;
+  Scsi_Cmnd        *SCpnt;
+  long              islogical;
+
+  while(1) {
+    DEQUEUE(SCpnt, Scsi_Cmnd, qCompleted, host_scribble);
+    if (SCpnt == NULL) return;
+
+    megaCfg = (mega_host_config *)SCpnt->host->hostdata;
+
+    /* Check if we're allowing access to RAID drives or physical
+     *  if use_raid == 1 and this wasn't a disk on the max channel or
+     *  if use_raid == 0 and this was a disk on the max channel
+     *  then fail.
+     */
+    islogical = (SCpnt->channel == megaCfg->host->max_channel) ? 1 : 0;
+    if (SCpnt->cmnd[0] == INQUIRY &&
+	((((u_char*)SCpnt->request_buffer)[0] & 0x1F) == TYPE_DISK) &&
+	(islogical != use_raid)) {
+       SCpnt->result = 0xF0;
+    }
+
+    /* Convert result to error */
+    switch(SCpnt->result) {
+    case 0x00: case 0x02:
+      SCpnt->result |= (DID_OK << 16);
+      break;
+    case 0x8:
+      SCpnt->result |= (DID_BUS_BUSY << 16);
+      break;
+    default:
+      SCpnt->result |= (DID_BAD_TARGET << 16);
+      break;
+    }
+
+    /* Callback */
+    callDone(SCpnt);
+  }
+}
+
+/* Add command to the list of completed requests */
+static void mega_cmd_done(mega_host_config *megaCfg,mega_scb *pScb, int
status)
+{
+  pScb->SCpnt->result = status;
+  ENQUEUE(pScb->SCpnt, Scsi_Cmnd, qCompleted, host_scribble);
+  freeSCB(pScb);
+}
+
+/*----------------------------------------------------
+ * Process pending queue list
+ *
+ * Run as a scheduled task 
+ *----------------------------------------------------*/
+static void mega_runque(void *dummy)
+{
+  mega_host_config *megaCfg;
+  mega_scb         *pScb;
+  long              flags;
+
+  /* Take care of any completed requests */
+  mega_rundoneq();
+
+  DEQUEUE(pScb,mega_scb,qPending,next);
+
+  if (pScb) {
+    megaCfg = (mega_host_config *)pScb->SCpnt->host->hostdata;
+
+    if (megaCfg->mbox->busy || megaCfg->flag & (IN_ISR|PENDING)) {
+      printk("PENDING = %x, IN_ISR = %x, mbox.busy =
%x\n",(u_int)(megaCfg->flag
+            & PENDING), (u_int)(megaCfg->flag & IN_ISR),
megaCfg->mbox->busy);
+      TRACE(("%.08lx %.02x <%d.%d.%d> intr%d busy%d isr%d pending%d\n",
+	     pScb->SCpnt->serial_number,
+	     pScb->SCpnt->cmnd[0],
+	     pScb->SCpnt->channel,
+	     pScb->SCpnt->target,
+	     pScb->SCpnt->lun,
+	     intr_count,
+	     megaCfg->mbox->busy,
+	     (megaCfg->flag & IN_ISR)  ? 1 : 0,
+	     (megaCfg->flag & PENDING) ? 1 : 0));
+    }
+
+    if (MegaIssueCmd(megaCfg, pScb->mboxData, pScb, 1)) {
+      printk("MegaIssueCmd returned BUSY.  Rescheduling command.\n");
+      /* We're BUSY... come back later */
+      spin_lock_irqsave(&mega_lock,flags);
+      pScb->next = qPending;
+      qPending   = pScb;
+      spin_unlock_irqrestore(&mega_lock,flags);
+
+      if (!(megaCfg->flag & PENDING)) { /* If PENDING, irq will schedule
task */
+          queue_task(&runq, &tq_scheduler);
+      }
+    }
+  }
+}
+
+/*-------------------------------------------------------------------
+ *
+ *                 Build a SCB from a Scsi_Cmnd
+ *
+ * Returns a SCB pointer, or NULL
+ * If NULL is returned, the scsi_done function MUST have been called
+ *
+ *-------------------------------------------------------------------*/
+static mega_scb *mega_build_cmd(mega_host_config *megaCfg, Scsi_Cmnd
*SCpnt)
+{
+  mega_scb      *pScb;
+  mega_mailbox  *mbox;
+  mega_passthru *pthru;
+  long           seg;
+
+  /* We don't support multi-luns */
+  if (SCpnt->lun != 0) {
+    SCpnt->result = (DID_BAD_TARGET << 16);
+    callDone(SCpnt);
+    return NULL;
+  }
+
+  /*-----------------------------------------------------
+   *
+   *               Logical drive commands
+   *
+   *-----------------------------------------------------*/
+  if (SCpnt->channel == megaCfg->host->max_channel) {
+    switch(SCpnt->cmnd[0]) {
+    case TEST_UNIT_READY:
+      memset(SCpnt->request_buffer, 0, SCpnt->request_bufflen);
+      SCpnt->result = (DID_OK << 16);
+      callDone(SCpnt);
+      return NULL;
+
+    case MODE_SENSE:
+      memset(SCpnt->request_buffer, 0, SCpnt->cmnd[4]);
+      SCpnt->result = (DID_OK << 16);
+      callDone(SCpnt);
+      return NULL;
+
+    case READ_CAPACITY:
+    case INQUIRY:
+      /* Allocate a SCB and initialize passthru */
+      if ((pScb = allocateSCB(megaCfg,SCpnt)) == NULL) {
+	SCpnt->result = (DID_ERROR << 16);
+	callDone(SCpnt);
+	return NULL;
+      }
+      pthru = &pScb->pthru;
+      mbox  = (mega_mailbox *)&pScb->mboxData;
+
+      memset(mbox,  0, sizeof(pScb->mboxData));
+      memset(pthru, 0, sizeof(mega_passthru));
+      pthru->timeout      = 0;
+      pthru->ars          = 0;
+      pthru->islogical    = 1;
+      pthru->logdrv       = SCpnt->target;
+      pthru->cdblen       = SCpnt->cmd_len;
+      pthru->dataxferaddr = virt_to_bus(SCpnt->request_buffer);
+      pthru->dataxferlen  = SCpnt->request_bufflen;
+      memcpy(pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len);
+
+      /* Initialize mailbox area */
+      mbox->cmd      = MEGA_MBOXCMD_PASSTHRU;
+      mbox->xferaddr = virt_to_bus(pthru);
+
+      return pScb;
+
+    case READ_6:
+    case WRITE_6:
+    case READ_10:
+    case WRITE_10:
+      /* Allocate a SCB and initialize mailbox */
+      if ((pScb = allocateSCB(megaCfg,SCpnt)) == NULL) {
+	SCpnt->result = (DID_ERROR << 16);
+	callDone(SCpnt);
+	return NULL;
+      }
+      mbox = (mega_mailbox *)&pScb->mboxData;
+
+      memset(mbox, 0, sizeof(pScb->mboxData));
+      mbox->logdrv = SCpnt->target;
+      mbox->cmd    = (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == READ_10) ?
+	MEGA_MBOXCMD_LREAD : MEGA_MBOXCMD_LWRITE;
+      
+      /* 6-byte */
+      if (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == WRITE_6) {
+	mbox->numsectors = 
+	  (u_long)SCpnt->cmnd[4];
+	mbox->lba = 
+	  ((u_long)SCpnt->cmnd[1] << 16) |
+	  ((u_long)SCpnt->cmnd[2] << 8) |
+	  (u_long)SCpnt->cmnd[3];
+	mbox->lba &= 0x1FFFFF;
+      }
+      
+      /* 10-byte */
+      if (*SCpnt->cmnd == READ_10 || *SCpnt->cmnd == WRITE_10) {
+	mbox->numsectors = 
+	  (u_long)SCpnt->cmnd[8] |
+	  ((u_long)SCpnt->cmnd[7] << 8);
+	mbox->lba =
+	  ((u_long)SCpnt->cmnd[2] << 24) |
+	  ((u_long)SCpnt->cmnd[3] << 16) |
+	  ((u_long)SCpnt->cmnd[4] << 8) |
+	  (u_long)SCpnt->cmnd[5];
+      }
+      
+      /* Calculate Scatter-Gather info */
+      mbox->numsgelements = build_sglist(megaCfg, pScb, 
+					 (u_long*)&mbox->xferaddr,
+					 (u_long*)&seg);
+
+      return pScb;
+      
+    default:
+      SCpnt->result = (DID_BAD_TARGET << 16);
+      callDone(SCpnt);
+      return NULL;
+    }
+  }
+  /*-----------------------------------------------------
+   *
+   *               Passthru drive commands
+   *
+   *-----------------------------------------------------*/
+  else {
+    /* Allocate a SCB and initialize passthru */
+    if ((pScb = allocateSCB(megaCfg,SCpnt)) == NULL) {
+      SCpnt->result = (DID_ERROR << 16);
+      callDone(SCpnt);
+      return NULL;
+    }
+    pthru = &pScb->pthru;
+    mbox  = (mega_mailbox *)pScb->mboxData;
+    
+    memset(mbox,  0, sizeof(pScb->mboxData));
+    memset(pthru, 0, sizeof(mega_passthru));
+    pthru->timeout   = 0;
+    pthru->ars       = 0;
+    pthru->islogical = 0;
+    pthru->channel   = SCpnt->channel;
+    pthru->target    = SCpnt->target;
+    pthru->cdblen    = SCpnt->cmd_len;
+    memcpy(pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len);
+   
+    pthru->numsgelements = build_sglist(megaCfg, pScb,
+					(u_long *)&pthru->dataxferaddr,
+					(u_long *)&pthru->dataxferlen);
+    
+    /* Initialize mailbox */
+    mbox->cmd      = MEGA_MBOXCMD_PASSTHRU;
+    mbox->xferaddr = virt_to_bus(pthru);
+
+    return pScb;
+  }
+  return NULL;
+}
+
+/*--------------------------------------------------------------------
+ * Interrupt service routine
+ *--------------------------------------------------------------------*/
+static void megaraid_isr(int irq, void *devp, struct pt_regs *regs)
+{
+  mega_host_config *megaCfg;
+  u_char            byte, idx, sIdx;
+  u_long            dword;
+  mega_mailbox     *mbox;
+  mega_scb         *pScb;
+  long              flags;
+  int               qCnt, qStatus;
+
+  megaCfg = (mega_host_config *)devp;
+  mbox    = (mega_mailbox *)megaCfg->mbox;
+
+  if (megaCfg->host->irq == irq) {
+    spin_lock_irqsave(&mega_lock,flags);
+
+    if (megaCfg->flag & IN_ISR) {
+      TRACE(("ISR called reentrantly!!\n"));
+    }
+
+    megaCfg->flag |= IN_ISR;
+
+    /* Check if a valid interrupt is pending */
+    if (megaCfg->flag & BOARD_QUARTZ) {
+        dword = RDOUTDOOR(megaCfg);
+        if (dword != 0x10001234) {
+            /* Spurious interrupt */
+            megaCfg->flag &= ~IN_ISR;
+            spin_unlock_irqrestore(&mega_lock,flags);
+            return;
+        }
+        WROUTDOOR(megaCfg,dword);
+    } else {
+        byte = READ_PORT(megaCfg->host->io_port, INTR_PORT);
+        if ((byte & VALID_INTR_BYTE) == 0) {
+          /* Spurious interrupt */
+          megaCfg->flag &= ~IN_ISR;
+          spin_unlock_irqrestore(&mega_lock,flags);
+          return;
+        }
+        WRITE_PORT(megaCfg->host->io_port, INTR_PORT, byte);
+    }
+    
+    qCnt    = mbox->numstatus;
+    qStatus = mbox->status;
+
+    if (qCnt > 1) {TRACE(("ISR: Received %d status\n", qCnt))
+        printk("Got numstatus = %d\n",qCnt);
+    }
+    
+    for(idx=0; idx<qCnt; idx++) {
+      sIdx = mbox->completed[idx];
+      if (sIdx > 0) {
+	pScb = &megaCfg->scbList[sIdx-1];
+        spin_unlock_irqrestore(&mega_lock,flags); /* locks within cmd_done
*/
+	mega_cmd_done(megaCfg,&megaCfg->scbList[sIdx-1], qStatus);
+        spin_lock_irqsave(&mega_lock,flags);
+      }
+    }
+    if (megaCfg->flag & BOARD_QUARTZ) {
+        WRINDOOR(megaCfg,virt_to_bus(megaCfg->mbox)|0x2);
+        while (RDINDOOR(megaCfg) & 0x02);
+    } else {
+        CLEAR_INTR(megaCfg->host->io_port);
+    }
+
+    megaCfg->flag &= ~IN_ISR;
+    megaCfg->flag &= ~PENDING;
+
+    /* Queue as a delayed ISR routine */
+    queue_task_irq_off(&runq, &tq_immediate);
+    mark_bh(IMMEDIATE_BH);
+    spin_unlock_irqrestore(&mega_lock,flags);
+
+  }
+}
+
+/*==================================================*/
+/* Wait until the controller's mailbox is available */
+/*==================================================*/
+static int busyWaitMbox(mega_host_config *megaCfg)
+{
+  mega_mailbox *mbox = (mega_mailbox *)megaCfg->mbox;
+  long          counter;
+
+  for(counter=0; counter<0xFFFFFF; counter++) {
+    if (!mbox->busy) return 0;
+  }
+  return -1;
+}
+
+//=====================================================
+// Post a command to the card
+//
+// Arguments:
+//   mega_host_config *megaCfg - Controller structure
+//   u_char *mboxData - Mailbox area, 16 bytes
+//   mega_scb *pScb   - SCB posting (or NULL if N/A)
+//   int intr         - if 1, interrupt, 0 is blocking
+//=====================================================
+static int MegaIssueCmd(mega_host_config *megaCfg,
+			u_char *mboxData,
+			mega_scb *pScb,
+			int intr)
+{
+  mega_mailbox *mbox = (mega_mailbox *)megaCfg->mbox;
+  long          flags;
+  u_char        byte;
+  u_long        cmdDone;
+
+  mboxData[0x1] = (pScb ? pScb->idx+1 : 0x00);  /* Set cmdid */
+  mboxData[0xF] = 1;                            /* Set busy */
+
+  /* one bad report of problem when issuing a command while pending.
+   * Wasn't able to duplicate, but it doesn't really affect performance
+   * anyway, so don't allow command while PENDING
+   */
+  if (megaCfg->flag & PENDING) {
+    return -1;
+  }
+
+  /* Wait until mailbox is free */
+  if (busyWaitMbox(megaCfg)) {
+    if (pScb) {
+      TRACE(("Mailbox busy %.08lx <%d.%d.%d>\n",
pScb->SCpnt->serial_number,
+	     pScb->SCpnt->channel, pScb->SCpnt->target, pScb->SCpnt->lun));
+    }
+    return -1;
+  }
+
+  /* Copy mailbox data into host structure */
+  spin_lock_irqsave(&mega_lock,flags);
+  memset(mbox, 0, sizeof(mega_mailbox));
+  memcpy(mbox, mboxData, 16);
+  spin_unlock_irqrestore(&mega_lock,flags);
+
+  /* Kick IO */
+  megaCfg->flag |= PENDING;
+  if (intr) {
+    /* Issue interrupt (non-blocking) command */
+    if (megaCfg->flag & BOARD_QUARTZ) {
+        mbox->mraid_poll = 0; 
+        mbox->mraid_ack = 0; 
+        WRINDOOR(megaCfg, virt_to_bus(megaCfg->mbox) | 0x1);
+    } else {
+        ENABLE_INTR(megaCfg->host->io_port);
+        ISSUE_COMMAND(megaCfg->host->io_port);
+    }
+  }
+  else {      /* Issue non-ISR (blocking) command */
+
+    if (megaCfg->flag & BOARD_QUARTZ) {
+
+      mbox->mraid_poll = 0; 
+      mbox->mraid_ack = 0; 
+      WRINDOOR(megaCfg, virt_to_bus(megaCfg->mbox) | 0x1);
+
+      while((cmdDone=RDOUTDOOR(megaCfg)) != 0x10001234);
+      WROUTDOOR(megaCfg, cmdDone);
+
+      if (pScb) {
+	mega_cmd_done(megaCfg,pScb, mbox->status);
+	mega_rundoneq();
+      }
+
+      WRINDOOR(megaCfg,virt_to_bus(megaCfg->mbox) | 0x2);
+      while(RDINDOOR(megaCfg) & 0x2);
+
+      megaCfg->flag &= ~PENDING;
+    }
+    else {
+      DISABLE_INTR(megaCfg->host->io_port);
+      ISSUE_COMMAND(megaCfg->host->io_port);
+      
+
while(!((byte=READ_PORT(megaCfg->host->io_port,INTR_PORT))&INTR_VALID));
+      WRITE_PORT(megaCfg->host->io_port, INTR_PORT, byte);
+      
+      ENABLE_INTR(megaCfg->host->io_port);
+      CLEAR_INTR(megaCfg->host->io_port);
+      
+      if (pScb) {
+	mega_cmd_done(megaCfg,pScb, mbox->status);
+	mega_rundoneq();
+      }
+      megaCfg->flag &= ~PENDING;
+    }
+  }
+
+  return 0;
+}
+
+/*-------------------------------------------------------------------
+ * Copies data to SGLIST
+ *-------------------------------------------------------------------*/
+static int build_sglist(mega_host_config *megaCfg, mega_scb *scb, 
+			u_long *buffer, u_long *length)
+{
+  struct scatterlist *sgList;
+  int idx;
+
+  /* Scatter-gather not used */
+  if (scb->SCpnt->use_sg == 0) {
+    *buffer = virt_to_bus(scb->SCpnt->request_buffer);
+    *length = (u_long)scb->SCpnt->request_bufflen;
+    return 0;
+  }
+
+  sgList = (struct scatterlist *)scb->SCpnt->buffer;
+  if (scb->SCpnt->use_sg == 1) {
+    *buffer = virt_to_bus(sgList[0].address);
+    *length = (u_long)sgList[0].length;
+    return 0;
+  }
+
+  /* Copy Scatter-Gather list info into controller structure */
+  for(idx=0; idx<scb->SCpnt->use_sg; idx++) {
+    scb->sgList[idx].address = virt_to_bus(sgList[idx].address);
+    scb->sgList[idx].length  = (u_long)sgList[idx].length;
+  }
+  
+  /* Reset pointer and length fields */
+  *buffer = virt_to_bus(scb->sgList);
+  *length = 0;
+
+  /* Return count of SG requests */
+  return scb->SCpnt->use_sg;
+}
+    
+/*--------------------------------------------------------------------
+ * Initializes the adress of the controller's mailbox register
+ *  The mailbox register is used to issue commands to the card.
+ *  Format of the mailbox area:
+ *   00 01 command
+ *   01 01 command id
+ *   02 02 # of sectors
+ *   04 04 logical bus address
+ *   08 04 physical buffer address
+ *   0C 01 logical drive #
+ *   0D 01 length of scatter/gather list
+ *   0E 01 reserved
+ *   0F 01 mailbox busy
+ *   10 01 numstatus byte
+ *   11 01 status byte
+ *--------------------------------------------------------------------*/
+static int mega_register_mailbox(mega_host_config *megaCfg, u_long paddr)
+{
+  /* align on 16-byte boundry */
+  megaCfg->mbox = &megaCfg->mailbox;
+  megaCfg->mbox = (mega_mailbox *) ((((ulong)megaCfg->mbox) +
16)&0xfffffff0);
+  paddr = (paddr+16)&0xfffffff0;
+
+  /* Register mailbox area with the firmware */
+  if (megaCfg->flag & BOARD_QUARTZ) {
+  }
+  else {
+    WRITE_PORT(megaCfg->host->io_port, MBOX_PORT0, paddr         & 0xFF);
+    WRITE_PORT(megaCfg->host->io_port, MBOX_PORT1, (paddr >>  8) & 0xFF);
+    WRITE_PORT(megaCfg->host->io_port, MBOX_PORT2, (paddr >> 16) & 0xFF);
+    WRITE_PORT(megaCfg->host->io_port, MBOX_PORT3, (paddr >> 24) & 0xFF);
+    WRITE_PORT(megaCfg->host->io_port, ENABLE_MBOX_REGION,
ENABLE_MBOX_BYTE);
+    
+    CLEAR_INTR(megaCfg->host->io_port);
+    ENABLE_INTR(megaCfg->host->io_port);
+  }
+  return 0;
+}
+
+/*-------------------------------------------------------------------
+ * Issue an adapter info query to the controller
+ *-------------------------------------------------------------------*/
+static int mega_i_query_adapter(mega_host_config *megaCfg)
+{
+  mega_RAIDINQ *adapterInfo;
+  mega_mailbox *mbox;
+  u_char        mboxData[16];
+  u_long        paddr;
+
+  spin_lock_init(&mega_lock);
+  /* Initialize adapter inquiry */
+  paddr = virt_to_bus(megaCfg->mega_buffer);
+  mbox  = (mega_mailbox *)mboxData;
+
+  memset((void *)megaCfg->mega_buffer, 0, sizeof(megaCfg->mega_buffer));
+  memset(mbox, 0, 16);
+
+  /* Initialize mailbox registers */
+  mbox->cmd      = MEGA_MBOXCMD_ADAPTERINQ;
+  mbox->xferaddr = paddr;
+
+  /* Issue a blocking command to the card */
+  MegaIssueCmd(megaCfg, mboxData, NULL, 0);
+  
+  /* Initialize host/local structures with Adapter info */
+  adapterInfo = (mega_RAIDINQ *)megaCfg->mega_buffer;
+  megaCfg->host->max_channel = adapterInfo->AdpInfo.ChanPresent;
+  megaCfg->host->max_id      = adapterInfo->AdpInfo.MaxTargPerChan;
+  megaCfg->numldrv           = adapterInfo->LogdrvInfo.NumLDrv;
+
+#if 0
+  printk("---- Logical drive info ----\n");
+  for(i=0; i<megaCfg->numldrv; i++) {
+    printk("%d: size: %ld prop: %x state: %x\n",i,
+	   adapterInfo->LogdrvInfo.LDrvSize[i],
+	   adapterInfo->LogdrvInfo.LDrvProp[i],
+	   adapterInfo->LogdrvInfo.LDrvState[i]);
+  }
+  printk("---- Physical drive info ----\n");
+  for(i=0; i<MAX_PHYSICAL_DRIVES; i++) {
+    if (i && !(i % 8)) printk("\n");
+    printk("%d: %x   ", i, adapterInfo->PhysdrvInfo.PDrvState[i]);
+  }
+  printk("\n");
+#endif
+
+  megaCfg->max_cmds = adapterInfo->AdpInfo.MaxConcCmds;
+
+#ifdef HP            /* use HP firmware and bios version encoding */
+      sprintf(megaCfg->fwVer,"%c%d%d.%d%d",
+          adapterInfo->AdpInfo.FwVer[2],
+          adapterInfo->AdpInfo.FwVer[1] >> 8,
+          adapterInfo->AdpInfo.FwVer[1] & 0x0f,
+          adapterInfo->AdpInfo.FwVer[2] >> 8,
+          adapterInfo->AdpInfo.FwVer[2] & 0x0f);
+      sprintf(megaCfg->biosVer,"%c%d%d.%d%d",
+          adapterInfo->AdpInfo.BiosVer[2],
+          adapterInfo->AdpInfo.BiosVer[1] >> 8,
+          adapterInfo->AdpInfo.BiosVer[1] & 0x0f,
+          adapterInfo->AdpInfo.BiosVer[2] >> 8,
+          adapterInfo->AdpInfo.BiosVer[2] & 0x0f);
+#else
+      memcpy(megaCfg->fwVer, adapterInfo->AdpInfo.FwVer, 4);
+      megaCfg->fwVer[4] = 0;
+
+      memcpy(megaCfg->biosVer, adapterInfo->AdpInfo.BiosVer, 4);
+      megaCfg->biosVer[4] = 0;
+#endif
+
+  printk("megaraid: [%s:%s] detected %d logical drives" CRLFSTR,
+	 megaCfg->fwVer,
+	 megaCfg->biosVer,
+	 megaCfg->numldrv);
+  return 0;
+}
+
+/*-------------------------------------------------------------------------
+ *
+ *                      Driver interface functions
+ *
+
*-------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------
+ * Returns data to be displayed in /proc/scsi/megaraid/X
+ *----------------------------------------------------------*/
+int megaraid_proc_info(char *buffer, char **start, off_t offset,
+		       int length, int inode, int inout)
+{
+  *start = buffer;
+  return 0;
+}
+
+int findCard(Scsi_Host_Template *pHostTmpl, 
+	     u_short pciVendor, u_short pciDev,
+	     long flag)
+{
+  mega_host_config *megaCfg;
+  struct Scsi_Host *host;
+  u_char            pciBus, pciDevFun, megaIrq;
+  u_long            megaBase;
+  u_short           pciIdx = 0;
+
+#if LINUX_VERSION_CODE < 0x20100
+  while(!pcibios_find_device(pciVendor, pciDev, pciIdx,&pciBus,&pciDevFun))
{
+#else
+  struct pci_dev   *pdev=pci_devices;
+
+  while((pdev = pci_find_device(pciVendor, pciDev, pdev))) {
+    pciBus = pdev->bus->number;
+    pciDevFun = pdev->devfn;
+#endif
+    printk("megaraid: found 0x%4.04x:0x%4.04x:idx %d:bus %d:slot %d:fun
%d\n",
+	   pciVendor, 
+	   pciDev, 
+	   pciIdx, pciBus, 
+	   PCI_SLOT(pciDevFun), 
+	   PCI_FUNC(pciDevFun));
+    
+    /* Read the base port and IRQ from PCI */
+#if LINUX_VERSION_CODE < 0x20100
+    pcibios_read_config_dword(pciBus, pciDevFun,
+			     PCI_BASE_ADDRESS_0,
+			     (u_int *)&megaBase);
+    pcibios_read_config_byte(pciBus, pciDevFun,

+			     PCI_INTERRUPT_LINE,
+			     &megaIrq);
+#else
+    megaBase = pdev->base_address[0];
+    megaIrq = pdev->irq;
+#endif
+    pciIdx++;
+
+    if (flag & BOARD_QUARTZ) {
+      megaBase &= PCI_BASE_ADDRESS_MEM_MASK;
+      megaBase = (long) ioremap(megaBase,128);
+    }
+    else {
+      megaBase &= PCI_BASE_ADDRESS_IO_MASK;
+      megaBase += 0x10;
+    }
+
+    /* Initialize SCSI Host structure */
+    host    = scsi_register(pHostTmpl, sizeof(mega_host_config));
+    megaCfg = (mega_host_config *)host->hostdata;
+    memset(megaCfg, 0, sizeof(mega_host_config));
+
+    printk(" scsi%d: Found a MegaRAID controller at 0x%x, IRQ: %d" CRLFSTR,

+	   host->host_no, (u_int)megaBase, megaIrq);
+    
+    /* Copy resource info into structure */
+    megaCfg->flag            = flag;
+    megaCfg->host            = host;
+    megaCfg->base            = megaBase;
+    megaCfg->host->irq       = megaIrq;
+    megaCfg->host->io_port   = megaBase;
+    megaCfg->host->n_io_port = 16;
+    megaCfg->host->unique_id = (pciBus << 8) | pciDevFun;
+    megaCtlrs[numCtlrs++]    = megaCfg;
+
+    if (flag != BOARD_QUARTZ) {
+      /* Request our IO Range */
+      if (check_region(megaBase, 16)) {
+	printk(KERN_WARNING "megaraid: Couldn't register I/O range!"
CRLFSTR);
+	scsi_unregister(host);
+	continue;
+      }
+      request_region(megaBase, 16, "megaraid");
+    }
+
+    /* Request our IRQ */
+    if (request_irq(megaIrq, megaraid_isr, SA_SHIRQ, 
+		    "megaraid", megaCfg)) {
+      printk(KERN_WARNING "megaraid: Couldn't register IRQ %d!" CRLFSTR,
+	     megaIrq);
+      scsi_unregister(host);
+      continue;
+    }
+
+    mega_register_mailbox(megaCfg, virt_to_bus((void*)&megaCfg->mailbox));
+    mega_i_query_adapter(megaCfg);
+
+    /* Initialize SCBs */
+    initSCB(megaCfg);
+
+  }
+  return pciIdx;
+}
+
+/*---------------------------------------------------------
+ * Detects if a megaraid controller exists in this system
+ *---------------------------------------------------------*/
+int megaraid_detect(Scsi_Host_Template *pHostTmpl)
+{
+  int count = 0;
+
+  pHostTmpl->proc_dir = &proc_scsi_megaraid;
+
+#if LINUX_VERSION_CODE < 0x20100
+  if (!pcibios_present()) 
+    {
+      printk("megaraid: PCI bios not present." CRLFSTR);
+      return 0;
+    }
+#endif
+
+  count += findCard(pHostTmpl, 0x101E, 0x9010, 0);
+  count += findCard(pHostTmpl, 0x101E, 0x9060, 0);
+  count += findCard(pHostTmpl, 0x8086, 0x1960, BOARD_QUARTZ);
+
+  return count;
+}
+
+/*---------------------------------------------------------------------
+ * Release the controller's resources
+ *---------------------------------------------------------------------*/
+int megaraid_release(struct Scsi_Host *pSHost)
+{
+  mega_host_config *megaCfg;
+  mega_mailbox         *mbox;
+  u_char                mboxData[16];
+
+  megaCfg = (mega_host_config*)pSHost->hostdata;
+  mbox    = (mega_mailbox *)mboxData;
+
+  /* Flush cache to disk */
+  memset(mbox, 0, 16);
+  mboxData[0] = 0xA;
+
+  /* Issue a blocking (interrupts disabled) command to the card */
+  MegaIssueCmd(megaCfg, mboxData, NULL, 0);
+
+  schedule();
+
+  /* Free our resources */
+  if (megaCfg->flag & BOARD_QUARTZ) {
+      iounmap((void *)megaCfg->base);
+  } else {
+      release_region(megaCfg->host->io_port, 16);
+  }
+  free_irq(megaCfg->host->irq, megaCfg); /* Must be freed first, otherwise
+                                            extra interrupt is generated */
+  scsi_unregister(pSHost);
+
+  return 0;
+}
+
+/*----------------------------------------------
+ * Get information about the card/driver 
+ *----------------------------------------------*/
+const char *megaraid_info(struct Scsi_Host *pSHost)
+{
+  static char           buffer[512];
+  mega_host_config  *megaCfg;
+  mega_RAIDINQ          *adapterInfo;
+
+  megaCfg     = (mega_host_config *)pSHost->hostdata;
+  adapterInfo = (mega_RAIDINQ *)megaCfg->mega_buffer;
+
+  sprintf(buffer, "AMI MegaRAID %s %d commands %d targs %d chans",
+	  megaCfg->fwVer,
+	  adapterInfo->AdpInfo.MaxConcCmds,
+	  megaCfg->host->max_id,
+	  megaCfg->host->max_channel);
+  return buffer;
+}
+
+/*-----------------------------------------------------------------
+ * Perform a SCSI command
+ * Mailbox area:
+ *   00 01 command
+ *   01 01 command id
+ *   02 02 # of sectors
+ *   04 04 logical bus address
+ *   08 04 physical buffer address
+ *   0C 01 logical drive #
+ *   0D 01 length of scatter/gather list
+ *   0E 01 reserved
+ *   0F 01 mailbox busy
+ *   10 01 numstatus byte
+ *   11 01 status byte 
+ *-----------------------------------------------------------------*/
+int megaraid_queue(Scsi_Cmnd *SCpnt, void (*pktComp)(Scsi_Cmnd *))
+{
+  mega_host_config *megaCfg;
+  mega_scb         *pScb;
+
+  megaCfg = (mega_host_config *)SCpnt->host->hostdata;
+
+  if (!(megaCfg->flag & (1L << SCpnt->channel))) {
+    printk("scsi%d: scanning channel %c for devices.\n",
+	   megaCfg->host->host_no,
+	   SCpnt->channel + 'A');
+    megaCfg->flag |= (1L << SCpnt->channel);
+  }
+
+  SCpnt->scsi_done = pktComp;
+
+  /* Allocate and build a SCB request */
+  if ((pScb = mega_build_cmd(megaCfg, SCpnt)) != NULL) {
+    /* Add SCB to the head of the pending queue */
+    ENQUEUE(pScb, mega_scb, qPending, next);
+
+    /* Issue the command to the card */
+      mega_runque(NULL);
+  }
+
+  return 0;
+}
+
+/*----------------------------------------------------------------------
+ * Issue a blocking command to the controller
+ *----------------------------------------------------------------------*/
+volatile static int internal_done_flag    = 0;
+volatile static int internal_done_errcode = 0;
+
+static void internal_done(Scsi_Cmnd *SCpnt)
+{
+  internal_done_errcode = SCpnt->result;
+  internal_done_flag++;
+}
+
+int megaraid_command(Scsi_Cmnd *SCpnt)
+{
+  internal_done_flag = 0;
+
+  /* Queue command, and wait until it has completed */
+  megaraid_queue(SCpnt, internal_done);
+
+  while(!internal_done_flag)
+    barrier();
+
+  return internal_done_errcode;
+}
+
+/*---------------------------------------------------------------------
+ * Abort a previous SCSI request
+ *---------------------------------------------------------------------*/
+int megaraid_abort(Scsi_Cmnd *SCpnt)
+{
+  mega_host_config *megaCfg;
+  int       idx;
+  long      flags;
+
+  spin_lock_irqsave(&mega_lock,flags);
+
+  megaCfg = (mega_host_config *)SCpnt->host->hostdata;
+
+  TRACE(("ABORT!!! %.08lx %.02x <%d.%d.%d>\n",
+	 SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel,
SCpnt->target, 
+	 SCpnt->lun));
+  /*
+   * Walk list of SCBs for any that are still outstanding
+   */
+  for(idx=0; idx<megaCfg->max_cmds; idx++) {
+    if (megaCfg->scbList[idx].idx >= 0) {
+      if (megaCfg->scbList[idx].SCpnt == SCpnt) {
+	freeSCB(&megaCfg->scbList[idx]);
+
+	SCpnt->result = (DID_RESET << 16) | (SUGGEST_RETRY<<24);
+	callDone(SCpnt);
+      }
+    }
+  }
+  spin_unlock_irqrestore(&mega_lock,flags);
+  return SCSI_ABORT_SNOOZE;
+}
+
+/*---------------------------------------------------------------------
+ * Reset a previous SCSI request
+ *---------------------------------------------------------------------*/
+int megaraid_reset(Scsi_Cmnd *SCpnt, unsigned int rstflags)
+{
+  mega_host_config *megaCfg;
+  int       idx;
+  long      flags;
+
+  spin_lock_irqsave(&mega_lock,flags);
+
+  megaCfg = (mega_host_config *)SCpnt->host->hostdata;
+
+  TRACE(("RESET: %.08lx %.02x <%d.%d.%d>\n",
+	 SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel,
SCpnt->target, 
+	 SCpnt->lun));
+
+  /*
+   * Walk list of SCBs for any that are still outstanding
+   */
+  for(idx=0; idx<megaCfg->max_cmds; idx++) {
+    if (megaCfg->scbList[idx].idx >= 0) {
+      SCpnt = megaCfg->scbList[idx].SCpnt;
+      freeSCB(&megaCfg->scbList[idx]);
+      SCpnt->result = (DID_RESET << 16) | (SUGGEST_RETRY<<24);
+      callDone(SCpnt);
+    }
+  }
+  spin_unlock_irqrestore(&mega_lock,flags);
+  return SCSI_RESET_PUNT;
+} 
+
+/*-------------------------------------------------------------
+ * Return the disk geometry for a particular disk
+ * Input:
+ *   Disk *disk - Disk geometry
+ *   kdev_t dev - Device node
+ *   int *geom  - Returns geometry fields
+ *     geom[0] = heads
+ *     geom[1] = sectors
+ *     geom[2] = cylinders
+ *-------------------------------------------------------------*/
+int megaraid_biosparam(Disk *disk, kdev_t dev, int *geom)
+{
+  int                   heads, sectors, cylinders;
+  mega_host_config *megaCfg;
+
+  /* Get pointer to host config structure */
+  megaCfg = (mega_host_config *)disk->device->host->hostdata;
+
+  /* Default heads (64) & sectors (32) */
+  heads     = 64;
+  sectors   = 32;
+  cylinders = disk->capacity / (heads * sectors);
+
+  /* Handle extended translation size for logical drives > 1Gb */
+  if (disk->capacity >= 0x200000) {
+    heads     = 255;
+    sectors   = 63;
+    cylinders = disk->capacity / (heads * sectors);
+  }
+
+  /* return result */
+  geom[0] = heads;
+  geom[1] = sectors;
+  geom[2] = cylinders;
+
+  return 0;
+}
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = MEGARAID;
+
+#include "scsi_module.c"
+#endif
diff -urN linux/drivers/scsi/megaraid.h
linux-2.0.32-megaraid/drivers/scsi/megaraid.h
--- linux/drivers/scsi/megaraid.h	Wed Dec 31 19:00:00 1969
+++ linux-2.0.32-megaraid/drivers/scsi/megaraid.h	Mon Aug 31 10:58:16
1998
@@ -0,0 +1,282 @@
+#ifndef __MEGARAID_H__
+#define __MEGARAID_H__
+
+#define IN_ISR                  0x80000000L
+#define NO_INTR                 0x40000000L
+#define IN_TIMEOUT              0x20000000L
+#define PENDING                 0x10000000L
+#define BOARD_QUARTZ            0x08000000L
+
+#define SCB_ACTIVE 0x1
+#define SCB_WAITQ  0x2
+#define SCB_ISSUED 0x4
+
+#define SCB_FREE                -1
+#define SCB_RESET               -2
+#define SCB_ABORT               -3
+#define SCB_LOCKED              -4
+
+#define MEGA_CMD_TIMEOUT        10
+
+#define MAX_SGLIST              20
+#define MAX_COMMANDS            254
+
+#define MAX_LOGICAL_DRIVES      8
+#define MAX_CHANNEL             5
+#define MAX_TARGET              15
+#define MAX_PHYSICAL_DRIVES     MAX_CHANNEL*MAX_TARGET
+
+#define INQUIRY_DATA_SIZE       0x24
+#define MAX_CDB_LEN             0x0A
+#define MAX_REQ_SENSE_LEN       0x20
+
+#define INTR_VALID              0x40
+
+/* Mailbox commands */
+#define MEGA_MBOXCMD_LREAD      0x01
+#define MEGA_MBOXCMD_LWRITE     0x02
+#define MEGA_MBOXCMD_PASSTHRU   0x03
+#define MEGA_MBOXCMD_ADAPTERINQ 0x05
+
+/* Offsets into Mailbox */
+#define COMMAND_PORT       0x00
+#define COMMAND_ID_PORT    0x01
+#define SG_LIST_PORT0      0x08
+#define SG_LIST_PORT1      0x09
+#define SG_LIST_PORT2      0x0a
+#define SG_LIST_PORT3      0x0b
+#define SG_ELEMENT_PORT    0x0d
+#define NO_FIRED_PORT      0x0f
+
+/* I/O Port offsets */
+#define I_CMD_PORT         0x00
+#define I_ACK_PORT         0x00
+#define I_TOGGLE_PORT      0x01
+#define INTR_PORT          0x0a
+
+#define MAILBOX_SIZE       70
+#define MBOX_BUSY_PORT     0x00
+#define MBOX_PORT0         0x04
+#define MBOX_PORT1         0x05
+#define MBOX_PORT2         0x06
+#define MBOX_PORT3         0x07
+#define ENABLE_MBOX_REGION 0x0B
+
+/* I/O Port Values */
+#define ISSUE_BYTE         0x10
+#define ACK_BYTE           0x08
+#define ENABLE_INTR_BYTE   0xc0
+#define DISABLE_INTR_BYTE  0x00
+#define VALID_INTR_BYTE    0x40
+#define MBOX_BUSY_BYTE     0x10
+#define ENABLE_MBOX_BYTE   0x00
+
+/* Setup some port macros here */
+#define WRITE_MAILBOX(base,offset,value)   *(base+offset)=value
+#define READ_MAILBOX(base,offset)          *(base+offset)
+
+#define WRITE_PORT(base,offset,value)      outb_p(value,base+offset)
+#define READ_PORT(base,offset)             inb_p(base+offset)
+
+#define ISSUE_COMMAND(base)   WRITE_PORT(base,I_CMD_PORT,ISSUE_BYTE)
+#define CLEAR_INTR(base)      WRITE_PORT(base,I_ACK_PORT,ACK_BYTE)
+#define ENABLE_INTR(base)
WRITE_PORT(base,I_TOGGLE_PORT,ENABLE_INTR_BYTE)
+#define DISABLE_INTR(base)
WRITE_PORT(base,I_TOGGLE_PORT,DISABLE_INTR_BYTE)
+
+/* Define AMI's PCI codes */
+#undef PCI_VENDOR_ID_AMI
+#undef PCI_DEVICE_ID_AMI_MEGARAID
+
+#ifndef PCI_VENDOR_ID_AMI
+#define PCI_VENDOR_ID_AMI          0x101E
+#define PCI_DEVICE_ID_AMI_MEGARAID 0x9010
+#endif
+
+#define PCI_CONF_BASE_ADDR_OFFSET  0x10
+#define PCI_CONF_IRQ_OFFSET        0x3c
+
+#if LINUX_VERSION_CODE < 0x20100
+#define MEGARAID \
+  { NULL,                               /* Next                      */\
+    NULL,                               /* Usage Count Pointer       */\
+    NULL,                               /* /proc Directory Entry     */\
+    megaraid_proc_info,                 /* /proc Info Function       */\
+    "MegaRAID",                         /* Driver Name               */\
+    megaraid_detect,                    /* Detect Host Adapter       */\
+    megaraid_release,                   /* Release Host Adapter      */\
+    megaraid_info,                      /* Driver Info Function      */\
+    megaraid_command,                   /* Command Function          */\
+    megaraid_queue,                     /* Queue Command Function    */\
+    megaraid_abort,                     /* Abort Command Function    */\
+    megaraid_reset,                     /* Reset Command Function    */\
+    NULL,                               /* Slave Attach Function     */\
+    megaraid_biosparam,                 /* Disk BIOS Parameters      */\
+    1,                                  /* # of cmds that can be\
+                                           outstanding at any time */\
+    7,                                  /* HBA Target ID             */\
+    MAX_SGLIST,                         /* Scatter/Gather Table Size */\
+    1,                                  /* SCSI Commands per LUN     */\
+    0,                                  /* Present                   */\
+    0,                                  /* Default Unchecked ISA DMA */\
+    ENABLE_CLUSTERING }                 /* Enable Clustering         */
+#else
+#define MEGARAID \
+  {\
+    name:            "MegaRAID",               /* Driver Name
*/\
+    proc_info:        megaraid_proc_info,      /* /proc driver info
*/\
+    detect:           megaraid_detect,         /* Detect Host Adapter
*/\
+    release:          megaraid_release,        /* Release Host Adapter
*/\
+    info:             megaraid_info,           /* Driver Info Function
*/\
+    command:          megaraid_command,        /* Command Function
*/\
+    queuecommand:     megaraid_queue,          /* Queue Command Function
*/\
+    abort:            megaraid_abort,          /* Abort Command Function
*/\
+    reset:            megaraid_reset,          /* Reset Command Function
*/\
+    bios_param:       megaraid_biosparam,      /* Disk BIOS Parameters
*/\
+    can_queue:        255,                     /* Can Queue
*/\
+    this_id:          7,                       /* HBA Target ID
*/\
+    sg_tablesize:     MAX_SGLIST,              /* Scatter/Gather Table Size
*/\
+    cmd_per_lun:      1,                       /* SCSI Commands per LUN
*/\
+    present:          0,                       /* Present
*/\
+    unchecked_isa_dma:0,                       /* Default Unchecked ISA DMA
*/\
+    use_clustering:   ENABLE_CLUSTERING       /* Enable Clustering
*/\
+  }
+#endif
+
+/* Structures */
+typedef struct _mega_ADP_INFO
+{
+  u_char    MaxConcCmds;
+  u_char    RbldRate;
+  u_char    MaxTargPerChan;
+  u_char    ChanPresent;
+  u_char    FwVer[4];
+  u_short   AgeOfFlash;
+  u_char    ChipSet;
+  u_char    DRAMSize;
+  u_char    CacheFlushInterval;
+  u_char    BiosVer[4];
+  u_char    resvd[7];
+} mega_ADP_INFO;
+
+typedef struct _mega_LDRV_INFO
+{
+  u_char   NumLDrv;
+  u_char   resvd[3];
+  u_long   LDrvSize[MAX_LOGICAL_DRIVES];
+  u_char   LDrvProp[MAX_LOGICAL_DRIVES];
+  u_char   LDrvState[MAX_LOGICAL_DRIVES];
+} mega_LDRV_INFO;
+
+typedef struct _mega_PDRV_INFO
+{
+  u_char   PDrvState[MAX_PHYSICAL_DRIVES];
+  u_char   resvd;
+} mega_PDRV_INFO;
+
+// RAID inquiry: Mailbox command 0x5
+typedef struct _mega_RAIDINQ
+{
+  mega_ADP_INFO    AdpInfo;
+  mega_LDRV_INFO   LogdrvInfo;
+  mega_PDRV_INFO   PhysdrvInfo;
+} mega_RAIDINQ;
+
+// Passthrough command: Mailbox command 0x3
+typedef struct mega_passthru
+{
+  u_char            timeout:3;              /*
0=6sec/1=60sec/2=10min/3=3hrs */
+  u_char            ars:1;
+  u_char            reserved:3;
+  u_char            islogical:1;
+  u_char            logdrv;                 /* if islogical == 1 */
+  u_char            channel;                /* if islogical == 0 */
+  u_char            target;                 /* if islogical == 0 */
+  u_char            queuetag;               /* unused */
+  u_char            queueaction;            /* unused */
+  u_char            cdb[MAX_CDB_LEN];
+  u_char            cdblen;
+  u_char            reqsenselen;
+  u_char            reqsensearea[MAX_REQ_SENSE_LEN];
+  u_char            numsgelements;
+  u_char            scsistatus;
+  u_long            dataxferaddr;
+  u_long            dataxferlen;
+} mega_passthru;
+
+typedef struct _mega_mailbox
+{
+  /* 0x0 */ u_char    cmd;
+  /* 0x1 */ u_char    cmdid;
+  /* 0x2 */ u_short   numsectors;
+  /* 0x4 */ u_long    lba;
+  /* 0x8 */ u_long    xferaddr;
+  /* 0xC */ u_char    logdrv;
+  /* 0xD */ u_char    numsgelements;
+  /* 0xE */ u_char    resvd;
+  /* 0xF */ u_char    busy;
+  /* 0x10*/ u_char    numstatus;
+  /* 0x11*/ u_char    status;
+  /* 0x12*/ u_char    completed[46];
+            u_char    mraid_poll;
+            u_char    mraid_ack;
+            u_char    pad[16];
+} mega_mailbox;
+
+typedef struct _mega_sglist
+{
+  u_long     address;
+  u_long     length;
+} mega_sglist;
+
+/* Queued command data */
+typedef struct _mega_scb mega_scb;
+
+struct _mega_scb
+{
+  int             idx;
+  u_long          flag;
+  Scsi_Cmnd      *SCpnt;
+  u_char          mboxData[16];
+  mega_passthru   pthru;
+  mega_sglist    *sgList;
+  mega_scb       *next;
+};
+
+/* Per-controller data */
+typedef struct _mega_host_config
+{
+  u_char               numldrv;
+  u_long               flag;
+  u_long               base;
+
+  struct tq_struct     megaTq;
+
+  /* Host adapter parameters */
+  u_char               fwVer[7];
+  u_char               biosVer[7];
+
+  struct Scsi_Host     *host;
+
+  /* The following must be DMA-able!! */
+  volatile mega_mailbox *mbox;
+  volatile mega_mailbox mailbox;
+  volatile u_char       mega_buffer[2*1024L];
+
+  u_char                max_cmds;
+  mega_scb              scbList[MAX_COMMANDS];
+} mega_host_config;
+
+extern struct proc_dir_entry proc_scsi_megaraid;
+
+const char *megaraid_info( struct Scsi_Host * );
+int        megaraid_detect( Scsi_Host_Template * );
+int        megaraid_release(struct Scsi_Host *);
+int        megaraid_command( Scsi_Cmnd * );
+int        megaraid_abort( Scsi_Cmnd * );
+int        megaraid_reset( Scsi_Cmnd *, unsigned int); 
+int        megaraid_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) );
+int        megaraid_biosparam( Disk *, kdev_t, int * );
+int        megaraid_proc_info( char *buffer, char **start, off_t offset,
+			       int length, int hostno, int inout );
+
+#endif
diff -urN linux/include/linux/proc_fs.h
linux-2.0.32-megaraid/include/linux/proc_fs.h
--- linux/include/linux/proc_fs.h	Tue Nov 18 14:46:46 1997
+++ linux-2.0.32-megaraid/include/linux/proc_fs.h	Fri Aug 28 17:28:07
1998
@@ -137,6 +137,7 @@
 	PROC_SCSI_AM53C974,
 	PROC_SCSI_SSC,
 	PROC_SCSI_NCR53C406A,
+	PROC_SCSI_MEGARAID,
 	PROC_SCSI_PPA,
 	PROC_SCSI_ESP,
 	PROC_SCSI_A3000,

-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.rutgers.edu

home help back first fref pref prev next nref lref last post