]> err.no Git - linux-2.6/blobdiff - drivers/scsi/aic7xxx/aic7xxx_osm.c
[SCSI] aic7xxx: fix U160 mode
[linux-2.6] / drivers / scsi / aic7xxx / aic7xxx_osm.c
index ca796b9d737bcd2848000352c76c5395baa9f48e..f90efa265ba276d8cdd47235822f3c55ee102705 100644 (file)
@@ -430,7 +430,6 @@ static void ahc_linux_queue_cmd_complete(struct ahc_softc *ahc,
 static void ahc_linux_sem_timeout(u_long arg);
 static void ahc_linux_freeze_simq(struct ahc_softc *ahc);
 static void ahc_linux_release_simq(u_long arg);
-static void ahc_linux_dev_timed_unfreeze(u_long arg);
 static int  ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag);
 static void ahc_linux_initialize_scsi_bus(struct ahc_softc *ahc);
 static u_int ahc_linux_user_tagdepth(struct ahc_softc *ahc,
@@ -457,7 +456,7 @@ static int  ahc_linux_next_unit(void);
 /********************************* Inlines ************************************/
 static __inline struct ahc_linux_device*
                     ahc_linux_get_device(struct ahc_softc *ahc, u_int channel,
-                                         u_int target, u_int lun, int alloc);
+                                         u_int target, u_int lun);
 static __inline void ahc_linux_unmap_scb(struct ahc_softc*, struct scb*);
 
 static __inline int ahc_linux_map_seg(struct ahc_softc *ahc, struct scb *scb,
@@ -466,7 +465,7 @@ static __inline int ahc_linux_map_seg(struct ahc_softc *ahc, struct scb *scb,
 
 static __inline struct ahc_linux_device*
 ahc_linux_get_device(struct ahc_softc *ahc, u_int channel, u_int target,
-                    u_int lun, int alloc)
+                    u_int lun)
 {
        struct ahc_linux_target *targ;
        struct ahc_linux_device *dev;
@@ -476,18 +475,9 @@ ahc_linux_get_device(struct ahc_softc *ahc, u_int channel, u_int target,
        if (channel != 0)
                target_offset += 8;
        targ = ahc->platform_data->targets[target_offset];
-       if (targ == NULL) {
-               if (alloc != 0) {
-                       targ = ahc_linux_alloc_target(ahc, channel, target);
-                       if (targ == NULL)
-                               return (NULL);
-               } else
-                       return (NULL);
-       }
+       BUG_ON(targ == NULL);
        dev = targ->devices[lun];
-       if (dev == NULL && alloc != 0)
-               dev = ahc_linux_alloc_device(ahc, targ, lun);
-       return (dev);
+       return dev;
 }
 
 static __inline void
@@ -640,7 +630,7 @@ ahc_linux_queue(struct scsi_cmnd * cmd, void (*scsi_done) (struct scsi_cmnd *))
                return SCSI_MLQUEUE_HOST_BUSY;
 
        dev = ahc_linux_get_device(ahc, cmd->device->channel, cmd->device->id,
-                                  cmd->device->lun, /*alloc*/TRUE);
+                                  cmd->device->lun);
        BUG_ON(dev == NULL);
 
        cmd->result = CAM_REQ_INPROG << 16;
@@ -652,11 +642,76 @@ static int
 ahc_linux_slave_alloc(struct scsi_device *device)
 {
        struct  ahc_softc *ahc;
+       struct ahc_linux_target *targ;
+       struct scsi_target *starget = device->sdev_target;
+       struct ahc_linux_device *dev;
+       unsigned int target_offset;
+       unsigned long flags;
+       int retval = -ENOMEM;
+
+       target_offset = starget->id;
+       if (starget->channel != 0)
+               target_offset += 8;
 
        ahc = *((struct ahc_softc **)device->host->hostdata);
        if (bootverbose)
                printf("%s: Slave Alloc %d\n", ahc_name(ahc), device->id);
-       return (0);
+       ahc_lock(ahc, &flags);
+       targ = ahc->platform_data->targets[target_offset];
+       if (targ == NULL) {
+               targ = ahc_linux_alloc_target(ahc, starget->channel, starget->id);
+               struct seeprom_config *sc = ahc->seep_config;
+               if (targ == NULL)
+                       goto out;
+
+               if (sc) {
+                       unsigned short scsirate;
+                       struct ahc_devinfo devinfo;
+                       struct ahc_initiator_tinfo *tinfo;
+                       struct ahc_tmode_tstate *tstate;
+                       char channel = starget->channel + 'A';
+                       unsigned int our_id = ahc->our_id;
+
+                       if (starget->channel)
+                               our_id = ahc->our_id_b;
+
+                       if ((ahc->features & AHC_ULTRA2) != 0) {
+                               scsirate = sc->device_flags[target_offset] & CFXFER;
+                       } else {
+                               scsirate = (sc->device_flags[target_offset] & CFXFER) << 4;
+                               if (sc->device_flags[target_offset] & CFSYNCH)
+                                       scsirate |= SOFS;
+                       }
+                       if (sc->device_flags[target_offset] & CFWIDEB) {
+                               scsirate |= WIDEXFER;
+                               spi_max_width(starget) = 1;
+                       } else
+                               spi_max_width(starget) = 0;
+                       spi_min_period(starget) = 
+                               ahc_find_period(ahc, scsirate, AHC_SYNCRATE_DT);
+                       tinfo = ahc_fetch_transinfo(ahc, channel, ahc->our_id,
+                                                   targ->target, &tstate);
+                       ahc_compile_devinfo(&devinfo, our_id, targ->target,
+                                           CAM_LUN_WILDCARD, channel,
+                                           ROLE_INITIATOR);
+                       ahc_set_syncrate(ahc, &devinfo, NULL, 0, 0, 0,
+                                        AHC_TRANS_GOAL, /*paused*/FALSE);
+                       ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+                                     AHC_TRANS_GOAL, /*paused*/FALSE);
+               }
+                       
+       }
+       dev = targ->devices[device->lun];
+       if (dev == NULL) {
+               dev = ahc_linux_alloc_device(ahc, targ, device->lun);
+               if (dev == NULL)
+                       goto out;
+       }
+       retval = 0;
+
+ out:
+       ahc_unlock(ahc, &flags);
+       return retval;
 }
 
 static int
