diff options
author | Tejun Heo <tj@kernel.org> | 2008-07-05 00:10:50 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-05 16:05:37 -0400 |
commit | d28f87aa87ce8b196349d7c306a7e6fe3abd7155 (patch) | |
tree | c6bc6d198536772cd50d08c6772e291c5ba91f31 | |
parent | d79df630f622806c4d0e116fbaf6ebf6baf53461 (diff) |
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>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | drivers/ata/ahci.c | 16 |
1 files changed, 13 insertions, 3 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 061817a3a0e5..5e6468a7ca4b 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) | |||
1777 | struct ahci_host_priv *hpriv; | 1777 | struct ahci_host_priv *hpriv; |
1778 | unsigned int i, handled = 0; | 1778 | unsigned int i, handled = 0; |
1779 | void __iomem *mmio; | 1779 | void __iomem *mmio; |
1780 | u32 irq_stat; | 1780 | u32 irq_stat, irq_masked; |
1781 | 1781 | ||
1782 | VPRINTK("ENTER\n"); | 1782 | VPRINTK("ENTER\n"); |
1783 | 1783 | ||
@@ -1786,16 +1786,17 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) | |||
1786 | 1786 | ||
1787 | /* sigh. 0xffffffff is a valid return from h/w */ | 1787 | /* sigh. 0xffffffff is a valid return from h/w */ |
1788 | irq_stat = readl(mmio + HOST_IRQ_STAT); | 1788 | irq_stat = readl(mmio + HOST_IRQ_STAT); |
1789 | irq_stat &= hpriv->port_map; | ||
1790 | if (!irq_stat) | 1789 | if (!irq_stat) |
1791 | return IRQ_NONE; | 1790 | return IRQ_NONE; |
1792 | 1791 | ||
1792 | irq_masked = irq_stat & hpriv->port_map; | ||
1793 | |||
1793 | spin_lock(&host->lock); | 1794 | spin_lock(&host->lock); |
1794 | 1795 | ||
1795 | for (i = 0; i < host->n_ports; i++) { | 1796 | for (i = 0; i < host->n_ports; i++) { |
1796 | struct ata_port *ap; | 1797 | struct ata_port *ap; |
1797 | 1798 | ||
1798 | if (!(irq_stat & (1 << i))) | 1799 | if (!(irq_masked & (1 << i))) |
1799 | continue; | 1800 | continue; |
1800 | 1801 | ||
1801 | ap = host->ports[i]; | 1802 | ap = host->ports[i]; |
@@ -1812,6 +1813,15 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) | |||
1812 | handled = 1; | 1813 | handled = 1; |
1813 | } | 1814 | } |
1814 | 1815 | ||
1816 | /* HOST_IRQ_STAT behaves as level triggered latch meaning that | ||
1817 | * it should be cleared after all the port events are cleared; | ||
1818 | * otherwise, it will raise a spurious interrupt after each | ||
1819 | * valid one. Please read section 10.6.2 of ahci 1.1 for more | ||
1820 | * information. | ||
1821 | * | ||
1822 | * Also, use the unmasked value to clear interrupt as spurious | ||
1823 | * pending event on a dummy port might cause screaming IRQ. | ||
1824 | */ | ||
1815 | writel(irq_stat, mmio + HOST_IRQ_STAT); | 1825 | writel(irq_stat, mmio + HOST_IRQ_STAT); |
1816 | 1826 | ||
1817 | spin_unlock(&host->lock); | 1827 | spin_unlock(&host->lock); |