diff options
Diffstat (limited to 'drivers/ata/libata-sff.c')
-rw-r--r-- | drivers/ata/libata-sff.c | 46 |
1 files changed, 45 insertions, 1 deletions
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 9a10cb055ac2..8332e97a9de3 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c | |||
@@ -65,6 +65,8 @@ const struct ata_port_operations ata_sff_port_ops = { | |||
65 | .sff_irq_on = ata_sff_irq_on, | 65 | .sff_irq_on = ata_sff_irq_on, |
66 | .sff_irq_clear = ata_sff_irq_clear, | 66 | .sff_irq_clear = ata_sff_irq_clear, |
67 | 67 | ||
68 | .lost_interrupt = ata_sff_lost_interrupt, | ||
69 | |||
68 | .port_start = ata_sff_port_start, | 70 | .port_start = ata_sff_port_start, |
69 | }; | 71 | }; |
70 | EXPORT_SYMBOL_GPL(ata_sff_port_ops); | 72 | EXPORT_SYMBOL_GPL(ata_sff_port_ops); |
@@ -1647,7 +1649,7 @@ EXPORT_SYMBOL_GPL(ata_sff_qc_fill_rtf); | |||
1647 | * RETURNS: | 1649 | * RETURNS: |
1648 | * One if interrupt was handled, zero if not (shared irq). | 1650 | * One if interrupt was handled, zero if not (shared irq). |
1649 | */ | 1651 | */ |
1650 | inline unsigned int ata_sff_host_intr(struct ata_port *ap, | 1652 | unsigned int ata_sff_host_intr(struct ata_port *ap, |
1651 | struct ata_queued_cmd *qc) | 1653 | struct ata_queued_cmd *qc) |
1652 | { | 1654 | { |
1653 | struct ata_eh_info *ehi = &ap->link.eh_info; | 1655 | struct ata_eh_info *ehi = &ap->link.eh_info; |
@@ -1776,6 +1778,48 @@ irqreturn_t ata_sff_interrupt(int irq, void *dev_instance) | |||
1776 | EXPORT_SYMBOL_GPL(ata_sff_interrupt); | 1778 | EXPORT_SYMBOL_GPL(ata_sff_interrupt); |
1777 | 1779 | ||
1778 | /** | 1780 | /** |
1781 | * ata_sff_lost_interrupt - Check for an apparent lost interrupt | ||
1782 | * @ap: port that appears to have timed out | ||
1783 | * | ||
1784 | * Called from the libata error handlers when the core code suspects | ||
1785 | * an interrupt has been lost. If it has complete anything we can and | ||
1786 | * then return. Interface must support altstatus for this faster | ||
1787 | * recovery to occur. | ||
1788 | * | ||
1789 | * Locking: | ||
1790 | * Caller holds host lock | ||
1791 | */ | ||
1792 | |||
1793 | void ata_sff_lost_interrupt(struct ata_port *ap) | ||
1794 | { | ||
1795 | u8 status; | ||
1796 | struct ata_queued_cmd *qc; | ||
1797 | |||
1798 | /* Only one outstanding command per SFF channel */ | ||
1799 | qc = ata_qc_from_tag(ap, ap->link.active_tag); | ||
1800 | /* Check we have a live one.. */ | ||
1801 | if (qc == NULL || !(qc->flags & ATA_QCFLAG_ACTIVE)) | ||
1802 | return; | ||
1803 | /* We cannot lose an interrupt on a polled command */ | ||
1804 | if (qc->tf.flags & ATA_TFLAG_POLLING) | ||
1805 | return; | ||
1806 | /* See if the controller thinks it is still busy - if so the command | ||
1807 | isn't a lost IRQ but is still in progress */ | ||
1808 | status = ata_sff_altstatus(ap); | ||
1809 | if (status & ATA_BUSY) | ||
1810 | return; | ||
1811 | |||
1812 | /* There was a command running, we are no longer busy and we have | ||
1813 | no interrupt. */ | ||
1814 | ata_port_printk(ap, KERN_WARNING, "lost interrupt (Status 0x%x)\n", | ||
1815 | status); | ||
1816 | /* Run the host interrupt logic as if the interrupt had not been | ||
1817 | lost */ | ||
1818 | ata_sff_host_intr(ap, qc); | ||
1819 | } | ||
1820 | EXPORT_SYMBOL_GPL(ata_sff_lost_interrupt); | ||
1821 | |||
1822 | /** | ||
1779 | * ata_sff_freeze - Freeze SFF controller port | 1823 | * ata_sff_freeze - Freeze SFF controller port |
1780 | * @ap: port to freeze | 1824 | * @ap: port to freeze |
1781 | * | 1825 | * |