Messages in this thread Patch in this message | | | Date | Sat, 05 Jul 2008 13:10:50 +0900 | From | Tejun Heo <> | Subject | [PATCH #upstream-fixes] 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 <tj@kernel.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> --- The original report requires further investigation regarding why the bogus change fixed the problem but this change is the correct thing to do regardless.
Thanks.
drivers/ata/ahci.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 061817a..5e6468a 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);
| |