[SCSI] aic79xx: sane pci probing
[pandora-kernel.git] / drivers / scsi / aic7xxx / aic79xx_osm.c
index 10a2570..3feb739 100644 (file)
@@ -59,11 +59,6 @@ static struct scsi_transport_template *ahd_linux_transport_template = NULL;
 #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;
-
 /*
  * Bucket size for counting good commands in between bad ones.
  */
@@ -302,13 +297,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:
@@ -387,7 +375,9 @@ 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*);
@@ -417,50 +407,6 @@ ahd_linux_unmap_scb(struct ahd_softc *ahd, struct scb *scb)
 #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;
-}
-
 /*
  * Return a string describing the driver.
  */
@@ -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,
@@ -1072,7 +1019,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) {
@@ -1100,29 +1047,6 @@ ahd_linux_get_memsize(void)
        return ((uint64_t)si.totalram << PAGE_SHIFT);
 }
 
-/*
- * 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
@@ -1617,9 +1541,9 @@ ahd_send_async(struct ahd_softc *ahd, char channel,
                 * are identical to those last reported.
                 */
                starget = ahd->platform_data->starget[target];
-               targ = scsi_transport_target_data(starget);
-               if (targ == NULL)
+               if (starget == NULL)
                        break;
+               targ = scsi_transport_target_data(starget);
 
                target_ppr_options =
                        (spi_dt(starget) ? MSG_EXT_PPR_DT_REQ : 0)
@@ -1628,7 +1552,8 @@ ahd_send_async(struct ahd_softc *ahd, char channel,
                        + (spi_rd_strm(starget) ? MSG_EXT_PPR_RD_STRM : 0)
                        + (spi_pcomp_en(starget) ? MSG_EXT_PPR_PCOMP_EN : 0)
                        + (spi_rti(starget) ? MSG_EXT_PPR_RTI : 0)
-                       + (spi_wr_flow(starget) ? MSG_EXT_PPR_WR_FLOW : 0);
+                       + (spi_wr_flow(starget) ? MSG_EXT_PPR_WR_FLOW : 0)
+                       + (spi_hold_mcs(starget) ? MSG_EXT_PPR_HOLD_MCS : 0);
 
                if (tinfo->curr.period == spi_period(starget)
                    && tinfo->curr.width == spi_width(starget)
@@ -1647,6 +1572,7 @@ ahd_send_async(struct ahd_softc *ahd, char channel,
                spi_pcomp_en(starget) =  tinfo->curr.ppr_options & MSG_EXT_PPR_PCOMP_EN ? 1 : 0;
                spi_rti(starget) =  tinfo->curr.ppr_options &  MSG_EXT_PPR_RTI ? 1 : 0;
                spi_wr_flow(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_WR_FLOW ? 1 : 0;
+               spi_hold_mcs(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_HOLD_MCS ? 1 : 0;
                spi_display_xfer_agreement(starget);
                break;
        }
@@ -2324,8 +2250,6 @@ done:
        return (retval);
 }
 
-static void ahd_linux_exit(void);
-
 static void ahd_linux_set_width(struct scsi_target *starget, int width)
 {
        struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
@@ -2438,6 +2362,7 @@ static void ahd_linux_set_dt(struct scsi_target *starget, int dt)
        unsigned int ppr_options = tinfo->goal.ppr_options
                & ~MSG_EXT_PPR_DT_REQ;
        unsigned int period = tinfo->goal.period;
+       unsigned int width = tinfo->goal.width;
        unsigned long flags;
 
 #ifdef AHD_DEBUG
@@ -2447,8 +2372,8 @@ static void ahd_linux_set_dt(struct scsi_target *starget, int dt)
 #endif
        if (dt) {
                ppr_options |= MSG_EXT_PPR_DT_REQ;
-               if (period > 9)
-                       period = 9; /* at least 12.5ns for DT */
+               if (!width)
+                       ahd_linux_set_width(starget, 1);
        } else {
                if (period <= 9)
                        period = 10; /* If resetting DT, period must be >= 25ns */
@@ -2697,6 +2622,38 @@ static void ahd_linux_set_pcomp_en(struct scsi_target *starget, int pcomp)
        ahd_unlock(ahd, &flags);
 }
 
+static void ahd_linux_set_hold_mcs(struct scsi_target *starget, int hold)
+{
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+       struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata);
+       struct ahd_tmode_tstate *tstate;
+       struct ahd_initiator_tinfo *tinfo 
+               = ahd_fetch_transinfo(ahd,
+                                     starget->channel + 'A',
+                                     shost->this_id, starget->id, &tstate);
+       struct ahd_devinfo devinfo;
+       unsigned int ppr_options = tinfo->goal.ppr_options
+               & ~MSG_EXT_PPR_HOLD_MCS;
+       unsigned int period = tinfo->goal.period;
+       unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ;
+       unsigned long flags;
+
+       if (hold)
+               ppr_options |= MSG_EXT_PPR_HOLD_MCS;
+
+       ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+                           starget->channel + 'A', ROLE_INITIATOR);
+       ahd_find_syncrate(ahd, &period, &ppr_options,
+                         dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
+
+       ahd_lock(ahd, &flags);
+       ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
+                        ppr_options, AHD_TRANS_GOAL, FALSE);
+       ahd_unlock(ahd, &flags);
+}
+
+
+
 static struct spi_function_template ahd_linux_transport_functions = {
        .set_offset     = ahd_linux_set_offset,
        .show_offset    = 1,
@@ -2718,25 +2675,35 @@ static struct spi_function_template ahd_linux_transport_functions = {
        .show_rti       = 1,
        .set_pcomp_en   = ahd_linux_set_pcomp_en,
        .show_pcomp_en  = 1,
+       .set_hold_mcs   = ahd_linux_set_hold_mcs,
+       .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);
-       ahd_linux_exit();
-       return -ENODEV;
+
+       error = ahd_linux_pci_init();
+       if (error)
+               spi_release_transport(ahd_linux_transport_template);
+       return error;
 }
 
 static void __exit