diff options
Diffstat (limited to 'drivers/ata/libata-sff.c')
-rw-r--r-- | drivers/ata/libata-sff.c | 46 |
1 files changed, 36 insertions, 10 deletions
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 561dec2481cb..e3877b6843c9 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c | |||
@@ -33,6 +33,7 @@ | |||
33 | */ | 33 | */ |
34 | 34 | ||
35 | #include <linux/kernel.h> | 35 | #include <linux/kernel.h> |
36 | #include <linux/gfp.h> | ||
36 | #include <linux/pci.h> | 37 | #include <linux/pci.h> |
37 | #include <linux/libata.h> | 38 | #include <linux/libata.h> |
38 | #include <linux/highmem.h> | 39 | #include <linux/highmem.h> |
@@ -1667,6 +1668,7 @@ unsigned int ata_sff_host_intr(struct ata_port *ap, | |||
1667 | { | 1668 | { |
1668 | struct ata_eh_info *ehi = &ap->link.eh_info; | 1669 | struct ata_eh_info *ehi = &ap->link.eh_info; |
1669 | u8 status, host_stat = 0; | 1670 | u8 status, host_stat = 0; |
1671 | bool bmdma_stopped = false; | ||
1670 | 1672 | ||
1671 | VPRINTK("ata%u: protocol %d task_state %d\n", | 1673 | VPRINTK("ata%u: protocol %d task_state %d\n", |
1672 | ap->print_id, qc->tf.protocol, ap->hsm_task_state); | 1674 | ap->print_id, qc->tf.protocol, ap->hsm_task_state); |
@@ -1699,6 +1701,7 @@ unsigned int ata_sff_host_intr(struct ata_port *ap, | |||
1699 | 1701 | ||
1700 | /* before we do anything else, clear DMA-Start bit */ | 1702 | /* before we do anything else, clear DMA-Start bit */ |
1701 | ap->ops->bmdma_stop(qc); | 1703 | ap->ops->bmdma_stop(qc); |
1704 | bmdma_stopped = true; | ||
1702 | 1705 | ||
1703 | if (unlikely(host_stat & ATA_DMA_ERR)) { | 1706 | if (unlikely(host_stat & ATA_DMA_ERR)) { |
1704 | /* error when transfering data to/from memory */ | 1707 | /* error when transfering data to/from memory */ |
@@ -1716,8 +1719,14 @@ unsigned int ata_sff_host_intr(struct ata_port *ap, | |||
1716 | 1719 | ||
1717 | /* check main status, clearing INTRQ if needed */ | 1720 | /* check main status, clearing INTRQ if needed */ |
1718 | status = ata_sff_irq_status(ap); | 1721 | status = ata_sff_irq_status(ap); |
1719 | if (status & ATA_BUSY) | 1722 | if (status & ATA_BUSY) { |
1720 | goto idle_irq; | 1723 | if (bmdma_stopped) { |
1724 | /* BMDMA engine is already stopped, we're screwed */ | ||
1725 | qc->err_mask |= AC_ERR_HSM; | ||
1726 | ap->hsm_task_state = HSM_ST_ERR; | ||
1727 | } else | ||
1728 | goto idle_irq; | ||
1729 | } | ||
1721 | 1730 | ||
1722 | /* ack bmdma irq events */ | 1731 | /* ack bmdma irq events */ |
1723 | ap->ops->sff_irq_clear(ap); | 1732 | ap->ops->sff_irq_clear(ap); |
@@ -1762,13 +1771,16 @@ EXPORT_SYMBOL_GPL(ata_sff_host_intr); | |||
1762 | irqreturn_t ata_sff_interrupt(int irq, void *dev_instance) | 1771 | irqreturn_t ata_sff_interrupt(int irq, void *dev_instance) |
1763 | { | 1772 | { |
1764 | struct ata_host *host = dev_instance; | 1773 | struct ata_host *host = dev_instance; |
1774 | bool retried = false; | ||
1765 | unsigned int i; | 1775 | unsigned int i; |
1766 | unsigned int handled = 0, polling = 0; | 1776 | unsigned int handled, idle, polling; |
1767 | unsigned long flags; | 1777 | unsigned long flags; |
1768 | 1778 | ||
1769 | /* TODO: make _irqsave conditional on x86 PCI IDE legacy mode */ | 1779 | /* TODO: make _irqsave conditional on x86 PCI IDE legacy mode */ |
1770 | spin_lock_irqsave(&host->lock, flags); | 1780 | spin_lock_irqsave(&host->lock, flags); |
1771 | 1781 | ||
1782 | retry: | ||
1783 | handled = idle = polling = 0; | ||
1772 | for (i = 0; i < host->n_ports; i++) { | 1784 | for (i = 0; i < host->n_ports; i++) { |
1773 | struct ata_port *ap = host->ports[i]; | 1785 | struct ata_port *ap = host->ports[i]; |
1774 | struct ata_queued_cmd *qc; | 1786 | struct ata_queued_cmd *qc; |
@@ -1782,7 +1794,8 @@ irqreturn_t ata_sff_interrupt(int irq, void *dev_instance) | |||
1782 | handled |= ata_sff_host_intr(ap, qc); | 1794 | handled |= ata_sff_host_intr(ap, qc); |
1783 | else | 1795 | else |
1784 | polling |= 1 << i; | 1796 | polling |= 1 << i; |
1785 | } | 1797 | } else |
1798 | idle |= 1 << i; | ||
1786 | } | 1799 | } |
1787 | 1800 | ||
1788 | /* | 1801 | /* |
@@ -1790,7 +1803,9 @@ irqreturn_t ata_sff_interrupt(int irq, void *dev_instance) | |||
1790 | * asserting IRQ line, nobody cared will ensue. Check IRQ | 1803 | * asserting IRQ line, nobody cared will ensue. Check IRQ |
1791 | * pending status if available and clear spurious IRQ. | 1804 | * pending status if available and clear spurious IRQ. |
1792 | */ | 1805 | */ |
1793 | if (!handled) { | 1806 | if (!handled && !retried) { |
1807 | bool retry = false; | ||
1808 | |||
1794 | for (i = 0; i < host->n_ports; i++) { | 1809 | for (i = 0; i < host->n_ports; i++) { |
1795 | struct ata_port *ap = host->ports[i]; | 1810 | struct ata_port *ap = host->ports[i]; |
1796 | 1811 | ||
@@ -1801,12 +1816,23 @@ irqreturn_t ata_sff_interrupt(int irq, void *dev_instance) | |||
1801 | !ap->ops->sff_irq_check(ap)) | 1816 | !ap->ops->sff_irq_check(ap)) |
1802 | continue; | 1817 | continue; |
1803 | 1818 | ||
1804 | if (printk_ratelimit()) | 1819 | if (idle & (1 << i)) { |
1805 | ata_port_printk(ap, KERN_INFO, | 1820 | ap->ops->sff_check_status(ap); |
1806 | "clearing spurious IRQ\n"); | 1821 | ap->ops->sff_irq_clear(ap); |
1822 | } else { | ||
1823 | /* clear INTRQ and check if BUSY cleared */ | ||
1824 | if (!(ap->ops->sff_check_status(ap) & ATA_BUSY)) | ||
1825 | retry |= true; | ||
1826 | /* | ||
1827 | * With command in flight, we can't do | ||
1828 | * sff_irq_clear() w/o racing with completion. | ||
1829 | */ | ||
1830 | } | ||
1831 | } | ||
1807 | 1832 | ||
1808 | ap->ops->sff_check_status(ap); | 1833 | if (retry) { |
1809 | ap->ops->sff_irq_clear(ap); | 1834 | retried = true; |
1835 | goto retry; | ||
1810 | } | 1836 | } |
1811 | } | 1837 | } |
1812 | 1838 | ||