[PATCH] libata-dev: Convert ata_pio_task() to use the new ata_hsm_move()
[powerpc.git] / drivers / scsi / libata-core.c
index 7214530..eeeeda0 100644 (file)
@@ -3805,9 +3805,36 @@ static void ata_pio_error(struct ata_port *ap)
        ata_poll_qc_complete(qc);
 }
 
-static void ata_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc,
-                        u8 status)
+/**
+ *     ata_hsm_move - move the HSM to the next state.
+ *     @ap: the target ata_port
+ *     @qc: qc on going
+ *     @status: current device status
+ *     @in_wq: 1 if called from workqueue, 0 otherwise
+ *
+ *     RETURNS:
+ *     1 when poll next status needed, 0 otherwise.
+ */
+
+static int ata_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc,
+                        u8 status, int in_wq)
 {
+       unsigned long flags = 0;
+       int poll_next;
+
+       WARN_ON((qc->flags & ATA_QCFLAG_ACTIVE) == 0);
+
+       /* Make sure ata_qc_issue_prot() does not throw things
+        * like DMA polling into the workqueue. Notice that
+        * in_wq is not equivalent to (qc->tf.flags & ATA_TFLAG_POLLING).
+        */
+       WARN_ON(in_wq != ((qc->tf.flags & ATA_TFLAG_POLLING) ||
+                         (ap->hsm_task_state == HSM_ST_FIRST &&
+                          ((qc->tf.protocol == ATA_PROT_PIO &&
+                            (qc->tf.flags & ATA_TFLAG_WRITE)) ||
+                           (is_atapi_taskfile(&qc->tf) &&
+                            !(qc->dev->flags & ATA_DFLAG_CDB_INTR))))));
+
        /* check error */
        if (unlikely(status & (ATA_ERR | ATA_DF))) {
                qc->err_mask |= AC_ERR_DEV;
@@ -3817,9 +3844,13 @@ static void ata_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc,
 fsm_start:
        switch (ap->hsm_task_state) {
        case HSM_ST_FIRST:
-               /* Some pre-ATAPI-4 devices assert INTRQ
-                * at this state when ready to receive CDB.
+               /* Send first data block or PACKET CDB */
+
+               /* If polling, we will stay in the work queue after
+                * sending the data. Otherwise, interrupt handler
+                * takes over after sending the data.
                 */
+               poll_next = (qc->tf.flags & ATA_TFLAG_POLLING);
 
                /* check device status */
                if (unlikely((status & (ATA_BUSY | ATA_DRQ)) != ATA_DRQ)) {
@@ -3829,8 +3860,36 @@ fsm_start:
                        goto fsm_start;
                }
 
-               atapi_send_cdb(ap, qc);
+               /* Send the CDB (atapi) or the first data block (ata pio out).
+                * During the state transition, interrupt handler shouldn't
+                * be invoked before the data transfer is complete and
+                * hsm_task_state is changed. Hence, the following locking.
+                */
+               if (in_wq)
+                       spin_lock_irqsave(&ap->host_set->lock, flags);
 
+               if (qc->tf.protocol == ATA_PROT_PIO) {
+                       /* PIO data out protocol.
+                        * send first data block.
+                        */
+
+                       /* ata_pio_sectors() might change the state
+                        * to HSM_ST_LAST. so, the state is changed here
+                        * before ata_pio_sectors().
+                        */
+                       ap->hsm_task_state = HSM_ST;
+                       ata_pio_sectors(qc);
+                       ata_altstatus(ap); /* flush */
+               } else
+                       /* send CDB */
+                       atapi_send_cdb(ap, qc);
+
+               if (in_wq)
+                       spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+               /* if polling, ata_pio_task() handles the rest.
+                * otherwise, interrupt handler takes over from here.
+                */
                break;
 
        case HSM_ST:
@@ -3870,12 +3929,12 @@ fsm_start:
                }
 
                ata_altstatus(ap); /* flush */
+               poll_next = 1;
                break;
 
        case HSM_ST_LAST:
-               if (unlikely(status & ATA_DRQ)) {
-                       /* handle DRQ=1 as error */
-                       qc->err_mask |= AC_ERR_HSM;
+               if (unlikely(!ata_ok(status))) {
+                       qc->err_mask |= __ac_err_mask(status);
                        ap->hsm_task_state = HSM_ST_ERR;
                        goto fsm_start;
                }
@@ -3884,17 +3943,23 @@ fsm_start:
                DPRINTK("ata%u: command complete, drv_stat 0x%x\n",
                        ap->id, status);
 
+               WARN_ON(qc->err_mask);
+
                ap->hsm_task_state = HSM_ST_IDLE;
 
                /* complete taskfile transaction */
-               qc->err_mask |= ac_err_mask(status);
-               ata_qc_complete(qc);
+               if (in_wq)
+                       ata_poll_qc_complete(qc);
+               else
+                       ata_qc_complete(qc);
+
+               poll_next = 0;
                break;
 
        case HSM_ST_ERR:
                if (qc->tf.command != ATA_CMD_PACKET)
-                       printk(KERN_ERR "ata%u: command error, drv_stat 0x%x host_stat 0x%x\n",
-                              ap->id, status, host_stat);
+                       printk(KERN_ERR "ata%u: command error, drv_stat 0x%x\n",
+                              ap->id, status);
 
                /* make sure qc->err_mask is available to
                 * know what's wrong and recover
@@ -3902,55 +3967,59 @@ fsm_start:
                WARN_ON(qc->err_mask == 0);
 
                ap->hsm_task_state = HSM_ST_IDLE;
-               ata_qc_complete(qc);
+
+               if (in_wq)
+                       ata_poll_qc_complete(qc);
+               else
+                       ata_qc_complete(qc);
+
+               poll_next = 0;
                break;
        default:
-               goto idle_irq;
+               poll_next = 0;
+               BUG();
        }
 
+       return poll_next;
 }
 
 static void ata_pio_task(void *_data)
 {
        struct ata_port *ap = _data;
-       unsigned long timeout;
-       int has_next;
+       struct ata_queued_cmd *qc;
+       u8 status;
+       int poll_next;
 
 fsm_start:
-       timeout = 0;
-       has_next = 1;
+       WARN_ON(ap->hsm_task_state == HSM_ST_IDLE);
 
-       switch (ap->hsm_task_state) {
-       case HSM_ST_FIRST:
-               has_next = ata_pio_first_block(ap);
-               break;
-
-       case HSM_ST:
-               ata_pio_block(ap);
-               break;
-
-       case HSM_ST_LAST:
-               has_next = ata_pio_complete(ap);
-               break;
-
-       case HSM_ST_POLL:
-       case HSM_ST_LAST_POLL:
-               timeout = ata_pio_poll(ap);
-               break;
-
-       case HSM_ST_TMOUT:
-       case HSM_ST_ERR:
-               ata_pio_error(ap);
-               return;
+       qc = ata_qc_from_tag(ap, ap->active_tag);
+       WARN_ON(qc == NULL);
 
-       default:
-               BUG();
-               return;
+       /*
+        * This is purely heuristic.  This is a fast path.
+        * Sometimes when we enter, BSY will be cleared in
+        * a chk-status or two.  If not, the drive is probably seeking
+        * or something.  Snooze for a couple msecs, then
+        * chk-status again.  If still busy, queue delayed work.
+        */
+       status = ata_busy_wait(ap, ATA_BUSY, 5);
+       if (status & ATA_BUSY) {
+               msleep(2);
+               status = ata_busy_wait(ap, ATA_BUSY, 10);
+               if (status & ATA_BUSY) {
+                       ata_port_queue_task(ap, ata_pio_task, ap, ATA_SHORT_PAUSE);
+                       return;
+               }
        }
 
-       if (timeout)
-               ata_port_queue_task(ap, ata_pio_task, ap, timeout);
-       else if (has_next)
+       /* move the HSM */
+       poll_next = ata_hsm_move(ap, qc, status, 1);
+
+       /* another command or interrupt handler
+        * may be running at this point.
+        */
+       if (poll_next)
                goto fsm_start;
 }
 
@@ -4371,6 +4440,10 @@ inline unsigned int ata_host_intr (struct ata_port *ap,
        /* Check whether we are expecting interrupt in this state */
        switch (ap->hsm_task_state) {
        case HSM_ST_FIRST:
+               /* Some pre-ATAPI-4 devices assert INTRQ
+                * at this state when ready to receive CDB.
+                */
+
                /* Check the ATA_DFLAG_CDB_INTR flag is enough here.
                 * The flag was turned on only for atapi devices.
                 * No need to check is_atapi_taskfile(&qc->tf) again.
@@ -4421,7 +4494,7 @@ inline unsigned int ata_host_intr (struct ata_port *ap,
        /* ack bmdma irq events */
        ap->ops->irq_clear(ap);
 
-       ata_hsm_move(ap, qc, status);
+       ata_hsm_move(ap, qc, status, 0);
        return 1;       /* irq handled */
 
 idle_irq: