[SCSI] aic79xx: SLOWCRC fix
[safe/jmp/linux-2.6] / drivers / scsi / aic7xxx / aic79xx_osm.c
index 2f15862..815c063 100644 (file)
 
 static struct scsi_transport_template *ahd_linux_transport_template = NULL;
 
-/*
- * Include aiclib.c as part of our
- * "module dependencies are hard" work around.
- */
-#include "aiclib.c"
-
 #include <linux/init.h>                /* __setup */
 #include <linux/mm.h>          /* For fetching system memory size */
 #include <linux/blkdev.h>              /* For block_size() */
 #include <linux/delay.h>       /* For ssleep/msleep */
-
-/*
- * Lock protecting manipulation of the ahd softc list.
- */
-spinlock_t ahd_list_spinlock;
+#include <linux/device.h>
 
 /*
  * Bucket size for counting good commands in between bad ones.
@@ -303,13 +293,6 @@ static uint32_t aic79xx_pci_parity = ~0;
 uint32_t aic79xx_allow_memio = ~0;
 
 /*
- * aic79xx_detect() has been run, so register all device arrivals
- * immediately with the system rather than deferring to the sorted
- * attachment performed by aic79xx_detect().
- */
-int aic79xx_detect_complete;
-
-/*
  * So that we can set how long each device is given as a selection timeout.
  * The table of values goes like this:
  *   0 - 256ms
@@ -331,6 +314,21 @@ static uint32_t aic79xx_seltime;
  */
 uint32_t aic79xx_periodic_otag;
 
+/* Some storage boxes are using an LSI chip which has a bug making it
+ * impossible to use aic79xx Rev B chip in 320 speeds.  The following
+ * storage boxes have been reported to be buggy:
+ * EonStor 3U 16-Bay: U16U-G3A3
+ * EonStor 2U 12-Bay: U12U-G3A3
+ * SentinelRAID: 2500F R5 / R6
+ * SentinelRAID: 2500F R1
+ * SentinelRAID: 2500F/1500F
+ * SentinelRAID: 150F
+ * 
+ * To get around this LSI bug, you can set your board to 160 mode
+ * or you can enable the SLOWCRC bit.
+ */
+uint32_t aic79xx_slowcrc;
+
 /*
  * Module information and settable options.
  */
