[PATCH] libata-dev: Convert ata_pio_task() to use the new ata_hsm_move()
[powerpc.git] / drivers / scsi / libata-core.c
index c3c4263..eeeeda0 100644 (file)
@@ -1299,6 +1299,7 @@ static int ata_dev_configure(struct ata_port *ap, struct ata_device *dev,
                                ap->id, device, dev->multi_count);
                }
 
+               dev->cdb_len = 16;
        }
 
        /* ATAPI-specific feature tests */
@@ -3804,121 +3805,222 @@ static void ata_pio_error(struct ata_port *ap)
        ata_poll_qc_complete(qc);
 }
 
-static void ata_pio_task(void *_data)
+/**
+ *     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)
 {
-       struct ata_port *ap = _data;
-       unsigned long timeout;
-       int has_next;
+       unsigned long flags = 0;
+       int poll_next;
 
-fsm_start:
-       timeout = 0;
-       has_next = 1;
+       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;
+               ap->hsm_task_state = HSM_ST_ERR;
+       }
+
+fsm_start:
        switch (ap->hsm_task_state) {
        case HSM_ST_FIRST:
-               has_next = ata_pio_first_block(ap);
+               /* 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)) {
+                       /* Wrong status. Let EH handle this */
+                       qc->err_mask |= AC_ERR_HSM;
+                       ap->hsm_task_state = HSM_ST_ERR;
+                       goto fsm_start;
+               }
+
+               /* 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:
-               ata_pio_block(ap);
+               /* complete command or read/write the data register */
+               if (qc->tf.protocol == ATA_PROT_ATAPI) {
+                       /* ATAPI PIO protocol */
+                       if ((status & ATA_DRQ) == 0) {
+                               /* no more data to transfer */
+                               ap->hsm_task_state = HSM_ST_LAST;
+                               goto fsm_start;
+                       }
+
+                       atapi_pio_bytes(qc);
+
+                       if (unlikely(ap->hsm_task_state == HSM_ST_ERR))
+                               /* bad ireason reported by device */
+                               goto fsm_start;
+
+               } else {
+                       /* ATA PIO protocol */
+                       if (unlikely((status & ATA_DRQ) == 0)) {
+                               /* handle BSY=0, DRQ=0 as error */
+                               qc->err_mask |= AC_ERR_HSM;
+                               ap->hsm_task_state = HSM_ST_ERR;
+                               goto fsm_start;
+                       }
+
+                       ata_pio_sectors(qc);
+
+                       if (ap->hsm_task_state == HSM_ST_LAST &&
+                           (!(qc->tf.flags & ATA_TFLAG_WRITE))) {
+                               /* all data read */
+                               ata_altstatus(ap);
+                               status = ata_chk_status(ap);
+                               goto fsm_start;
+                       }
+               }
+
+               ata_altstatus(ap); /* flush */
+               poll_next = 1;
                break;
 
        case HSM_ST_LAST:
-               has_next = ata_pio_complete(ap);
-               break;
+               if (unlikely(!ata_ok(status))) {
+                       qc->err_mask |= __ac_err_mask(status);
+                       ap->hsm_task_state = HSM_ST_ERR;
+                       goto fsm_start;
+               }
 
-       case HSM_ST_POLL:
-       case HSM_ST_LAST_POLL:
-               timeout = ata_pio_poll(ap);
+               /* no more data to transfer */
+               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 */
+               if (in_wq)
+                       ata_poll_qc_complete(qc);
+               else
+                       ata_qc_complete(qc);
+
+               poll_next = 0;
                break;
 
-       case HSM_ST_TMOUT:
        case HSM_ST_ERR:
-               ata_pio_error(ap);
-               return;
+               if (qc->tf.command != ATA_CMD_PACKET)
+                       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
+                */
+               WARN_ON(qc->err_mask == 0);
+
+               ap->hsm_task_state = HSM_ST_IDLE;
+
+               if (in_wq)
+                       ata_poll_qc_complete(qc);
+               else
+                       ata_qc_complete(qc);
+
+               poll_next = 0;
+               break;
        default:
