From d28f87aa87ce8b196349d7c306a7e6fe3abd7155 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 5 Jul 2008 13:10:50 +0900 Subject: ahci: give another shot at clearing all bits in irq_stat Commit ea0c62f7cf70f13a67830471b613337bd0c9a62e tried to clear all bits in irq_stat but it didn't actually achieve that as irq_stat was anded with port_map right after read. This patch makes ahci driver always use the unmasked value to clear irq_status. While at it, add explanation on the peculiarities of ahci IRQ clearing. This was spotted by Linus Torvalds. Signed-off-by: Tejun Heo Signed-off-by: Linus Torvalds --- drivers/ata/ahci.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers/ata') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 061817a3a0e..5e6468a7ca4 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1777,7 +1777,7 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) struct ahci_host_priv *hpriv; unsigned int i, handled = 0; void __iomem *mmio; - u32 irq_stat; + u32 irq_stat, irq_masked; VPRINTK("ENTER\n"); @@ -1786,16 +1786,17 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) /* sigh. 0xffffffff is a valid return from h/w */ irq_stat = readl(mmio + HOST_IRQ_STAT); - irq_stat &= hpriv->port_map; if (!irq_stat) return IRQ_NONE; + irq_masked = irq_stat & hpriv->port_map; + spin_lock(&host->lock); for (i = 0; i < host->n_ports; i++) { struct ata_port *ap; - if (!(irq_stat & (1 << i))) + if (!(irq_masked & (1 << i))) continue; ap = host->ports[i]; @@ -1812,6 +1813,15 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) handled = 1; } + /* HOST_IRQ_STAT behaves as level triggered latch meaning that + * it should be cleared after all the port events are cleared; + * otherwise, it will raise a spurious interrupt after each + * valid one. Please read section 10.6.2 of ahci 1.1 for more + * information. + * + * Also, use the unmasked value to clear interrupt as spurious + * pending event on a dummy port might cause screaming IRQ. + */ writel(irq_stat, mmio + HOST_IRQ_STAT); spin_unlock(&host->lock); -- cgit v1.2.3