diff options
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/ata_piix.c | 20 | ||||
-rw-r--r-- | drivers/ata/libata-sff.c | 35 |
2 files changed, 47 insertions, 8 deletions
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index b5f614b9c245..c33806654e46 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -173,6 +173,7 @@ static int piix_sidpr_scr_read(struct ata_link *link, unsigned int reg, u32 *val); static int piix_sidpr_scr_write(struct ata_link *link, unsigned int reg, u32 val); +static bool piix_irq_check(struct ata_port *ap); #ifdef CONFIG_PM static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); static int piix_pci_device_resume(struct pci_dev *pdev); @@ -317,8 +318,13 @@ static struct scsi_host_template piix_sht = { ATA_BMDMA_SHT(DRV_NAME), }; -static struct ata_port_operations piix_pata_ops = { +static struct ata_port_operations piix_sata_ops = { .inherits = &ata_bmdma32_port_ops, + .sff_irq_check = piix_irq_check, +}; + +static struct ata_port_operations piix_pata_ops = { + .inherits = &piix_sata_ops, .cable_detect = ata_cable_40wire, .set_piomode = piix_set_piomode, .set_dmamode = piix_set_dmamode, @@ -336,10 +342,6 @@ static struct ata_port_operations ich_pata_ops = { .set_dmamode = ich_set_dmamode, }; -static struct ata_port_operations piix_sata_ops = { - .inherits = &ata_bmdma32_port_ops, -}; - static struct ata_port_operations piix_sidpr_sata_ops = { .inherits = &piix_sata_ops, .hardreset = sata_std_hardreset, @@ -970,6 +972,14 @@ static int piix_sidpr_scr_write(struct ata_link *link, return 0; } +static bool piix_irq_check(struct ata_port *ap) +{ + if (unlikely(!ap->ioaddr.bmdma_addr)) + return false; + + return ap->ops->bmdma_status(ap) & ATA_DMA_INTR; +} + #ifdef CONFIG_PM static int piix_broken_suspend(void) { diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index c62ed13b0596..c2661ea70330 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -1763,7 +1763,7 @@ irqreturn_t ata_sff_interrupt(int irq, void *dev_instance) { struct ata_host *host = dev_instance; unsigned int i; - unsigned int handled = 0; + unsigned int handled = 0, polling = 0; unsigned long flags; /* TODO: make _irqsave conditional on x86 PCI IDE legacy mode */ @@ -1777,8 +1777,37 @@ irqreturn_t ata_sff_interrupt(int irq, void *dev_instance) continue; qc = ata_qc_from_tag(ap, ap->link.active_tag); - if (qc && !(qc->tf.flags & ATA_TFLAG_POLLING)) - handled |= ata_sff_host_intr(ap, qc); + if (qc) { + if (!(qc->tf.flags & ATA_TFLAG_POLLING)) + handled |= ata_sff_host_intr(ap, qc); + else + polling |= 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) { + 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 (printk_ratelimit()) + ata_port_printk(ap, KERN_INFO, + "clearing spurious IRQ\n"); + + ap->ops->sff_check_status(ap); + ap->ops->sff_irq_clear(ap); + } } spin_unlock_irqrestore(&host->lock, flags); |