@@ -360,6 +358,7 @@ MODULE_PARM_DESC(aic79xx,
 "      amplitude:<int>         Set the signal amplitude (0-7).\n"
 "      seltime:<int>           Selection Timeout:\n"
 "                              (0/256ms,1/128ms,2/64ms,3/32ms)\n"
+"      slowcrc                 Turn on the SLOWCRC bit (Rev B only)\n"          
 "\n"
 "      Sample /etc/modprobe.conf line:\n"
 "              Enable verbose logging\n"
@@ -384,10 +383,10 @@ static int ahd_linux_run_command(struct ahd_softc*,
                                 struct ahd_linux_device *,
                                 struct scsi_cmnd *);
 static void ahd_linux_setup_tag_info_global(char *p);
-static aic_option_callback_t ahd_linux_setup_tag_info;
-static aic_option_callback_t ahd_linux_setup_iocell_info;
 static int  aic79xx_setup(char *c);
-static int  ahd_linux_next_unit(void);
+
+static int ahd_linux_unit;
+
 
 /****************************** Inlines ***************************************/
 static __inline void ahd_linux_unmap_scb(struct ahd_softc*, struct scb*);
@@ -415,51 +414,7 @@ ahd_linux_unmap_scb(struct ahd_softc *ahd, struct scb *scb)
 
 /******************************** Macros **************************************/
 #define BUILD_SCSIID(ahd, cmd)                                         \
-       ((((cmd)->device->id << TID_SHIFT) & TID) | (ahd)->our_id)
-
-/*
- * Try to detect an Adaptec 79XX controller.
- */
-static int
-ahd_linux_detect(struct scsi_host_template *template)
-{
-       struct  ahd_softc *ahd;
-       int     found;
-       int     error = 0;
-
-       /*
-        * If we've been passed any parameters, process them now.
-        */
-       if (aic79xx)
-               aic79xx_setup(aic79xx);
-
-       template->proc_name = "aic79xx";
-
-       /*
-        * Initialize our softc list lock prior to
-        * probing for any adapters.
-        */
-       ahd_list_lockinit();
-
-#ifdef CONFIG_PCI
-       error = ahd_linux_pci_init();
-       if (error)
-               return error;
-#endif
-
-       /*
-        * Register with the SCSI layer all
-        * controllers we've found.
-        */
-       found = 0;
-       TAILQ_FOREACH(ahd, &ahd_tailq, links) {
-
-               if (ahd_linux_register_host(ahd, template) == 0)
-                       found++;
-       }
-       aic79xx_detect_complete++;
-       return found;
-}
+       (((scmd_id(cmd) << TID_SHIFT) & TID) | (ahd)->our_id)
 
 /*
  * Return a string describing the driver.
@@ -497,29 +452,20 @@ ahd_linux_queue(struct scsi_cmnd * cmd, void (*scsi_done) (struct scsi_cmnd *))
 {
        struct   ahd_softc *ahd;
        struct   ahd_linux_device *dev = scsi_transport_device_data(cmd->device);
+       int rtn = SCSI_MLQUEUE_HOST_BUSY;
+       unsigned long flags;
 
        ahd = *(struct ahd_softc **)cmd->device->host->hostdata;
 
-       /*
-        * Close the race of a command that was in the process of
-        * being queued to us just as our simq was frozen.  Let
-        * DV commands through so long as we are only frozen to
-        * perform DV.
-        */
-       if (ahd->platform_data->qfrozen != 0) {
-               printf("%s: queue frozen\n", ahd_name(ahd));
+       ahd_lock(ahd, &flags);
+       if (ahd->platform_data->qfrozen == 0) {
+               cmd->scsi_done = scsi_done;
+               cmd->result = CAM_REQ_INPROG << 16;
+               rtn = ahd_linux_run_command(ahd, dev, cmd);
 
-               return SCSI_MLQUEUE_HOST_BUSY;
        }
-
-       /*
-        * Save the callback on completion function.
-        */
-       cmd->scsi_done = scsi_done;
-
-       cmd->result = CAM_REQ_INPROG << 16;
-
-       return ahd_linux_run_command(ahd, dev, cmd);
+       ahd_unlock(ahd, &flags);
+       return rtn;
 }
 
 static inline struct scsi_target **
@@ -627,7 +573,7 @@ ahd_linux_slave_configure(struct scsi_device *sdev)
 
        ahd = *((struct ahd_softc **)sdev->host->hostdata);
        if (bootverbose)
-               printf("%s: Slave Configure %d\n", ahd_name(ahd), sdev->id);
+               sdev_printk(KERN_INFO, sdev, "Slave Configure\n");
 
        ahd_linux_device_queue_depth(sdev);
 
@@ -746,7 +692,7 @@ ahd_linux_bus_reset(struct scsi_cmnd *cmd)
                       ahd_name(ahd), cmd);
 #endif
        ahd_lock(ahd, &s);
-       found = ahd_reset_channel(ahd, cmd->device->channel + 'A',
+       found = ahd_reset_channel(ahd, scmd_channel(cmd) + 'A',
                                  /*initiate reset*/TRUE);
        ahd_unlock(ahd, &s);
 
@@ -760,6 +706,7 @@ ahd_linux_bus_reset(struct scsi_cmnd *cmd)
 struct scsi_host_template aic79xx_driver_template = {
        .module                 = THIS_MODULE,
        .name                   = "aic79xx",
+       .proc_name              = "aic79xx",
        .proc_info              = ahd_linux_proc_info,
        .info                   = ahd_linux_info,
        .queuecommand           = ahd_linux_queue,
@@ -960,6 +907,86 @@ ahd_linux_setup_tag_info(u_long arg, int instance, int targ, int32_t value)
        }
 }
 
