]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/scsi/libata-core.c
[libata irq-pio] use PageHighMem() to optimize the kmap_atomic() usage
[linux-2.6.git] / drivers / scsi / libata-core.c
index 03d422e99e585f674a6002d061da0fbda82e9044..5e750c33dd9684ce8da629de44f083f63e017fbe 100644 (file)
@@ -48,6 +48,7 @@
 #include <linux/completion.h>
 #include <linux/suspend.h>
 #include <linux/workqueue.h>
+#include <linux/jiffies.h>
 #include <scsi/scsi.h>
 #include "scsi.h"
 #include "scsi_priv.h"
@@ -70,7 +71,6 @@ static int fgb(u32 bitmap);
 static int ata_choose_xfer_mode(struct ata_port *ap,
                                u8 *xfer_mode_out,
                                unsigned int *xfer_shift_out);
-static int ata_qc_complete_noop(struct ata_queued_cmd *qc, u8 drv_stat);
 static void __ata_qc_complete(struct ata_queued_cmd *qc);
 
 static unsigned int ata_unique_id = 1;
@@ -1330,6 +1330,9 @@ retry:
                ap->cdb_len = (unsigned int) rc;
                ap->host->max_cmd_len = (unsigned char) ap->cdb_len;
 
+               if (ata_id_cdb_intr(dev->id))
+                       dev->flags |= ATA_DFLAG_CDB_INTR;
+
                /* print device info to dmesg */
                printk(KERN_INFO "ata%u: dev %u ATAPI, max %s\n",
                       ap->id, device,
@@ -2491,7 +2494,6 @@ void ata_poll_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat)
        unsigned long flags;
 
        spin_lock_irqsave(&ap->host_set->lock, flags);
-       ap->flags &= ~ATA_FLAG_NOINTR;
        ata_irq_on(ap);
        ata_qc_complete(qc, drv_stat);
        spin_unlock_irqrestore(&ap->host_set->lock, flags);
@@ -2757,7 +2759,23 @@ static void ata_pio_sector(struct ata_queued_cmd *qc)
        page = nth_page(page, (offset >> PAGE_SHIFT));
        offset %= PAGE_SIZE;
 
-       buf = kmap(page) + offset;
+       DPRINTK("data %s\n", qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read");
+
+       if (PageHighMem(page)) {
+               unsigned long flags;
+
+               local_irq_save(flags);
+               buf = kmap_atomic(page, KM_IRQ0);
+
+               /* do the actual data transfer */
+               ata_data_xfer(ap, buf + offset, ATA_SECT_SIZE, do_write);
+
+               kunmap_atomic(buf, KM_IRQ0);
+               local_irq_restore(flags);
+       } else {
+               buf = page_address(page);
+               ata_data_xfer(ap, buf + offset, ATA_SECT_SIZE, do_write);
+       }
 
        qc->cursect++;
        qc->cursg_ofs++;
@@ -2766,14 +2784,114 @@ static void ata_pio_sector(struct ata_queued_cmd *qc)
                qc->cursg++;
                qc->cursg_ofs = 0;
        }
+}
 
