diff options
Diffstat (limited to 'drivers/ata/ahci.c')
| -rw-r--r-- | drivers/ata/ahci.c | 23 |
1 files changed, 15 insertions, 8 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 6a4a2a25d97a..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, irq_ack = 0; | 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]; |
| @@ -1809,14 +1810,20 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) | |||
| 1809 | "interrupt on disabled port %u\n", i); | 1810 | "interrupt on disabled port %u\n", i); |
| 1810 | } | 1811 | } |
| 1811 | 1812 | ||
| 1812 | irq_ack |= (1 << i); | ||
| 1813 | } | ||
| 1814 | |||
| 1815 | if (irq_ack) { | ||
| 1816 | writel(irq_ack, mmio + HOST_IRQ_STAT); | ||
| 1817 | handled = 1; | 1813 | handled = 1; |
| 1818 | } | 1814 | } |
| 1819 | 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 | */ | ||
| 1825 | writel(irq_stat, mmio + HOST_IRQ_STAT); | ||
| 1826 | |||
| 1820 | spin_unlock(&host->lock); | 1827 | spin_unlock(&host->lock); |
| 1821 | 1828 | ||
| 1822 | VPRINTK("EXIT\n"); | 1829 | VPRINTK("EXIT\n"); |