+static char *
+ahd_parse_brace_option(char *opt_name, char *opt_arg, char *end, int depth,
+                      void (*callback)(u_long, int, int, int32_t),
+                      u_long callback_arg)
+{
+       char    *tok_end;
+       char    *tok_end2;
+       int      i;
+       int      instance;
+       int      targ;
+       int      done;
+       char     tok_list[] = {'.', ',', '{', '}', '\0'};
+
+       /* All options use a ':' name/arg separator */
+       if (*opt_arg != ':')
+               return (opt_arg);
+       opt_arg++;
+       instance = -1;
+       targ = -1;
+       done = FALSE;
+       /*
+        * Restore separator that may be in
+        * the middle of our option argument.
+        */
+       tok_end = strchr(opt_arg, '\0');
+       if (tok_end < end)
+               *tok_end = ',';
+       while (!done) {
+               switch (*opt_arg) {
+               case '{':
+                       if (instance == -1) {
+                               instance = 0;
+                       } else {
+                               if (depth > 1) {
+                                       if (targ == -1)
+                                               targ = 0;
+                               } else {
+                                       printf("Malformed Option %s\n",
+                                              opt_name);
+                                       done = TRUE;
+                               }
+                       }
+                       opt_arg++;
+                       break;
+               case '}':
+                       if (targ != -1)
+                               targ = -1;
+                       else if (instance != -1)
+                               instance = -1;
+                       opt_arg++;
+                       break;
+               case ',':
+               case '.':
+                       if (instance == -1)
+                               done = TRUE;
+                       else if (targ >= 0)
+                               targ++;
+                       else if (instance >= 0)
+                               instance++;
+                       opt_arg++;
+                       break;
+               case '\0':
+                       done = TRUE;
+                       break;
+               default:
+                       tok_end = end;
+                       for (i = 0; tok_list[i]; i++) {
+                               tok_end2 = strchr(opt_arg, tok_list[i]);
+                               if ((tok_end2) && (tok_end2 < tok_end))
+                                       tok_end = tok_end2;
+                       }
+                       callback(callback_arg, instance, targ,
+                                simple_strtol(opt_arg, NULL, 0));
+                       opt_arg = tok_end;
+                       break;
+               }
+       }
+       return (opt_arg);
+}
+
 /*
  * Handle Linux boot parameters. This routine allows for assigning a value
  * to a parameter with a ':' between the parameter and the value.
@@ -992,6 +1019,7 @@ aic79xx_setup(char *s)
                { "slewrate", NULL },
                { "precomp", NULL },
                { "amplitude", NULL },
+               { "slowcrc", &aic79xx_slowcrc },
        };
 
        end = strchr(s, '\0');
@@ -1017,18 +1045,18 @@ aic79xx_setup(char *s)
                if (strncmp(p, "global_tag_depth", n) == 0) {
                        ahd_linux_setup_tag_info_global(p + n);
                } else if (strncmp(p, "tag_info", n) == 0) {
-                       s = aic_parse_brace_option("tag_info", p + n, end,
+                       s = ahd_parse_brace_option("tag_info", p + n, end,
                            2, ahd_linux_setup_tag_info, 0);
                } else if (strncmp(p, "slewrate", n) == 0) {
-                       s = aic_parse_brace_option("slewrate",
+                       s = ahd_parse_brace_option("slewrate",
                            p + n, end, 1, ahd_linux_setup_iocell_info,
                            AIC79XX_SLEWRATE_INDEX);
                } else if (strncmp(p, "precomp", n) == 0) {
-                       s = aic_parse_brace_option("precomp",
+                       s = ahd_parse_brace_option("precomp",
                            p + n, end, 1, ahd_linux_setup_iocell_info,
                            AIC79XX_PRECOMP_INDEX);
                } else if (strncmp(p, "amplitude", n) == 0) {
-                       s = aic_parse_brace_option("amplitude",
+                       s = ahd_parse_brace_option("amplitude",
                            p + n, end, 1, ahd_linux_setup_iocell_info,
                            AIC79XX_AMPLITUDE_INDEX);
                } else if (p[n] == ':') {
@@ -1053,6 +1081,7 @@ ahd_linux_register_host(struct ahd_softc *ahd, struct scsi_host_template *templa
        struct  Scsi_Host *host;
        char    *new_name;
        u_long  s;
+       int     retval;
 
        template->name = ahd->description;
        host = scsi_host_alloc(template, sizeof(struct ahd_softc *));
@@ -1061,7 +1090,6 @@ ahd_linux_register_host(struct ahd_softc *ahd, struct scsi_host_template *templa
 
        *((struct ahd_softc **)host->hostdata) = ahd;
        ahd_lock(ahd, &s);
-       scsi_assign_lock(host, &ahd->platform_data->spin_lock);
        ahd->platform_data->host = host;
        host->can_queue = AHD_MAX_QUEUE;
        host->cmd_per_lun = 2;
@@ -1072,7 +1100,7 @@ ahd_linux_register_host(struct ahd_softc *ahd, struct scsi_host_template *templa
        host->max_lun = AHD_NUM_LUNS;
        host->max_channel = 0;
        host->sg_tablesize = AHD_NSEG;
-       ahd_set_unit(ahd, ahd_linux_next_unit());
+       ahd_set_unit(ahd, ahd_linux_unit++);
        sprintf(buf, "scsi%d", host->host_no);
        new_name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
        if (new_name != NULL) {
@@ -1086,9 +1114,15 @@ ahd_linux_register_host(struct ahd_softc *ahd, struct scsi_host_template *templa
 
        host->transportt = ahd_linux_transport_template;
 
-       scsi_add_host(host, &ahd->dev_softc->dev); /* XXX handle failure */
+       retval = scsi_add_host(host, &ahd->dev_softc->dev);
+       if (retval) {
+               printk(KERN_WARNING "aic79xx: scsi_add_host failed\n");
+               scsi_host_put(host);
+               return retval;
+       }
+
        scsi_scan_host(host);
