diff options
author | Dan Williams <dan.j.williamps@intel.com> | 2015-11-11 16:27:33 -0800 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2015-11-16 11:29:10 -0500 |
commit | d684a90d38e24dcaf95fdb32c83efe05f80d152a (patch) | |
tree | 855bb8396232c9b682394125c46ada2671452bca /drivers/ata/libahci.c | |
parent | 4d92f0099a06ef0e36c7673f7c090f1a448b2d1b (diff) |
ahci: per-port msix support
Some AHCI controllers support per-port MSI-X vectors. At the same time
the Linux AHCI driver needs to support one-off architectures that
implement a single MSI-X vector for all ports. The heuristic for
enabling AHCI ports becomes, in order of preference:
1/ per-port multi-MSI-X
2/ per-port multi-MSI
3/ single MSI
4/ single MSI-X
5/ legacy INTX
This all depends on AHCI implementations with potentially broken MSI-X
requesting less vectors than the number of ports. If this assumption is
violated we will need to start explicitly white-listing AHCI-MSIX
implementations.
Reported-by: Ricardo Neri <ricardo.neri@intel.com>
[ricardo: fix struct msix_entry handling]
Reported-by: kernel test robot <ying.huang@linux.intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'drivers/ata/libahci.c')
-rw-r--r-- | drivers/ata/libahci.c | 19 |
1 files changed, 14 insertions, 5 deletions
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 096064cd6c52..0a5645fb02f8 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -43,6 +43,7 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_cmnd.h> #include <linux/libata.h> +#include <linux/pci.h> #include "ahci.h" #include "libata.h" @@ -2470,9 +2471,10 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv, } EXPORT_SYMBOL_GPL(ahci_set_em_messages); -static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq, +static int ahci_host_activate_multi_irqs(struct ata_host *host, struct scsi_host_template *sht) { + struct ahci_host_priv *hpriv = host->private_data; int i, rc; rc = ata_host_start(host); @@ -2484,6 +2486,12 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq, */ for (i = 0; i < host->n_ports; i++) { struct ahci_port_priv *pp = host->ports[i]->private_data; + int irq; + + if (hpriv->flags & AHCI_HFLAG_MULTI_MSIX) + irq = hpriv->msix[i].vector; + else + irq = hpriv->irq + i; /* Do not receive interrupts sent by dummy ports */ if (!pp) { @@ -2491,14 +2499,15 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq, continue; } - rc = devm_request_threaded_irq(host->dev, irq + i, + rc = devm_request_threaded_irq(host->dev, irq, ahci_multi_irqs_intr, ahci_port_thread_fn, 0, pp->irq_desc, host->ports[i]); if (rc) return rc; - ata_port_desc(host->ports[i], "irq %d", irq + i); + ata_port_desc(host->ports[i], "irq %d", irq); } + return ata_host_register(host, sht); } @@ -2519,8 +2528,8 @@ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht) int irq = hpriv->irq; int rc; - if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) - rc = ahci_host_activate_multi_irqs(host, irq, sht); + if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI | AHCI_HFLAG_MULTI_MSIX)) + rc = ahci_host_activate_multi_irqs(host, sht); else if (hpriv->flags & AHCI_HFLAG_EDGE_IRQ) rc = ata_host_activate(host, irq, ahci_single_edge_irq_intr, IRQF_SHARED, sht); |