]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/ata/libata-sff.c
Merge branch 'origin' into devel
[linux-2.6-omap-h63xx.git] / drivers / ata / libata-sff.c
index f93dc029dfde99061b5416e66568d1658e101fe4..8332e97a9de38026ea33b39799f1cf16c1d3837e 100644 (file)
@@ -52,6 +52,7 @@ const struct ata_port_operations ata_sff_port_ops = {
        .softreset              = ata_sff_softreset,
        .hardreset              = sata_sff_hardreset,
        .postreset              = ata_sff_postreset,
+       .drain_fifo             = ata_sff_drain_fifo,
        .error_handler          = ata_sff_error_handler,
        .post_internal_cmd      = ata_sff_post_internal_cmd,
 
@@ -64,6 +65,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);
@@ -1646,7 +1649,7 @@ 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;
@@ -1774,6 +1777,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
@@ -2198,6 +2243,39 @@ void ata_sff_postreset(struct ata_link *link, unsigned int *classes)
 }
 EXPORT_SYMBOL_GPL(ata_sff_postreset);
 
+/**
+ *     ata_sff_drain_fifo - Stock FIFO drain logic for SFF controllers
+ *     @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
+ *     reset will recover the device.
+ *
+ */
+
+void ata_sff_drain_fifo(struct ata_queued_cmd *qc)
+{
+       int count;
+       struct ata_port *ap;
+
+       /* We only need to flush incoming data when a command was running */
+       if (qc == NULL || qc->dma_dir == DMA_TO_DEVICE)
+               return;
+
+       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++)
+               ioread16(ap->ioaddr.data_addr);
+
+       /* Can become DEBUG later */
+       if (count)
+               ata_port_printk(ap, KERN_DEBUG,
+                       "drained %d bytes to clear DRQ.\n", count);
+
+}
+EXPORT_SYMBOL_GPL(ata_sff_drain_fifo);
+
 /**
  *     ata_sff_error_handler - Stock error handler for BMDMA controller
  *     @ap: port to handle error for
@@ -2239,7 +2317,8 @@ void ata_sff_error_handler(struct ata_port *ap)
                 * really a timeout event, adjust error mask and
                 * cancel frozen state.
                 */
-               if (qc->err_mask == AC_ERR_TIMEOUT && (host_stat & ATA_DMA_ERR)) {
+               if (qc->err_mask == AC_ERR_TIMEOUT
+                                               && (host_stat & ATA_DMA_ERR)) {
                        qc->err_mask = AC_ERR_HOST_BUS;
                        thaw = 1;
                }
@@ -2250,6 +2329,13 @@ void ata_sff_error_handler(struct ata_port *ap)
        ata_sff_sync(ap);               /* FIXME: We don't need this */
        ap->ops->sff_check_status(ap);
        ap->ops->sff_irq_clear(ap);
+       /* We *MUST* do FIFO draining before we issue a reset as several
+        * devices helpfully clear their internal state and will lock solid
+        * if we touch the data port post reset. Pass qc in case anyone wants
+        *  to do different PIO/DMA recovery or has per command fixups
+        */
+       if (ap->ops->drain_fifo)
+               ap->ops->drain_fifo(qc);
 
        spin_unlock_irqrestore(ap->lock, flags);
 
@@ -2959,4 +3045,3 @@ out:
 EXPORT_SYMBOL_GPL(ata_pci_sff_init_one);
 
 #endif /* CONFIG_PCI */
-