patch-2.2.18 linux/drivers/scsi/ips.c

Next file: linux/drivers/scsi/ips.h
Previous file: linux/drivers/scsi/inia100.h
Back to the patch index
Back to the overall index

diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/drivers/scsi/ips.c linux/drivers/scsi/ips.c
@@ -77,18 +77,41 @@
 /*            2.3.18 and later                                               */
 /*          - Sync with other changes from the 2.3 kernels                   */
 /* 4.00.06  - Fix timeout with initial FFDC command                          */
+/* 4.10.00  - Add support for ServeRAID 4M/4L                                */
+/* 4.10.13  - Fix for dynamic unload and proc file system                    */
+/* 4.20.03  - Rename version to coincide with new release schedules          */
+/*            Performance fixes                                              */
+/*            Fix truncation of /proc files with cat                         */
+/*            Merge in changes through kernel 2.4.0test1ac21                 */
+/* 4.20.13  - Fix some failure cases / reset code                            */
+/*          - Hook into the reboot_notifier to flush the controller cache    */
 /*                                                                           */
 /*****************************************************************************/
 
 /*
  * Conditional Compilation directives for this driver:
  *
- * NO_IPS_RESET         - Don't reset the controller (no matter what)
- * IPS_DEBUG            - More verbose error messages
- * IPS_PCI_PROBE_DEBUG  - Print out more detail on the PCI probe
+ * IPS_DEBUG            - Turn on debugging info
  *
+ *
+ * Parameters:
+ *
+ * debug:<number>       - Set debug level to <number>
+ *                        NOTE: only works when IPS_DEBUG compile directive
+ *                              is used.
+ *
+ *       1              - Normal debug messages
+ *       2              - Verbose debug messages
+ *       11             - Method trace (non interrupt)
+ *       12             - Method trace (includes interrupt)
+ *
+ * noreset              - Don't reset the controller
+ * nocmdline            - Turn off passthru support
+ * noi2o                - Don't use I2O Queues (ServeRAID 4 only)
+ * nommap               - Don't use memory mapped I/O
  */
 
+
 #if defined (MODULE)
    #include <linux/module.h>
 #endif /* MODULE */
@@ -102,10 +125,12 @@
 #include <linux/kernel.h>
 #include <linux/ioport.h>
 #include <linux/malloc.h>
+#include <linux/vmalloc.h>
 #include <linux/delay.h>
 #include <linux/sched.h>
 #include <linux/pci.h>
 #include <linux/proc_fs.h>
+#include <linux/reboot.h>
 
 #include <linux/blk.h>
 #include <linux/types.h>
@@ -128,25 +153,29 @@
 #include <asm/spinlock.h>
 #endif
 
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+#include <linux/init.h>
+#endif
+
 #include <linux/smp.h>
 
+#ifdef MODULE
+   static char *ips = NULL;
+   MODULE_PARM(ips, "s");
+#endif
+
 /*
  * DRIVER_VER
  */
-#define IPS_VERSION_HIGH        "4.00"  /* MUST be 4 chars */
-#define IPS_VERSION_LOW         ".06 "  /* MUST be 4 chars */
+#define IPS_VERSION_HIGH        "4.20"
+#define IPS_VERSION_LOW         ".20 "
 
 #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
 struct proc_dir_entry proc_scsi_ips = {
-#if !defined(PROC_SCSI_IPS)
-   0,                                /* Use dynamic inode allocation */
-#else
-   PROC_SCSI_IPS,
-#endif
+   0,
    3, "ips",
    S_IFDIR | S_IRUGO | S_IXUGO, 2
-}
-;
+};
 #endif
 
 #if !defined(__i386__)
@@ -161,12 +190,14 @@
    #error "To use the command-line interface you need to define SG_BIG_BUFF"
 #endif
 
-#if IPS_DEBUG >= 12
-   #define DBG(s)       printk(KERN_NOTICE s "\n"); MDELAY(2*IPS_ONE_SEC)
-#elif IPS_DEBUG >= 11
-   #define DBG(s)       printk(KERN_NOTICE s "\n")
+#ifdef IPS_DEBUG
+   #define METHOD_TRACE(s, i)    if (ips_debug >= (i+10)) printk(KERN_NOTICE s "\n");
+   #define DEBUG(i, s)           if (ips_debug >= i) printk(KERN_NOTICE s "\n");
+   #define DEBUG_VAR(i, s, v...) if (ips_debug >= i) printk(KERN_NOTICE s "\n", v);
 #else
-   #define DBG(s)
+   #define METHOD_TRACE(s, i)
+   #define DEBUG(i, s)
+   #define DEBUG_VAR(i, s, v...)
 #endif
 
 /*
@@ -175,11 +206,26 @@
 static const char * ips_name = "ips";
 static struct Scsi_Host * ips_sh[IPS_MAX_ADAPTERS];  /* Array of host controller structures */
 static ips_ha_t * ips_ha[IPS_MAX_ADAPTERS];          /* Array of HA structures */
+static unsigned int ips_next_controller = 0;
 static unsigned int ips_num_controllers = 0;
+static unsigned int ips_released_controllers = 0;
 static int          ips_cmd_timeout = 60;
 static int          ips_reset_timeout = 60 * 5;
+static int          ips_force_memio = 1;             /* Always use Memory Mapped I/O    */
+static int          ips_force_i2o = 1;               /* Always use I2O command delivery */
+static int          ips_resetcontroller = 1;         /* Reset the controller            */
+static int          ips_cmdline = 1;                 /* Support for passthru            */
+
+#ifdef IPS_DEBUG
+static int          ips_debug = 0;                   /* Debug mode                      */
+#endif
+
+/*
+ * Necessary forward function protoypes
+ */
+static int ips_halt(struct notifier_block *nb, ulong event, void *buf);
 
-#define MAX_ADAPTER_NAME 7
+#define MAX_ADAPTER_NAME 9
 
 static char ips_adapter_name[][30] = {
    "ServeRAID",
@@ -188,7 +234,13 @@
    "ServeRAID on motherboard",
    "ServeRAID 3H",
    "ServeRAID 3L",
-   "ServeRAID 4H"
+   "ServeRAID 4H",
+   "ServeRAID 4M",
+   "ServeRAID 4L"
+};
+
+static struct notifier_block ips_notifier = {
+   ips_halt, NULL, 0
 };
 
 /*
@@ -253,8 +305,6 @@
  */
 int ips_detect(Scsi_Host_Template *);
 int ips_release(struct Scsi_Host *);
-int ips_abort(Scsi_Cmnd *);
-int ips_reset(Scsi_Cmnd *, unsigned int);
 int ips_eh_abort(Scsi_Cmnd *);
 int ips_eh_reset(Scsi_Cmnd *);
 int ips_queue(Scsi_Cmnd *, void (*) (Scsi_Cmnd *));
@@ -262,21 +312,26 @@
 const char * ips_info(struct Scsi_Host *);
 void do_ipsintr(int, void *, struct pt_regs *);
 static int ips_hainit(ips_ha_t *);
-static int ips_map_status(ips_scb_t *, ips_stat_t *);
+static int ips_map_status(ips_ha_t *, ips_scb_t *, ips_stat_t *);
 static int ips_send(ips_ha_t *, ips_scb_t *, ips_scb_callback);
 static int ips_send_wait(ips_ha_t *, ips_scb_t *, int, int);
 static int ips_send_cmd(ips_ha_t *, ips_scb_t *);
-static int ips_chkstatus(ips_ha_t *);
 static int ips_online(ips_ha_t *, ips_scb_t *);
 static int ips_inquiry(ips_ha_t *, ips_scb_t *);
 static int ips_rdcap(ips_ha_t *, ips_scb_t *);
 static int ips_msense(ips_ha_t *, ips_scb_t *);
 static int ips_reqsen(ips_ha_t *, ips_scb_t *);
 static int ips_allocatescbs(ips_ha_t *);
-static int ips_reset_adapter(ips_ha_t *);
-static int ips_statupd(ips_ha_t *);
-static int ips_issue(ips_ha_t *, ips_scb_t *);
-static int ips_isintr(ips_ha_t *);
+static int ips_reset_copperhead(ips_ha_t *);
+static int ips_reset_copperhead_memio(ips_ha_t *);
+static int ips_reset_morpheus(ips_ha_t *);
+static int ips_issue_copperhead(ips_ha_t *, ips_scb_t *);
+static int ips_issue_copperhead_memio(ips_ha_t *, ips_scb_t *);
+static int ips_issue_i2o(ips_ha_t *, ips_scb_t *);
+static int ips_issue_i2o_memio(ips_ha_t *, ips_scb_t *);
+static int ips_isintr_copperhead(ips_ha_t *);
+static int ips_isintr_copperhead_memio(ips_ha_t *);
+static int ips_isintr_morpheus(ips_ha_t *);
 static int ips_wait(ips_ha_t *, int, int);
 static int ips_write_driver_status(ips_ha_t *, int);
 static int ips_read_adapter_status(ips_ha_t *, int);
@@ -284,7 +339,22 @@
 static int ips_read_config(ips_ha_t *, int);
 static int ips_clear_adapter(ips_ha_t *, int);
 static int ips_readwrite_page5(ips_ha_t *, int, int);
-static void ips_intr(ips_ha_t *);
+static int ips_init_copperhead(ips_ha_t *);
+static int ips_init_copperhead_memio(ips_ha_t *);
+static int ips_init_morpheus(ips_ha_t *);
+static int ips_isinit_copperhead(ips_ha_t *);
+static int ips_isinit_copperhead_memio(ips_ha_t *);
+static int ips_isinit_morpheus(ips_ha_t *);
+static u32 ips_statupd_copperhead(ips_ha_t *);
+static u32 ips_statupd_copperhead_memio(ips_ha_t *);
+static u32 ips_statupd_morpheus(ips_ha_t *);
+static void ips_select_queue_depth(struct Scsi_Host *, Scsi_Device *);
+static void ips_chkstatus(ips_ha_t *, IPS_STATUS *);
+static void ips_enable_int_copperhead(ips_ha_t *);
+static void ips_enable_int_copperhead_memio(ips_ha_t *);
+static void ips_enable_int_morpheus(ips_ha_t *);
+static void ips_intr_copperhead(ips_ha_t *);
+static void ips_intr_morpheus(ips_ha_t *);
 static void ips_next(ips_ha_t *, int);
 static void ipsintr_blocking(ips_ha_t *, struct ips_scb *);
 static void ipsintr_done(ips_ha_t *, struct ips_scb *);
@@ -293,6 +363,7 @@
 static void ips_init_scb(ips_ha_t *, ips_scb_t *);
 static void ips_freescb(ips_ha_t *, ips_scb_t *);
 static void ips_statinit(ips_ha_t *);
+static void ips_statinit_memio(ips_ha_t *);
 static void ips_fix_ffdc_time(ips_ha_t *, ips_scb_t *, time_t);
 static void ips_ffdc_reset(ips_ha_t *, int);
 static void ips_ffdc_time(ips_ha_t *, int);
@@ -312,6 +383,9 @@
 static int ips_erase_bios(ips_ha_t *);
 static int ips_program_bios(ips_ha_t *, char *, int);
 static int ips_verify_bios(ips_ha_t *, char *, int);
+static int ips_erase_bios_memio(ips_ha_t *);
+static int ips_program_bios_memio(ips_ha_t *, char *, int);
+static int ips_verify_bios_memio(ips_ha_t *, char *, int);
 
 #ifndef NO_IPS_CMDLINE
 static int ips_is_passthru(Scsi_Cmnd *);
@@ -332,6 +406,76 @@
 
 /****************************************************************************/
 /*                                                                          */