-       DPRINTK("data %s\n", qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read");
+/**
+ *     atapi_send_cdb - Write CDB bytes to hardware
+ *     @ap: Port to which ATAPI device is attached.
+ *     @qc: Taskfile currently active
+ *
+ *     When device has indicated its readiness to accept
+ *     a CDB, this function is called.  Send the CDB.
+ *
+ *     LOCKING:
+ *     caller.
+ */
+
+static void atapi_send_cdb(struct ata_port *ap, struct ata_queued_cmd *qc)
+{
+       /* send SCSI cdb */
+       DPRINTK("send cdb\n");
+       assert(ap->cdb_len >= 12);
+
+       ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1);
+       ata_altstatus(ap); /* flush */
+
+       switch (qc->tf.protocol) {
+       case ATA_PROT_ATAPI:
+               ap->hsm_task_state = HSM_ST;
+               break;
+       case ATA_PROT_ATAPI_NODATA:
+               ap->hsm_task_state = HSM_ST_LAST;
+               break;
+       case ATA_PROT_ATAPI_DMA:
+               ap->hsm_task_state = HSM_ST_LAST;
+               /* initiate bmdma */
+               ap->ops->bmdma_start(qc);
+               break;
+       }
+}
+
+/**
+ *     ata_dataout_task - Write first data block to hardware
+ *     @_data: Port to which ATA/ATAPI device is attached.
+ *
+ *     When device has indicated its readiness to accept
+ *     the data, this function sends out the CDB or 
+ *     the first data block by PIO.
+ *     After this, 
+ *       - If polling, ata_pio_task() handles the rest.
+ *       - Otherwise, interrupt handler takes over.
+ *
+ *     LOCKING:
+ *     Kernel thread context (may sleep)
+ */
+
+static void ata_dataout_task(void *_data)
+{
+       struct ata_port *ap = _data;
+       struct ata_queued_cmd *qc;
+       u8 status;
+       unsigned long flags;
+
+       qc = ata_qc_from_tag(ap, ap->active_tag);
+       assert(qc != NULL);
+       assert(qc->flags & ATA_QCFLAG_ACTIVE);
+
+       /* sleep-wait for BSY to clear */
+       DPRINTK("busy wait\n");
+       if (ata_busy_sleep(ap, ATA_TMOUT_DATAOUT_QUICK, ATA_TMOUT_DATAOUT))
+               goto err_out;
+
+       /* make sure DRQ is set */
+       status = ata_chk_status(ap);
+       if ((status & (ATA_BUSY | ATA_DRQ)) != ATA_DRQ)
+               goto err_out;
+
+       /* 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.
+        */
+       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_sector() might change the state to HSM_ST_LAST.
+                * so, the state is changed here before ata_pio_sector().
+                */
+               ap->hsm_task_state = HSM_ST;
+               ata_pio_sector(qc);
+               ata_altstatus(ap); /* flush */
+       } else
+               /* send CDB */
+               atapi_send_cdb(ap, qc);
+
+       /* if polling, ata_pio_task() handles the rest.
+        * otherwise, interrupt handler takes over from here.
+        */
+       if (qc->tf.flags & ATA_TFLAG_POLLING)
+               queue_work(ata_wq, &ap->pio_task);
+
+       spin_unlock_irqrestore(&ap->host_set->lock, flags);
 
-       /* do the actual data transfer */
-       do_write = (qc->tf.flags & ATA_TFLAG_WRITE);
-       ata_data_xfer(ap, buf, ATA_SECT_SIZE, do_write);
+       return;
 
-       kunmap(page);
+err_out:
+       ata_pio_error(ap);
 }
 
 /**
@@ -2839,7 +2957,23 @@ next_sg:
        /* don't cross page boundaries */
        count = min(count, (unsigned int)PAGE_SIZE - offset);
 
-       buf = kmap(page) + offset;
+       DPRINTK("data %s\n", qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read");
+
+       if (PageHighMem(page)) {
+               unsigned long flags;
+
+               local_irq_save(flags);
+               buf = kmap_atomic(page, KM_IRQ0);
+
+               /* do the actual data transfer */
+               ata_data_xfer(ap, buf + offset, count, do_write);
+
+               kunmap_atomic(buf, KM_IRQ0);
+               local_irq_restore(flags);
+       } else {
+               buf = page_address(page);
+               ata_data_xfer(ap, buf + offset, count, do_write);
+       }
 
        bytes -= count;
        qc->curbytes += count;
@@ -2850,13 +2984,6 @@ next_sg:
                qc->cursg_ofs = 0;
        }
 
-       DPRINTK("data %s\n", qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read");
-
-       /* do the actual data transfer */
-       ata_data_xfer(ap, buf, count, do_write);
-
-       kunmap(page);
-
        if (bytes)
                goto next_sg;
 }
@@ -2894,6 +3021,8 @@ static void atapi_pio_bytes(struct ata_queued_cmd *qc)
        if (do_write != i_write)
                goto err_out;
 