@@ -666,27 +721,20 @@ ahc_linux_slave_configure(struct scsi_device *device)
        struct  ahc_linux_device *dev;
 
        ahc = *((struct ahc_softc **)device->host->hostdata);
+
        if (bootverbose)
                printf("%s: Slave Configure %d\n", ahc_name(ahc), device->id);
-       /*
-        * Since Linux has attached to the device, configure
-        * it so we don't free and allocate the device
-        * structure on every command.
-        */
-       dev = ahc_linux_get_device(ahc, device->channel,
-                                  device->id, device->lun,
-                                  /*alloc*/TRUE);
-       if (dev != NULL) {
-               dev->flags &= ~AHC_DEV_UNCONFIGURED;
-               dev->scsi_device = device;
-               ahc_linux_device_queue_depth(ahc, dev);
-       }
+
+       dev = ahc_linux_get_device(ahc, device->channel, device->id,
+                                  device->lun);
+       dev->scsi_device = device;
+       ahc_linux_device_queue_depth(ahc, dev);
 
        /* Initial Domain Validation */
        if (!spi_initial_dv(device->sdev_target))
                spi_dv_device(device);
 
-       return (0);
+       return 0;
 }
 
 static void
@@ -699,22 +747,11 @@ ahc_linux_slave_destroy(struct scsi_device *device)
        if (bootverbose)
                printf("%s: Slave Destroy %d\n", ahc_name(ahc), device->id);
        dev = ahc_linux_get_device(ahc, device->channel,
-                                  device->id, device->lun,
-                                          /*alloc*/FALSE);
-       /*
-        * Filter out "silly" deletions of real devices by only
-        * deleting devices that have had slave_configure()
-        * called on them.  All other devices that have not
-        * been configured will automatically be deleted by
-        * the refcounting process.
-        */
-       if (dev != NULL
-        && (dev->flags & AHC_DEV_SLAVE_CONFIGURED) != 0) {
-               dev->flags |= AHC_DEV_UNCONFIGURED;
-               if (dev->active == 0
-                && (dev->flags & AHC_DEV_TIMER_ACTIVE) == 0)
-                       ahc_linux_free_device(ahc, dev);
-       }
+                                  device->id, device->lun);
+
+       BUG_ON(dev->active);
+
+       ahc_linux_free_device(ahc, dev);
 }
 
 #if defined(__i386__)
@@ -1361,7 +1398,7 @@ ahc_platform_set_tags(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
 
        dev = ahc_linux_get_device(ahc, devinfo->channel - 'A',
                                   devinfo->target,
-                                  devinfo->lun, /*alloc*/FALSE);
+                                  devinfo->lun);
        if (dev == NULL)
                return;
        was_queuing = dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED);