-       return (0);
+       return 0;
 }
 
 uint64_t
@@ -1101,29 +1135,6 @@ ahd_linux_get_memsize(void)
 }
 
 /*
- * Find the smallest available unit number to use
- * for a new device.  We don't just use a static
- * count to handle the "repeated hot-(un)plug"
- * scenario.
- */
-static int
-ahd_linux_next_unit(void)
-{
-       struct ahd_softc *ahd;
-       int unit;
-
-       unit = 0;
-retry:
-       TAILQ_FOREACH(ahd, &ahd_tailq, links) {
-               if (ahd->unit == unit) {
-                       unit++;
-                       goto retry;
-               }
-       }
-       return (unit);
-}
-
-/*
  * Place the SCSI bus into a known state by either resetting it,
  * or forcing transfer negotiations on the next command to any
  * target.
@@ -1196,11 +1207,6 @@ ahd_platform_free(struct ahd_softc *ahd)
        int i, j;
 
        if (ahd->platform_data != NULL) {
-               if (ahd->platform_data->host != NULL) {
-                       scsi_remove_host(ahd->platform_data->host);
-                       scsi_host_put(ahd->platform_data->host);
-               }
-
                /* destroy all of the device and target objects */
                for (i = 0; i < AHD_NUM_TARGETS; i++) {
                        starget = ahd->platform_data->starget[i];
@@ -1230,6 +1236,9 @@ ahd_platform_free(struct ahd_softc *ahd)
                        release_mem_region(ahd->platform_data->mem_busaddr,
                                           0x1000);
                }
+               if (ahd->platform_data->host)
+                       scsi_host_put(ahd->platform_data->host);
+
                free(ahd->platform_data, M_DEVBUF);
        }
 }
