diff options
Diffstat (limited to 'drivers/ata/libahci.c')
-rw-r--r-- | drivers/ata/libahci.c | 118 |
1 files changed, 112 insertions, 6 deletions
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 6cd7805e47ca..34c82167b962 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c | |||
@@ -1655,19 +1655,16 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) | |||
1655 | ata_port_abort(ap); | 1655 | ata_port_abort(ap); |
1656 | } | 1656 | } |
1657 | 1657 | ||
1658 | static void ahci_port_intr(struct ata_port *ap) | 1658 | static void ahci_handle_port_interrupt(struct ata_port *ap, |
1659 | void __iomem *port_mmio, u32 status) | ||
1659 | { | 1660 | { |
1660 | void __iomem *port_mmio = ahci_port_base(ap); | ||
1661 | struct ata_eh_info *ehi = &ap->link.eh_info; | 1661 | struct ata_eh_info *ehi = &ap->link.eh_info; |
1662 | struct ahci_port_priv *pp = ap->private_data; | 1662 | struct ahci_port_priv *pp = ap->private_data; |
1663 | struct ahci_host_priv *hpriv = ap->host->private_data; | 1663 | struct ahci_host_priv *hpriv = ap->host->private_data; |
1664 | int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING); | 1664 | int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING); |
1665 | u32 status, qc_active = 0; | 1665 | u32 qc_active = 0; |
1666 | int rc; | 1666 | int rc; |
1667 | 1667 | ||
1668 | status = readl(port_mmio + PORT_IRQ_STAT); | ||
1669 | writel(status, port_mmio + PORT_IRQ_STAT); | ||
1670 | |||
1671 | /* ignore BAD_PMP while resetting */ | 1668 | /* ignore BAD_PMP while resetting */ |
1672 | if (unlikely(resetting)) | 1669 | if (unlikely(resetting)) |
1673 | status &= ~PORT_IRQ_BAD_PMP; | 1670 | status &= ~PORT_IRQ_BAD_PMP; |
@@ -1743,6 +1740,107 @@ static void ahci_port_intr(struct ata_port *ap) | |||
1743 | } | 1740 | } |
1744 | } | 1741 | } |
1745 | 1742 | ||
1743 | void ahci_port_intr(struct ata_port *ap) | ||
1744 | { | ||
1745 | void __iomem *port_mmio = ahci_port_base(ap); | ||
1746 | u32 status; | ||
1747 | |||
1748 | status = readl(port_mmio + PORT_IRQ_STAT); | ||
1749 | writel(status, port_mmio + PORT_IRQ_STAT); | ||
1750 | |||
1751 | ahci_handle_port_interrupt(ap, port_mmio, status); | ||
1752 | } | ||
1753 | |||
1754 | irqreturn_t ahci_thread_fn(int irq, void *dev_instance) | ||
1755 | { | ||
1756 | struct ata_port *ap = dev_instance; | ||
1757 | struct ahci_port_priv *pp = ap->private_data; | ||
1758 | void __iomem *port_mmio = ahci_port_base(ap); | ||
1759 | unsigned long flags; | ||
1760 | u32 status; | ||
1761 | |||
1762 | spin_lock_irqsave(&ap->host->lock, flags); | ||
1763 | status = pp->intr_status; | ||
1764 | if (status) | ||
1765 | pp->intr_status = 0; | ||
1766 | spin_unlock_irqrestore(&ap->host->lock, flags); | ||
1767 | |||
1768 | spin_lock_bh(ap->lock); | ||
1769 | ahci_handle_port_interrupt(ap, port_mmio, status); | ||
1770 | spin_unlock_bh(ap->lock); | ||
1771 | |||
1772 | return IRQ_HANDLED; | ||
1773 | } | ||
1774 | EXPORT_SYMBOL_GPL(ahci_thread_fn); | ||
1775 | |||
1776 | void ahci_hw_port_interrupt(struct ata_port *ap) | ||
1777 | { | ||
1778 | void __iomem *port_mmio = ahci_port_base(ap); | ||
1779 | struct ahci_port_priv *pp = ap->private_data; | ||
1780 | u32 status; | ||
1781 | |||
1782 | status = readl(port_mmio + PORT_IRQ_STAT); | ||
1783 | writel(status, port_mmio + PORT_IRQ_STAT); | ||
1784 | |||
1785 | pp->intr_status |= status; | ||
1786 | } | ||
1787 | |||
1788 | irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance) | ||
1789 | { | ||
1790 | struct ata_port *ap_this = dev_instance; | ||
1791 | struct ahci_port_priv *pp = ap_this->private_data; | ||
1792 | struct ata_host *host = ap_this->host; | ||
1793 | struct ahci_host_priv *hpriv = host->private_data; | ||
1794 | void __iomem *mmio = hpriv->mmio; | ||
1795 | unsigned int i; | ||
1796 | u32 irq_stat, irq_masked; | ||
1797 | |||
1798 | VPRINTK("ENTER\n"); | ||
1799 | |||
1800 | spin_lock(&host->lock); | ||
1801 | |||
1802 | irq_stat = readl(mmio + HOST_IRQ_STAT); | ||
1803 | |||
1804 | if (!irq_stat) { | ||
1805 | u32 status = pp->intr_status; | ||
1806 | |||
1807 | spin_unlock(&host->lock); | ||
1808 | |||
1809 | VPRINTK("EXIT\n"); | ||
1810 | |||
1811 | return status ? IRQ_WAKE_THREAD : IRQ_NONE; | ||
1812 | } | ||
1813 | |||
1814 | irq_masked = irq_stat & hpriv->port_map; | ||
1815 | |||
1816 | for (i = 0; i < host->n_ports; i++) { | ||
1817 | struct ata_port *ap; | ||
1818 | |||
1819 | if (!(irq_masked & (1 << i))) | ||
1820 | continue; | ||
1821 | |||
1822 | ap = host->ports[i]; | ||
1823 | if (ap) { | ||
1824 | ahci_hw_port_interrupt(ap); | ||
1825 | VPRINTK("port %u\n", i); | ||
1826 | } else { | ||
1827 | VPRINTK("port %u (no irq)\n", i); | ||
1828 | if (ata_ratelimit()) | ||
1829 | dev_warn(host->dev, | ||
1830 | "interrupt on disabled port %u\n", i); | ||
1831 | } | ||
1832 | } | ||
1833 | |||
1834 | writel(irq_stat, mmio + HOST_IRQ_STAT); | ||
1835 | |||
1836 | spin_unlock(&host->lock); | ||
1837 | |||
1838 | VPRINTK("EXIT\n"); | ||
1839 | |||
1840 | return IRQ_WAKE_THREAD; | ||
1841 | } | ||
1842 | EXPORT_SYMBOL_GPL(ahci_hw_interrupt); | ||
1843 | |||
1746 | irqreturn_t ahci_interrupt(int irq, void *dev_instance) | 1844 | irqreturn_t ahci_interrupt(int irq, void *dev_instance) |
1747 | { | 1845 | { |
1748 | struct ata_host *host = dev_instance; | 1846 | struct ata_host *host = dev_instance; |
@@ -2196,6 +2294,14 @@ static int ahci_port_start(struct ata_port *ap) | |||
2196 | */ | 2294 | */ |
2197 | pp->intr_mask = DEF_PORT_IRQ; | 2295 | pp->intr_mask = DEF_PORT_IRQ; |
2198 | 2296 | ||
2297 | /* | ||
2298 | * Switch to per-port locking in case each port has its own MSI vector. | ||
2299 | */ | ||
2300 | if ((hpriv->flags & AHCI_HFLAG_MULTI_MSI)) { | ||
2301 | spin_lock_init(&pp->lock); | ||
2302 | ap->lock = &pp->lock; | ||
2303 | } | ||
2304 | |||
2199 | ap->private_data = pp; | 2305 | ap->private_data = pp; |
2200 | 2306 | ||
2201 | /* engage engines, captain */ | 2307 | /* engage engines, captain */ |