@@ -1793,8 +1830,6 @@ ahc_linux_alloc_device(struct ahc_softc *ahc,
        if (dev == NULL)
                return (NULL);
        memset(dev, 0, sizeof(*dev));
-       init_timer(&dev->timer);
-       dev->flags = AHC_DEV_UNCONFIGURED;
        dev->lun = lun;
        dev->target = targ;
 
@@ -1817,7 +1852,7 @@ ahc_linux_alloc_device(struct ahc_softc *ahc,
 }
 
 static void
-__ahc_linux_free_device(struct ahc_softc *ahc, struct ahc_linux_device *dev)
+ahc_linux_free_device(struct ahc_softc *ahc, struct ahc_linux_device *dev)
 {
        struct ahc_linux_target *targ;
 
@@ -1829,13 +1864,6 @@ __ahc_linux_free_device(struct ahc_softc *ahc, struct ahc_linux_device *dev)
                ahc_linux_free_target(ahc, targ);
 }
 
-static void
-ahc_linux_free_device(struct ahc_softc *ahc, struct ahc_linux_device *dev)
-{
-       del_timer_sync(&dev->timer);
-       __ahc_linux_free_device(ahc, dev);
-}
-
 void
 ahc_send_async(struct ahc_softc *ahc, char channel,
               u_int target, u_int lun, ac_code code, void *arg)
@@ -2008,8 +2036,6 @@ ahc_done(struct ahc_softc *ahc, struct scb *scb)
                }
        } else if (ahc_get_transaction_status(scb) == CAM_SCSI_STATUS_ERROR) {
                ahc_linux_handle_scsi_status(ahc, dev, scb);
-       } else if (ahc_get_transaction_status(scb) == CAM_SEL_TIMEOUT) {
-               dev->flags |= AHC_DEV_UNCONFIGURED;
        }
 
        if (dev->openings == 1
@@ -2031,11 +2057,6 @@ ahc_done(struct ahc_softc *ahc, struct scb *scb)
        if (dev->active == 0)
                dev->commands_since_idle_or_otag = 0;
 
-       if ((dev->flags & AHC_DEV_UNCONFIGURED) != 0
-           && dev->active == 0
-           && (dev->flags & AHC_DEV_TIMER_ACTIVE) == 0)
-               ahc_linux_free_device(ahc, dev);
-
        if ((scb->flags & SCB_RECOVERY_SCB) != 0) {
                printf("Recovery SCB completes\n");
                if (ahc_get_transaction_status(scb) == CAM_BDR_SENT
@@ -2174,27 +2195,6 @@ ahc_linux_handle_scsi_status(struct ahc_softc *ahc,
                ahc_platform_set_tags(ahc, &devinfo,
                             (dev->flags & AHC_DEV_Q_BASIC)
                           ? AHC_QUEUE_BASIC : AHC_QUEUE_TAGGED);
-               /* FALLTHROUGH */
-       }
-       case SCSI_STATUS_BUSY:
-       {
-               /*
-                * Set a short timer to defer sending commands for
-                * a bit since Linux will not delay in this case.
-                */
-               if ((dev->flags & AHC_DEV_TIMER_ACTIVE) != 0) {
-                       printf("%s:%c:%d: Device Timer still active during "
-                              "busy processing\n", ahc_name(ahc),
-                               dev->target->channel, dev->target->target);
-                       break;
-               }
-               dev->flags |= AHC_DEV_TIMER_ACTIVE;
-               dev->qfrozen++;
-               init_timer(&dev->timer);
-               dev->timer.data = (u_long)dev;
-               dev->timer.expires = jiffies + (HZ/2);
-               dev->timer.function = ahc_linux_dev_timed_unfreeze;
-               add_timer(&dev->timer);
                break;
        }
        }
@@ -2326,24 +2326,6 @@ ahc_linux_release_simq(u_long arg)
                scsi_unblock_requests(ahc->platform_data->host);
 }
 
-static void
-ahc_linux_dev_timed_unfreeze(u_long arg)
-{
-       struct ahc_linux_device *dev;
-       struct ahc_softc *ahc;
-       u_long s;
-
-       dev = (struct ahc_linux_device *)arg;
-       ahc = dev->target->ahc;
-       ahc_lock(ahc, &s);
-       dev->flags &= ~AHC_DEV_TIMER_ACTIVE;
-       if (dev->qfrozen > 0)
-               dev->qfrozen--;
-       if (dev->active == 0)
-               __ahc_linux_free_device(ahc, dev);
-       ahc_unlock(ahc, &s);
-}
-
 static int
 ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
 {
@@ -2384,7 +2366,7 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
         * command, return success.
         */
        dev = ahc_linux_get_device(ahc, cmd->device->channel, cmd->device->id,
-                                  cmd->device->lun, /*alloc*/FALSE);
+                                  cmd->device->lun);
 
        if (dev == NULL) {
                /*
@@ -2697,6 +2679,11 @@ static void ahc_linux_set_period(struct scsi_target *starget, int period)
        if (offset == 0)
                offset = MAX_OFFSET;
 
+       if (period < 9)
+               period = 9;     /* 12.5ns is our minimum */
+       if (period == 9)
+               ppr_options |= MSG_EXT_PPR_DT_REQ;
+
        ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
                            starget->channel + 'A', ROLE_INITIATOR);
 
@@ -2782,6 +2769,12 @@ static void ahc_linux_set_dt(struct scsi_target *starget, int dt)
        unsigned long flags;
        struct ahc_syncrate *syncrate;
 
+       if (dt) {
+               period = 9;     /* 12.5ns is the only period valid for DT */
+               ppr_options |= MSG_EXT_PPR_DT_REQ;
+       } else if (period == 9)
+               period = 10;    /* if resetting DT, period must be >= 25ns */
+
        ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
                            starget->channel + 'A', ROLE_INITIATOR);
        syncrate = ahc_find_syncrate(ahc, &period, &ppr_options,AHC_SYNCRATE_DT);