+       VPRINTK("ata%u: xfering %d bytes\n", ap->id, bytes);
+
        __atapi_pio_bytes(qc, bytes);
 
        return;
@@ -3014,52 +3143,6 @@ fsm_start:
                goto fsm_start;
 }
 
-static void atapi_request_sense(struct ata_port *ap, struct ata_device *dev,
-                               struct scsi_cmnd *cmd)
-{
-       DECLARE_COMPLETION(wait);
-       struct ata_queued_cmd *qc;
-       unsigned long flags;
-       int rc;
-
-       DPRINTK("ATAPI request sense\n");
-
-       qc = ata_qc_new_init(ap, dev);
-       BUG_ON(qc == NULL);
-
-       /* FIXME: is this needed? */
-       memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
-
-       ata_sg_init_one(qc, cmd->sense_buffer, sizeof(cmd->sense_buffer));
-       qc->dma_dir = DMA_FROM_DEVICE;
-
-       memset(&qc->cdb, 0, ap->cdb_len);
-       qc->cdb[0] = REQUEST_SENSE;
-       qc->cdb[4] = SCSI_SENSE_BUFFERSIZE;
-
-       qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
-       qc->tf.command = ATA_CMD_PACKET;
-
-       qc->tf.protocol = ATA_PROT_ATAPI;
-       qc->tf.lbam = (8 * 1024) & 0xff;
-       qc->tf.lbah = (8 * 1024) >> 8;
-       qc->nbytes = SCSI_SENSE_BUFFERSIZE;
-
-       qc->waiting = &wait;
-       qc->complete_fn = ata_qc_complete_noop;
-
-       spin_lock_irqsave(&ap->host_set->lock, flags);
-       rc = ata_qc_issue(qc);
-       spin_unlock_irqrestore(&ap->host_set->lock, flags);
-
-       if (rc)
-               ata_port_disable(ap);
-       else
-               wait_for_completion(&wait);
-
-       DPRINTK("EXIT\n");
-}
-
 /**
  *     ata_qc_timeout - Handle timeout of queued command
  *     @qc: Command that timed out
@@ -3140,6 +3223,8 @@ static void ata_qc_timeout(struct ata_queued_cmd *qc)
                printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x host_stat 0x%x\n",
                       ap->id, qc->tf.command, drv_stat, host_stat);
 
+               ap->hsm_task_state = HSM_ST_IDLE;
+
                /* complete taskfile transaction */
                ata_qc_complete(qc, drv_stat);
                break;
@@ -3177,14 +3262,14 @@ void ata_eng_timeout(struct ata_port *ap)
        DPRINTK("ENTER\n");
 
        qc = ata_qc_from_tag(ap, ap->active_tag);
-       if (!qc) {
+       if (qc)
+               ata_qc_timeout(qc);
+       else {
                printk(KERN_ERR "ata%u: BUG: timeout without command\n",
                       ap->id);
                goto out;
        }
 
-       ata_qc_timeout(qc);
-
 out:
        DPRINTK("EXIT\n");
 }
@@ -3253,7 +3338,7 @@ struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap,
        return qc;
 }
 
-static int ata_qc_complete_noop(struct ata_queued_cmd *qc, u8 drv_stat)
+int ata_qc_complete_noop(struct ata_queued_cmd *qc, u8 drv_stat)
 {
        return 0;
 }
