diff options
-rw-r--r-- | drivers/ata/ahci.c | 93 | ||||
-rw-r--r-- | drivers/ata/ahci.h | 6 | ||||
-rw-r--r-- | drivers/ata/libahci.c | 118 |
3 files changed, 207 insertions, 10 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 497912732566..495aeed26779 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c | |||
@@ -1061,6 +1061,86 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host) | |||
1061 | {} | 1061 | {} |
1062 | #endif | 1062 | #endif |
1063 | 1063 | ||
1064 | int ahci_init_interrupts(struct pci_dev *pdev, struct ahci_host_priv *hpriv) | ||
1065 | { | ||
1066 | int rc; | ||
1067 | unsigned int maxvec; | ||
1068 | |||
1069 | if (!(hpriv->flags & AHCI_HFLAG_NO_MSI)) { | ||
1070 | rc = pci_enable_msi_block_auto(pdev, &maxvec); | ||
1071 | if (rc > 0) { | ||
1072 | if ((rc == maxvec) || (rc == 1)) | ||
1073 | return rc; | ||
1074 | /* | ||
1075 | * Assume that advantage of multipe MSIs is negated, | ||
1076 | * so fallback to single MSI mode to save resources | ||
1077 | */ | ||
1078 | pci_disable_msi(pdev); | ||
1079 | if (!pci_enable_msi(pdev)) | ||
1080 | return 1; | ||
1081 | } | ||
1082 | } | ||
1083 | |||
1084 | pci_intx(pdev, 1); | ||
1085 | return 0; | ||
1086 | } | ||
1087 | |||
1088 | /** | ||
1089 | * ahci_host_activate - start AHCI host, request IRQs and register it | ||
1090 | * @host: target ATA host | ||
1091 | * @irq: base IRQ number to request | ||
1092 | * @n_msis: number of MSIs allocated for this host | ||
1093 | * @irq_handler: irq_handler used when requesting IRQs | ||
1094 | * @irq_flags: irq_flags used when requesting IRQs | ||
1095 | * | ||
1096 | * Similar to ata_host_activate, but requests IRQs according to AHCI-1.1 | ||
1097 | * when multiple MSIs were allocated. That is one MSI per port, starting | ||
1098 | * from @irq. | ||
1099 | * | ||
1100 | * LOCKING: | ||
1101 | * Inherited from calling layer (may sleep). | ||
1102 | * | ||
1103 | * RETURNS: | ||
1104 | * 0 on success, -errno otherwise. | ||
1105 | */ | ||
1106 | int ahci_host_activate(struct ata_host *host, int irq, unsigned int n_msis) | ||
1107 | { | ||
1108 | int i, rc; | ||
1109 | |||
1110 | /* Sharing Last Message among several ports is not supported */ | ||
1111 | if (n_msis < host->n_ports) | ||
1112 | return -EINVAL; | ||
1113 | |||
1114 | rc = ata_host_start(host); | ||
1115 | if (rc) | ||
1116 | return rc; | ||
1117 | |||
1118 | for (i = 0; i < host->n_ports; i++) { | ||
1119 | rc = devm_request_threaded_irq(host->dev, | ||
1120 | irq + i, ahci_hw_interrupt, ahci_thread_fn, IRQF_SHARED, | ||
1121 | dev_driver_string(host->dev), host->ports[i]); | ||
1122 | if (rc) | ||
1123 | goto out_free_irqs; | ||
1124 | } | ||
1125 | |||
1126 | for (i = 0; i < host->n_ports; i++) | ||
1127 | ata_port_desc(host->ports[i], "irq %d", irq + i); | ||
1128 | |||
1129 | rc = ata_host_register(host, &ahci_sht); | ||
1130 | if (rc) | ||
1131 | goto out_free_all_irqs; | ||
1132 | |||
1133 | return 0; | ||
1134 | |||
1135 | out_free_all_irqs: | ||
1136 | i = host->n_ports; | ||
1137 | out_free_irqs: | ||
1138 | for (i--; i >= 0; i--) | ||
1139 | devm_free_irq(host->dev, irq + i, host->ports[i]); | ||
1140 | |||
1141 | return rc; | ||
1142 | } | ||
1143 | |||
1064 | static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | 1144 | static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) |
1065 | { | 1145 | { |
1066 | unsigned int board_id = ent->driver_data; | 1146 | unsigned int board_id = ent->driver_data; |
@@ -1069,7 +1149,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
1069 | struct device *dev = &pdev->dev; | 1149 | struct device *dev = &pdev->dev; |
1070 | struct ahci_host_priv *hpriv; | 1150 | struct ahci_host_priv *hpriv; |
1071 | struct ata_host *host; | 1151 | struct ata_host *host; |
1072 | int n_ports, i, rc; | 1152 | int n_ports, n_msis, i, rc; |
1073 | int ahci_pci_bar = AHCI_PCI_BAR_STANDARD; | 1153 | int ahci_pci_bar = AHCI_PCI_BAR_STANDARD; |
1074 | 1154 | ||
1075 | VPRINTK("ENTER\n"); | 1155 | VPRINTK("ENTER\n"); |
@@ -1156,11 +1236,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
1156 | if (ahci_sb600_enable_64bit(pdev)) | 1236 | if (ahci_sb600_enable_64bit(pdev)) |
1157 | hpriv->flags &= ~AHCI_HFLAG_32BIT_ONLY; | 1237 | hpriv->flags &= ~AHCI_HFLAG_32BIT_ONLY; |
1158 | 1238 | ||
1159 | if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev)) | ||
1160 | pci_intx(pdev, 1); | ||
1161 | |||
1162 | hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar]; | 1239 | hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar]; |
1163 | 1240 | ||
1241 | n_msis = ahci_init_interrupts(pdev, hpriv); | ||
1242 | if (n_msis > 1) | ||
1243 | hpriv->flags |= AHCI_HFLAG_MULTI_MSI; | ||
1244 | |||
1164 | /* save initial config */ | 1245 | /* save initial config */ |
1165 | ahci_pci_save_initial_config(pdev, hpriv); | 1246 | ahci_pci_save_initial_config(pdev, hpriv); |
1166 | 1247 | ||
@@ -1256,6 +1337,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
1256 | ahci_pci_print_info(host); | 1337 | ahci_pci_print_info(host); |
1257 | 1338 | ||
1258 | pci_set_master(pdev); | 1339 | pci_set_master(pdev); |
1340 | |||
1341 | if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) | ||
1342 | return ahci_host_activate(host, pdev->irq, n_msis); | ||
1343 | |||
1259 | return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED, | 1344 | return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED, |
1260 | &ahci_sht); | 1345 | &ahci_sht); |
1261 | } | 1346 | } |
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 9be471200a07..b830e6c9fe49 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h | |||
@@ -231,6 +231,7 @@ enum { | |||
231 | AHCI_HFLAG_DELAY_ENGINE = (1 << 15), /* do not start engine on | 231 | AHCI_HFLAG_DELAY_ENGINE = (1 << 15), /* do not start engine on |
232 | port start (wait until | 232 | port start (wait until |
233 | error-handling stage) */ | 233 | error-handling stage) */ |
234 | AHCI_HFLAG_MULTI_MSI = (1 << 16), /* multiple PCI MSIs */ | ||
234 | 235 | ||
235 | /* ap->flags bits */ | 236 | /* ap->flags bits */ |
236 | 237 | ||
@@ -297,6 +298,8 @@ struct ahci_port_priv { | |||
297 | unsigned int ncq_saw_d2h:1; | 298 | unsigned int ncq_saw_d2h:1; |
298 | unsigned int ncq_saw_dmas:1; | 299 | unsigned int ncq_saw_dmas:1; |
299 | unsigned int ncq_saw_sdb:1; | 300 | unsigned int ncq_saw_sdb:1; |
301 | u32 intr_status; /* interrupts to handle */ | ||
302 | spinlock_t lock; /* protects parent ata_port */ | ||
300 | u32 intr_mask; /* interrupts to enable */ | 303 | u32 intr_mask; /* interrupts to enable */ |
301 | bool fbs_supported; /* set iff FBS is supported */ | 304 | bool fbs_supported; /* set iff FBS is supported */ |
302 | bool fbs_enabled; /* set iff FBS is enabled */ | 305 | bool fbs_enabled; /* set iff FBS is enabled */ |
@@ -359,7 +362,10 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv, | |||
359 | struct ata_port_info *pi); | 362 | struct ata_port_info *pi); |
360 | int ahci_reset_em(struct ata_host *host); | 363 | int ahci_reset_em(struct ata_host *host); |
361 | irqreturn_t ahci_interrupt(int irq, void *dev_instance); | 364 | irqreturn_t ahci_interrupt(int irq, void *dev_instance); |
365 | irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance); | ||
366 | irqreturn_t ahci_thread_fn(int irq, void *dev_instance); | ||
362 | void ahci_print_info(struct ata_host *host, const char *scc_s); | 367 | void ahci_print_info(struct ata_host *host, const char *scc_s); |
368 | int ahci_host_activate(struct ata_host *host, int irq, unsigned int n_msis); | ||
363 | 369 | ||
364 | static inline void __iomem *__ahci_port_base(struct ata_host *host, | 370 | static inline void __iomem *__ahci_port_base(struct ata_host *host, |
365 | unsigned int port_no) | 371 | unsigned int port_no) |
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 */ |