+/* Routine Name: ips_setup                                                  */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   setup parameters to the driver                                         */
+/*                                                                          */
+/****************************************************************************/
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+static int
+ips_setup(char *ips_str) {
+#else
+void
+ips_setup(char *ips_str, int *dummy) {
+#endif
+   int        i;
+   char      *p;
+   char      *key;
+   char      *value;
+   char       tokens[3] = {',', '.', 0};
+   IPS_OPTION options[] = {
+      {"noreset", &ips_resetcontroller, 0},
+#ifdef IPS_DEBUG
+      {"debug", &ips_debug, 1},
+#endif
+      {"noi2o", &ips_force_i2o, 0},
+      {"nommap", &ips_force_memio, 0},
+      {"nocmdline", &ips_cmdline, 0},
+   };
+
+   METHOD_TRACE("ips_setup", 1);
+
+   for (key = strtok(ips_str, tokens); key; key = strtok(NULL, tokens)) {
+      p = key;
+
+      /* Search for value */
+      while ((p) && (*p != ':'))
+         p++;
+
+      if (p) {
+         *p = '\0';
+         value = p+1;
+      } else
+         value = NULL;
+
+      /*
+       * We now have key/value pairs.
+       * Update the variables
+       */
+      for (i = 0; i < (sizeof(options) / sizeof(options[0])); i++) {
+         if (strnicmp(key, options[i].option_name, strlen(ips_str)) == 0) {
+            if (value)
+               *options[i].option_flag = simple_strtoul(value, NULL, 0);
+            else
+               *options[i].option_flag = options[i].option_value;
+
+            break;
+         }
+      }
+   }
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+   return (1);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+__setup("ips=", ips_setup);
+#endif
+
+/****************************************************************************/
+/*                                                                          */
 /* Routine Name: ips_detect                                                 */
 /*                                                                          */
 /* Routine Description:                                                     */
@@ -346,15 +490,38 @@
    struct Scsi_Host *sh;
    ips_ha_t         *ha;
    u32               io_addr;
+   u32               mem_addr;
+   u32               io_len;
+   u32               mem_len;
    u16               planer;
    u8                revision_id;
    u8                bus;
    u8                func;
    u8                irq;
-   int               index;
-   struct pci_dev   *dev = NULL;
-
-   DBG("ips_detect");
+   u16               deviceID[2];
+   int               i;
+   int               j;
+   char             *ioremap_ptr;
+   char             *mem_ptr;
+   struct pci_dev   *dev[2];
+   struct pci_dev   *morpheus = NULL;
+   struct pci_dev   *trombone = NULL;
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,14)
+   u32               currbar;
+   u32               maskbar;
+   u8                barnum;
+#endif
+
+   METHOD_TRACE("ips_detect", 1);
+
+#ifdef MODULE
+   if (ips)
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+      ips_setup(ips);
+#else
+      ips_setup(ips, NULL);
+#endif
+#endif
 
    SHT->proc_info = ips_proc_info;
 #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
@@ -367,232 +534,522 @@
 
    /* initalize number of controllers */
    ips_num_controllers = 0;
+   ips_next_controller = 0;
+   ips_released_controllers = 0;
 
    if (!pci_present())
       return (0);
 
-   for (index = 0; index < IPS_MAX_ADAPTERS; index++) {
+   morpheus = pci_find_device(IPS_VENDORID, IPS_MORPHEUS_DEVICEID, morpheus);
+   trombone = pci_find_device(IPS_VENDORID, IPS_COPPERHEAD_DEVICEID, trombone);
 
-      if (!(dev = pci_find_device(IPS_VENDORID, IPS_DEVICEID, dev)))
-         break;
+   /* determine which controller to probe first */
+   if (!morpheus) {
+      /* we only have trombone */
+      dev[0] = trombone;
+      dev[1] = NULL;
+      deviceID[0] = IPS_COPPERHEAD_DEVICEID;
+   } else if (!trombone) {
+      /* we only have morpheus */
+      dev[0] = morpheus;
+      dev[1] = NULL;
+      deviceID[0] = IPS_MORPHEUS_DEVICEID;
+   } else {
+      /* we have both in the system */
+      if (trombone->bus < morpheus->bus) {
+         dev[0] = trombone;
+         dev[1] = morpheus;
+         deviceID[0] = IPS_COPPERHEAD_DEVICEID;
+         deviceID[1] = IPS_MORPHEUS_DEVICEID;
+      } else if (trombone->bus > morpheus->bus) {
+         dev[0] = morpheus;
+         dev[1] = trombone;
+         deviceID[0] = IPS_MORPHEUS_DEVICEID;
+         deviceID[1] = IPS_COPPERHEAD_DEVICEID;
+      } else {
+         /* further detection required */
+         if (trombone->devfn < morpheus->devfn) {
+            dev[0] = trombone;
+            dev[1] = morpheus;
+            deviceID[0] = IPS_COPPERHEAD_DEVICEID;
+            deviceID[1] = IPS_MORPHEUS_DEVICEID;
+         } else {
+            dev[0] = morpheus;
+            dev[1] = trombone;
+            deviceID[0] = IPS_MORPHEUS_DEVICEID;
+            deviceID[1] = IPS_COPPERHEAD_DEVICEID;
+         }
+      }
+   }
 
-      /* stuff that we get in dev */
-      irq = dev->irq;
-      bus = dev->bus->number;
-      func = dev->devfn;
+   /* Now scan the controllers */
+   for (i = 0; i < 2; i++) {
+      if (!dev[i])
+         break;
 
-      /* get planer status */
-      if (pci_read_config_word(dev, 0x04, &planer)) {
-         printk(KERN_WARNING "(%s%d) can't get planer status.\n",
-                ips_name, index);
+      do {
+         if (ips_next_controller >= IPS_MAX_ADAPTERS)
+            break;
 
-         continue;
-      }
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
+         if (pci_enable_device(dev[i]))
+            break;
+#endif
 
-      /* check I/O address */
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,13)
-      io_addr = dev->base_address[0];
+         /* stuff that we get in dev */
+         irq = dev[i]->irq;
+         bus = dev[i]->bus->number;
+         func = dev[i]->devfn;
+
+         /* Init MEM/IO addresses to 0 */
+         mem_addr = 0;
+         io_addr = 0;
+         mem_len = 0;
+         io_len = 0;
+
+         for (j = 0; j < 2; j++) {
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
+            if (!pci_resource_start(dev[i], j))
+               break;
 
-      if ((io_addr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
-         continue;
+            if (pci_resource_flags(dev[i], j) & IORESOURCE_IO) {
+               io_addr = pci_resource_start(dev[i], j);
+               io_len = pci_resource_len(dev[i], j);
+            } else {
+               mem_addr = pci_resource_start(dev[i], j);
+               mem_len = pci_resource_len(dev[i], j);
+            }
+#elif LINUX_VERSION_CODE >= LinuxVersionCode(2,3,14)
+            if (!dev[i]->resource[j].start)
+               break;
 
-      /* get the BASE IO Address */
-      io_addr &= PCI_BASE_ADDRESS_IO_MASK;
+            if ((dev[i]->resource[j].start & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
+               io_addr = dev[i]->resource[j].start;
+               io_len = dev[i]->resource[j].end - dev[i]->resource[j].start + 1;
+            } else {
+               mem_addr = dev[i]->resource[j].start;
+               mem_len = dev[i]->resource[j].end - dev[i]->resource[j].start + 1;
+            }
 #else
-      io_addr = dev->resource[0].start;
+            if (!dev[i]->base_address[j])
+               break;
 
-      if ((dev->resource[0].flags & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
-         continue;
+            if ((dev[i]->base_address[j] & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
+               barnum = PCI_BASE_ADDRESS_0 + (j * 4);
+               io_addr = dev[i]->base_address[j] & PCI_BASE_ADDRESS_IO_MASK;
+
+               /* Get Size */
+               pci_read_config_dword(dev[i], barnum, &currbar);
+               pci_write_config_dword(dev[i], barnum, ~0);
+               pci_read_config_dword(dev[i], barnum, &maskbar);
+               pci_write_config_dword(dev[i], barnum, currbar);
+
+               io_len = ~(maskbar & PCI_BASE_ADDRESS_IO_MASK) + 1;
+            } else {
+               barnum = PCI_BASE_ADDRESS_0 + (j * 4);
+               mem_addr = dev[i]->base_address[j] & PCI_BASE_ADDRESS_MEM_MASK;
+
+               /* Get Size */
+               pci_read_config_dword(dev[i], barnum, &currbar);
+               pci_write_config_dword(dev[i], barnum, ~0);
+               pci_read_config_dword(dev[i], barnum, &maskbar);
+               pci_write_config_dword(dev[i], barnum, currbar);
+
+               mem_len = ~(maskbar & PCI_BASE_ADDRESS_MEM_MASK) + 1;
+            }
 #endif
+         }
 
-      /* check to see if an onboard planer controller is disabled */
-      if (!(planer & 0x000C)) {
+         /* setup memory mapped area (if applicable) */
+         if (mem_addr) {
+            u32 base;
+            u32 offs;
+
+            DEBUG_VAR(1, "(%s%d) detect, Memory region %x, size: %d",
+                      ips_name, ips_next_controller, mem_addr, mem_len);
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17)
+            if (check_mem_region(mem_addr, mem_len)) {
+               /* Couldn't allocate io space */
+               printk(KERN_WARNING "(%s%d) couldn't allocate IO space %x len %d.\n",
+                      ips_name, ips_next_controller, io_addr, io_len);
 
-   #ifdef IPS_PCI_PROBE_DEBUG
-         printk(KERN_NOTICE "(%s%d) detect, Onboard ServeRAID disabled by BIOS\n",
-                ips_name, index);
-   #endif
+               ips_next_controller++;
 
-         continue;
-      }
+               continue;
+            }
 
-   #ifdef IPS_PCI_PROBE_DEBUG
-      printk(KERN_NOTICE "(%s%d) detect bus %d, func %x, irq %d, io %x\n",
-             ips_name, index, bus, func, irq, io_addr);
-   #endif
-
-      /* get the revision ID */
-      if (pci_read_config_byte(dev, 0x08, &revision_id)) {
-         printk(KERN_WARNING "(%s%d) can't get revision id.\n",
-                ips_name, index);
+            request_mem_region(mem_addr, mem_len, "ips");
+#endif
 
-         continue;
-      }
+            base = mem_addr & PAGE_MASK;
+            offs = mem_addr - base;
 
-      /* found a controller */
-      sh = scsi_register(SHT, sizeof(ips_ha_t));
+            ioremap_ptr = ioremap(base, PAGE_SIZE);
+            mem_ptr = ioremap_ptr + offs;
+         } else {
+            ioremap_ptr = NULL;
+            mem_ptr = NULL;
+         }
 
-      if (sh == NULL) {
-         printk(KERN_WARNING "(%s%d) Unable to register controller with SCSI subsystem - skipping controller\n",
-                ips_name, index);
+         /* setup I/O mapped area (if applicable) */
+         if (io_addr) {
+            DEBUG_VAR(1, "(%s%d) detect, IO region %x, size: %d",
+                      ips_name, ips_next_controller, io_addr, io_len);
+
+            if (check_region(io_addr, io_len)) {
+               /* Couldn't allocate io space */
+               printk(KERN_WARNING "(%s%d) couldn't allocate IO space %x len %d.\n",
+                      ips_name, ips_next_controller, io_addr, io_len);
 
-         continue;
-      }
+               ips_next_controller++;
 
-      ha = IPS_HA(sh);
-      memset(ha, 0, sizeof(ips_ha_t));
+               continue;
+            }
 
-      /* Initialize spin lock */
-      spin_lock_init(&ha->scb_lock);
-      spin_lock_init(&ha->copp_lock);
-      spin_lock_init(&ha->ips_lock);
-      spin_lock_init(&ha->copp_waitlist.lock);
-      spin_lock_init(&ha->scb_waitlist.lock);
-      spin_lock_init(&ha->scb_activelist.lock);
-
-      ips_sh[ips_num_controllers] = sh;
-      ips_ha[ips_num_controllers] = ha;
-      ips_num_controllers++;
-      ha->active = 1;
-
-      ha->enq = kmalloc(sizeof(IPS_ENQ), GFP_KERNEL|GFP_DMA);
-
-      if (!ha->enq) {
-         printk(KERN_WARNING "(%s%d) Unable to allocate host inquiry structure - skipping contoller\n",
-                ips_name, index);
+            request_region(io_addr, io_len, "ips");
+         }
 
-         ha->active = 0;
+         /* get planer status */
+         if (pci_read_config_word(dev[i], 0x04, &planer)) {
+            printk(KERN_WARNING "(%s%d) can't get planer status.\n",
+                   ips_name, ips_next_controller);
 
-         continue;
-      }
+            ips_next_controller++;
 
-      ha->adapt = kmalloc(sizeof(IPS_ADAPTER), GFP_KERNEL|GFP_DMA);
+            continue;
+         }
 
-      if (!ha->adapt) {
-         printk(KERN_WARNING "(%s%d) Unable to allocate host adapt structure - skipping controller\n",
-                ips_name, index);
+         /* check to see if an onboard planer controller is disabled */
+         if (!(planer & 0x000C)) {
 
-         ha->active = 0;
+            DEBUG_VAR(1, "(%s%d) detect, Onboard ServeRAID disabled by BIOS",
+                      ips_name, ips_next_controller);
 
-         continue;
-      }
+            ips_next_controller++;
 
-      ha->conf = kmalloc(sizeof(IPS_CONF), GFP_KERNEL|GFP_DMA);
+            continue;
+         }
 
-      if (!ha->conf) {
-         printk(KERN_WARNING "(%s%d) Unable to allocate host conf structure - skipping controller\n",
-                ips_name, index);
+         DEBUG_VAR(1, "(%s%d) detect bus %d, func %x, irq %d, io %x, mem: %x, ptr: %x",
+                   ips_name, ips_next_controller, bus, func, irq, io_addr, mem_addr, (u32) mem_ptr);
 
-         ha->active = 0;
+         /* get the revision ID */
+         if (pci_read_config_byte(dev[i], 0x08, &revision_id)) {
+            printk(KERN_WARNING "(%s%d) can't get revision id.\n",
+                   ips_name, ips_next_controller);
 
-         continue;
-      }
+            ips_next_controller++;
 
-      ha->nvram = kmalloc(sizeof(IPS_NVRAM_P5), GFP_KERNEL|GFP_DMA);
+            continue;
+         }
 
-      if (!ha->nvram) {
-         printk(KERN_WARNING "(%s%d) Unable to allocate host nvram structure - skipping controller\n",
-                ips_name, index);
+         /* found a controller */
+         sh = scsi_register(SHT, sizeof(ips_ha_t));
 
-         ha->active = 0;
+         if (sh == NULL) {
+            printk(KERN_WARNING "(%s%d) Unable to register controller with SCSI subsystem - skipping controller\n",
+                   ips_name, ips_next_controller);
 
-         continue;
-      }
+            ips_next_controller++;
 
-      ha->subsys = kmalloc(sizeof(IPS_SUBSYS), GFP_KERNEL|GFP_DMA);
+            continue;
+         }
 
-      if (!ha->subsys) {
-         printk(KERN_WARNING "(%s%d) Unable to allocate host subsystem structure - skipping controller\n",
-                ips_name, index);
+         ha = IPS_HA(sh);
+         memset(ha, 0, sizeof(ips_ha_t));
 
-         ha->active = 0;
+         /* Initialize spin lock */
+         spin_lock_init(&ha->scb_lock);
+         spin_lock_init(&ha->copp_lock);
+         spin_lock_init(&ha->ips_lock);
+         spin_lock_init(&ha->copp_waitlist.lock);
+         spin_lock_init(&ha->scb_waitlist.lock);
+         spin_lock_init(&ha->scb_activelist.lock);
+
+         ips_sh[ips_next_controller] = sh;
+         ips_ha[ips_next_controller] = ha;
+         ips_num_controllers++;
+         ha->active = 1;
+
+         ha->enq = kmalloc(sizeof(IPS_ENQ), GFP_ATOMIC|GFP_DMA);
+
+         if (!ha->enq) {
+            printk(KERN_WARNING "(%s%d) Unable to allocate host inquiry structure - skipping contoller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            ips_next_controller++;
+            ips_num_controllers--;
 
-         continue;
-      }
+            continue;
+         }
 
-      ha->dummy = kmalloc(sizeof(IPS_IO_CMD), GFP_KERNEL|GFP_DMA);
+         ha->adapt = kmalloc(sizeof(IPS_ADAPTER), GFP_ATOMIC|GFP_DMA);
 
-      if (!ha->dummy) {
-         printk(KERN_WARNING "(%s%d) Unable to allocate host dummy structure - skipping controller\n",
-                ips_name, index);
+         if (!ha->adapt) {
+            printk(KERN_WARNING "(%s%d) Unable to allocate host adapt structure - skipping controller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            ips_next_controller++;
+            ips_num_controllers--;
 
-         ha->active = 0;
+            continue;
+         }
 
-         continue;
-      }
+         ha->conf = kmalloc(sizeof(IPS_CONF), GFP_ATOMIC|GFP_DMA);
 
-      ha->ioctl_data = kmalloc(IPS_IOCTL_SIZE, GFP_KERNEL|GFP_DMA);
-      ha->ioctl_datasize = IPS_IOCTL_SIZE;
-      if (!ha->ioctl_data) {
-         printk(KERN_WARNING "(%s%d) Unable to allocate ioctl data - skipping controller\n",
-                ips_name, index);
+         if (!ha->conf) {
+            printk(KERN_WARNING "(%s%d) Unable to allocate host conf structure - skipping controller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            ips_next_controller++;
+            ips_num_controllers--;
 
-         ha->active = 0;
+            continue;
+         }
 
-         continue;
-      }
+         ha->nvram = kmalloc(sizeof(IPS_NVRAM_P5), GFP_ATOMIC|GFP_DMA);
 
-      /* Store away needed values for later use */
-      sh->io_port = io_addr;
-      sh->n_io_port = 255;
-      sh->unique_id = io_addr;
-      sh->irq = irq;
-      sh->select_queue_depths = NULL;
-      sh->sg_tablesize = sh->hostt->sg_tablesize;
-      sh->can_queue = sh->hostt->can_queue;
-      sh->cmd_per_lun = sh->hostt->cmd_per_lun;
-      sh->unchecked_isa_dma = sh->hostt->unchecked_isa_dma;
-      sh->use_clustering = sh->hostt->use_clustering;
-
-      /* Store info in HA structure */
-      ha->io_addr = io_addr;
-      ha->irq = irq;
-      ha->host_num = index;
-      ha->revision_id = revision_id;
-
-      /* install the interrupt handler */
-      if (request_irq(irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) {
-         printk(KERN_WARNING "(%s%d) unable to install interrupt handler - skipping controller\n",
-                ips_name, index);
+         if (!ha->nvram) {
+            printk(KERN_WARNING "(%s%d) Unable to allocate host nvram structure - skipping controller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            ips_next_controller++;
+            ips_num_controllers--;
 
-         ha->active = 0;
+            continue;
+         }
 
-         continue;
-      }
+         ha->subsys = kmalloc(sizeof(IPS_SUBSYS), GFP_ATOMIC|GFP_DMA);
 
-      /*
-       * Allocate a temporary SCB for initialization
-       */
-      ha->scbs = (ips_scb_t *) kmalloc(sizeof(ips_scb_t), GFP_KERNEL|GFP_DMA);
-      if (!ha->scbs) {
-         /* couldn't allocate a temp SCB */
-         printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
-                ips_name, index);
+         if (!ha->subsys) {
+            printk(KERN_WARNING "(%s%d) Unable to allocate host subsystem structure - skipping controller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            ips_next_controller++;
+            ips_num_controllers--;
 
-         ha->active = 0;
+            continue;
+         }
 
-         continue;
-      }
+         ha->dummy = kmalloc(sizeof(IPS_IO_CMD), GFP_ATOMIC|GFP_DMA);
 
-      memset(ha->scbs, 0, sizeof(ips_scb_t));
-      ha->scbs->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_KERNEL|GFP_DMA);
-      if (!ha->scbs->sg_list) {
-         /* couldn't allocate a temp SCB S/G list */
-         printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
-                ips_name, index);
+         if (!ha->dummy) {
+            printk(KERN_WARNING "(%s%d) Unable to allocate host dummy structure - skipping controller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            ips_next_controller++;
+            ips_num_controllers--;
 
-         ha->active = 0;
+            continue;
+         }
+
+         ha->ioctl_data = kmalloc(IPS_IOCTL_SIZE, GFP_ATOMIC|GFP_DMA);
+         ha->ioctl_datasize = IPS_IOCTL_SIZE;
+         if (!ha->ioctl_data) {
+            printk(KERN_WARNING "(%s%d) Unable to allocate ioctl data - skipping controller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            ips_next_controller++;
+            ips_num_controllers--;
+
+            continue;
+         }
+
+         /* Store away needed values for later use */
+         sh->io_port = io_addr;
+         sh->n_io_port = io_addr ? 255 : 0;
+         sh->unique_id = (io_addr) ? io_addr : mem_addr;
+         sh->irq = irq;
+         sh->select_queue_depths = ips_select_queue_depth;
+         sh->sg_tablesize = sh->hostt->sg_tablesize;
+         sh->can_queue = sh->hostt->can_queue;
+         sh->cmd_per_lun = sh->hostt->cmd_per_lun;
+         sh->unchecked_isa_dma = sh->hostt->unchecked_isa_dma;
+         sh->use_clustering = sh->hostt->use_clustering;
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,32)
+         sh->wish_block = FALSE;
+#endif
+
+         /* Store info in HA structure */
+         ha->irq = irq;
+         ha->io_addr = io_addr;
+         ha->io_len = io_len;
+         ha->mem_addr = mem_addr;
+         ha->mem_len = mem_len;
+         ha->mem_ptr = mem_ptr;
+         ha->ioremap_ptr = ioremap_ptr;
+         ha->host_num = ips_next_controller;
+         ha->revision_id = revision_id;
+         ha->device_id = deviceID[i];
+         ha->pcidev = dev[i];
+
+         /*
+          * Setup Functions
+          */
+         if (IPS_IS_MORPHEUS(ha)) {
+            /* morpheus */
+            ha->func.isintr = ips_isintr_morpheus;
+            ha->func.isinit = ips_isinit_morpheus;
+            ha->func.issue = ips_issue_i2o_memio;
+            ha->func.init = ips_init_morpheus;
+            ha->func.statupd = ips_statupd_morpheus;
+            ha->func.reset = ips_reset_morpheus;
+            ha->func.intr = ips_intr_morpheus;
+            ha->func.enableint = ips_enable_int_morpheus;
+         } else if (IPS_USE_MEMIO(ha)) {
+            /* copperhead w/MEMIO */
+            ha->func.isintr = ips_isintr_copperhead_memio;
+            ha->func.isinit = ips_isinit_copperhead_memio;
+            ha->func.init = ips_init_copperhead_memio;
+            ha->func.statupd = ips_statupd_copperhead_memio;
+            ha->func.statinit = ips_statinit_memio;
+            ha->func.reset = ips_reset_copperhead_memio;
+            ha->func.intr = ips_intr_copperhead;
+            ha->func.erasebios = ips_erase_bios_memio;
+            ha->func.programbios = ips_program_bios_memio;
+            ha->func.verifybios = ips_verify_bios_memio;
+            ha->func.enableint = ips_enable_int_copperhead_memio;
+
+            if (IPS_USE_I2O_DELIVER(ha))
+               ha->func.issue = ips_issue_i2o_memio;
+            else
+               ha->func.issue = ips_issue_copperhead_memio;
+         } else {
+            /* copperhead */
+            ha->func.isintr = ips_isintr_copperhead;
+            ha->func.isinit = ips_isinit_copperhead;
+            ha->func.init = ips_init_copperhead;
+            ha->func.statupd = ips_statupd_copperhead;
+            ha->func.statinit = ips_statinit;
+            ha->func.reset = ips_reset_copperhead;
+            ha->func.intr = ips_intr_copperhead;
+            ha->func.erasebios = ips_erase_bios;
+            ha->func.programbios = ips_program_bios;
+            ha->func.verifybios = ips_verify_bios;
+            ha->func.enableint = ips_enable_int_copperhead;
+
+            if (IPS_USE_I2O_DELIVER(ha))
+               ha->func.issue = ips_issue_i2o;
+            else
+               ha->func.issue = ips_issue_copperhead;
+         }
+
+         /*
+          * Initialize the card if it isn't already
+          */
+         if (!(*ha->func.isinit)(ha)) {
+            if (!(*ha->func.init)(ha)) {
+               /*
+                * Initialization failed
+                */
+               printk(KERN_WARNING "(%s%d) unable to initialize controller - skipping controller\n",
+                      ips_name, ips_next_controller);
+
+               ha->active = 0;
+               ips_free(ha);
+               ips_next_controller++;
+               ips_num_controllers--;
+
+               continue;
+            }
+         }
+
+         /* install the interrupt handler */
+         if (request_irq(irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) {
+            printk(KERN_WARNING "(%s%d) unable to install interrupt handler - skipping controller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            ips_next_controller++;
+            ips_num_controllers--;
+
+            continue;
+         }
+
+         /*
+          * Allocate a temporary SCB for initialization
+          */
+         ha->scbs = (ips_scb_t *) kmalloc(sizeof(ips_scb_t), GFP_ATOMIC|GFP_DMA);
+         if (!ha->scbs) {
+            /* couldn't allocate a temp SCB */
+            printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            free_irq(ha->irq, ha);
+            ips_next_controller++;
+            ips_num_controllers--;
+
+            continue;
+         }
+
+         memset(ha->scbs, 0, sizeof(ips_scb_t));
+         ha->scbs->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_ATOMIC|GFP_DMA);
+         if (!ha->scbs->sg_list) {
+            /* couldn't allocate a temp SCB S/G list */
+            printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
+                   ips_name, ips_next_controller);
+
+            ha->active = 0;
+            ips_free(ha);
+            free_irq(ha->irq, ha);
+            ips_next_controller++;
+            ips_num_controllers--;
+
+            continue;
+         }
+
+         ha->max_cmds = 1;
+
+         ips_next_controller++;
+      } while ((dev[i] = pci_find_device(IPS_VENDORID, deviceID[i], dev[i])));
+   }
+
+   /*
+    * Do Phase 2 Initialization
+    * Controller init
+    */
+   for (i = 0; i < ips_next_controller; i++) {
+      ha = ips_ha[i];
+      sh = ips_sh[i];
+
+      if (!ha->active) {
+         scsi_unregister(sh);
+         ips_ha[i] = NULL;
+         ips_sh[i] = NULL;
 
          continue;
       }
 
-      ha->max_cmds = 1;
-
       if (!ips_hainit(ha)) {
          printk(KERN_WARNING "(%s%d) unable to initialize controller - skipping\n",
-                ips_name, index);
+                ips_name, i);
 
          ha->active = 0;
+         ips_free(ha);
+         free_irq(ha->irq, ha);
+         scsi_unregister(sh);
+         ips_ha[i] = NULL;
+         ips_sh[i] = NULL;
+         ips_num_controllers--;
 
          continue;
       }
@@ -607,9 +1064,15 @@
       /* allocate CCBs */
       if (!ips_allocatescbs(ha)) {
          printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
-                ips_name, index);
+                ips_name, i);
 
          ha->active = 0;
+         ips_free(ha);
+         free_irq(ha->irq, ha);
+         scsi_unregister(sh);
+         ips_ha[i] = NULL;
+         ips_sh[i] = NULL;
+         ips_num_controllers--;
 
          continue;
       }
@@ -617,9 +1080,12 @@
       /* finish setting values */
       sh->max_id = ha->ntargets;
       sh->max_lun = ha->nlun;
-      sh->max_channel = ha->nbus;
+      sh->max_channel = ha->nbus - 1;
       sh->can_queue = ha->max_cmds-1;
-   } /* end for */
+   }
+
+   if (ips_num_controllers > 0)
+      register_reboot_notifier(&ips_notifier);
 
    return (ips_num_controllers);
 
@@ -645,7 +1111,7 @@
    ips_ha_t  *ha;
    int        i;
 
-   DBG("ips_release");
+   METHOD_TRACE("ips_release", 1);
 
    for (i = 0; i < IPS_MAX_ADAPTERS && ips_sh[i] != sh; i++);
 
@@ -688,17 +1154,85 @@
    /* free extra memory */
    ips_free(ha);
 
+   /* Free I/O Region */
+   if (ha->io_addr)
+      release_region(ha->io_addr, ha->io_len);
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17)
+   if (ha->mem_addr)
+      release_mem_region(ha->mem_addr, ha->mem_len);
+#endif
+
    /* free IRQ */
    free_irq(ha->irq, ha);
 
-   /* unregister with SCSI sub system */
-   scsi_unregister(sh);
+   ips_released_controllers++;
+
+   if (ips_num_controllers == ips_released_controllers)
+      unregister_reboot_notifier(&ips_notifier);
 
    return (FALSE);
 }
 
 /****************************************************************************/
 /*                                                                          */
+/* Routine Name: ips_halt                                                   */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Perform cleanup when the system reboots                                */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_halt(struct notifier_block *nb, ulong event, void *buf) {
+   ips_scb_t *scb;
+   ips_ha_t  *ha;
+   int        i;
+
+   if ((event != SYS_RESTART) && (event != SYS_HALT) &&
+       (event != SYS_POWER_OFF))
+      return (NOTIFY_DONE);
+
+   for (i = 0; i < ips_next_controller; i++) {
+      ha = (ips_ha_t *) ips_ha[i];
+
+      if (!ha)
+         continue;
+
+      if (!ha->active)
+         continue;
+
+      /* flush the cache on the controller */
+      scb = &ha->scbs[ha->max_cmds-1];
+
+      ips_init_scb(ha, scb);
+
+      scb->timeout = ips_cmd_timeout;
+      scb->cdb[0] = IPS_CMD_FLUSH;
+
+      scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
+      scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb);
+      scb->cmd.flush_cache.state = IPS_NORM_STATE;
+      scb->cmd.flush_cache.reserved = 0;
+      scb->cmd.flush_cache.reserved2 = 0;
+      scb->cmd.flush_cache.reserved3 = 0;
+      scb->cmd.flush_cache.reserved4 = 0;
+
+      printk("(%s%d) Flushing Cache.\n", ips_name, ha->host_num);
+
+      /* send command */
+      if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == IPS_FAILURE)
+         printk("(%s%d) Incomplete Flush.\n", ips_name, ha->host_num);
+      else
+         printk("(%s%d) Flushing Complete.\n", ips_name, ha->host_num);
+   }
+
+   unregister_reboot_notifier(&ips_notifier);
+   return (NOTIFY_OK);
+}
+
+/****************************************************************************/
+/*                                                                          */
 /* Routine Name: ips_eh_abort                                               */
 /*                                                                          */
 /* Routine Description:                                                     */
@@ -711,7 +1245,7 @@
    ips_ha_t         *ha;
    ips_copp_wait_item_t *item;
 
-   DBG("ips_eh_abort");
+   METHOD_TRACE("ips_eh_abort", 1);
 
    if (!SC)
       return (FAILED);
@@ -726,9 +1260,7 @@
 
    if (SC->serial_number != SC->serial_number_at_timeout) {
       /* HMM, looks like a bogus command */
-#if IPS_DEBUG >= 1
-      printk(KERN_NOTICE "Abort called with bogus scsi command\n");
-#endif
+      DEBUG(1, "Abort called with bogus scsi command");
 
       return (FAILED);
    }
@@ -767,121 +1299,49 @@
 
 /****************************************************************************/
 /*                                                                          */
-/* Routine Name: ips_abort                                                  */
+/* Routine Name: ips_eh_reset                                               */
 /*                                                                          */
 /* Routine Description:                                                     */
 /*                                                                          */
-/*   Abort a command                                                        */
+/*   Reset the controller (with new eh error code)                          */
+/*                                                                          */
+/* NOTE: this routine is called under the io_request_lock spinlock          */
 /*                                                                          */
 /****************************************************************************/
 int
-ips_abort(Scsi_Cmnd *SC) {
-   ips_ha_t         *ha;
+ips_eh_reset(Scsi_Cmnd *SC) {
+   int                   ret;
+   int                   i;
+   u32                   cpu_flags;
+   ips_ha_t             *ha;
+   ips_scb_t            *scb;
    ips_copp_wait_item_t *item;
 
-   DBG("ips_abort");
+   METHOD_TRACE("ips_eh_reset", 1);
 
-   if (!SC)
-      return (SCSI_ABORT_SNOOZE);
+#ifdef NO_IPS_RESET
+   return (FAILED);
+#else
 
-   ha = (ips_ha_t *) SC->host->hostdata;
+   if (!SC) {
+      DEBUG(1, "Reset called with NULL scsi command");
 
-   if (!ha)
-      return (SCSI_ABORT_SNOOZE);
+      return (FAILED);
+   }
 
-   if (!ha->active)
-      return (SCSI_ABORT_SNOOZE);
+   ha = (ips_ha_t *) SC->host->hostdata;
 
-   if (SC->serial_number != SC->serial_number_at_timeout) {
-      /* HMM, looks like a bogus command */
-#if IPS_DEBUG >= 1
-      printk(KERN_NOTICE "Abort called with bogus scsi command\n");
-#endif
+   if (!ha) {
+      DEBUG(1, "Reset called with NULL ha struct");
 
-      return (SCSI_ABORT_NOT_RUNNING);
+      return (FAILED);
    }
 
-   if (test_and_set_bit(IPS_IN_ABORT, &ha->flags))
-      return (SCSI_ABORT_SNOOZE);
-
-   /* See if the command is on the copp queue */
-   IPS_QUEUE_LOCK(&ha->copp_waitlist);
-   item = ha->copp_waitlist.head;
-   while ((item) && (item->scsi_cmd != SC))
-      item = item->next;
-   IPS_QUEUE_UNLOCK(&ha->copp_waitlist);
-
-   if (item) {
-      /* Found it */
-      ips_removeq_copp(&ha->copp_waitlist, item);
-      clear_bit(IPS_IN_ABORT, &ha->flags);
-
-      return (SCSI_ABORT_PENDING);
-   }
-
-   /* See if the command is on the wait queue */
-   if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
-      /* command not sent yet */
-      clear_bit(IPS_IN_ABORT, &ha->flags);
-
-      return (SCSI_ABORT_PENDING);
-   } else {
-      /* command must have already been sent */
-      clear_bit(IPS_IN_ABORT, &ha->flags);
-
-      return (SCSI_ABORT_SNOOZE);
-   }
-}
-
-/****************************************************************************/
-/*                                                                          */
-/* Routine Name: ips_eh_reset                                               */
-/*                                                                          */
-/* Routine Description:                                                     */
-/*                                                                          */
-/*   Reset the controller (with new eh error code)                          */
-/*                                                                          */
-/* NOTE: this routine is called under the io_request_lock spinlock          */
-/*                                                                          */
-/****************************************************************************/
-int
-ips_eh_reset(Scsi_Cmnd *SC) {
-   u32                   cpu_flags;
-   ips_ha_t             *ha;
-   ips_scb_t            *scb;
-   ips_copp_wait_item_t *item;
-
-   DBG("ips_eh_reset");
-
-#ifdef NO_IPS_RESET
-   return (FAILED);
-#else
-
-   if (!SC) {
-
-#if IPS_DEBUG >= 1
-      printk(KERN_NOTICE "Reset called with NULL scsi command\n");
-#endif
-
-      return (FAILED);
-   }
-
-   ha = (ips_ha_t *) SC->host->hostdata;
-
-   if (!ha) {
-
-#if IPS_DEBUG >= 1
-      printk(KERN_NOTICE "Reset called with NULL ha struct\n");
-#endif
-
-      return (FAILED);
-   }
-
-   if (!ha->active)
-      return (FAILED);
-
-   if (test_and_set_bit(IPS_IN_RESET, &ha->flags))
-      return (FAILED);
+   if (!ha->active)
+      return (FAILED);
+
+   if (test_and_set_bit(IPS_IN_RESET, &ha->flags))
+      return (FAILED);
 
    /* See if the command is on the copp queue */
    IPS_QUEUE_LOCK(&ha->copp_waitlist);
@@ -910,149 +1370,72 @@
     * command must have already been sent
     * reset the controller
     */
-   if (!ips_reset_adapter(ha)) {
-      clear_bit(IPS_IN_RESET, &ha->flags);
-
-      return (FAILED);
-   }
-
-   if (!ips_clear_adapter(ha, IPS_INTR_IORL)) {
-      clear_bit(IPS_IN_RESET, &ha->flags);
-
-      return (FAILED);
-   }
-
-   /* FFDC */
-   if (ha->subsys->param[3] & 0x300000) {
-      struct timeval tv;
-
-      do_gettimeofday(&tv);
-      IPS_HA_LOCK(cpu_flags);
-      ha->last_ffdc = tv.tv_sec;
-      ha->reset_count++;
-      IPS_HA_UNLOCK(cpu_flags);
-      ips_ffdc_reset(ha, IPS_INTR_IORL);
-   }
-
-   /* Now fail all of the active commands */
-#if IPS_DEBUG >= 1
-   printk(KERN_WARNING "(%s%d) Failing active commands\n",
+   printk(KERN_NOTICE "(%s%d) Resetting controller.\n",
           ips_name, ha->host_num);
-#endif
-   while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
-      scb->scsi_cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24);
-      scb->scsi_cmd->scsi_done(scb->scsi_cmd);
-      ips_freescb(ha, scb);
-   }
-
-   /* Reset the number of active IOCTLs */
-   IPS_HA_LOCK(cpu_flags);
-   ha->num_ioctl = 0;
-   IPS_HA_UNLOCK(cpu_flags);
-
-   clear_bit(IPS_IN_RESET, &ha->flags);
-
-   if (!test_bit(IPS_IN_INTR, &ha->flags)) {
-      /*
-       * Only execute the next command when
-       * we are not being called from the
-       * interrupt handler.  The interrupt
-       * handler wants to do this and since
-       * interrupts are turned off here....
-       */
-      ips_next(ha, IPS_INTR_IORL);
-   }
-
-   return (SUCCESS);
-
-#endif /* NO_IPS_RESET */
-
-}
-
-/****************************************************************************/
-/*                                                                          */
-/* Routine Name: ips_reset                                                  */
-/*                                                                          */
-/* Routine Description:                                                     */
-/*                                                                          */
-/*   Reset the controller                                                   */
-/*                                                                          */
-/* NOTE: this routine is called under the io_request_lock spinlock          */
-/*                                                                          */
-/****************************************************************************/
-int
-ips_reset(Scsi_Cmnd *SC, unsigned int flags) {
-   u32                   cpu_flags;
-   ips_ha_t             *ha;
-   ips_scb_t            *scb;
-   ips_copp_wait_item_t *item;
-
-   DBG("ips_reset");
+   ret = (*ha->func.reset)(ha);
 
-#ifdef NO_IPS_RESET
-   return (SCSI_RESET_SNOOZE);
-#else
+   if (!ret) {
+      Scsi_Cmnd *scsi_cmd;
 
-   if (!SC) {
+      printk(KERN_NOTICE 
+             "(%s%d) Controller reset failed - controller now offline.\n",
+             ips_name, ha->host_num);
 
-#if IPS_DEBUG >= 1
-      printk(KERN_NOTICE "Reset called with NULL scsi command\n");
-#endif
+      /* Now fail all of the active commands */
+      DEBUG_VAR(1, "(%s%d) Failing active commands",
+                ips_name, ha->host_num);
 
-      return (SCSI_RESET_SNOOZE);
-   }
+      while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
+         scb->scsi_cmd->result = DID_ERROR << 16;
+         scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+         ips_freescb(ha, scb);
+      }
 
-   ha = (ips_ha_t *) SC->host->hostdata;
+      /* Now fail all of the pending commands */
+      DEBUG_VAR(1, "(%s%d) Failing pending commands",
+                ips_name, ha->host_num);
 
-   if (!ha) {
+      while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) {
+         scsi_cmd->result = DID_ERROR;
+         scsi_cmd->scsi_done(scsi_cmd);
+      }
 
-#if IPS_DEBUG >= 1
-      printk(KERN_NOTICE "Reset called with NULL ha struct\n");
-#endif
+      ha->active = FALSE;
+      clear_bit(IPS_IN_RESET, &ha->flags);
 
-      return (SCSI_RESET_SNOOZE);
+      return (FAILED);
    }
 
-   if (!ha->active)
-      return (SCSI_RESET_SNOOZE);
-
-   if (test_and_set_bit(IPS_IN_RESET, &ha->flags))
-      return (SCSI_RESET_SNOOZE);
-
-   /* See if the command is on the copp queue */
-   IPS_QUEUE_LOCK(&ha->copp_waitlist);
-   item = ha->copp_waitlist.head;
-   while ((item) && (item->scsi_cmd != SC))
-      item = item->next;
-   IPS_QUEUE_UNLOCK(&ha->copp_waitlist);
-
-   if (item) {
-      /* Found it */
-      ips_removeq_copp(&ha->copp_waitlist, item);
-      clear_bit(IPS_IN_RESET, &ha->flags);
+   if (!ips_clear_adapter(ha, IPS_INTR_IORL)) {
+      Scsi_Cmnd *scsi_cmd;
 
-      return (SCSI_RESET_SNOOZE);
-   }
+      printk(KERN_NOTICE 
+             "(%s%d) Controller reset failed - controller now offline.\n",
+             ips_name, ha->host_num);
 
-   /* See if the command is on the wait queue */
-   if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
-      /* command not sent yet */
-      clear_bit(IPS_IN_RESET, &ha->flags);
+      /* Now fail all of the active commands */
+      DEBUG_VAR(1, "(%s%d) Failing active commands",
+                ips_name, ha->host_num);
 
-      return (SCSI_RESET_SNOOZE);
-   }
+      while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
+         scb->scsi_cmd->result = DID_ERROR << 16;
+         scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+         ips_freescb(ha, scb);
+      }
 
-   /* reset the controller */
-   if (!ips_reset_adapter(ha)) {
-      clear_bit(IPS_IN_RESET, &ha->flags);
+      /* Now fail all of the pending commands */
+      DEBUG_VAR(1, "(%s%d) Failing pending commands",
+                ips_name, ha->host_num);
 
-      return (SCSI_RESET_ERROR);
-   }
+      while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) {
+         scsi_cmd->result = DID_ERROR << 16;
+         scsi_cmd->scsi_done(scsi_cmd);
+      }
 
-   if (!ips_clear_adapter(ha, IPS_INTR_IORL)) {
+      ha->active = FALSE;
       clear_bit(IPS_IN_RESET, &ha->flags);
 
-      return (SCSI_RESET_ERROR);
+      return (FAILED);
    }
 
    /* FFDC */
@@ -1068,16 +1451,19 @@
    }
 
    /* Now fail all of the active commands */
