[SCSI] aic79xx: Sequencer update
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / scsi / aic7xxx / aic79xx_osm.c
index 5e023dca4e525b07b36be71509880773b301ee57..2567e29960bd1697565490d2840a3312f6414ab1 100644 (file)
@@ -398,7 +398,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)
+       (((scmd_id(cmd) << TID_SHIFT) & TID) | (ahd)->our_id)
 
 /*
  * Return a string describing the driver.
@@ -436,29 +436,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 **
@@ -685,7 +676,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);
 
@@ -1073,6 +1064,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 *));
@@ -1081,7 +1073,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;
@@ -1106,9 +1097,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
@@ -1471,6 +1468,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) {
@@ -2061,7 +2082,9 @@ 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;
@@ -2077,7 +2100,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.
@@ -2110,10 +2133,11 @@ 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;
                }
        }
@@ -2177,7 +2201,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);
@@ -2195,28 +2220,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);
-               scmd_printk(KERN_INFO, cmd, "Device is active, asserting ATN\n");
+               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;
@@ -2290,23 +2328,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);
 }