+               poll_next = 0;
                BUG();
-               return;
        }
 
-       if (timeout)
-               ata_port_queue_task(ap, ata_pio_task, ap, timeout);
-       else if (has_next)
-               goto fsm_start;
+       return poll_next;
 }
 
-/**
- *     atapi_packet_task - Write CDB bytes to hardware
- *     @_data: Port to which ATAPI device is attached.
- *
- *     When device has indicated its readiness to accept
- *     a CDB, this function is called.  Send the CDB.
- *     If DMA is to be performed, exit immediately.
- *     Otherwise, we are in polling mode, so poll
- *     status under operation succeeds or fails.
- *
- *     LOCKING:
- *     Kernel thread context (may sleep)
- */
-
-static void atapi_packet_task(void *_data)
+static void ata_pio_task(void *_data)
 {
        struct ata_port *ap = _data;
        struct ata_queued_cmd *qc;
        u8 status;
+       int poll_next;
+
+fsm_start:
+       WARN_ON(ap->hsm_task_state == HSM_ST_IDLE);
 
        qc = ata_qc_from_tag(ap, ap->active_tag);
        WARN_ON(qc == NULL);
-       WARN_ON(!(qc->flags & ATA_QCFLAG_ACTIVE));
-
-       /* sleep-wait for BSY to clear */
-       DPRINTK("busy wait\n");
-       if (ata_busy_sleep(ap, ATA_TMOUT_CDB_QUICK, ATA_TMOUT_CDB)) {
-               qc->err_mask |= AC_ERR_TIMEOUT;
-               goto err_out;
-       }
-
-       /* make sure DRQ is set */
-       status = ata_chk_status(ap);
-       if ((status & (ATA_BUSY | ATA_DRQ)) != ATA_DRQ) {
-               qc->err_mask |= AC_ERR_HSM;
-               goto err_out;
-       }
 
-       /* send SCSI cdb */
-       DPRINTK("send cdb\n");
-       WARN_ON(qc->dev->cdb_len < 12);
-
-       if (qc->tf.protocol == ATA_PROT_ATAPI_DMA ||
-           qc->tf.protocol == ATA_PROT_ATAPI_NODATA) {
-               unsigned long flags;
-
-               /* Once we're done issuing command and kicking bmdma,
-                * irq handler takes over.  To not lose irq, we need
-                * to clear NOINTR flag before sending cdb, but
-                * interrupt handler shouldn't be invoked before we're
-                * finished.  Hence, the following locking.
-                */
-               spin_lock_irqsave(&ap->host_set->lock, flags);
-#warning FIXME
-               /* ap->flags &= ~ATA_FLAG_NOINTR; */
-               ata_data_xfer(ap, qc->cdb, qc->dev->cdb_len, 1);
-               if (qc->tf.protocol == ATA_PROT_ATAPI_DMA)
-                       ap->ops->bmdma_start(qc);       /* initiate bmdma */
-               spin_unlock_irqrestore(&ap->host_set->lock, flags);
-       } else {
-               ata_data_xfer(ap, qc->cdb, qc->dev->cdb_len, 1);
-
-               /* PIO commands are handled by polling */
-               ap->hsm_task_state = HSM_ST;
-               ata_port_queue_task(ap, ata_pio_task, ap, 0);
+       /*
+        * 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;
+               }
        }
 
-       return;
+       /* move the HSM */
+       poll_next = ata_hsm_move(ap, qc, status, 1);
 
-err_out:
-       ata_poll_qc_complete(qc);
+       /* another command or interrupt handler
+        * may be running at this point.
+        */
+       if (poll_next)
+               goto fsm_start;
 }
 
 /**
@@ -4288,7 +4390,7 @@ unsigned int ata_qc_issue_prot(struct ata_queued_cmd *qc)
                /* send cdb by polling if no cdb interrupt */
                if ((!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) ||
                    (qc->tf.flags & ATA_TFLAG_POLLING))
-                       ata_port_queue_task(ap, atapi_packet_task, ap, 0);
+                       ata_port_queue_task(ap, ata_pio_task, ap, 0);
                break;
 
        case ATA_PROT_ATAPI_DMA:
@@ -4300,7 +4402,7 @@ unsigned int ata_qc_issue_prot(struct ata_queued_cmd *qc)
 
                /* send cdb by polling if no cdb interrupt */
                if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR))