-#if IPS_DEBUG >= 1
-   printk(KERN_WARNING "(%s%d) Failing active commands\n",
-          ips_name, ha->host_num);
-#endif
+   DEBUG_VAR(1, "(%s%d) Failing active commands",
+             ips_name, ha->host_num);
+
    while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
       scb->scsi_cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24);
       scb->scsi_cmd->scsi_done(scb->scsi_cmd);
       ips_freescb(ha, scb);
    }
 
+   /* Reset DCDB active command bits */
+   for (i = 1; i < ha->nbus; i++)
+      ha->dcdb_active[i-1] = 0;
+
    /* Reset the number of active IOCTLs */
    IPS_HA_LOCK(cpu_flags);
    ha->num_ioctl = 0;
@@ -1096,7 +1482,7 @@
       ips_next(ha, IPS_INTR_IORL);
    }
 
-   return (SCSI_RESET_SUCCESS);
+   return (SUCCESS);
 
 #endif /* NO_IPS_RESET */
 
@@ -1124,7 +1510,7 @@
    DECLARE_MUTEX_LOCKED(sem);
 #endif
 
-   DBG("ips_queue");
+   METHOD_TRACE("ips_queue", 1);
 
    ha = (ips_ha_t *) SC->host->hostdata;
 
@@ -1132,7 +1518,7 @@
       return (1);
 
    if (!ha->active)