@@ -3434,43 +3519,103 @@ int ata_qc_issue_prot(struct ata_queued_cmd *qc)
 {
        struct ata_port *ap = qc->ap;
 
+       /* Use polling pio if the LLD doesn't handle
+        * interrupt driven pio and atapi CDB interrupt.
+        */
+       if (ap->flags & ATA_FLAG_PIO_POLLING) {
+               switch (qc->tf.protocol) {
+               case ATA_PROT_PIO:
+               case ATA_PROT_ATAPI:
+               case ATA_PROT_ATAPI_NODATA:
+                       qc->tf.flags |= ATA_TFLAG_POLLING;
+                       break;
+               case ATA_PROT_ATAPI_DMA:
+                       if (qc->dev->flags & ATA_DFLAG_CDB_INTR)
+                               BUG();
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       /* select the device */
        ata_dev_select(ap, qc->dev->devno, 1, 0);
 
+       /* start the command */
        switch (qc->tf.protocol) {
        case ATA_PROT_NODATA:
+               if (qc->tf.flags & ATA_TFLAG_POLLING)
+                       ata_qc_set_polling(qc);
+
                ata_tf_to_host_nolock(ap, &qc->tf);
+               ap->hsm_task_state = HSM_ST_LAST;
+
+               if (qc->tf.flags & ATA_TFLAG_POLLING)
+                       queue_work(ata_wq, &ap->pio_task);
+
                break;
 
        case ATA_PROT_DMA:
+               assert(!(qc->tf.flags & ATA_TFLAG_POLLING));
+
                ap->ops->tf_load(ap, &qc->tf);   /* load tf registers */
                ap->ops->bmdma_setup(qc);           /* set up bmdma */
                ap->ops->bmdma_start(qc);           /* initiate bmdma */
+               ap->hsm_task_state = HSM_ST_LAST;
                break;
 
-       case ATA_PROT_PIO: /* load tf registers, initiate polling pio */
-               ata_qc_set_polling(qc);
-               ata_tf_to_host_nolock(ap, &qc->tf);
-               ap->hsm_task_state = HSM_ST;
-               queue_work(ata_wq, &ap->pio_task);
-               break;
+       case ATA_PROT_PIO:
+               if (qc->tf.flags & ATA_TFLAG_POLLING)
+                       ata_qc_set_polling(qc);
 
-       case ATA_PROT_ATAPI:
-               ata_qc_set_polling(qc);
                ata_tf_to_host_nolock(ap, &qc->tf);
-               queue_work(ata_wq, &ap->packet_task);
+
+               if (qc->tf.flags & ATA_TFLAG_WRITE) {
+                       /* PIO data out protocol */
+                       ap->hsm_task_state = HSM_ST_FIRST;
+                       queue_work(ata_wq, &ap->dataout_task);
+
+                       /* always send first data block using
+                        * the ata_dataout_task() codepath.
+                        */
+               } else {
+                       /* PIO data in protocol */
+                       ap->hsm_task_state = HSM_ST;
+
+                       if (qc->tf.flags & ATA_TFLAG_POLLING)
+                               queue_work(ata_wq, &ap->pio_task);
+
+                       /* if polling, ata_pio_task() handles the rest.
+                        * otherwise, interrupt handler takes over from here.
+                        */
+               }
+
                break;
 
+       case ATA_PROT_ATAPI:
        case ATA_PROT_ATAPI_NODATA:
-               ap->flags |= ATA_FLAG_NOINTR;
+               if (qc->tf.flags & ATA_TFLAG_POLLING)
+                       ata_qc_set_polling(qc);
+
                ata_tf_to_host_nolock(ap, &qc->tf);
-               queue_work(ata_wq, &ap->packet_task);
+               ap->hsm_task_state = HSM_ST_FIRST;
+
+               /* send cdb by polling if no cdb interrupt */
+               if ((!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) ||
+                   (qc->tf.flags & ATA_TFLAG_POLLING))
+                       queue_work(ata_wq, &ap->dataout_task);
                break;
 
        case ATA_PROT_ATAPI_DMA:
-               ap->flags |= ATA_FLAG_NOINTR;
+               assert(!(qc->tf.flags & ATA_TFLAG_POLLING));
+
                ap->ops->tf_load(ap, &qc->tf);   /* load tf registers */
                ap->ops->bmdma_setup(qc);           /* set up bmdma */
-               queue_work(ata_wq, &ap->packet_task);
+               ap->hsm_task_state = HSM_ST_FIRST;
+
+               /* send cdb by polling if no cdb interrupt */
+               if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR))
+                       queue_work(ata_wq, &ap->dataout_task);
                break;
 
        default:
@@ -3731,47 +3876,142 @@ void ata_bmdma_stop(struct ata_queued_cmd *qc)
 inline unsigned int ata_host_intr (struct ata_port *ap,
                                   struct ata_queued_cmd *qc)
 {
-       u8 status, host_stat;
-
-       switch (qc->tf.protocol) {
+       u8 status, host_stat = 0;
 
-       case ATA_PROT_DMA:
-       case ATA_PROT_ATAPI_DMA:
-       case ATA_PROT_ATAPI:
-               /* check status of DMA engine */
-               host_stat = ap->ops->bmdma_status(ap);
-               VPRINTK("ata%u: host_stat 0x%X\n", ap->id, host_stat);
+       VPRINTK("ata%u: protocol %d task_state %d\n",
+               ap->id, qc->tf.protocol, ap->hsm_task_state);
 
-               /* if it's not our irq... */
-               if (!(host_stat & ATA_DMA_INTR))
+       /* Check whether we are expecting interrupt in this state */
+       switch (ap->hsm_task_state) {
+       case HSM_ST_FIRST:
+               /* 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.
+                */
+               if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR))
                        goto idle_irq;
+               break;
+       case HSM_ST_LAST:
+               if (qc->tf.protocol == ATA_PROT_DMA ||
+                   qc->tf.protocol == ATA_PROT_ATAPI_DMA) {
+                       /* check status of DMA engine */
+                       host_stat = ap->ops->bmdma_status(ap);
+                       VPRINTK("ata%u: host_stat 0x%X\n", ap->id, host_stat);
+
+                       /* if it's not our irq... */
+                       if (!(host_stat & ATA_DMA_INTR))
+                               goto idle_irq;
+
+                       /* before we do anything else, clear DMA-Start bit */
+                       ap->ops->bmdma_stop(qc);
+               }
+               break;
+       case HSM_ST:
+               break;
+       default:
+               goto idle_irq;
+       }
 
-               /* before we do anything else, clear DMA-Start bit */
-               ap->ops->bmdma_stop(qc);
+       /* check altstatus */
+       status = ata_altstatus(ap);
+       if (status & ATA_BUSY)
+               goto idle_irq;
 
-               /* fall through */
+       /* check main status, clearing INTRQ */
+       status = ata_chk_status(ap);
+       if (unlikely(status & ATA_BUSY))
+               goto idle_irq;
 
-       case ATA_PROT_ATAPI_NODATA:
-       case ATA_PROT_NODATA:
-               /* check altstatus */
-               status = ata_altstatus(ap);
-               if (status & ATA_BUSY)
-                       goto idle_irq;
+       DPRINTK("ata%u: protocol %d task_state %d (dev_stat 0x%X)\n",
+               ap->id, qc->tf.protocol, ap->hsm_task_state, status);
 
-               /* check main status, clearing INTRQ */
-               status = ata_chk_status(ap);
-               if (unlikely(status & ATA_BUSY))
-                       goto idle_irq;
-               DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n",
-                       ap->id, qc->tf.protocol, status);
+       /* ack bmdma irq events */
+       ap->ops->irq_clear(ap);
 
-               /* ack bmdma irq events */
-               ap->ops->irq_clear(ap);
+       /* check error */
+       if (unlikely((status & ATA_ERR) || (host_stat & ATA_DMA_ERR)))
+               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 */
+                       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 */
+                               ap->hsm_task_state = HSM_ST_ERR;
+                               goto fsm_start;
+                       }
+
+                       ata_pio_sector(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 */
+                       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 */
                ata_qc_complete(qc, status);
                break;
 
+       case HSM_ST_ERR:
+               printk(KERN_ERR "ata%u: command error, drv_stat 0x%x host_stat 0x%x\n",
+                      ap->id, status, host_stat);
+
+               ap->hsm_task_state = HSM_ST_IDLE;
+               ata_qc_complete(qc, status | ATA_ERR);
+               break;
        default:
                goto idle_irq;
        }
@@ -3823,11 +4063,11 @@ irqreturn_t ata_interrupt (int irq, void *dev_instance, struct pt_regs *regs)
 
                ap = host_set->ports[i];
                if (ap &&
-                   !(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) {
+                   !(ap->flags & ATA_FLAG_PORT_DISABLED)) {
                        struct ata_queued_cmd *qc;
 
                        qc = ata_qc_from_tag(ap, ap->active_tag);
-                       if (qc && (!(qc->tf.ctl & ATA_NIEN)) &&
+                       if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING)) &&
                            (qc->flags & ATA_QCFLAG_ACTIVE))
                                handled |= ata_host_intr(ap, qc);
                }
@@ -3838,75 +4078,6 @@ irqreturn_t ata_interrupt (int irq, void *dev_instance, struct pt_regs *regs)
        return IRQ_RETVAL(handled);
 }
 
-/**
- *     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)
-{
-       struct ata_port *ap = _data;
-       struct ata_queued_cmd *qc;
-       u8 status;
-
-       qc = ata_qc_from_tag(ap, ap->active_tag);
-       assert(qc != NULL);
-       assert(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))
-               goto err_out;
-
-       /* make sure DRQ is set */
-       status = ata_chk_status(ap);
-       if ((status & (ATA_BUSY | ATA_DRQ)) != ATA_DRQ)
-               goto err_out;
-
-       /* send SCSI cdb */
-       DPRINTK("send cdb\n");
-       assert(ap->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);
-               ap->flags &= ~ATA_FLAG_NOINTR;
-               ata_data_xfer(ap, qc->cdb, ap->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, ap->cdb_len, 1);
-
-               /* PIO commands are handled by polling */
-               ap->hsm_task_state = HSM_ST;
-               queue_work(ata_wq, &ap->pio_task);
-       }
-
-       return;
-
-err_out:
-       ata_poll_qc_complete(qc, ATA_ERR);
-}
-
-
 /**
  *     ata_port_start - Set port up for dma.
  *     @ap: Port to initialize
@@ -4025,7 +4196,7 @@ static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host,
        ap->active_tag = ATA_TAG_POISON;
        ap->last_ctl = 0xFF;
 
-       INIT_WORK(&ap->packet_task, atapi_packet_task, ap);
+       INIT_WORK(&ap->dataout_task, ata_dataout_task, ap);
        INIT_WORK(&ap->pio_task, ata_pio_task, ap);
 
        for (i = 0; i < ATA_MAX_DEVICES; i++)
@@ -4203,7 +4374,7 @@ int ata_device_add(struct ata_probe_ent *ent)
        for (i = 0; i < count; i++) {
                struct ata_port *ap = host_set->ports[i];
 
-               scsi_scan_host(ap->host);
+               ata_scsi_scan_host(ap);
        }
 
        dev_set_drvdata(dev, host_set);
@@ -4363,85 +4534,87 @@ void ata_pci_host_stop (struct ata_host_set *host_set)
  *     ata_pci_init_native_mode - Initialize native-mode driver
  *     @pdev:  pci device to be initialized
  *     @port:  array[2] of pointers to port info structures.
+ *     @ports: bitmap of ports present
  *
  *     Utility function which allocates and initializes an
  *     ata_probe_ent structure for a standard dual-port
  *     PIO-based IDE controller.  The returned ata_probe_ent
  *     structure can be passed to ata_device_add().  The returned
  *     ata_probe_ent structure should then be freed with kfree().
+ *
+ *     The caller need only pass the address of the primary port, the
+ *     secondary will be deduced automatically. If the device has non
+ *     standard secondary port mappings this function can be called twice,
+ *     once for each interface.
  */
 
 struct ata_probe_ent *
-ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port)
+ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int ports)
 {
        struct ata_probe_ent *probe_ent =
                ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
+       int p = 0;
+
        if (!probe_ent)
                return NULL;
 
-       probe_ent->n_ports = 2;
        probe_ent->irq = pdev->irq;
        probe_ent->irq_flags = SA_SHIRQ;
 
-       probe_ent->port[0].cmd_addr = pci_resource_start(pdev, 0);
-       probe_ent->port[0].altstatus_addr =
-       probe_ent->port[0].ctl_addr =
-               pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS;
-       probe_ent->port[0].bmdma_addr = pci_resource_start(pdev, 4);
-
-       probe_ent->port[1].cmd_addr = pci_resource_start(pdev, 2);
-       probe_ent->port[1].altstatus_addr =
-       probe_ent->port[1].ctl_addr =
-               pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS;
-       probe_ent->port[1].bmdma_addr = pci_resource_start(pdev, 4) + 8;
+       if (ports & ATA_PORT_PRIMARY) {
+               probe_ent->port[p].cmd_addr = pci_resource_start(pdev, 0);
+               probe_ent->port[p].altstatus_addr =
+               probe_ent->port[p].ctl_addr =
+                       pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS;
+               probe_ent->port[p].bmdma_addr = pci_resource_start(pdev, 4);
+               ata_std_ports(&probe_ent->port[p]);
+               p++;
+       }
 
-       ata_std_ports(&probe_ent->port[0]);
-       ata_std_ports(&probe_ent->port[1]);
+       if (ports & ATA_PORT_SECONDARY) {
+               probe_ent->port[p].cmd_addr = pci_resource_start(pdev, 2);
+               probe_ent->port[p].altstatus_addr =
+               probe_ent->port[p].ctl_addr =
+                       pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS;
+               probe_ent->port[p].bmdma_addr = pci_resource_start(pdev, 4) + 8;
+               ata_std_ports(&probe_ent->port[p]);
+               p++;
+       }
 
+       probe_ent->n_ports = p;
        return probe_ent;
 }
 
-static struct ata_probe_ent *
-ata_pci_init_legacy_mode(struct pci_dev *pdev, struct ata_port_info **port,
-    struct ata_probe_ent **ppe2)
+static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev, struct ata_port_info **port, int port_num)
 {
-       struct ata_probe_ent *probe_ent, *probe_ent2;
+       struct ata_probe_ent *probe_ent;
 
        probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
        if (!probe_ent)
                return NULL;
-       probe_ent2 = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[1]);
-       if (!probe_ent2) {
-               kfree(probe_ent);
-               return NULL;
-       }
 
-       probe_ent->n_ports = 1;
-       probe_ent->irq = 14;
-
-       probe_ent->hard_port_no = 0;
+       
        probe_ent->legacy_mode = 1;
-
-       probe_ent2->n_ports = 1;
-       probe_ent2->irq = 15;
-
-       probe_ent2->hard_port_no = 1;
-       probe_ent2->legacy_mode = 1;
-
-       probe_ent->port[0].cmd_addr = 0x1f0;
-       probe_ent->port[0].altstatus_addr =
-       probe_ent->port[0].ctl_addr = 0x3f6;
-       probe_ent->port[0].bmdma_addr = pci_resource_start(pdev, 4);
-
-       probe_ent2->port[0].cmd_addr = 0x170;
-       probe_ent2->port[0].altstatus_addr =
-       probe_ent2->port[0].ctl_addr = 0x376;
-       probe_ent2->port[0].bmdma_addr = pci_resource_start(pdev, 4)+8;
-
+       probe_ent->n_ports = 1;
+       probe_ent->hard_port_no = port_num;
+
+       switch(port_num)
+       {
+               case 0:
+                       probe_ent->irq = 14;
+                       probe_ent->port[0].cmd_addr = 0x1f0;
+                       probe_ent->port[0].altstatus_addr =
+                       probe_ent->port[0].ctl_addr = 0x3f6;
+                       break;
+               case 1:
+                       probe_ent->irq = 15;
+                       probe_ent->port[0].cmd_addr = 0x170;
+                       probe_ent->port[0].altstatus_addr =
+                       probe_ent->port[0].ctl_addr = 0x376;
+                       break;
+       }
+       probe_ent->port[0].bmdma_addr = pci_resource_start(pdev, 4) + 8 * port_num;
        ata_std_ports(&probe_ent->port[0]);
-       ata_std_ports(&probe_ent2->port[0]);
-
-       *ppe2 = probe_ent2;
        return probe_ent;
 }
 
@@ -4470,7 +4643,7 @@ ata_pci_init_legacy_mode(struct pci_dev *pdev, struct ata_port_info **port,
 int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
                      unsigned int n_ports)
 {
-       struct ata_probe_ent *probe_ent, *probe_ent2 = NULL;
+       struct ata_probe_ent *probe_ent = NULL, *probe_ent2 = NULL;
        struct ata_port_info *port[2];
        u8 tmp8, mask;
        unsigned int legacy_mode = 0;
@@ -4487,7 +4660,7 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
 
        if ((port[0]->host_flags & ATA_FLAG_NO_LEGACY) == 0
            && (pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
-               /* TODO: support transitioning to native mode? */
+               /* TODO: What if one channel is in native mode ... */
                pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8);
                mask = (1 << 2) | (1 << 0);
                if ((tmp8 & mask) != mask)
@@ -4495,11 +4668,20 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
        }
 
        /* FIXME... */
-       if ((!legacy_mode) && (n_ports > 1)) {
-               printk(KERN_ERR "ata: BUG: native mode, n_ports > 1\n");
-               return -EINVAL;
+       if ((!legacy_mode) && (n_ports > 2)) {
+               printk(KERN_ERR "ata: BUG: native mode, n_ports > 2\n");
+               n_ports = 2;
+               /* For now */
        }
 
+       /* FIXME: Really for ATA it isn't safe because the device may be
+          multi-purpose and we want to leave it alone if it was already
+          enabled. Secondly for shared use as Arjan says we want refcounting
+          
+          Checking dev->is_enabled is insufficient as this is not set at
+          boot for the primary video which is BIOS enabled
+         */
+         
        rc = pci_enable_device(pdev);
        if (rc)
                return rc;
@@ -4510,6 +4692,7 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
                goto err_out;
        }
 
+       /* FIXME: Should use platform specific mappers for legacy port ranges */
        if (legacy_mode) {
                if (!request_region(0x1f0, 8, "libata")) {
                        struct resource *conflict, res;
@@ -4554,10 +4737,17 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
                goto err_out_regions;
 
        if (legacy_mode) {
-               probe_ent = ata_pci_init_legacy_mode(pdev, port, &probe_ent2);
-       } else
-               probe_ent = ata_pci_init_native_mode(pdev, port);
-       if (!probe_ent) {
+               if (legacy_mode & (1 << 0))
+                       probe_ent = ata_pci_init_legacy_port(pdev, port, 0);
+               if (legacy_mode & (1 << 1))
+                       probe_ent2 = ata_pci_init_legacy_port(pdev, port, 1);
+       } else {
+               if (n_ports == 2)
+                       probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
+               else
+                       probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY);
+       }
+       if (!probe_ent && !probe_ent2) {
                rc = -ENOMEM;
                goto err_out_regions;
        }
@@ -4669,6 +4859,27 @@ static void __exit ata_exit(void)
 module_init(ata_init);
 module_exit(ata_exit);
 
+static unsigned long ratelimit_time;
+static spinlock_t ata_ratelimit_lock = SPIN_LOCK_UNLOCKED;
+
+int ata_ratelimit(void)
+{
+       int rc;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ata_ratelimit_lock, flags);
+
+       if (time_after(jiffies, ratelimit_time)) {
+               rc = 1;
+               ratelimit_time = jiffies + (HZ/5);
+       } else
+               rc = 0;
+
+       spin_unlock_irqrestore(&ata_ratelimit_lock, flags);
+
+       return rc;
+}
+
 /*
  * libata is essentially a library of internal helper functions for
  * low-level ATA host controller drivers.  As such, the API/ABI is
@@ -4710,6 +4921,7 @@ EXPORT_SYMBOL_GPL(sata_phy_reset);
 EXPORT_SYMBOL_GPL(__sata_phy_reset);
 EXPORT_SYMBOL_GPL(ata_bus_reset);
 EXPORT_SYMBOL_GPL(ata_port_disable);
+EXPORT_SYMBOL_GPL(ata_ratelimit);
 EXPORT_SYMBOL_GPL(ata_scsi_ioctl);
 EXPORT_SYMBOL_GPL(ata_scsi_queuecmd);
 EXPORT_SYMBOL_GPL(ata_scsi_error);