@@ -1476,6 +1485,30 @@ ahd_linux_run_command(struct ahd_softc *ahd, struct ahd_linux_device *dev,
        if ((tstate->auto_negotiate & mask) != 0) {
                scb->flags |= SCB_AUTO_NEGOTIATE;
                scb->hscb->control |= MK_MESSAGE;
+               } else if (cmd->cmnd[0] == INQUIRY
+                       && (tinfo->curr.offset != 0
+                        || tinfo->curr.width != MSG_EXT_WDTR_BUS_8_BIT
+                        || tinfo->curr.ppr_options != 0)
+                       && (tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ)==0) {
+                       /*
+                        * The SCSI spec requires inquiry
+                        * commands to complete without
+                        * reporting unit attention conditions.
+                        * Because of this, an inquiry command
+                        * that occurs just after a device is
+                        * reset will result in a data phase
+                        * with mismatched negotiated rates.
+                        * The core already forces a renegotiation
+                        * for reset events that are visible to
+                        * our controller or that we initiate,
+                        * but a third party device reset or a
+                        * hot-plug insertion can still cause this
+                        * issue.  Therefore, we force a re-negotiation
+                        * for every inquiry command unless we
+                        * are async.
+                        */
+                       scb->flags |= SCB_NEGOTIATE;
+                       scb->hscb->control |= MK_MESSAGE;
        }
 
        if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) != 0) {
@@ -2066,16 +2099,17 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
        int    paused;
        int    wait;
        int    disconnected;
+       int    found;
        ahd_mode_state saved_modes;
+       unsigned long flags;
 
        pending_scb = NULL;
        paused = FALSE;
        wait = FALSE;
        ahd = *(struct ahd_softc **)cmd->device->host->hostdata;
 
-       printf("%s:%d:%d:%d: Attempting to queue a%s message:",
-              ahd_name(ahd), cmd->device->channel,
-              cmd->device->id, cmd->device->lun,
+       scmd_printk(KERN_INFO, cmd,
+              "Attempting to queue a%s message:",
               flag == SCB_ABORT ? "n ABORT" : " TARGET RESET");
 
        printf("CDB:");
@@ -2083,7 +2117,7 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
                printf(" 0x%x", cmd->cmnd[cdb_byte]);
        printf("\n");
 
-       spin_lock_irq(&ahd->platform_data->spin_lock);
+       ahd_lock(ahd, &flags);
 
        /*
         * First determine if we currently own this command.
@@ -2099,9 +2133,7 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
                 * No target device for this command exists,
                 * so we must not still own the command.
                 */
-               printf("%s:%d:%d:%d: Is not an active device\n",
-                      ahd_name(ahd), cmd->device->channel, cmd->device->id,
-                      cmd->device->lun);
+               scmd_printk(KERN_INFO, cmd, "Is not an active device\n");
                retval = SUCCESS;
                goto no_cmd;
        }
@@ -2118,18 +2150,17 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
 
                /* Any SCB for this device will do for a target reset */
                LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) {
-                       if (ahd_match_scb(ahd, pending_scb, cmd->device->id,
-                                         cmd->device->channel + 'A',
+                       if (ahd_match_scb(ahd, pending_scb,
+                                         scmd_id(cmd),
+                                         scmd_channel(cmd) + 'A',
                                          CAM_LUN_WILDCARD,
-                                         SCB_LIST_NULL, ROLE_INITIATOR) == 0)
+                                         SCB_LIST_NULL, ROLE_INITIATOR))
                                break;
                }
        }
 
        if (pending_scb == NULL) {
-               printf("%s:%d:%d:%d: Command not found\n",
-                      ahd_name(ahd), cmd->device->channel, cmd->device->id,
-                      cmd->device->lun);
+               scmd_printk(KERN_INFO, cmd, "Command not found\n");
                goto no_cmd;
        }
 
@@ -2152,9 +2183,7 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
        paused = TRUE;
 
        if ((pending_scb->flags & SCB_ACTIVE) == 0) {
-               printf("%s:%d:%d:%d: Command already completed\n",
-                      ahd_name(ahd), cmd->device->channel, cmd->device->id,
-                      cmd->device->lun);
+               scmd_printk(KERN_INFO, cmd, "Command already completed\n");
                goto no_cmd;
        }
 
@@ -2189,7 +2218,8 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
        last_phase = ahd_inb(ahd, LASTPHASE);
        saved_scbptr = ahd_get_scbptr(ahd);
        active_scbptr = saved_scbptr;
