]> git.hungrycats.org Git - linux/commitdiff
[libata] catch, and ack, spurious DMA interrupts
authorJeff Garzik <jgarzik@redhat.com>
Fri, 13 Feb 2004 07:27:20 +0000 (02:27 -0500)
committerJeff Garzik <jgarzik@redhat.com>
Fri, 13 Feb 2004 07:27:20 +0000 (02:27 -0500)
Hardware issue on Intel ICH5 requires an additional ack sequence
over and above the normal IDE DMA interrupt ack requirements.  Issue
described in post to freebsd list:
http://www.mail-archive.com/freebsd-stable@freebsd.org/msg58421.html

Since the bug workaround only requires a single additional PIO or
MMIO read in the interrupt handler, it is applied to all chipsets
using the standard libata interrupt handler.

Credit for research the issue, creating the patch, and testing the
patch all go to Jon Burgess.

drivers/scsi/libata-core.c

index c7e81135211441d2147e307ac9eab8b0a9480121..3009f8ab6a7f25ee2b861af1f3c9731a9ad2c8e4 100644 (file)
@@ -2385,6 +2385,41 @@ static inline unsigned int ata_host_intr (struct ata_port *ap,
        return handled;
 }
 
+/**
+ *     ata_chk_spurious_int - Check for spurious interrupts
+ *     @ap: port to which command is being issued
+ *
+ *     Examines the DMA status registers and clears
+ *      unexpected interrupts.  Created to work around
+ *     hardware bug on Intel ICH5, but is applied to all
+ *     chipsets using the standard irq handler, just for safety.
+ *     If the bug is not present, this is simply a single
+ *     PIO or MMIO read addition to the irq handler.
+ *
+ *     LOCKING:
+ */
+static inline void ata_chk_spurious_int(struct ata_port *ap) {
+       int host_stat;
+       
+       if (ap->flags & ATA_FLAG_MMIO) {
+               void *mmio = (void *) ap->ioaddr.bmdma_addr;
+               host_stat = readb(mmio + ATA_DMA_STATUS);
+       } else
+               host_stat = inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS);
+       
+       if ((host_stat & (ATA_DMA_INTR | ATA_DMA_ERR | ATA_DMA_ACTIVE)) == ATA_DMA_INTR) {
+               if (ap->flags & ATA_FLAG_MMIO) {
+                       void *mmio = (void *) ap->ioaddr.bmdma_addr;
+                       writeb(host_stat & ~ATA_DMA_ERR, mmio + ATA_DMA_STATUS);
+               } else
+                       outb(host_stat & ~ATA_DMA_ERR, ap->ioaddr.bmdma_addr + ATA_DMA_STATUS);
+               
+               DPRINTK("ata%u: Caught spurious interrupt, status 0x%X\n", ap->id, host_stat);
+               udelay(1);
+       }
+}
+
+
 /**
  *     ata_interrupt -
  *     @irq:
@@ -2417,6 +2452,7 @@ irqreturn_t ata_interrupt (int irq, void *dev_instance, struct pt_regs *regs)
                        qc = ata_qc_from_tag(ap, ap->active_tag);
                        if (qc && ((qc->flags & ATA_QCFLAG_POLL) == 0))
                                handled += ata_host_intr(ap, qc);
+                       ata_chk_spurious_int(ap);
                }
        }