-      return (1);
+      return (DID_ERROR);
 
 #ifndef NO_IPS_CMDLINE
    if (ips_is_passthru(SC)) {
@@ -1165,24 +1551,28 @@
 
    SC->scsi_done = done;
 
-#if IPS_DEBUG >= 10
-   printk(KERN_NOTICE "%s: ips_queue: cmd 0x%X (%d %d %d)\n",
-          ips_name,
-          SC->cmnd[0],
-          SC->channel,
-          SC->target,
-          SC->lun);
-#if IPS_DEBUG >= 11
-        MDELAY(2*IPS_ONE_SEC);
-#endif
-#endif
+   DEBUG_VAR(2, "(%s%d): ips_queue: cmd 0x%X (%d %d %d)",
+             ips_name,
+             ha->host_num,
+             SC->cmnd[0],
+             SC->channel,
+             SC->target,
+             SC->lun);
+
+   /* Check for command to initiator IDs */
+   if ((SC->channel > 0) && (SC->target == ha->ha_id[SC->channel])) {
+      SC->result = DID_NO_CONNECT << 16;
+      done(SC);
+
+      return (0);
+   }
 
 #ifndef NO_IPS_CMDLINE
    if (ips_is_passthru(SC)) {
       ips_copp_wait_item_t *scratch;
 
       /* allocate space for the scribble */
-      scratch = kmalloc(sizeof(ips_copp_wait_item_t), GFP_KERNEL);
+      scratch = kmalloc(sizeof(ips_copp_wait_item_t), GFP_ATOMIC);
 
       if (!scratch) {
          SC->result = DID_ERROR << 16;
@@ -1238,10 +1628,8 @@
       datasize = *((u32 *) &SC->cmnd[8]);
 
       if (copy_to_user(user_area, kern_area, datasize) > 0) {
-#if IPS_DEBUG_PT >= 1
-         printk(KERN_NOTICE "(%s%d) passthru failed - unable to copy out user data\n",
-                ips_name, ha->host_num);
-#endif
+         DEBUG_VAR(1, "(%s%d) passthru failed - unable to copy out user data",
+                   ips_name, ha->host_num);
 
          SC->result = DID_ERROR << 16;
          SC->scsi_done(SC);
@@ -1269,7 +1657,7 @@
    int               sectors;
    int               cylinders;
 
-   DBG("ips_biosparam");
+   METHOD_TRACE("ips_biosparam", 1);
 
    ha = (ips_ha_t *) disk->device->host->hostdata;
 
@@ -1295,10 +1683,8 @@
 
    cylinders = disk->capacity / (heads * sectors);
 
-#if IPS_DEBUG >= 2
-   printk(KERN_NOTICE "Geometry: heads: %d, sectors: %d, cylinders: %d\n",
-          heads, sectors, cylinders);
-#endif
+   DEBUG_VAR(2, "Geometry: heads: %d, sectors: %d, cylinders: %d",
+             heads, sectors, cylinders);
 
    geom[0] = heads;
    geom[1] = sectors;
@@ -1309,6 +1695,40 @@
 
 /****************************************************************************/
 /*                                                                          */
+/* Routine Name: ips_select_queue_depth                                     */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Select queue depths for the devices on the contoller                   */
+/*                                                                          */
+/****************************************************************************/
+static void
+ips_select_queue_depth(struct Scsi_Host *host, Scsi_Device *scsi_devs) {
+   Scsi_Device *device;
+   ips_ha_t    *ha;
+   int          count = 0;
+
+   ha = IPS_HA(host);
+
+   for (device = scsi_devs; device; device = device->next) {
+     if (device->host == host) {
+        if ((device->channel == 0) && (device->type == 0))
+           count++;
+     }
+   }
+
+   for (device = scsi_devs; device; device = device->next) {
+      if (device->host == host) {
+         if ((device->channel == 0) && (device->type == 0))
+            device->queue_depth = ha->max_cmds / count - 1;
+         else
+            device->queue_depth = 2;
+      }
+   }
+}
+
+/****************************************************************************/
+/*                                                                          */
 /* Routine Name: do_ipsintr                                                 */
 /*                                                                          */
 /* Routine Description:                                                     */
@@ -1321,7 +1741,7 @@
    ips_ha_t         *ha;
    u32               cpu_flags;
 
-   DBG("do_ipsintr");
+   METHOD_TRACE("do_ipsintr", 2);
 
    ha = (ips_ha_t *) dev_id;
 
@@ -1347,7 +1767,7 @@
       return;
    }
 
-   ips_intr(ha);
+   (*ha->func.intr)(ha);
 
    clear_bit(IPS_IN_INTR, &ha->flags);
 
@@ -1359,7 +1779,7 @@
 
 /****************************************************************************/
 /*                                                                          */
-/* Routine Name: ips_intr                                                   */
+/* Routine Name: ips_intr_copperhead                                        */
 /*                                                                          */
 /* Routine Description:                                                     */
 /*                                                                          */
@@ -1369,13 +1789,14 @@
 /*                                                                          */
 /****************************************************************************/
 void
-ips_intr(ips_ha_t *ha) {
+ips_intr_copperhead(ips_ha_t *ha) {
    ips_stat_t       *sp;
    ips_scb_t        *scb;
-   int               status;
+   IPS_STATUS        cstatus;
+   int               intrstatus;
    u32               cpu_flags;
 
-   DBG("ips_intr");
+   METHOD_TRACE("ips_intr", 2);
 
    if (!ha)
       return;
@@ -1384,16 +1805,36 @@
       return;
 
    IPS_HA_LOCK(cpu_flags);
-   while (ips_isintr(ha)) {
+
+   intrstatus = (*ha->func.isintr)(ha);
+
+   if (!intrstatus) {
+      /*
+       * Unexpected/Shared interrupt
+       */
+      IPS_HA_UNLOCK(cpu_flags);
+
+      return;
+   }
+
+   while (TRUE) {
       sp = &ha->sp;
 
-      if ((status = ips_chkstatus(ha)) < 0) {
-         /* unexpected interrupt - no ccb */
+      intrstatus = (*ha->func.isintr)(ha);
+
+      if (!intrstatus)
+         break;
+      else
+         cstatus.value = (*ha->func.statupd)(ha);
+
+      if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) {
          printk(KERN_WARNING "(%s%d) Spurious interrupt; no ccb.\n",
                 ips_name, ha->host_num);
-         continue ;
+
+         continue;
       }
 
+      ips_chkstatus(ha, &cstatus);
       scb = (ips_scb_t *) sp->scb_addr;
 
       /*
@@ -1403,27 +1844,103 @@
       IPS_HA_UNLOCK(cpu_flags);
       (*scb->callback) (ha, scb);
       IPS_HA_LOCK(cpu_flags);
-   }
+   } /* end while */
 
    IPS_HA_UNLOCK(cpu_flags);
 }
 
 /****************************************************************************/
 /*                                                                          */
-/* Routine Name: ips_info                                                   */
+/* Routine Name: ips_intr_morpheus                                          */
 /*                                                                          */
 /* Routine Description:                                                     */
 /*                                                                          */
-/*   Return info about the driver                                           */
+/*   Polling interrupt handler                                              */
+/*                                                                          */
+/*   ASSUMES interrupts are disabled                                        */
 /*                                                                          */
 /****************************************************************************/
-const char *
-ips_info(struct Scsi_Host *SH) {
-   static char  buffer[256];
-   char        *bp;
-   ips_ha_t    *ha;
-
-   DBG("ips_info");
+void
+ips_intr_morpheus(ips_ha_t *ha) {
+   ips_stat_t       *sp;
+   ips_scb_t        *scb;
+   IPS_STATUS        cstatus;
+   int               intrstatus;
+   u32               cpu_flags;
+
+   METHOD_TRACE("ips_intr_morpheus", 2);
+
+   if (!ha)
+      return;
+
+   if (!ha->active)
+      return;
+
+   IPS_HA_LOCK(cpu_flags);
+
+   intrstatus = (*ha->func.isintr)(ha);
+
+   if (!intrstatus) {
+      /*
+       * Unexpected/Shared interrupt
+       */
+      IPS_HA_UNLOCK(cpu_flags);
+
+      return;
+   }
+
+   while (TRUE) {
+      sp = &ha->sp;
+
+      intrstatus = (*ha->func.isintr)(ha);
+
+      if (!intrstatus)
+         break;
+      else
+         cstatus.value = (*ha->func.statupd)(ha);
+
+      if (cstatus.value == 0xffffffff)
+         /* No more to process */
+         break;
+
+      if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) {
+         printk(KERN_WARNING "(%s%d) Spurious interrupt; no ccb.\n",
+                ips_name, ha->host_num);
+
+         continue;
+      }
+
+      ips_chkstatus(ha, &cstatus);
+      scb = (ips_scb_t *) sp->scb_addr;
+
+      /*
+       * use the callback function to finish things up
+       * NOTE: interrupts are OFF for this
+       */
+      IPS_HA_UNLOCK(cpu_flags);
+      (*scb->callback) (ha, scb);
+      IPS_HA_LOCK(cpu_flags);
+   } /* end while */
+
+   IPS_HA_UNLOCK(cpu_flags);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_info                                                   */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Return info about the driver                                           */
+/*                                                                          */
+/****************************************************************************/
+const char *
+ips_info(struct Scsi_Host *SH) {
+   static char  buffer[256];
+   char        *bp;
+   ips_ha_t    *ha;
+
+   METHOD_TRACE("ips_info", 1);
 
    ha = IPS_HA(SH);
 
@@ -1463,10 +1980,10 @@
    int           ret;
    ips_ha_t     *ha = NULL;
 
-   DBG("ips_proc_info");
+   METHOD_TRACE("ips_proc_info", 1);
 
    /* Find our host structure */
-   for (i = 0; i < ips_num_controllers; i++) {
+   for (i = 0; i < ips_next_controller; i++) {
       if (ips_sh[i] && ips_sh[i]->host_no == hostno) {
          ha = (ips_ha_t *) ips_sh[i]->hostdata;
 
@@ -1508,7 +2025,7 @@
 /****************************************************************************/
 static int
 ips_is_passthru(Scsi_Cmnd *SC) {
-   DBG("ips_is_passthru");
+   METHOD_TRACE("ips_is_passthru", 1);
 
    if (!SC)
       return (0);
@@ -1542,24 +2059,20 @@
 ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb) {
    ips_passthru_t *pt;
 
-   DBG("ips_make_passthru");
+   METHOD_TRACE("ips_make_passthru", 1);
 
    if (!SC->request_bufflen || !SC->request_buffer) {
       /* no data */
-#if IPS_DEBUG_PT >= 1
-      printk(KERN_NOTICE "(%s%d) No passthru structure\n",
-             ips_name, ha->host_num);
-#endif
+      DEBUG_VAR(1, "(%s%d) No passthru structure",
+                ips_name, ha->host_num);
 
       return (IPS_FAILURE);
    }
 
    if (SC->request_bufflen < sizeof(ips_passthru_t)) {
       /* wrong size */
-#if IPS_DEBUG_PT >= 1
-      printk(KERN_NOTICE "(%s%d) Passthru structure wrong size\n",
+      DEBUG_VAR(1, "(%s%d) Passthru structure wrong size",
              ips_name, ha->host_num);
-#endif
 
       return (IPS_FAILURE);
    }
@@ -1569,10 +2082,8 @@
        (((char *) SC->request_buffer)[2] != 'P') ||
        (((char *) SC->request_buffer)[3] != 'P')) {
       /* signature doesn't match */
-#if IPS_DEBUG_PT >= 1
-      printk(KERN_NOTICE "(%s%d) Wrong signature on passthru structure.\n",
-             ips_name, ha->host_num);
-#endif
+      DEBUG_VAR(1, "(%s%d) Wrong signature on passthru structure.",
+                ips_name, ha->host_num);
 
       return (IPS_FAILURE);
    }
@@ -1616,10 +2127,8 @@
       if (SC->cmnd[0] == IPS_IOCTL_COMMAND) {
          if (SC->request_bufflen < (sizeof(ips_passthru_t) + pt->CmdBSize)) {
             /* wrong size */
-   #if IPS_DEBUG_PT >= 1
-            printk(KERN_NOTICE "(%s%d) Passthru structure wrong size\n",
-                   ips_name, ha->host_num);
-   #endif
+            DEBUG_VAR(1, "(%s%d) Passthru structure wrong size",
+                      ips_name, ha->host_num);
 
             return (IPS_FAILURE);
          }
@@ -1631,10 +2140,8 @@
       } else if (SC->cmnd[0] == IPS_IOCTL_NEW_COMMAND) {
          if (SC->request_bufflen < (sizeof(ips_passthru_t))) {
             /* wrong size */
-   #if IPS_DEBUG_PT >= 1
-            printk(KERN_NOTICE "(%s%d) Passthru structure wrong size\n",
-                   ips_name, ha->host_num);
-   #endif
+            DEBUG_VAR(1, "(%s%d) Passthru structure wrong size",
+                      ips_name, ha->host_num);
 
             return (IPS_FAILURE);
          }
@@ -1653,14 +2160,23 @@
          return (IPS_FAILURE);
 
       /* don't flash the BIOS on future cards */
-      if (ha->revision_id > IPS_REVID_TROMBONE64) {
-#if IPS_DEBUG_PT >= 1
-         printk(KERN_NOTICE "(%s%d) flash bios failed - unsupported controller\n",
-                ips_name, ha->host_num);
-#endif
+      if ((ha->device_id != IPS_COPPERHEAD_DEVICEID) ||
+          (ha->revision_id > IPS_REVID_TROMBONE64)) {
+         DEBUG_VAR(1, "(%s%d) flash bios failed - unsupported controller",
+                   ips_name, ha->host_num);
+
          return (IPS_FAILURE);
       }
 
+      /*
+       * Check to make sure we have functions
+       * to handle the request
+       */
+      if ((!ha->func.programbios) ||
+          (!ha->func.erasebios) ||
+          (!ha->func.verifybios))
+         return (IPS_FAILURE);
+
       /* copy in the size/buffer ptr from the scsi command */
       memcpy(&pt->CmdBuffer, &SC->cmnd[4], 4);
       memcpy(&pt->CmdBSize, &SC->cmnd[8], 4);
@@ -1674,7 +2190,7 @@
          void *bigger_struct;
 
          /* try to allocate a bigger struct */
-         bigger_struct = kmalloc(pt->CmdBSize, GFP_KERNEL|GFP_DMA);
+         bigger_struct = kmalloc(pt->CmdBSize, GFP_ATOMIC|GFP_DMA);
          if (bigger_struct) {
             /* free the old memory */
             kfree(ha->ioctl_data);
@@ -1688,37 +2204,29 @@
 
       /* copy in the buffer */
       if (copy_from_user(ha->ioctl_data, pt->CmdBuffer, pt->CmdBSize) > 0) {
-#if IPS_DEBUG_PT >= 1
-         printk(KERN_NOTICE "(%s%d) flash bios failed - unable to copy user buffer\n",
-                ips_name, ha->host_num);
-#endif
+         DEBUG_VAR(1, "(%s%d) flash bios failed - unable to copy user buffer",
+                   ips_name, ha->host_num);
 
          return (IPS_FAILURE);
       }
 
-      if (ips_erase_bios(ha)) {
-#if IPS_DEBUG_PT >= 1
-         printk(KERN_NOTICE "(%s%d) flash bios failed - unable to erase flash\n",
-                ips_name, ha->host_num);
-#endif
+      if ((*ha->func.erasebios)(ha)) {
+         DEBUG_VAR(1, "(%s%d) flash bios failed - unable to erase flash",
+                   ips_name, ha->host_num);
 
          return (IPS_FAILURE);
       }
 
-      if (ips_program_bios(ha, ha->ioctl_data, pt->CmdBSize)) {
-#if IPS_DEBUG_PT >= 1
-         printk(KERN_NOTICE "(%s%d) flash bios failed - unable to program flash\n",
-                ips_name, ha->host_num);
-#endif
+      if ((*ha->func.programbios)(ha, ha->ioctl_data, pt->CmdBSize)) {
+         DEBUG_VAR(1, "(%s%d) flash bios failed - unable to program flash",
+                   ips_name, ha->host_num);
 
          return (IPS_FAILURE);
       }
 
-      if (ips_verify_bios(ha, ha->ioctl_data, pt->CmdBSize)) {
-#if IPS_DEBUG_PT >= 1
-         printk(KERN_NOTICE "(%s%d) flash bios failed - unable to verify flash\n",
-                ips_name, ha->host_num);
-#endif
+      if ((*ha->func.verifybios)(ha, ha->ioctl_data, pt->CmdBSize)) {
+         DEBUG_VAR(1, "(%s%d) flash bios failed - unable to verify flash",
+                   ips_name, ha->host_num);
 
          return (IPS_FAILURE);
       }
@@ -1742,7 +2250,7 @@
 ips_usrcmd(ips_ha_t *ha, ips_passthru_t *pt, ips_scb_t *scb) {
    IPS_SG_LIST *sg_list;
 
-   DBG("ips_usrcmd");
+   METHOD_TRACE("ips_usrcmd", 1);
 
    if ((!scb) || (!pt) || (!ha))
       return (0);
@@ -1825,7 +2333,7 @@
    char           *kern_area;
    u32             datasize;
 
-   DBG("ips_usrcmd");
+   METHOD_TRACE("ips_usrcmd", 1);
 
    if ((!scb) || (!pt) || (!ha))
       return (0);
@@ -1862,7 +2370,7 @@
          void *bigger_struct;
 
          /* try to allocate a bigger struct */
-         bigger_struct = kmalloc(pt->CmdBSize, GFP_KERNEL|GFP_DMA);
+         bigger_struct = kmalloc(pt->CmdBSize, GFP_ATOMIC|GFP_DMA);
          if (bigger_struct) {
             /* free the old memory */
             kfree(ha->ioctl_data);
@@ -1883,10 +2391,8 @@
       datasize = *((u32 *) &scb->scsi_cmd->cmnd[8]);
 
       if (copy_from_user(kern_area, user_area, datasize) > 0) {
-#if IPS_DEBUG_PT >= 1
-         printk(KERN_NOTICE "(%s%d) passthru failed - unable to copy in user data\n",
-                ips_name, ha->host_num);
-#endif
+         DEBUG_VAR(1, "(%s%d) passthru failed - unable to copy in user data",
+                   ips_name, ha->host_num);
 
          return (0);
       }
@@ -1937,12 +2443,11 @@
 ips_cleanup_passthru(ips_ha_t *ha, ips_scb_t *scb) {
    ips_passthru_t *pt;
 
-   DBG("ips_cleanup_passthru");
+   METHOD_TRACE("ips_cleanup_passthru", 1);
 
    if ((!scb) || (!scb->scsi_cmd) || (!scb->scsi_cmd->request_buffer)) {
-#if IPS_DEBUG_PT >= 1
-      printk(KERN_NOTICE "IPS couldn't cleanup\n");
-#endif
+      DEBUG_VAR(1, "(%s%d) couldn't cleanup after passthru",
+                ips_name, ha->host_num);
 
       return ;
    }
@@ -1976,12 +2481,13 @@
 ips_host_info(ips_ha_t *ha, char *ptr, off_t offset, int len) {
    IPS_INFOSTR info;
 
-   DBG("ips_host_info");
+   METHOD_TRACE("ips_host_info", 1);
 
    info.buffer = ptr;
    info.length = len;
    info.offset = offset;
    info.pos = 0;
+   info.localpos = 0;
 
    copy_info(&info, "\nIBM ServeRAID General Information:\n\n");
 
@@ -1991,7 +2497,16 @@
    else
       copy_info(&info, "\tController Type                   : Unknown\n");
 
-   copy_info(&info, "\tIO port address                   : 0x%lx\n", ha->io_addr);
+   if (ha->io_addr)
+      copy_info(&info, "\tIO region                         : 0x%lx (%d bytes)\n",
+                ha->io_addr, ha->io_len);
+
+   if (ha->mem_addr) {
+      copy_info(&info, "\tMemory region                     : 0x%lx (%d bytes)\n",
+                ha->mem_addr, ha->mem_len);
+      copy_info(&info, "\tShared memory address             : 0x%lx\n", ha->mem_ptr);
+   }
+
    copy_info(&info, "\tIRQ number                        : %d\n", ha->irq);
 
    if (ha->nvram->signature == IPS_NVRAM_P5_SIG)
@@ -2031,7 +2546,7 @@
 
    copy_info(&info, "\n");
 
-   return (info.pos > info.offset ? info.pos - info.offset : 0);
+   return (info.localpos);
 }
 
 /****************************************************************************/
@@ -2045,10 +2560,7 @@
 /****************************************************************************/
 static void
 copy_mem_info(IPS_INFOSTR *info, char *data, int len) {
-   DBG("copy_mem_info");
-
-   if (info->pos + len > info->length)
-      len = info->length - info->pos;
+   METHOD_TRACE("copy_mem_info", 1);
 
    if (info->pos + len < info->offset) {
       info->pos += len;
@@ -2057,12 +2569,17 @@
 
    if (info->pos < info->offset) {
       data += (info->offset - info->pos);
-      len  -= (info->offset - info->pos);
+      len -= (info->offset - info->pos);
+      info->pos += (info->offset - info->pos);
    }
 
+   if (info->localpos + len > info->length)
+      len = info->length - info->localpos;
+
    if (len > 0) {
-      memcpy(info->buffer + info->pos, data, len);
+      memcpy(info->buffer + info->localpos, data, len);
       info->pos += len;
+      info->localpos += len;
    }
 }
 
@@ -2078,10 +2595,10 @@
 static int
 copy_info(IPS_INFOSTR *info, char *fmt, ...) {
    va_list args;
-   char buf[81];
+   char buf[128];
    int len;
 
-   DBG("copy_info");
+   METHOD_TRACE("copy_info", 1);
 
    va_start(args, fmt);
    len = vsprintf(buf, fmt, args);
@@ -2105,57 +2622,32 @@
 /****************************************************************************/
 static int
 ips_hainit(ips_ha_t *ha) {
-   int       i;
+   int            i;
+   struct timeval tv;
 
-   DBG("ips_hainit");
+   METHOD_TRACE("ips_hainit", 1);
 
    if (!ha)
       return (0);
 
-   /* initialize status queue */
-   ips_statinit(ha);
+   if (ha->func.statinit)
+      (*ha->func.statinit)(ha);
+
+   if (ha->func.enableint)
+      (*ha->func.enableint)(ha);
 
+   /* Send FFDC */
    ha->reset_count = 1;
+   do_gettimeofday(&tv);
+   ha->last_ffdc = tv.tv_sec;
+   ips_ffdc_reset(ha, IPS_INTR_IORL);
 
-   /* Setup HBA ID's */
    if (!ips_read_config(ha, IPS_INTR_IORL)) {
-
-#ifndef NO_IPS_RESET
-
-      ha->reset_count++;
-
-      /* Try to reset the controller and try again */
-      if (!ips_reset_adapter(ha)) {
-         printk(KERN_WARNING "(%s%d) unable to reset controller.\n",
-                ips_name, ha->host_num);
-
-         return (0);
-      }
-
-      if (!ips_clear_adapter(ha, IPS_INTR_IORL)) {
-         printk(KERN_WARNING "(%s%d) unable to initialize controller.\n",
-                ips_name, ha->host_num);
-
-         return (0);
-      }
-
-#endif
-
-      if (!ips_read_config(ha, IPS_INTR_IORL)) {
-         printk(KERN_WARNING "(%s%d) unable to read config from controller.\n",
-                ips_name, ha->host_num);
-
-         return (0);
-      }
-   } /* end if */
-
-   /* write driver version */
-   if (!ips_write_driver_status(ha, IPS_INTR_IORL)) {
-      printk(KERN_WARNING "(%s%d) unable to write driver info to controller.\n",
+      printk(KERN_WARNING "(%s%d) unable to read config from controller.\n",
              ips_name, ha->host_num);
 
       return (0);
-   }
+   } /* end if */
 
    if (!ips_read_adapter_status(ha, IPS_INTR_IORL)) {
       printk(KERN_WARNING "(%s%d) unable to read controller status.\n",
@@ -2171,19 +2663,18 @@
       return (0);
    }
 
-   /* FFDC */
-   if (ha->subsys->param[3] & 0x300000) {
-      struct timeval tv;
+   /* write nvram user page 5 */
+   if (!ips_write_driver_status(ha, IPS_INTR_IORL)) {
+      printk(KERN_WARNING "(%s%d) unable to write driver info to controller.\n",
+             ips_name, ha->host_num);
 
-      do_gettimeofday(&tv);
-      ha->last_ffdc = tv.tv_sec;
-      ips_ffdc_reset(ha, IPS_INTR_IORL);
+      return (0);
    }
 
    /* set limits on SID, LUN, BUS */
    ha->ntargets = IPS_MAX_TARGETS + 1;
    ha->nlun = 1;
-   ha->nbus = (ha->enq->ucMaxPhysicalDevices / IPS_MAX_TARGETS);
+   ha->nbus = (ha->enq->ucMaxPhysicalDevices / IPS_MAX_TARGETS) + 1;
 
    switch (ha->conf->logical_drive[0].ucStripeSize) {
    case 4:
@@ -2254,13 +2745,14 @@
    ips_scb_t            *scb;
    Scsi_Cmnd            *SC;
    Scsi_Cmnd            *p;
+   Scsi_Cmnd            *q;
    ips_copp_wait_item_t *item;
    int                   ret;
    int                   intr_status;
    u32                   cpu_flags;
    u32                   cpu_flags2;
 
-   DBG("ips_next");
+   METHOD_TRACE("ips_next", 1);
 
    if (!ha)
       return ;
@@ -2406,7 +2898,8 @@
 
       IPS_HA_UNLOCK(cpu_flags);
 
-      SC = ips_removeq_wait(&ha->scb_waitlist, p);
+      q = p;
+      SC = ips_removeq_wait(&ha->scb_waitlist, q);
 
       SC->result = DID_OK;
       SC->host_scribble = NULL;
@@ -2433,28 +2926,41 @@
 
          sg = SC->request_buffer;
 
-         for (i = 0; i < SC->use_sg; i++) {
-            scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address);
-            scb->sg_list[i].length = sg[i].length;
+         if (SC->use_sg == 1) {
+            if (sg[0].length > ha->max_xfer) {
+               scb->breakup = 1;
+               scb->data_len = ha->max_xfer;
+            } else
+               scb->data_len = sg[0].length;
 
-            if (scb->data_len + sg[i].length > ha->max_xfer) {
-               /*
-                * Data Breakup required
-                */
-               scb->breakup = i;
-               break;
-            }
+            scb->dcdb.transfer_length = scb->data_len;
+            scb->data_busaddr = VIRT_TO_BUS(sg[0].address);
+            scb->sg_len = 0;
+         } else {
 
-            scb->data_len += sg[i].length;
-         }
+            for (i = 0; i < SC->use_sg; i++) {
+               scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address);
+               scb->sg_list[i].length = sg[i].length;
 
-         if (!scb->breakup)
-            scb->sg_len = SC->use_sg;
-         else
-            scb->sg_len = scb->breakup;
+               if (scb->data_len + sg[i].length > ha->max_xfer) {
+                  /*
+                   * Data Breakup required
+                   */
+                  scb->breakup = i;
+                  break;
+               }
+
+               scb->data_len += sg[i].length;
+            }
+
+            if (!scb->breakup)
+               scb->sg_len = SC->use_sg;
+            else
+               scb->sg_len = scb->breakup;
 
-         scb->dcdb.transfer_length = scb->data_len;
-         scb->data_busaddr = VIRT_TO_BUS(scb->sg_list);
+            scb->dcdb.transfer_length = scb->data_len;
+            scb->data_busaddr = VIRT_TO_BUS(scb->sg_list);
+         }
       } else {
          if (SC->request_bufflen) {
             if (SC->request_bufflen > ha->max_xfer) {
@@ -2541,7 +3047,7 @@
 /****************************************************************************/
 static inline void
 ips_putq_scb_head(ips_scb_queue_t *queue, ips_scb_t *item) {
-   DBG("ips_putq_scb_head");
+   METHOD_TRACE("ips_putq_scb_head", 1);
 
    if (!item)
       return ;
@@ -2572,7 +3078,7 @@
 /****************************************************************************/
 static inline void
 ips_putq_scb_tail(ips_scb_queue_t *queue, ips_scb_t *item) {
-   DBG("ips_putq_scb_tail");
+   METHOD_TRACE("ips_putq_scb_tail", 1);
 
    if (!item)
       return ;
@@ -2609,7 +3115,7 @@
 ips_removeq_scb_head(ips_scb_queue_t *queue) {
    ips_scb_t  *item;
 
-   DBG("ips_removeq_scb_head");
+   METHOD_TRACE("ips_removeq_scb_head", 1);
 
    IPS_QUEUE_LOCK(queue);
 
@@ -2649,7 +3155,7 @@
 ips_removeq_scb(ips_scb_queue_t *queue, ips_scb_t *item) {
    ips_scb_t  *p;
 
-   DBG("ips_removeq_scb");
+   METHOD_TRACE("ips_removeq_scb", 1);
 
    if (!item)
       return (NULL);
@@ -2700,7 +3206,7 @@
 /****************************************************************************/
 static inline void
 ips_putq_wait_head(ips_wait_queue_t *queue, Scsi_Cmnd *item) {
-   DBG("ips_putq_wait_head");
+   METHOD_TRACE("ips_putq_wait_head", 1);
 
    if (!item)
       return ;
@@ -2731,7 +3237,7 @@
 /****************************************************************************/
 static inline void
 ips_putq_wait_tail(ips_wait_queue_t *queue, Scsi_Cmnd *item) {
-   DBG("ips_putq_wait_tail");
+   METHOD_TRACE("ips_putq_wait_tail", 1);
 
    if (!item)
       return ;
@@ -2768,7 +3274,7 @@
 ips_removeq_wait_head(ips_wait_queue_t *queue) {
    Scsi_Cmnd  *item;
 
-   DBG("ips_removeq_wait_head");
+   METHOD_TRACE("ips_removeq_wait_head", 1);
 
    IPS_QUEUE_LOCK(queue);
 
@@ -2808,7 +3314,7 @@
 ips_removeq_wait(ips_wait_queue_t *queue, Scsi_Cmnd *item) {
    Scsi_Cmnd  *p;
 
-   DBG("ips_removeq_wait");
+   METHOD_TRACE("ips_removeq_wait", 1);
 
    if (!item)
       return (NULL);
@@ -2859,7 +3365,7 @@
 /****************************************************************************/
 static inline void
 ips_putq_copp_head(ips_copp_queue_t *queue, ips_copp_wait_item_t *item) {
-   DBG("ips_putq_copp_head");
+   METHOD_TRACE("ips_putq_copp_head", 1);
 
    if (!item)
       return ;
@@ -2890,7 +3396,7 @@
 /****************************************************************************/
 static inline void
 ips_putq_copp_tail(ips_copp_queue_t *queue, ips_copp_wait_item_t *item) {
-   DBG("ips_putq_copp_tail");
+   METHOD_TRACE("ips_putq_copp_tail", 1);
 
    if (!item)
       return ;
@@ -2927,7 +3433,7 @@
 ips_removeq_copp_head(ips_copp_queue_t *queue) {
    ips_copp_wait_item_t *item;
 
-   DBG("ips_removeq_copp_head");
+   METHOD_TRACE("ips_removeq_copp_head", 1);
 
    IPS_QUEUE_LOCK(queue);
 
@@ -2967,7 +3473,7 @@
 ips_removeq_copp(ips_copp_queue_t *queue, ips_copp_wait_item_t *item) {
    ips_copp_wait_item_t *p;
 
-   DBG("ips_removeq_copp");
+   METHOD_TRACE("ips_removeq_copp", 1);
 
    if (!item)
       return (NULL);
@@ -3016,7 +3522,7 @@
 /****************************************************************************/
 static void
 ipsintr_blocking(ips_ha_t *ha, ips_scb_t *scb) {
-   DBG("ipsintr_blocking");
+   METHOD_TRACE("ipsintr_blocking", 2);
 
    if ((ha->waitflag == TRUE) &&
        (ha->cmd_in_progress == scb->cdb[0])) {
@@ -3037,7 +3543,14 @@
 /****************************************************************************/
 static void
 ipsintr_done(ips_ha_t *ha, ips_scb_t *scb) {
-   DBG("ipsintr_done");
+   METHOD_TRACE("ipsintr_done", 2);
+
+   if (!scb) {
+      printk(KERN_WARNING "(%s%d) Spurious interrupt; scb NULL.\n",
+             ips_name, ha->host_num);
+
+      return ;
+   }
 
    if (scb->scsi_cmd == NULL) {
       /* unexpected interrupt */
@@ -3064,7 +3577,7 @@
    int ret;
    u32 cpu_flags;
 
-   DBG("ips_done");
+   METHOD_TRACE("ips_done", 1);
 
    if (!scb)
       return ;
@@ -3096,31 +3609,46 @@
 
             sg = scb->scsi_cmd->request_buffer;
 
-            scb->data_len = 0;
-
-            for (i = bk_save; i < scb->scsi_cmd->use_sg; i++) {
-               scb->sg_list[i - bk_save].address = VIRT_TO_BUS(sg[i].address);
-               scb->sg_list[i - bk_save].length = sg[i].length;
-
-               if (scb->data_len + sg[i].length > ha->max_xfer) {
-                  /*
-                   * Data Breakup required
-                   */
-                  scb->breakup = i;
-                  break;
+            if (scb->scsi_cmd->use_sg == 1) {
+               if (sg[0].length - (bk_save * ha->max_xfer)) {
+                  /* Further breakup required */
+                  scb->data_len = ha->max_xfer;
+                  scb->data_busaddr = VIRT_TO_BUS(sg[0].address + (bk_save * ha->max_xfer));
+                  scb->breakup = bk_save + 1;
+               } else {
+                  scb->data_len = sg[0].length - (bk_save * ha->max_xfer);
+                  scb->data_busaddr = VIRT_TO_BUS(sg[0].address + (bk_save * ha->max_xfer));
                }
 
-               scb->data_len += sg[i].length;
-            }
+               scb->dcdb.transfer_length = scb->data_len;
+               scb->sg_len = 0;
+            } else {
+               scb->data_len = 0;
 
-            if (!scb->breakup)
-               scb->sg_len = scb->scsi_cmd->use_sg - bk_save;
-            else
-               scb->sg_len = scb->breakup - bk_save;
+               for (i = bk_save; i < scb->scsi_cmd->use_sg; i++) {
+                  scb->sg_list[i - bk_save].address = VIRT_TO_BUS(sg[i].address);
+                  scb->sg_list[i - bk_save].length = sg[i].length;
+
+                  if (scb->data_len + sg[i].length > ha->max_xfer) {
+                     /*
+                      * Data Breakup required
+                      */
+                     scb->breakup = i;
+                     break;
+                  }
 
-            scb->dcdb.transfer_length = scb->data_len;
-            scb->data_busaddr = VIRT_TO_BUS(scb->sg_list);
-         } else {
+                  scb->data_len += sg[i].length;
+               }
+
+               if (!scb->breakup)
+                  scb->sg_len = scb->scsi_cmd->use_sg - bk_save;
+               else
+                  scb->sg_len = scb->breakup - bk_save;
+
+               scb->dcdb.transfer_length = scb->data_len;
+               scb->data_busaddr = VIRT_TO_BUS(scb->sg_list);
+           }
+        } else {
             /* Non S/G Request */
             if (scb->scsi_cmd->request_bufflen - (bk_save * ha->max_xfer)) {
                /* Further breakup required */
@@ -3199,31 +3727,29 @@
 /*                                                                          */
 /****************************************************************************/
 static int
-ips_map_status(ips_scb_t *scb, ips_stat_t *sp) {
+ips_map_status(ips_ha_t *ha, ips_scb_t *scb, ips_stat_t *sp) {
    int       errcode;
+   int       device_error;
 
-   DBG("ips_map_status");
+   METHOD_TRACE("ips_map_status", 1);
 
    if (scb->bus) {
-#if IPS_DEBUG >= 10
-      printk(KERN_NOTICE "(%s) Physical device error: %x %x, Sense Key: %x, ASC: %x, ASCQ: %x\n",
-             ips_name,
-             scb->basic_status,
-             scb->extended_status,
-             scb->dcdb.sense_info[2] & 0xf,
-             scb->dcdb.sense_info[12],
-             scb->dcdb.sense_info[13]);
-#endif
-
-      /* copy SCSI status and sense data for DCDB commands */
-      memcpy(scb->scsi_cmd->sense_buffer, scb->dcdb.sense_info,
-             sizeof(scb->scsi_cmd->sense_buffer));
-      scb->scsi_cmd->result = scb->dcdb.scsi_status;
-   } else
-      scb->scsi_cmd->result = 0;
+      DEBUG_VAR(2, "(%s%d) Physical device error (%d %d %d): %x %x, Sense Key: %x, ASC: %x, ASCQ: %x",
+                ips_name,
+                ha->host_num,
+                scb->scsi_cmd->channel,
+                scb->scsi_cmd->target,
+                scb->scsi_cmd->lun,
+                scb->basic_status,
+                scb->extended_status,
+                scb->extended_status == IPS_ERR_CKCOND ? scb->dcdb.sense_info[2] & 0xf : 0,
+                scb->extended_status == IPS_ERR_CKCOND ? scb->dcdb.sense_info[12] : 0,
+                scb->extended_status == IPS_ERR_CKCOND ? scb->dcdb.sense_info[13] : 0);
+   }
 
    /* default driver error */
    errcode = DID_ERROR;
+   device_error = 0;
 
    switch (scb->basic_status & IPS_GSC_STATUS_MASK) {
    case IPS_CMD_TIMEOUT:
@@ -3238,25 +3764,19 @@
       break;
 
    case IPS_PHYS_DRV_ERROR:
-      /*
-       * For physical drive errors that
-       * are not on a logical drive should
-       * be DID_OK.  The SCSI errcode will
-       * show what the real error is.
-       */
-      if (scb->bus)
-         errcode = DID_OK;
-
       switch (scb->extended_status) {
       case IPS_ERR_SEL_TO:
-         if (scb->bus) {
-            scb->scsi_cmd->result |= DID_TIME_OUT << 16;
+         if (scb->bus)
+            errcode = DID_NO_CONNECT;
 
-            return (0);
-         }
          break;
+
       case IPS_ERR_OU_RUN:
          if ((scb->bus) && (scb->dcdb.transfer_length < scb->data_len)) {
+            /* Underrun - set default to no error */
+            errcode = DID_OK;
+
+            /* Restrict access to physical DASD */
             if ((scb->scsi_cmd->cmnd[0] == INQUIRY) &&
                 ((((char *) scb->scsi_cmd->buffer)[0] & 0x1f) == TYPE_DISK)) {
                /* underflow -- no error               */
@@ -3264,23 +3784,16 @@
                errcode = DID_TIME_OUT;
                break;
             }
-
-            /* normal underflow Occured */
-            if (scb->dcdb.transfer_length >= scb->scsi_cmd->underflow) {
-               scb->scsi_cmd->result |= DID_OK << 16;
-
-               return (0);
-            }
-         }
+         } else
+            errcode = DID_ERROR;
 
          break;
+
       case IPS_ERR_RECOVERY:
          /* don't fail recovered errors */
-         if (scb->bus) {
-            scb->scsi_cmd->result |= DID_OK << 16;
+         if (scb->bus)
+            errcode = DID_OK;
 
-            return (0);
-         }
          break;
 
       case IPS_ERR_HOST_RESET:
@@ -3289,11 +3802,25 @@
          break;
 
       case IPS_ERR_CKCOND:
+         if (scb->bus) {
+            memcpy(scb->scsi_cmd->sense_buffer, scb->dcdb.sense_info,
+                   sizeof(scb->scsi_cmd->sense_buffer));
+
+            device_error = 2; /* check condition */
+         }
+
+         errcode = DID_OK;
+
+         break;
+
+      default:
+         errcode = DID_ERROR;
          break;
+
       } /* end switch */
    } /* end switch */
 
-   scb->scsi_cmd->result |= (errcode << 16);
+   scb->scsi_cmd->result = device_error | (errcode << 16);
 
    return (1);
 }
@@ -3311,7 +3838,7 @@
 ips_send(ips_ha_t *ha, ips_scb_t *scb, ips_scb_callback callback) {
    int ret;
 
-   DBG("ips_send");
+   METHOD_TRACE("ips_send", 1);
 
    scb->callback = callback;
 
@@ -3333,7 +3860,7 @@
 ips_send_wait(ips_ha_t *ha, ips_scb_t *scb, int timeout, int intr) {
    int       ret;
 
-   DBG("ips_send_wait");
+   METHOD_TRACE("ips_send_wait", 1);
 
    ha->waitflag = TRUE;
    ha->cmd_in_progress = scb->cdb[0];
@@ -3361,7 +3888,7 @@
 ips_send_cmd(ips_ha_t *ha, ips_scb_t *scb) {
    int       ret;
 
-   DBG("ips_send_cmd");
+   METHOD_TRACE("ips_send_cmd", 1);
 
    ret = IPS_SUCCESS;
 
@@ -3590,7 +4117,7 @@
       memcpy(scb->dcdb.scsi_cdb, scb->scsi_cmd->cmnd, scb->scsi_cmd->cmd_len);
    }
 
-   return (ips_issue(ha, scb));
+   return ((*ha->func.issue)(ha, scb));
 }
 
 /****************************************************************************/
@@ -3602,60 +4129,57 @@
 /*   Check the status of commands to logical drives                         */
 /*                                                                          */
 /****************************************************************************/
-static int
-ips_chkstatus(ips_ha_t *ha) {
+static void
+ips_chkstatus(ips_ha_t *ha, IPS_STATUS *pstatus) {
    ips_scb_t  *scb;
-   ips_stat_t *sp = &ha->sp;
+   ips_stat_t *sp;
    u8          basic_status;
    u8          ext_status;
-   int         command_id;
    int         errcode;
-   int         ret;
 
-   DBG("ips_chkstatus");
+   METHOD_TRACE("ips_chkstatus", 1);
 
-   command_id = ips_statupd(ha);
+   scb = &ha->scbs[pstatus->fields.command_id];
+   scb->basic_status = basic_status = pstatus->fields.basic_status & IPS_BASIC_STATUS_MASK;
+   scb->extended_status = ext_status = pstatus->fields.extended_status;
 
-   if (command_id > (IPS_MAX_CMDS-1)) {
-      printk(KERN_NOTICE "(%s%d) invalid command id received: %d\n",
-             ips_name, ha->host_num, command_id);
-
-      return (-1);
-   }
-
-   scb = &ha->scbs[command_id];
-   sp->scb_addr = (u32) scb;
+   sp = &ha->sp;
    sp->residue_len = 0;
-   scb->basic_status = basic_status = ha->adapt->p_status_tail->basic_status & IPS_BASIC_STATUS_MASK;
-   scb->extended_status = ext_status = ha->adapt->p_status_tail->extended_status;
+   sp->scb_addr = (u32) scb;
 
    /* Remove the item from the active queue */
    ips_removeq_scb(&ha->scb_activelist, scb);
 
    if (!scb->scsi_cmd)
       /* internal commands are handled in do_ipsintr */
-      return (0);
+      return ;
+
+   DEBUG_VAR(2, "(%s%d) ips_chkstatus: cmd 0x%X id %d (%d %d %d)",
+             ips_name,
+             ha->host_num,
+             scb->cdb[0],
+             scb->cmd.basic_io.command_id,
+             scb->bus,
+             scb->target_id,
+             scb->lun);
 
 #ifndef NO_IPS_CMDLINE
    if ((scb->scsi_cmd) && (ips_is_passthru(scb->scsi_cmd)))
       /* passthru - just returns the raw result */
-      return (0);
+      return ;
 #endif
 
    errcode = DID_OK;
-   ret = 0;
 
    if (((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_SUCCESS) ||
        ((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_RECOVERED_ERROR)) {
 
       if (scb->bus == 0) {
-#if IPS_DEBUG >= 1
          if ((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_RECOVERED_ERROR) {
-            printk(KERN_NOTICE "(%s%d) Recovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x\n",
-                   ips_name, ha->host_num,
-                   scb->cmd.basic_io.op_code, basic_status, ext_status);
+            DEBUG_VAR(1, "(%s%d) Recovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x",
+                      ips_name, ha->host_num,
+                      scb->cmd.basic_io.op_code, basic_status, ext_status);
          }
-#endif
 
          switch (scb->scsi_cmd->cmnd[0]) {
          case ALLOW_MEDIUM_REMOVAL:
@@ -3664,7 +4188,6 @@
          case WRITE_FILEMARKS:
          case SPACE:
             errcode = DID_ERROR;
-            ret = 1;
             break;
 
          case START_STOP:
@@ -3673,7 +4196,6 @@
          case TEST_UNIT_READY:
             if (!ips_online(ha, scb)) {
                errcode = DID_TIME_OUT;
-               ret = 1;
             }
             break;
 
@@ -3682,7 +4204,6 @@
                ips_inquiry(ha, scb);
             } else {
                errcode = DID_TIME_OUT;
-               ret = 1;
             }
             break;
 
@@ -3701,7 +4222,6 @@
          case MODE_SENSE:
             if (!ips_online(ha, scb) || !ips_msense(ha, scb)) {
                errcode = DID_ERROR;
-               ret = 1;
             }
             break;
 
@@ -3710,7 +4230,6 @@
                ips_rdcap(ha, scb);
             else {
                errcode = DID_TIME_OUT;
-               ret = 1;
             }
             break;
 
@@ -3720,7 +4239,6 @@
 
          case FORMAT_UNIT:
             errcode = DID_ERROR;
-            ret = 1;
             break;
 
          case SEEK_10:
@@ -3732,7 +4250,6 @@
 
          default:
             errcode = DID_ERROR;
-            ret = 1;
          } /* end switch */
 
          scb->scsi_cmd->result = errcode << 16;
@@ -3742,23 +4259,17 @@
              ((((char *) scb->scsi_cmd->buffer)[0] & 0x1f) == TYPE_DISK)) {
 
             scb->scsi_cmd->result = DID_TIME_OUT << 16;
-
-            ret = 1;
          }
       } /* else */
    } else { /* recovered error / success */
-#if IPS_DEBUG >= 1
       if (scb->bus == 0) {
-         printk(KERN_NOTICE "(%s%d) Unrecovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x\n",
-                ips_name, ha->host_num,
-                scb->cmd.basic_io.op_code, basic_status, ext_status);
+         DEBUG_VAR(1, "(%s%d) Unrecovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x",
+                   ips_name, ha->host_num,
+                   scb->cmd.basic_io.op_code, basic_status, ext_status);
       }
-#endif
 
-      ret = ips_map_status(scb, sp);
+      ips_map_status(ha, scb, sp);
    } /* else */
-
-   return (ret);
 }
 
 /****************************************************************************/
@@ -3772,7 +4283,7 @@
 /****************************************************************************/
 static int
 ips_online(ips_ha_t *ha, ips_scb_t *scb) {
-   DBG("ips_online");
+   METHOD_TRACE("ips_online", 1);
 
    if (scb->target_id >= IPS_MAX_LD)
       return (0);
@@ -3806,7 +4317,7 @@
 ips_inquiry(ips_ha_t *ha, ips_scb_t *scb) {
    IPS_INQ_DATA inq;
 
-   DBG("ips_inquiry");
+   METHOD_TRACE("ips_inquiry", 1);
 
    memset(&inq, 0, sizeof(IPS_INQ_DATA));
 
@@ -3837,7 +4348,7 @@
 ips_rdcap(ips_ha_t *ha, ips_scb_t *scb) {
    IPS_CAPACITY *cap;
 
-   DBG("ips_rdcap");
+   METHOD_TRACE("ips_rdcap", 1);
 
    if (scb->scsi_cmd->bufflen < 8)
       return (0);
@@ -3866,7 +4377,7 @@
    u32          cylinders;
    ips_mdata_t  mdata;
 
-   DBG("ips_msense");
+   METHOD_TRACE("ips_msense", 1);
 
    if (ha->enq->ulDriveSize[scb->target_id] > 0x400000 &&
        (ha->enq->ucMiscFlag & 0x8) == 0) {
@@ -3944,7 +4455,7 @@
 ips_reqsen(ips_ha_t *ha, ips_scb_t *scb) {
    char *sp;
 
-   DBG("ips_reqsen");
+   METHOD_TRACE("ips_reqsen", 1);
 
    sp = (char *) scb->scsi_cmd->sense_buffer;
    memset(sp, 0, sizeof(scb->scsi_cmd->sense_buffer));
@@ -3970,7 +4481,7 @@
 ips_free(ips_ha_t *ha) {
    int i;
 
-   DBG("ips_free");
+   METHOD_TRACE("ips_free", 1);
 
    if (ha) {
       if (ha->enq) {
@@ -4018,6 +4529,14 @@
          kfree(ha->scbs);
          ha->scbs = NULL;
       } /* end if */
+
+      /* free memory mapped (if applicable) */
+      if (ha->mem_ptr) {
+         iounmap(ha->ioremap_ptr);
+         ha->ioremap_ptr = NULL;
+         ha->mem_ptr = NULL;
+         ha->mem_addr = 0;
+      }
    }
 }
 
@@ -4035,10 +4554,10 @@
    ips_scb_t *scb_p;
    int        i;
 
-   DBG("ips_allocatescbs");
+   METHOD_TRACE("ips_allocatescbs", 1);
 
    /* Allocate memory for the CCBs */
-   ha->scbs = (ips_scb_t *) kmalloc(ha->max_cmds * sizeof(ips_scb_t), GFP_KERNEL|GFP_DMA);
+   ha->scbs = (ips_scb_t *) kmalloc(ha->max_cmds * sizeof(ips_scb_t), GFP_ATOMIC|GFP_DMA);
 
    memset(ha->scbs, 0, ha->max_cmds * sizeof(ips_scb_t));
 
@@ -4046,7 +4565,7 @@
       scb_p = &ha->scbs[i];
 
       /* allocate S/G list */
-      scb_p->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_KERNEL|GFP_DMA);
+      scb_p->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_ATOMIC|GFP_DMA);
 
       if (! scb_p->sg_list)
          return (0);
@@ -4075,7 +4594,7 @@
 ips_init_scb(ips_ha_t *ha, ips_scb_t *scb) {
    IPS_SG_LIST *sg_list;
 
-   DBG("ips_init_scb");
+   METHOD_TRACE("ips_init_scb", 1);
 
    if (scb == NULL)
       return ;
@@ -4116,7 +4635,7 @@
    ips_scb_t     *scb;
    u32            cpu_flags;
 
-   DBG("ips_getscb");
+   METHOD_TRACE("ips_getscb", 1);
 
    IPS_SCB_LOCK(cpu_flags);
    if ((scb = ha->scb_freelist) == NULL) {
@@ -4150,7 +4669,7 @@
 ips_freescb(ips_ha_t *ha, ips_scb_t *scb) {
    u32          cpu_flags;
 
-   DBG("ips_freescb");
+   METHOD_TRACE("ips_freescb", 1);
 
    /* check to make sure this is not our "special" scb */
    if (IPS_COMMAND_ID(ha, scb) < (ha->max_cmds - 1)) {
@@ -4163,7 +4682,7 @@
 
 /****************************************************************************/
 /*                                                                          */
-/* Routine Name: ips_reset_adapter                                          */
+/* Routine Name: ips_isinit_copperhead                                      */
 /*                                                                          */
 /* Routine Description:                                                     */
 /*                                                                          */
@@ -4171,21 +4690,402 @@
 /*                                                                          */
 /****************************************************************************/
 static int
-ips_reset_adapter(ips_ha_t *ha) {
-   u8  Isr;
+ips_isinit_copperhead(ips_ha_t *ha) {
+   u8 scpr;
+   u8 isr;
+
+   METHOD_TRACE("ips_isinit_copperhead", 1);
+
+   isr = inb(ha->io_addr + IPS_REG_HISR);
+   scpr = inb(ha->io_addr + IPS_REG_SCPR);
+
+   if (((isr & IPS_BIT_EI) == 0) && ((scpr & IPS_BIT_EBM) == 0))
+       return (0);
+   else
+       return (1);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_isinit_copperhead_memio                                */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Reset the controller                                                   */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_isinit_copperhead_memio(ips_ha_t *ha) {
+   u8 isr;
+   u8 scpr;
+
+   METHOD_TRACE("ips_is_init_copperhead_memio", 1);
+
+   isr = readb(ha->mem_ptr + IPS_REG_HISR);
+   scpr = readb(ha->mem_ptr + IPS_REG_SCPR);
+
+   if (((isr & IPS_BIT_EI) == 0) && ((scpr & IPS_BIT_EBM) == 0))
+       return (0);
+   else
+       return (1);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_isinit_morpheus                                        */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Reset the controller                                                   */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_isinit_morpheus(ips_ha_t *ha) {
+   u32 post;
+   u32 bits;
+
+   METHOD_TRACE("ips_is_init_morpheus", 1);
+
+   post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
+   bits = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
+
+   if (post == 0)
+       return (0);
+   else if (bits & 0x3)
+       return (0);
+   else
+       return (1);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_enable_int_copperhead                                  */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*   Turn on interrupts                                                     */
+/*                                                                          */
+/****************************************************************************/
+static void
+ips_enable_int_copperhead(ips_ha_t *ha) {
+   METHOD_TRACE("ips_enable_int_copperhead", 1);
+
+   outb(ha->io_addr + IPS_REG_HISR, IPS_BIT_EI);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_enable_int_copperhead_memio                            */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*   Turn on interrupts                                                     */
+/*                                                                          */
+/****************************************************************************/
+static void
+ips_enable_int_copperhead_memio(ips_ha_t *ha) {
+   METHOD_TRACE("ips_enable_int_copperhead_memio", 1);
+
+   writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_enable_int_morpheus                                    */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*   Turn on interrupts                                                     */
+/*                                                                          */
+/****************************************************************************/
+static void
+ips_enable_int_morpheus(ips_ha_t *ha) {
+   u32              Oimr;
+
+   METHOD_TRACE("ips_enable_int_morpheus", 1);
+
+   Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR);
+   Oimr &= ~0x08;
+   writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_init_copperhead                                        */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Initialize a copperhead controller                                     */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_init_copperhead(ips_ha_t *ha) {
+   u8  Isr;
+   u8  Cbsp;
+   u8  PostByte[IPS_MAX_POST_BYTES];
+   u8  ConfigByte[IPS_MAX_CONFIG_BYTES];
+   int i, j;
+
+   METHOD_TRACE("ips_init_copperhead", 1);
+
+   for (i = 0; i < IPS_MAX_POST_BYTES; i++) {
+      for (j = 0; j < 45; j++) {
+         Isr = inb(ha->io_addr + IPS_REG_HISR);
+         if (Isr & IPS_BIT_GHI)
+            break;
+
+         MDELAY(IPS_ONE_SEC);
+      }
+
+      if (j >= 45)
+         /* error occured */
+         return (0);
+
+      PostByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
+      outb(Isr, ha->io_addr + IPS_REG_HISR);
+   }
+
+   if (PostByte[0] < IPS_GOOD_POST_STATUS) {
+      printk(KERN_WARNING "(%s%d) reset controller fails (post status %x %x).\n",
+             ips_name, ha->host_num, PostByte[0], PostByte[1]);
+
+      return (0);
+   }
+
+   for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) {
+      for (j = 0; j < 240; j++) {
+         Isr = inb(ha->io_addr + IPS_REG_HISR);
+         if (Isr & IPS_BIT_GHI)
+            break;
+
+         MDELAY(IPS_ONE_SEC); /* 100 msec */
+      }
+
+      if (j >= 240)
+         /* error occured */
+         return (0);
+
+      ConfigByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
+      outb(Isr, ha->io_addr + IPS_REG_HISR);
+   }
+
+   for (i = 0; i < 240; i++) {
+      Cbsp = inb(ha->io_addr + IPS_REG_CBSP);
+
+      if ((Cbsp & IPS_BIT_OP) == 0)
+         break;
+
+      MDELAY(IPS_ONE_SEC);
+   }
+
+   if (i >= 240)
+      /* reset failed */
+      return (0);
+
+   /* setup CCCR */
+   outw(0x1010, ha->io_addr + IPS_REG_CCCR);
+
+   /* Enable busmastering */
+   outb(IPS_BIT_EBM, ha->io_addr + IPS_REG_SCPR);
+
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      /* fix for anaconda64 */
+      outl(0, ha->io_addr + IPS_REG_NDAE);
+
+   /* Enable interrupts */
+   outb(IPS_BIT_EI, ha->io_addr + IPS_REG_HISR);
+
+   return (1);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_init_copperhead_memio                                  */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Initialize a copperhead controller with memory mapped I/O              */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_init_copperhead_memio(ips_ha_t *ha) {
+   u8  Isr;
    u8  Cbsp;
    u8  PostByte[IPS_MAX_POST_BYTES];
    u8  ConfigByte[IPS_MAX_CONFIG_BYTES];
    int i, j;
+
+   METHOD_TRACE("ips_init_copperhead_memio", 1);
+
+   for (i = 0; i < IPS_MAX_POST_BYTES; i++) {
+      for (j = 0; j < 45; j++) {
+         Isr = readb(ha->mem_ptr + IPS_REG_HISR);
+         if (Isr & IPS_BIT_GHI)
+            break;
+
+         MDELAY(IPS_ONE_SEC);
+      }
+
+      if (j >= 45)
+         /* error occured */
+         return (0);
+
+      PostByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR);
+      writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
+   }
+
+   if (PostByte[0] < IPS_GOOD_POST_STATUS) {
+      printk(KERN_WARNING "(%s%d) reset controller fails (post status %x %x).\n",
+             ips_name, ha->host_num, PostByte[0], PostByte[1]);
+
+      return (0);
+   }
+
+   for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) {
+      for (j = 0; j < 240; j++) {
+         Isr = readb(ha->mem_ptr + IPS_REG_HISR);
+         if (Isr & IPS_BIT_GHI)
+            break;
+
+         MDELAY(IPS_ONE_SEC); /* 100 msec */
+      }
+
+      if (j >= 240)
+         /* error occured */
+         return (0);
+
+      ConfigByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR);
+      writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
+   }
+
+   for (i = 0; i < 240; i++) {
+      Cbsp = readb(ha->mem_ptr + IPS_REG_CBSP);
+
+      if ((Cbsp & IPS_BIT_OP) == 0)
+         break;
+
+      MDELAY(IPS_ONE_SEC);
+   }
+
+   if (i >= 240)
+      /* error occured */
+      return (0);
+
+   /* setup CCCR */
+   writel(0x1010, ha->mem_ptr + IPS_REG_CCCR);
+
+   /* Enable busmastering */
+   writeb(IPS_BIT_EBM, ha->mem_ptr + IPS_REG_SCPR);
+
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      /* fix for anaconda64 */
+      writel(0, ha->mem_ptr + IPS_REG_NDAE);
+
+   /* Enable interrupts */
+   writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR);
+
+   /* if we get here then everything went OK */
+   return (1);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_init_morpheus                                          */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Initialize a morpheus controller                                       */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_init_morpheus(ips_ha_t *ha) {
+   u32 Post;
+   u32 Config;
+   u32 Isr;
+   u32 Oimr;
+   int i;
+
+   METHOD_TRACE("ips_init_morpheus", 1);
+
+   /* Wait up to 45 secs for Post */
+   for (i = 0; i < 45; i++) {
+      Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
+
+      if (Isr & IPS_BIT_I960_MSG0I)
+         break;
+
+      MDELAY(IPS_ONE_SEC);
+   }
+
+   if (i >= 45) {
+      /* error occured */
+      printk(KERN_WARNING "(%s%d) timeout waiting for post.\n",
+             ips_name, ha->host_num);
+
+      return (0);
+   }
+
+   Post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
+
+   /* Clear the interrupt bit */
+   Isr = (u32) IPS_BIT_I960_MSG0I;
+   writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
+
+   if (Post < (IPS_GOOD_POST_STATUS << 8)) {
+      printk(KERN_WARNING "(%s%d) reset controller fails (post status %x).\n",
+             ips_name, ha->host_num, Post);
+
+      return (0);
+   }
+
+   /* Wait up to 240 secs for config bytes */
+   for (i = 0; i < 240; i++) {
+      Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
+
+      if (Isr & IPS_BIT_I960_MSG1I)
+         break;
+
+      MDELAY(IPS_ONE_SEC); /* 100 msec */
+   }
+
+   if (i >= 240) {
+      /* error occured */
+      printk(KERN_WARNING "(%s%d) timeout waiting for config.\n",
+             ips_name, ha->host_num);
+
+      return (0);
+   }
+
+   Config = readl(ha->mem_ptr + IPS_REG_I960_MSG1);
+
+   /* Clear interrupt bit */
+   Isr = (u32) IPS_BIT_I960_MSG1I;
+   writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
+
+   /* Turn on the interrupts */
+   Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR);
+   Oimr &= ~0x8;
+   writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR);
+
+   /* if we get here then everything went OK */
+   return (1);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_reset_copperhead                                       */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Reset the controller                                                   */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_reset_copperhead(ips_ha_t *ha) {
    int reset_counter;
    u32 cpu_flags;
 
-   DBG("ips_reset_adapter");
+   METHOD_TRACE("ips_reset_copperhead", 1);
 
-#if IPS_DEBUG >= 1
-   printk(KERN_WARNING "ips_reset_adapter: io addr: %x, irq: %d\n",
-          ha->io_addr, ha->irq);
-#endif
+   DEBUG_VAR(1, "(%s%d) ips_reset_copperhead: io addr: %x, irq: %d",
+             ips_name, ha->host_num, ha->io_addr, ha->irq);
 
    IPS_HA_LOCK(cpu_flags);
 
@@ -4199,109 +5099,107 @@
       outb(0, ha->io_addr + IPS_REG_SCPR);
       MDELAY(IPS_ONE_SEC);
 
-      for (i = 0; i < IPS_MAX_POST_BYTES; i++) {
-         for (j = 0; j < 45; j++) {
-            Isr = inb(ha->io_addr + IPS_REG_HISR);
-            if (Isr & IPS_BIT_GHI)
-               break;
-
-            MDELAY(IPS_ONE_SEC);
-         }
-
-         if (j >= 45) {
-            /* error occured */
-            if (reset_counter < 2)
-               continue;
-            else {
-               /* reset failed */
-               IPS_HA_UNLOCK(cpu_flags);
-
-               return (0);
-            }
-         }
+      if ((*ha->func.init)(ha))
+         break;
+      else if (reset_counter >= 2) {
+         IPS_HA_UNLOCK(cpu_flags);
 
-         PostByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
-         outb(Isr, ha->io_addr + IPS_REG_HISR);
+         return (0);
       }
+   }
 
-      if (PostByte[0] < IPS_GOOD_POST_STATUS) {
-         printk("(%s%d) reset controller fails (post status %x %x).\n",
-                ips_name, ha->host_num, PostByte[0], PostByte[1]);
+   IPS_HA_UNLOCK(cpu_flags);
 
-         IPS_HA_UNLOCK(cpu_flags);
+   return (1);
+}
 
-         return (0);
-      }
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_reset_copperhead_memio                                 */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Reset the controller                                                   */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_reset_copperhead_memio(ips_ha_t *ha) {
+   int reset_counter;
+   u32 cpu_flags;
 
-      for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) {
-         for (j = 0; j < 240; j++) {
-            Isr = inb(ha->io_addr + IPS_REG_HISR);
-            if (Isr & IPS_BIT_GHI)
-               break;
+   METHOD_TRACE("ips_reset_copperhead_memio", 1);
 
-            MDELAY(IPS_ONE_SEC); /* 100 msec */
-         }
+   DEBUG_VAR(1, "(%s%d) ips_reset_copperhead_memio: mem addr: %x, irq: %d",
+             ips_name, ha->host_num, ha->mem_addr, ha->irq);
 
-         if (j >= 240) {
-            /* error occured */
-            if (reset_counter < 2)
-               continue;
-            else {
-               /* reset failed */
-               IPS_HA_UNLOCK(cpu_flags);
+   IPS_HA_LOCK(cpu_flags);
 
-               return (0);
-            }
-         }
+   reset_counter = 0;
 
-         ConfigByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
-         outb(Isr, ha->io_addr + IPS_REG_HISR);
-      }
+   while (reset_counter < 2) {
+      reset_counter++;
 
-      if (ConfigByte[0] == 0 && ConfigByte[1] == 2) {
-         printk("(%s%d) reset controller fails (status %x %x).\n",
-                ips_name, ha->host_num, ConfigByte[0], ConfigByte[1]);
+      writeb(IPS_BIT_RST, ha->mem_ptr + IPS_REG_SCPR);
+      MDELAY(IPS_ONE_SEC);
+      writeb(0, ha->mem_ptr + IPS_REG_SCPR);
+      MDELAY(IPS_ONE_SEC);
 
+      if ((*ha->func.init)(ha))
+         break;
+      else if (reset_counter >= 2) {
          IPS_HA_UNLOCK(cpu_flags);
 
          return (0);
       }
+   }
 
-      for (i = 0; i < 240; i++) {
-         Cbsp = inb(ha->io_addr + IPS_REG_CBSP);
+   IPS_HA_UNLOCK(cpu_flags);
 
-         if ((Cbsp & IPS_BIT_OP) == 0)
-            break;
+   return (1);
+}
 
-         MDELAY(IPS_ONE_SEC);
-      }
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_reset_morpheus                                         */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Reset the controller                                                   */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_reset_morpheus(ips_ha_t *ha) {
+   int reset_counter;
+   u8  junk;
+   u32 cpu_flags;
 
-      if (i >= 240) {
-         /* error occured */
-         if (reset_counter < 2)
-            continue;
-         else {
-            /* reset failed */
-            IPS_HA_UNLOCK(cpu_flags);
+   METHOD_TRACE("ips_reset_morpheus", 1);
 
-            return (0);
-         }
-      }
+   DEBUG_VAR(1, "(%s%d) ips_reset_morpheus: mem addr: %x, irq: %d",
+             ips_name, ha->host_num, ha->mem_addr, ha->irq);
 
-      /* setup CCCR */
-      outw(0x1010, ha->io_addr + IPS_REG_CCCR);
+   IPS_HA_LOCK(cpu_flags);
 
-      /* Enable busmastering */
-      outb(IPS_BIT_EBM, ha->io_addr + IPS_REG_SCPR);
+   reset_counter = 0;
 
-      /* setup status queues */
-      ips_statinit(ha);
+   while (reset_counter < 2) {
+      reset_counter++;
 
-      /* Enable interrupts */
-      outb(IPS_BIT_EI, ha->io_addr + IPS_REG_HISR);
+      writel(0x80000000, ha->mem_ptr + IPS_REG_I960_IDR);
 
-      /* if we get here then everything went OK */
-      break;
+      /* Delay for 300 msec */
+      MDELAY(300 * IPS_ONE_MSEC);
+
+      /* Do a PCI config read to wait for adapter */
+      pci_read_config_byte(ha->pcidev, 4, &junk);
+
+      if ((*ha->func.init)(ha))
+         break;
+      else if (reset_counter >= 2) {
+         IPS_HA_UNLOCK(cpu_flags);
+
+         return (0);
+      }
    }
 
    IPS_HA_UNLOCK(cpu_flags);
@@ -4322,7 +5220,7 @@
 ips_statinit(ips_ha_t *ha) {
    u32        phys_status_start;
 
-   DBG("ips_statinit");
+   METHOD_TRACE("ips_statinit", 1);
 
    ha->adapt->p_status_start = ha->adapt->status;
    ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS;
@@ -4340,18 +5238,45 @@
 
 /****************************************************************************/
 /*                                                                          */
-/* Routine Name: ips_statupd                                                */
+/* Routine Name: ips_statinit_memio                                         */
 /*                                                                          */
 /* Routine Description:                                                     */
 /*                                                                          */
-/*   Remove an element from the status queue                                */
+/*   Initialize the status queues on the controller                         */
 /*                                                                          */
 /****************************************************************************/
-static int
-ips_statupd(ips_ha_t *ha) {
-   int       command_id;
+static void
+ips_statinit_memio(ips_ha_t *ha) {
+   u32        phys_status_start;
+
+   METHOD_TRACE("ips_statinit_memio", 1);
+
+   ha->adapt->p_status_start = ha->adapt->status;
+   ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS;
+   ha->adapt->p_status_tail = ha->adapt->status;
+
+   phys_status_start = VIRT_TO_BUS(ha->adapt->status);
+   writel(phys_status_start, ha->mem_ptr + IPS_REG_SQSR);
+   writel(phys_status_start + IPS_STATUS_Q_SIZE, ha->mem_ptr + IPS_REG_SQER);
+   writel(phys_status_start + IPS_STATUS_SIZE, ha->mem_ptr + IPS_REG_SQHR);
+   writel(phys_status_start, ha->mem_ptr + IPS_REG_SQTR);
+
+   ha->adapt->hw_status_start = phys_status_start;
+   ha->adapt->hw_status_tail = phys_status_start;
+}
 
-   DBG("ips_statupd");
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_statupd_copperhead                                     */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Remove an element from the status queue                                */
+/*                                                                          */
+/****************************************************************************/
+static u32
+ips_statupd_copperhead(ips_ha_t *ha) {
+   METHOD_TRACE("ips_statupd_copperhead", 1);
 
    if (ha->adapt->p_status_tail != ha->adapt->p_status_end) {
       ha->adapt->p_status_tail++;
@@ -4363,47 +5288,87 @@
 
    outl(ha->adapt->hw_status_tail, ha->io_addr + IPS_REG_SQTR);
 
-   command_id = ha->adapt->p_status_tail->command_id;
+   return (ha->adapt->p_status_tail->value);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_statupd_copperhead_memio                               */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Remove an element from the status queue                                */
+/*                                                                          */
+/****************************************************************************/
+static u32
+ips_statupd_copperhead_memio(ips_ha_t *ha) {
+   METHOD_TRACE("ips_statupd_copperhead_memio", 1);
+
+   if (ha->adapt->p_status_tail != ha->adapt->p_status_end) {
+      ha->adapt->p_status_tail++;
+      ha->adapt->hw_status_tail += sizeof(IPS_STATUS);
+   } else {
+      ha->adapt->p_status_tail = ha->adapt->p_status_start;
+      ha->adapt->hw_status_tail = ha->adapt->hw_status_start;
+   }
+
+   writel(ha->adapt->hw_status_tail, ha->mem_ptr + IPS_REG_SQTR);
 
-   return (command_id);
+   return (ha->adapt->p_status_tail->value);
 }
 
 /****************************************************************************/
 /*                                                                          */
-/* Routine Name: ips_issue                                                  */
+/* Routine Name: ips_statupd_morpheus                                       */
 /*                                                                          */
 /* Routine Description:                                                     */
 /*                                                                          */
-/*   Send a command down to the controller                                  */
+/*   Remove an element from the status queue                                */
 /*                                                                          */
-/* ASSUMED to be called from within a lock                                  */
+/****************************************************************************/
+static u32
+ips_statupd_morpheus(ips_ha_t *ha) {
+   u32       val;
+
+   METHOD_TRACE("ips_statupd_morpheus", 1);
+
+   val = readl(ha->mem_ptr + IPS_REG_I2O_OUTMSGQ);
+
+   return (val);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_issue_copperhead                                       */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Send a command down to the controller                                  */
 /*                                                                          */
 /****************************************************************************/
 static int
-ips_issue(ips_ha_t *ha, ips_scb_t *scb) {
+ips_issue_copperhead(ips_ha_t *ha, ips_scb_t *scb) {
    u32       TimeOut;
    u16       val;
    u32       cpu_flags;
 
-   DBG("ips_issue");
+   METHOD_TRACE("ips_issue_copperhead", 1);
 
-#if IPS_DEBUG >= 10
-   if (scb->scsi_cmd)
-      printk(KERN_NOTICE "%s: ips_issue: cmd 0x%X id %d (%d %d %d)\n",
-             ips_name,
-             scb->cdb[0],
-             scb->cmd.basic_io.command_id,
-             scb->bus,
-             scb->target_id,
-             scb->lun);
-   else
-      printk(KERN_NOTICE "%s: ips_issue: logical cmd id %d\n",
-             ips_name,
-             scb->cmd.basic_io.command_id);
-#if IPS_DEBUG >= 11
-      MDELAY(IPS_ONE_SEC);
-#endif
-#endif
+   if (scb->scsi_cmd) {
+      DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
+                ips_name,
+                ha->host_num,
+                scb->cdb[0],
+                scb->cmd.basic_io.command_id,
+                scb->bus,
+                scb->target_id,
+                scb->lun);
+   } else {
+      DEBUG_VAR(2, KERN_NOTICE "(%s%d) ips_issue: logical cmd id %d",
+                ips_name,
+                ha->host_num,
+                scb->cmd.basic_io.command_id);
+   }
 
    IPS_HA_LOCK(cpu_flags);
 
@@ -4430,14 +5395,189 @@
    outl(scb->scb_busaddr, ha->io_addr + IPS_REG_CCSAR);
    outw(IPS_BIT_START_CMD, ha->io_addr + IPS_REG_CCCR);
 
-   IPS_HA_UNLOCK(cpu_flags);
+   IPS_HA_UNLOCK(cpu_flags);
+
+   return (IPS_SUCCESS);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_issue_copperhead_memio                                 */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Send a command down to the controller                                  */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_issue_copperhead_memio(ips_ha_t *ha, ips_scb_t *scb) {
+   u32       TimeOut;
+   u32       val;
+   u32       cpu_flags;
+
+   METHOD_TRACE("ips_issue_copperhead_memio", 1);
+
+   if (scb->scsi_cmd) {
+      DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
+                ips_name,
+                ha->host_num,
+                scb->cdb[0],
+                scb->cmd.basic_io.command_id,
+                scb->bus,
+                scb->target_id,
+                scb->lun);
+   } else {
+      DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
+                ips_name,
+                ha->host_num,
+                scb->cmd.basic_io.command_id);
+   }
+
+   IPS_HA_LOCK(cpu_flags);
+
+   TimeOut = 0;
+
+   while ((val = readl(ha->mem_ptr + IPS_REG_CCCR)) & IPS_BIT_SEM) {
+      UDELAY(1000);
+
+      if (++TimeOut >= IPS_SEM_TIMEOUT) {
+         if (!(val & IPS_BIT_START_STOP))
+            break;
+
+         printk(KERN_WARNING "(%s%d) ips_issue val [0x%x].\n",
+                ips_name, ha->host_num, val);
+         printk(KERN_WARNING "(%s%d) ips_issue semaphore chk timeout.\n",
+                ips_name, ha->host_num);
+
+         IPS_HA_UNLOCK(cpu_flags);
+
+         return (IPS_FAILURE);
+      } /* end if */
+   } /* end while */
+
+   writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_CCSAR);
+   writel(IPS_BIT_START_CMD, ha->mem_ptr + IPS_REG_CCCR);
+
+   IPS_HA_UNLOCK(cpu_flags);
+
+   return (IPS_SUCCESS);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_issue_i2o                                              */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Send a command down to the controller                                  */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_issue_i2o(ips_ha_t *ha, ips_scb_t *scb) {
+   u32       cpu_flags;
+
+   METHOD_TRACE("ips_issue_i2o", 1);
+
+   if (scb->scsi_cmd) {
+      DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
+                ips_name,
+                ha->host_num,
+                scb->cdb[0],
+                scb->cmd.basic_io.command_id,
+                scb->bus,
+                scb->target_id,
+                scb->lun);
+   } else {
+      DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
+                ips_name,
+                ha->host_num,
+                scb->cmd.basic_io.command_id);
+   }
+
+   IPS_HA_LOCK(cpu_flags);
+
+   outl(scb->scb_busaddr, ha->io_addr + IPS_REG_I2O_INMSGQ);
+
+   IPS_HA_UNLOCK(cpu_flags);
+
+   return (IPS_SUCCESS);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_issue_i2o_memio                                        */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Send a command down to the controller                                  */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_issue_i2o_memio(ips_ha_t *ha, ips_scb_t *scb) {
+   u32       cpu_flags;
+
+   METHOD_TRACE("ips_issue_i2o_memio", 1);
+
+   if (scb->scsi_cmd) {
+      DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
+                ips_name,
+                ha->host_num,
+                scb->cdb[0],
+                scb->cmd.basic_io.command_id,
+                scb->bus,
+                scb->target_id,
+                scb->lun);
+   } else {
+      DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
+                ips_name,
+                ha->host_num,
+                scb->cmd.basic_io.command_id);
+   }
+
+   IPS_HA_LOCK(cpu_flags);
+
+   writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_I2O_INMSGQ);
+
+   IPS_HA_UNLOCK(cpu_flags);
+
+   return (IPS_SUCCESS);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_isintr_copperhead                                      */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Test to see if an interrupt is for us                                  */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_isintr_copperhead(ips_ha_t *ha) {
+   u8 Isr;
+
+   METHOD_TRACE("ips_isintr_copperhead", 2);
+
+   Isr = inb(ha->io_addr + IPS_REG_HISR);
+
+   if (Isr == 0xFF)
+      /* ?!?! Nothing really there */
+      return (0);
 
-   return (IPS_SUCCESS);
+   if (Isr & IPS_BIT_SCE)
+      return (1);
+   else if (Isr & (IPS_BIT_SQO | IPS_BIT_GHI)) {
+      /* status queue overflow or GHI */
+      /* just clear the interrupt */
+      outb(Isr, ha->io_addr + IPS_REG_HISR);
+   }
+
+   return (0);
 }
 
 /****************************************************************************/
 /*                                                                          */
-/* Routine Name: ips_isintr                                                 */
+/* Routine Name: ips_isintr_copperhead_memio                                */
 /*                                                                          */
 /* Routine Description:                                                     */
 /*                                                                          */
@@ -4445,12 +5585,12 @@
 /*                                                                          */
 /****************************************************************************/
 static int
-ips_isintr(ips_ha_t *ha) {
+ips_isintr_copperhead_memio(ips_ha_t *ha) {
    u8 Isr;
 
-   DBG("ips_isintr");
+   METHOD_TRACE("ips_isintr_memio", 2);
 
-   Isr = inb(ha->io_addr + IPS_REG_HISR);
+   Isr = readb(ha->mem_ptr + IPS_REG_HISR);
 
    if (Isr == 0xFF)
       /* ?!?! Nothing really there */
@@ -4461,7 +5601,7 @@
    else if (Isr & (IPS_BIT_SQO | IPS_BIT_GHI)) {
       /* status queue overflow or GHI */
       /* just clear the interrupt */
-      outb(Isr, ha->io_addr + IPS_REG_HISR);
+      writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
    }
 
    return (0);
@@ -4469,6 +5609,29 @@
 
 /****************************************************************************/
 /*                                                                          */
+/* Routine Name: ips_isintr_morpheus                                        */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*                                                                          */
+/*   Test to see if an interrupt is for us                                  */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_isintr_morpheus(ips_ha_t *ha) {
+   u32 Isr;
+
+   METHOD_TRACE("ips_isintr_morpheus", 2);
+
+   Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
+
+   if (Isr & IPS_BIT_I2O_OPQI)
+      return (1);
+   else
+      return (0);
+}
+
+/****************************************************************************/
+/*                                                                          */
 /* Routine Name: ips_wait                                                   */
 /*                                                                          */
 /* Routine Description:                                                     */
@@ -4481,7 +5644,7 @@
    int        ret;
    u8         done;
 
-   DBG("ips_wait");
+   METHOD_TRACE("ips_wait", 1);
 
    ret = IPS_FAILURE;
    done = FALSE;
@@ -4516,7 +5679,7 @@
          while (test_and_set_bit(IPS_IN_INTR, &ha->flags))
             UDELAY(1000);
 
-         ips_intr(ha);
+         (*ha->func.intr)(ha);
 
          clear_bit(IPS_IN_INTR, &ha->flags);
       } else if (intr == IPS_INTR_HAL) {
@@ -4542,7 +5705,7 @@
          while (test_and_set_bit(IPS_IN_INTR, &ha->flags))
             UDELAY(1000);
 
-         ips_intr(ha);
+         (*ha->func.intr)(ha);
 
          clear_bit(IPS_IN_INTR, &ha->flags);
 
@@ -4567,7 +5730,7 @@
 /****************************************************************************/
 static int
 ips_write_driver_status(ips_ha_t *ha, int intr) {
-   DBG("ips_write_driver_status");
+   METHOD_TRACE("ips_write_driver_status", 1);
 
    if (!ips_readwrite_page5(ha, FALSE, intr)) {
       printk(KERN_WARNING "(%s%d) unable to read NVRAM page 5.\n",
@@ -4579,21 +5742,18 @@
    /* check to make sure the page has a valid */
    /* signature */
    if (ha->nvram->signature != IPS_NVRAM_P5_SIG) {
-#if IPS_DEBUG >= 1
-      printk("(%s%d) NVRAM page 5 has an invalid signature: %X.\n",
-             ips_name, ha->host_num, ha->nvram->signature);
-#endif
+      DEBUG_VAR(1, "(%s%d) NVRAM page 5 has an invalid signature: %X.",
+                ips_name, ha->host_num, ha->nvram->signature);
+
       return (1);
    }
 
-#if IPS_DEBUG >= 2
-   printk("(%s%d) Ad Type: %d, Ad Slot: %d, BIOS: %c%c%c%c %c%c%c%c.\n",
-          ips_name, ha->host_num, ha->nvram->adapter_type, ha->nvram->adapter_slot,
-          ha->nvram->bios_high[0], ha->nvram->bios_high[1],
-          ha->nvram->bios_high[2], ha->nvram->bios_high[3],
-          ha->nvram->bios_low[0], ha->nvram->bios_low[1],
-          ha->nvram->bios_low[2], ha->nvram->bios_low[3]);
-#endif
+   DEBUG_VAR(2, "(%s%d) Ad Type: %d, Ad Slot: %d, BIOS: %c%c%c%c %c%c%c%c.",
+             ips_name, ha->host_num, ha->nvram->adapter_type, ha->nvram->adapter_slot,
+             ha->nvram->bios_high[0], ha->nvram->bios_high[1],
+             ha->nvram->bios_high[2], ha->nvram->bios_high[3],
+             ha->nvram->bios_low[0], ha->nvram->bios_low[1],
+             ha->nvram->bios_low[2], ha->nvram->bios_low[3]);
 
    /* save controller type */
    ha->ad_type = ha->nvram->adapter_type;
@@ -4628,7 +5788,7 @@
    ips_scb_t *scb;
    int        ret;
 
-   DBG("ips_read_adapter_status");
+   METHOD_TRACE("ips_read_adapter_status", 1);
 
    scb = &ha->scbs[ha->max_cmds-1];
 
@@ -4647,8 +5807,9 @@
    scb->cmd.basic_io.reserved = 0;
 
    /* send command */
-   ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr);
-   if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM))
+   if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) ||
+       (ret == IPS_SUCCESS_IMM) ||
+       ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
       return (0);
 
    return (1);
@@ -4668,7 +5829,7 @@
    ips_scb_t *scb;
    int        ret;
 
-   DBG("ips_read_subsystem_parameters");
+   METHOD_TRACE("ips_read_subsystem_parameters", 1);
 
    scb = &ha->scbs[ha->max_cmds-1];
 
@@ -4687,8 +5848,9 @@
    scb->cmd.basic_io.reserved = 0;
 
    /* send command */
-   ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr);
-   if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM))
+   if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) ||
+       (ret == IPS_SUCCESS_IMM) ||
+       ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
       return (0);
 
    return (1);
@@ -4709,7 +5871,7 @@
    int        i;
    int        ret;
 
-   DBG("ips_read_config");
+   METHOD_TRACE("ips_read_config", 1);
 
    /* set defaults for initiator IDs */
    ha->conf->init_id[0] = IPS_ADAPTER_ID;
@@ -4759,7 +5921,7 @@
    ips_scb_t *scb;
    int        ret;
 
-   DBG("ips_readwrite_page5");
+   METHOD_TRACE("ips_readwrite_page5", 1);
 
    scb = &ha->scbs[ha->max_cmds-1];
 
@@ -4803,7 +5965,7 @@
    ips_scb_t *scb;
    int        ret;
 
-   DBG("ips_clear_adapter");
+   METHOD_TRACE("ips_clear_adapter", 1);
 
    scb = &ha->scbs[ha->max_cmds-1];
 
@@ -4821,8 +5983,9 @@
    scb->cmd.config_sync.reserved3 = 0;
 
    /* issue command */
-   ret = ips_send_wait(ha, scb, ips_reset_timeout, intr);
-   if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM))
+   if (((ret = ips_send_wait(ha, scb, ips_reset_timeout, intr)) == IPS_FAILURE) ||
+       (ret == IPS_SUCCESS_IMM) ||
+       ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
       return (0);
 
    /* send unlock stripe command */
@@ -4840,8 +6003,9 @@
    scb->cmd.unlock_stripe.reserved3 = 0;
 
    /* issue command */
-   ret = ips_send_wait(ha, scb, ips_reset_timeout, intr);
-   if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM))
+   if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) ||
+       (ret == IPS_SUCCESS_IMM) ||
+       ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
       return (0);
 
    return (1);
@@ -4860,7 +6024,7 @@
 ips_ffdc_reset(ips_ha_t *ha, int intr) {
    ips_scb_t *scb;
 
-   DBG("ips_ffdc_reset");
+   METHOD_TRACE("ips_ffdc_reset", 1);
 
    scb = &ha->scbs[ha->max_cmds-1];
 
@@ -4893,12 +6057,10 @@
 ips_ffdc_time(ips_ha_t *ha, int intr) {
    ips_scb_t *scb;
 
-   DBG("ips_ffdc_time");
+   METHOD_TRACE("ips_ffdc_time", 1);
 
-#if IPS_DEBUG >= 1
-   printk(KERN_NOTICE "(%s%d) Sending time update.\n",
-          ips_name, ha->host_num);
-#endif
+   DEBUG_VAR(1, "(%s%d) Sending time update.",
+             ips_name, ha->host_num);
 
    scb = &ha->scbs[ha->max_cmds-1];
 
@@ -4947,6 +6109,8 @@
                                  {30, 30},
                                  {31, 31} };
 
+   METHOD_TRACE("ips_fix_ffdc_time", 1);
+
    days = current_time / IPS_SECS_DAY;
    rem = current_time % IPS_SECS_DAY;
 
@@ -4995,6 +6159,8 @@
    int   timeout;
    u8    status;
 
+   METHOD_TRACE("ips_erase_bios", 1);
+
    /* Clear the status register */
    outl(0, ha->io_addr + IPS_REG_FLAP);
    if (ha->revision_id == IPS_REVID_TROMBONE64)
@@ -5091,6 +6257,115 @@
 
 /****************************************************************************/
 /*                                                                          */
+/* Routine Name: ips_erase_bios_memio                                       */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*   Erase the BIOS on the adapter                                          */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_erase_bios_memio(ips_ha_t *ha) {
+   int   timeout;
+   u8    status;
+
+   METHOD_TRACE("ips_erase_bios_memio", 1);
+
+   /* Clear the status register */
+   writel(0, ha->mem_ptr + IPS_REG_FLAP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   writeb(0x50, ha->mem_ptr + IPS_REG_FLDP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   /* Erase Setup */
+   writeb(0x20, ha->mem_ptr + IPS_REG_FLDP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   /* Erase Confirm */
+   writeb(0xD0, ha->mem_ptr + IPS_REG_FLDP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   /* Erase Status */
+   writeb(0x70, ha->mem_ptr + IPS_REG_FLDP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   timeout = 80000; /* 80 seconds */
+
+   while (timeout > 0) {
+      if (ha->revision_id == IPS_REVID_TROMBONE64) {
+         writel(0, ha->mem_ptr + IPS_REG_FLAP);
+         UDELAY(5); /* 5 us */
+      }
+
+      status = readb(ha->mem_ptr + IPS_REG_FLDP);
+
+      if (status & 0x80)
+         break;
+
+      MDELAY(1);
+      timeout--;
+   }
+
+   /* check for timeout */
+   if (timeout <= 0) {
+      /* timeout */
+
+      /* try to suspend the erase */
+      writeb(0xB0, ha->mem_ptr + IPS_REG_FLDP);
+      if (ha->revision_id == IPS_REVID_TROMBONE64)
+         UDELAY(5); /* 5 us */
+
+      /* wait for 10 seconds */
+      timeout = 10000;
+      while (timeout > 0) {
+         if (ha->revision_id == IPS_REVID_TROMBONE64) {
+            writel(0, ha->mem_ptr + IPS_REG_FLAP);
+            UDELAY(5); /* 5 us */
+         }
+
+         status = readb(ha->mem_ptr + IPS_REG_FLDP);
+
+         if (status & 0xC0)
+            break;
+
+         MDELAY(1);
+         timeout--;
+      }
+
+      return (1);
+   }
+
+   /* check for valid VPP */
+   if (status & 0x08)
+      /* VPP failure */
+      return (1);
+
+   /* check for succesful flash */
+   if (status & 0x30)
+      /* sequence error */
+      return (1);
+
+   /* Otherwise, we were successful */
+   /* clear status */
+   writeb(0x50, ha->mem_ptr + IPS_REG_FLDP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   /* enable reads */
+   writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   return (0);
+}
+
+/****************************************************************************/
+/*                                                                          */
 /* Routine Name: ips_program_bios                                           */
 /*                                                                          */
 /* Routine Description:                                                     */
@@ -5103,6 +6378,8 @@
    int   timeout;
    u8    status;
 
+   METHOD_TRACE("ips_program_bios", 1);
+
    for (i = 0; i < buffersize; i++) {
       /* write a byte */
       outl(i, ha->io_addr + IPS_REG_FLAP);
@@ -5176,6 +6453,93 @@
 
 /****************************************************************************/
 /*                                                                          */
+/* Routine Name: ips_program_bios_memio                                     */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*   Program the BIOS on the adapter                                        */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_program_bios_memio(ips_ha_t *ha, char *buffer, int buffersize) {
+   int   i;
+   int   timeout;
+   u8    status;
+
+   METHOD_TRACE("ips_program_bios_memio", 1);
+
+   for (i = 0; i < buffersize; i++) {
+      /* write a byte */
+      writel(i, ha->mem_ptr + IPS_REG_FLAP);
+      if (ha->revision_id == IPS_REVID_TROMBONE64)
+         UDELAY(5); /* 5 us */
+
+      writeb(0x40, ha->mem_ptr + IPS_REG_FLDP);
+      if (ha->revision_id == IPS_REVID_TROMBONE64)
+         UDELAY(5); /* 5 us */
+
+      writeb(buffer[i], ha->mem_ptr + IPS_REG_FLDP);
+      if (ha->revision_id == IPS_REVID_TROMBONE64)
+         UDELAY(5); /* 5 us */
+
+      /* wait up to one second */
+      timeout = 1000;
+      while (timeout > 0) {
+         if (ha->revision_id == IPS_REVID_TROMBONE64) {
+            writel(0, ha->mem_ptr + IPS_REG_FLAP);
+            UDELAY(5); /* 5 us */
+         }
+
+         status = readb(ha->mem_ptr + IPS_REG_FLDP);
+
+         if (status & 0x80)
+            break;
+
+         MDELAY(1);
+         timeout--;
+      }
+
+      if (timeout == 0) {
+         /* timeout error */
+         writel(0, ha->mem_ptr + IPS_REG_FLAP);
+         if (ha->revision_id == IPS_REVID_TROMBONE64)
+            UDELAY(5); /* 5 us */
+
+         writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
+         if (ha->revision_id == IPS_REVID_TROMBONE64)
+            UDELAY(5); /* 5 us */
+
+         return (1);
+      }
+
+      /* check the status */
+      if (status & 0x18) {
+         /* programming error */
+         writel(0, ha->mem_ptr + IPS_REG_FLAP);
+         if (ha->revision_id == IPS_REVID_TROMBONE64)
+            UDELAY(5); /* 5 us */
+
+         writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
+         if (ha->revision_id == IPS_REVID_TROMBONE64)
+            UDELAY(5); /* 5 us */
+
+         return (1);
+      }
+   } /* end for */
+
+   /* Enable reading */
+   writel(0, ha->mem_ptr + IPS_REG_FLAP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   return (0);
+}
+
+/****************************************************************************/
+/*                                                                          */
 /* Routine Name: ips_verify_bios                                            */
 /*                                                                          */
 /* Routine Description:                                                     */
@@ -5187,6 +6551,8 @@
    u8    checksum;
    int   i;
 
+   METHOD_TRACE("ips_verify_bios", 1);
+
    /* test 1st byte */
    outl(0, ha->io_addr + IPS_REG_FLAP);
    if (ha->revision_id == IPS_REVID_TROMBONE64)
@@ -5209,6 +6575,53 @@
          UDELAY(5); /* 5 us */
 
       checksum = (u8) checksum + inb(ha->io_addr + IPS_REG_FLDP);
+   }
+
+   if (checksum != 0)
+      /* failure */
+      return (1);
+   else
+      /* success */
+      return (0);
+}
+
+/****************************************************************************/
+/*                                                                          */
+/* Routine Name: ips_verify_bios_memio                                      */
+/*                                                                          */
+/* Routine Description:                                                     */
+/*   Verify the BIOS on the adapter                                         */
+/*                                                                          */
+/****************************************************************************/
+static int
+ips_verify_bios_memio(ips_ha_t *ha, char *buffer, int buffersize) {
+   u8    checksum;
+   int   i;
+
+   METHOD_TRACE("ips_verify_bios_memio", 1);
+
+   /* test 1st byte */
+   writel(0, ha->mem_ptr + IPS_REG_FLAP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+
+   if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0x55)
+      return (1);
+
+   writel(1, ha->mem_ptr + IPS_REG_FLAP);
+   if (ha->revision_id == IPS_REVID_TROMBONE64)
+      UDELAY(5); /* 5 us */
+   if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0xAA)
+      return (1);
+
+   checksum = 0xff;
+   for (i = 2; i < buffersize; i++) {
+
+      writel(i, ha->mem_ptr + IPS_REG_FLAP);
+      if (ha->revision_id == IPS_REVID_TROMBONE64)
+         UDELAY(5); /* 5 us */
+
+      checksum = (u8) checksum + readb(ha->mem_ptr + IPS_REG_FLDP);
    }
 
    if (checksum != 0)

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)