-       if (disconnected && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) == 0) {
+       if (disconnected && ((last_phase != P_BUSFREE) || 
+                            (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) == 0)) {
                struct scb *bus_scb;
 
                bus_scb = ahd_lookup_scb(ahd, active_scbptr);
@@ -2207,30 +2237,41 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
         * bus or is in the disconnected state.
         */
        saved_scsiid = ahd_inb(ahd, SAVED_SCSIID);
-       if (last_phase != P_BUSFREE
-        && (SCB_GET_TAG(pending_scb) == active_scbptr
+       if (SCB_GET_TAG(pending_scb) == active_scbptr
             || (flag == SCB_DEVICE_RESET
-                && SCSIID_TARGET(ahd, saved_scsiid) == cmd->device->id))) {
+                && SCSIID_TARGET(ahd, saved_scsiid) == scmd_id(cmd))) {
 
                /*
                 * We're active on the bus, so assert ATN
                 * and hope that the target responds.
                 */
                pending_scb = ahd_lookup_scb(ahd, active_scbptr);
-               pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+               pending_scb->flags |= SCB_RECOVERY_SCB|SCB_DEVICE_RESET;
                ahd_outb(ahd, MSG_OUT, HOST_MSG);
                ahd_outb(ahd, SCSISIGO, last_phase|ATNO);
-               printf("%s:%d:%d:%d: Device is active, asserting ATN\n",
-                      ahd_name(ahd), cmd->device->channel,
-                      cmd->device->id, cmd->device->lun);
+               scmd_printk(KERN_INFO, cmd, "BDR message in message buffer\n");
                wait = TRUE;
+       } else if (last_phase != P_BUSFREE
+                  && ahd_inb(ahd, SCSIPHASE) == 0) {
+               /*
+                * SCB is not identified, there
+                * is no pending REQ, and the sequencer
+                * has not seen a busfree.  Looks like
+                * a stuck connection waiting to
+                * go busfree.  Reset the bus.
+                */
+               found = ahd_reset_channel(ahd, cmd->device->channel + 'A',
+                                         /*Initiate Reset*/TRUE);
+               printf("%s: Issued Channel %c Bus Reset. "
+                      "%d SCBs aborted\n", ahd_name(ahd),
+                      cmd->device->channel + 'A', found);
        } else if (disconnected) {
 
                /*
                 * Actually re-queue this SCB in an attempt
                 * to select the device before it reconnects.
                 */
-               pending_scb->flags |= SCB_RECOVERY_SCB|SCB_ABORT;
+               pending_scb->flags |= SCB_RECOVERY_SCB|flag;
                ahd_set_scbptr(ahd, SCB_GET_TAG(pending_scb));
                pending_scb->hscb->cdb_len = 0;
                pending_scb->hscb->task_attribute = 0;
@@ -2283,9 +2324,7 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
                printf("Device is disconnected, re-queuing SCB\n");
                wait = TRUE;
        } else {
-               printf("%s:%d:%d:%d: Unable to deliver message\n",
-                      ahd_name(ahd), cmd->device->channel,
-                      cmd->device->id, cmd->device->lun);
+               scmd_printk(KERN_INFO, cmd, "Unable to deliver message\n");
                retval = FAILED;
                goto done;
        }
@@ -2306,23 +2345,24 @@ done:
                int ret;
 
                ahd->platform_data->flags |= AHD_SCB_UP_EH_SEM;
-               spin_unlock_irq(&ahd->platform_data->spin_lock);
+               ahd_unlock(ahd, &flags);
+
                init_timer(&timer);
                timer.data = (u_long)ahd;
                timer.expires = jiffies + (5 * HZ);
                timer.function = ahd_linux_sem_timeout;
                add_timer(&timer);
-               printf("Recovery code sleeping\n");
+               printf("%s: Recovery code sleeping\n", ahd_name(ahd));
                down(&ahd->platform_data->eh_sem);
-               printf("Recovery code awake\n");
+               printf("%s: Recovery code awake\n", ahd_name(ahd));
                ret = del_timer_sync(&timer);
                if (ret == 0) {
-                       printf("Timer Expired\n");
+                       printf("%s: Timer Expired (active %d)\n",
+                              ahd_name(ahd), dev->active);
                        retval = FAILED;
                }
-               spin_lock_irq(&ahd->platform_data->spin_lock);
        }
-       spin_unlock_irq(&ahd->platform_data->spin_lock);
+       ahd_unlock(ahd, &flags);
        return (retval);
 }
 
@@ -2755,23 +2795,31 @@ static struct spi_function_template ahd_linux_transport_functions = {
        .show_hold_mcs  = 1,
 };
 
-
-
 static int __init
 ahd_linux_init(void)
 {
-       ahd_linux_transport_template = spi_attach_transport(&ahd_linux_transport_functions);
+       int     error = 0;
+
+       /*
+        * If we've been passed any parameters, process them now.
+        */
+       if (aic79xx)
+               aic79xx_setup(aic79xx);
+
+       ahd_linux_transport_template =
+               spi_attach_transport(&ahd_linux_transport_functions);
        if (!ahd_linux_transport_template)
                return -ENODEV;
+
        scsi_transport_reserve_target(ahd_linux_transport_template,
                                      sizeof(struct ahd_linux_target));
        scsi_transport_reserve_device(ahd_linux_transport_template,
                                      sizeof(struct ahd_linux_device));
-       if (ahd_linux_detect(&aic79xx_driver_template) > 0)
-               return 0;
-       spi_release_transport(ahd_linux_transport_template);
 
-       return -ENODEV;
+       error = ahd_linux_pci_init();
+       if (error)
+               spi_release_transport(ahd_linux_transport_template);
+       return error;
 }
 
 static void __exit