-                       ata_port_queue_task(ap, atapi_packet_task, ap, 0);
+                       ata_port_queue_task(ap, ata_pio_task, ap, 0);
                break;
 
        default:
@@ -4338,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.
@@ -4388,106 +4494,7 @@ inline unsigned int ata_host_intr (struct ata_port *ap,
        /* ack bmdma irq events */
        ap->ops->irq_clear(ap);
 
-       /* check error */
-       if (unlikely(status & (ATA_ERR | ATA_DF))) {
-               qc->err_mask |= AC_ERR_DEV;
-               ap->hsm_task_state = HSM_ST_ERR;
-       }
-
-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.
-                */
-
-               /* check device status */
-               if (unlikely((status & (ATA_BUSY | ATA_DRQ)) != ATA_DRQ)) {
-                       /* Wrong status. Let EH handle this */
-                       qc->err_mask |= AC_ERR_HSM;
-                       ap->hsm_task_state = HSM_ST_ERR;
-                       goto fsm_start;
-               }
-
-               atapi_send_cdb(ap, qc);
-
-               break;
-
-       case HSM_ST:
-               /* complete command or read/write the data register */
-               if (qc->tf.protocol == ATA_PROT_ATAPI) {
-                       /* ATAPI PIO protocol */
-                       if ((status & ATA_DRQ) == 0) {
-                               /* no more data to transfer */
-                               ap->hsm_task_state = HSM_ST_LAST;
-                               goto fsm_start;
-                       }
-                       
-                       atapi_pio_bytes(qc);
-
-                       if (unlikely(ap->hsm_task_state == HSM_ST_ERR))
-                               /* bad ireason reported by device */
-                               goto fsm_start;
-
-               } else {
-                       /* ATA PIO protocol */
-                       if (unlikely((status & ATA_DRQ) == 0)) {
-                               /* handle BSY=0, DRQ=0 as error */
-                               qc->err_mask |= AC_ERR_HSM;
-                               ap->hsm_task_state = HSM_ST_ERR;
-                               goto fsm_start;
-                       }
-
-                       ata_pio_sectors(qc);
-
-                       if (ap->hsm_task_state == HSM_ST_LAST &&
-                           (!(qc->tf.flags & ATA_TFLAG_WRITE))) {
-                               /* all data read */
-                               ata_altstatus(ap);
-                               status = ata_chk_status(ap);
-                               goto fsm_start;
-                       }
-               }
-
-               ata_altstatus(ap); /* flush */
-               break;
-
-       case HSM_ST_LAST:
-               if (unlikely(status & ATA_DRQ)) {
-                       /* handle DRQ=1 as error */
-                       qc->err_mask |= AC_ERR_HSM;
-                       ap->hsm_task_state = HSM_ST_ERR;
-                       goto fsm_start;
-               }
-
-               /* no more data to transfer */
-               DPRINTK("ata%u: command complete, drv_stat 0x%x\n",
-                       ap->id, status);
-
-               ap->hsm_task_state = HSM_ST_IDLE;
-
-               /* complete taskfile transaction */
-               qc->err_mask |= ac_err_mask(status);
-               ata_qc_complete(qc);
-               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);
-
-               /* make sure qc->err_mask is available to 
-                * know what's wrong and recover
-                */
-               WARN_ON(qc->err_mask == 0);
-
-               ap->hsm_task_state = HSM_ST_IDLE;
-               ata_qc_complete(qc);
-               break;
-       default:
-               goto idle_irq;
-       }
-
+       ata_hsm_move(ap, qc, status, 0);
        return 1;       /* irq handled */
 
 idle_irq: