Merge branch 'topic/misc' into for-linus
[safe/jmp/linux-2.6] / drivers / ata / libata-sff.c
index 9a10cb0..e3877b6 100644 (file)
@@ -33,6 +33,7 @@
  */
 
 #include <linux/kernel.h>
+#include <linux/gfp.h>
 #include <linux/pci.h>
 #include <linux/libata.h>
 #include <linux/highmem.h>
@@ -65,6 +66,8 @@ const struct ata_port_operations ata_sff_port_ops = {
        .sff_irq_on             = ata_sff_irq_on,
        .sff_irq_clear          = ata_sff_irq_clear,
 
+       .lost_interrupt         = ata_sff_lost_interrupt,
+
        .port_start             = ata_sff_port_start,
 };
 EXPORT_SYMBOL_GPL(ata_sff_port_ops);
@@ -85,6 +88,7 @@ const struct ata_port_operations ata_bmdma32_port_ops = {
        .inherits               = &ata_bmdma_port_ops,
 
        .sff_data_xfer          = ata_sff_data_xfer32,
+       .port_start             = ata_sff_port_start32,
 };
 EXPORT_SYMBOL_GPL(ata_bmdma32_port_ops);
 
@@ -724,17 +728,23 @@ unsigned int ata_sff_data_xfer(struct ata_device *dev, unsigned char *buf,
        else
                iowrite16_rep(data_addr, buf, words);
 
-       /* Transfer trailing byte, if any. */
+       /* Transfer trailing byte, if any. */
        if (unlikely(buflen & 0x01)) {
-               __le16 align_buf[1] = { 0 };
-               unsigned char *trailing_buf = buf + buflen - 1;
+               unsigned char pad[2];
+
+               /* Point buf to the tail of buffer */
+               buf += buflen - 1;
 
+               /*
+                * Use io*16_rep() accessors here as well to avoid pointlessly
+                * swapping bytes to and from on the big endian machines...
+                */
                if (rw == READ) {
-                       align_buf[0] = cpu_to_le16(ioread16(data_addr));
-                       memcpy(trailing_buf, align_buf, 1);
+                       ioread16_rep(data_addr, pad, 1);
+                       *buf = pad[0];
                } else {
-                       memcpy(align_buf, trailing_buf, 1);
-                       iowrite16(le16_to_cpu(align_buf[0]), data_addr);
+                       pad[0] = *buf;
+                       iowrite16_rep(data_addr, pad, 1);
                }
                words++;
        }
@@ -768,6 +778,9 @@ unsigned int ata_sff_data_xfer32(struct ata_device *dev, unsigned char *buf,
        unsigned int words = buflen >> 2;
        int slop = buflen & 3;
 
+       if (!(ap->pflags & ATA_PFLAG_PIO32))
+               return ata_sff_data_xfer(dev, buf, buflen, rw);
+
        /* Transfer multiple of 4 bytes */
        if (rw == READ)
                ioread32_rep(data_addr, buf, words);
@@ -783,7 +796,7 @@ unsigned int ata_sff_data_xfer32(struct ata_device *dev, unsigned char *buf,
 
                /*
                 * Use io*_rep() accessors here as well to avoid pointlessly
-                * swapping bytes to and fro on the big endian machines...
+                * swapping bytes to and from on the big endian machines...
                 */
                if (rw == READ) {
                        if (slop < 3)
@@ -881,6 +894,9 @@ static void ata_pio_sector(struct ata_queued_cmd *qc)
                                       do_write);
        }
 
+       if (!do_write)
+               flush_dcache_page(page);
+
        qc->curbytes += qc->sect_size;
        qc->cursg_ofs += qc->sect_size;
 
@@ -1647,11 +1663,12 @@ EXPORT_SYMBOL_GPL(ata_sff_qc_fill_rtf);
  *     RETURNS:
  *     One if interrupt was handled, zero if not (shared irq).
  */
-inline unsigned int ata_sff_host_intr(struct ata_port *ap,
+unsigned int ata_sff_host_intr(struct ata_port *ap,
                                      struct ata_queued_cmd *qc)
 {
        struct ata_eh_info *ehi = &ap->link.eh_info;
        u8 status, host_stat = 0;
+       bool bmdma_stopped = false;
 
        VPRINTK("ata%u: protocol %d task_state %d\n",
                ap->print_id, qc->tf.protocol, ap->hsm_task_state);
@@ -1684,6 +1701,7 @@ inline unsigned int ata_sff_host_intr(struct ata_port *ap,
 
                        /* before we do anything else, clear DMA-Start bit */
                        ap->ops->bmdma_stop(qc);
+                       bmdma_stopped = true;
 
                        if (unlikely(host_stat & ATA_DMA_ERR)) {
                                /* error when transfering data to/from memory */
@@ -1701,8 +1719,14 @@ inline unsigned int ata_sff_host_intr(struct ata_port *ap,
 
        /* check main status, clearing INTRQ if needed */
        status = ata_sff_irq_status(ap);
-       if (status & ATA_BUSY)
-               goto idle_irq;
+       if (status & ATA_BUSY) {
+               if (bmdma_stopped) {
+                       /* BMDMA engine is already stopped, we're screwed */
+                       qc->err_mask |= AC_ERR_HSM;
+                       ap->hsm_task_state = HSM_ST_ERR;
+               } else
+                       goto idle_irq;
+       }
 
        /* ack bmdma irq events */
        ap->ops->sff_irq_clear(ap);
@@ -1747,25 +1771,68 @@ EXPORT_SYMBOL_GPL(ata_sff_host_intr);
 irqreturn_t ata_sff_interrupt(int irq, void *dev_instance)
 {
        struct ata_host *host = dev_instance;
+       bool retried = false;
        unsigned int i;
-       unsigned int handled = 0;
+       unsigned int handled, idle, polling;
        unsigned long flags;
 
        /* TODO: make _irqsave conditional on x86 PCI IDE legacy mode */
        spin_lock_irqsave(&host->lock, flags);
 
+retry:
+       handled = idle = polling = 0;
        for (i = 0; i < host->n_ports; i++) {
-               struct ata_port *ap;
+               struct ata_port *ap = host->ports[i];
+               struct ata_queued_cmd *qc;
 
-               ap = host->ports[i];
-               if (ap &&
-                   !(ap->flags & ATA_FLAG_DISABLED)) {
-                       struct ata_queued_cmd *qc;
+               if (unlikely(ap->flags & ATA_FLAG_DISABLED))
+                       continue;
 
-                       qc = ata_qc_from_tag(ap, ap->link.active_tag);
-                       if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING)) &&
-                           (qc->flags & ATA_QCFLAG_ACTIVE))
+               qc = ata_qc_from_tag(ap, ap->link.active_tag);
+               if (qc) {
+                       if (!(qc->tf.flags & ATA_TFLAG_POLLING))
                                handled |= ata_sff_host_intr(ap, qc);
+                       else
+                               polling |= 1 << i;
+               } else
+                       idle |= 1 << i;
+       }
+
+       /*
+        * If no port was expecting IRQ but the controller is actually
+        * asserting IRQ line, nobody cared will ensue.  Check IRQ
+        * pending status if available and clear spurious IRQ.
+        */
+       if (!handled && !retried) {
+               bool retry = false;
+
+               for (i = 0; i < host->n_ports; i++) {
+                       struct ata_port *ap = host->ports[i];
+
+                       if (polling & (1 << i))
+                               continue;
+
+                       if (!ap->ops->sff_irq_check ||
+                           !ap->ops->sff_irq_check(ap))
+                               continue;
+
+                       if (idle & (1 << i)) {
+                               ap->ops->sff_check_status(ap);
+                               ap->ops->sff_irq_clear(ap);
+                       } else {
+                               /* clear INTRQ and check if BUSY cleared */
+                               if (!(ap->ops->sff_check_status(ap) & ATA_BUSY))
+                                       retry |= true;
+                               /*
+                                * With command in flight, we can't do
+                                * sff_irq_clear() w/o racing with completion.
+                                */
+                       }
+               }
+
+               if (retry) {
+                       retried = true;
+                       goto retry;
                }
        }
 
@@ -1776,6 +1843,48 @@ irqreturn_t ata_sff_interrupt(int irq, void *dev_instance)
 EXPORT_SYMBOL_GPL(ata_sff_interrupt);
 
 /**
+ *     ata_sff_lost_interrupt  -       Check for an apparent lost interrupt
+ *     @ap: port that appears to have timed out
+ *
+ *     Called from the libata error handlers when the core code suspects
+ *     an interrupt has been lost. If it has complete anything we can and
+ *     then return. Interface must support altstatus for this faster
+ *     recovery to occur.
+ *
+ *     Locking:
+ *     Caller holds host lock
+ */
+
+void ata_sff_lost_interrupt(struct ata_port *ap)
+{
+       u8 status;
+       struct ata_queued_cmd *qc;
+
+       /* Only one outstanding command per SFF channel */
+       qc = ata_qc_from_tag(ap, ap->link.active_tag);
+       /* Check we have a live one.. */
+       if (qc == NULL ||  !(qc->flags & ATA_QCFLAG_ACTIVE))
+               return;
+       /* We cannot lose an interrupt on a polled command */
+       if (qc->tf.flags & ATA_TFLAG_POLLING)
+               return;
+       /* See if the controller thinks it is still busy - if so the command
+          isn't a lost IRQ but is still in progress */
+       status = ata_sff_altstatus(ap);
+       if (status & ATA_BUSY)
+               return;
+
+       /* There was a command running, we are no longer busy and we have
+          no interrupt. */
+       ata_port_printk(ap, KERN_WARNING, "lost interrupt (Status 0x%x)\n",
+                                                               status);
+       /* Run the host interrupt logic as if the interrupt had not been
+          lost */
+       ata_sff_host_intr(ap, qc);
+}
+EXPORT_SYMBOL_GPL(ata_sff_lost_interrupt);
+
+/**
  *     ata_sff_freeze - Freeze SFF controller port
  *     @ap: port to freeze
  *
@@ -2204,7 +2313,7 @@ EXPORT_SYMBOL_GPL(ata_sff_postreset);
  *     @qc: command
  *
  *     Drain the FIFO and device of any stuck data following a command
- *     failing to complete. In some cases this is neccessary before a
+ *     failing to complete. In some cases this is necessary before a
  *     reset will recover the device.
  *
  */
@@ -2221,7 +2330,7 @@ void ata_sff_drain_fifo(struct ata_queued_cmd *qc)
        ap = qc->ap;
        /* Drain up to 64K of data before we give up this recovery method */
        for (count = 0; (ap->ops->sff_check_status(ap) & ATA_DRQ)
-                                               && count < 32768; count++)
+                                               && count < 65536; count += 2)
                ioread16(ap->ioaddr.data_addr);
 
        /* Can become DEBUG later */
@@ -2330,7 +2439,7 @@ void ata_sff_post_internal_cmd(struct ata_queued_cmd *qc)
        ap->hsm_task_state = HSM_ST_IDLE;
 
        if (ap->ioaddr.bmdma_addr)
-               ata_bmdma_stop(qc);
+               ap->ops->bmdma_stop(qc);
 
        spin_unlock_irqrestore(ap->lock, flags);
 }
@@ -2358,6 +2467,29 @@ int ata_sff_port_start(struct ata_port *ap)
 EXPORT_SYMBOL_GPL(ata_sff_port_start);
 
 /**
+ *     ata_sff_port_start32 - Set port up for dma.
+ *     @ap: Port to initialize
+ *
+ *     Called just after data structures for each port are
+ *     initialized.  Allocates space for PRD table if the device
+ *     is DMA capable SFF.
+ *
+ *     May be used as the port_start() entry in ata_port_operations for
+ *     devices that are capable of 32bit PIO.
+ *
+ *     LOCKING:
+ *     Inherited from caller.
+ */
+int ata_sff_port_start32(struct ata_port *ap)
+{
+       ap->pflags |= ATA_PFLAG_PIO32 | ATA_PFLAG_PIO32CHANGE;
+       if (ap->ioaddr.bmdma_addr)
+               return ata_port_start(ap);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ata_sff_port_start32);
+
+/**
  *     ata_sff_std_ports - initialize ioaddr with standard port offsets.
  *     @ioaddr: IO address structure to be initialized
  *
@@ -2931,6 +3063,7 @@ EXPORT_SYMBOL_GPL(ata_pci_sff_activate_host);
  *     @ppi: array of port_info, must be enough for two ports
  *     @sht: scsi_host_template to use when registering the host
  *     @host_priv: host private_data
+ *     @hflag: host flags
  *
  *     This is a helper function which can be called from a driver's
  *     xxx_init_one() probe function if the hardware uses traditional
@@ -2951,8 +3084,8 @@ EXPORT_SYMBOL_GPL(ata_pci_sff_activate_host);
  *     Zero on success, negative on errno-based value on error.
  */
 int ata_pci_sff_init_one(struct pci_dev *pdev,
-                        const struct ata_port_info * const *ppi,
-                        struct scsi_host_template *sht, void *host_priv)
+                const struct ata_port_info * const *ppi,
+                struct scsi_host_template *sht, void *host_priv, int hflag)
 {
        struct device *dev = &pdev->dev;
        const struct ata_port_info *pi = NULL;
@@ -2987,6 +3120,7 @@ int ata_pci_sff_init_one(struct pci_dev *pdev,
        if (rc)
                goto out;
        host->private_data = host_priv;
+       host->flags |= hflag;
 
        pci_set_master(pdev);
        rc = ata_pci_sff_activate_host(host, ata_sff_interrupt, sht);