|
@@ -1400,142 +1400,56 @@ static irqreturn_t ahci_thunderx_irq_handler(int irq, void *dev_instance)
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
-/*
|
|
|
|
- * ahci_init_msix() - optionally enable per-port MSI-X otherwise defer
|
|
|
|
- * to single msi.
|
|
|
|
- */
|
|
|
|
-static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports,
|
|
|
|
- struct ahci_host_priv *hpriv, unsigned long flags)
|
|
|
|
|
|
+static int ahci_get_irq_vector(struct ata_host *host, int port)
|
|
{
|
|
{
|
|
- int nvec, i, rc;
|
|
|
|
-
|
|
|
|
- /* Do not init MSI-X if MSI is disabled for the device */
|
|
|
|
- if (hpriv->flags & AHCI_HFLAG_NO_MSI)
|
|
|
|
- return -ENODEV;
|
|
|
|
-
|
|
|
|
- nvec = pci_msix_vec_count(pdev);
|
|
|
|
- if (nvec < 0)
|
|
|
|
- return nvec;
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * Proper MSI-X implementations will have a vector per-port.
|
|
|
|
- * Barring that, we prefer single-MSI over single-MSIX. If this
|
|
|
|
- * check fails (not enough MSI-X vectors for all ports) we will
|
|
|
|
- * be called again with the flag clear iff ahci_init_msi()
|
|
|
|
- * fails.
|
|
|
|
- */
|
|
|
|
- if (flags & AHCI_HFLAG_MULTI_MSIX) {
|
|
|
|
- if (nvec < n_ports)
|
|
|
|
- return -ENODEV;
|
|
|
|
- nvec = n_ports;
|
|
|
|
- } else if (nvec) {
|
|
|
|
- nvec = 1;
|
|
|
|
- } else {
|
|
|
|
- /*
|
|
|
|
- * Emit dev_err() since this was the non-legacy irq
|
|
|
|
- * method of last resort.
|
|
|
|
- */
|
|
|
|
- rc = -ENODEV;
|
|
|
|
- goto fail;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- for (i = 0; i < nvec; i++)
|
|
|
|
- hpriv->msix[i].entry = i;
|
|
|
|
- rc = pci_enable_msix_exact(pdev, hpriv->msix, nvec);
|
|
|
|
- if (rc < 0)
|
|
|
|
- goto fail;
|
|
|
|
-
|
|
|
|
- if (nvec > 1)
|
|
|
|
- hpriv->flags |= AHCI_HFLAG_MULTI_MSIX;
|
|
|
|
- hpriv->irq = hpriv->msix[0].vector; /* for single msi-x */
|
|
|
|
-
|
|
|
|
- return nvec;
|
|
|
|
-fail:
|
|
|
|
- dev_err(&pdev->dev,
|
|
|
|
- "failed to enable MSI-X with error %d, # of vectors: %d\n",
|
|
|
|
- rc, nvec);
|
|
|
|
-
|
|
|
|
- return rc;
|
|
|
|
|
|
+ return pci_irq_vector(to_pci_dev(host->dev), port);
|
|
}
|
|
}
|
|
|
|
|
|
static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports,
|
|
static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports,
|
|
struct ahci_host_priv *hpriv)
|
|
struct ahci_host_priv *hpriv)
|
|
{
|
|
{
|
|
- int rc, nvec;
|
|
|
|
|
|
+ int nvec;
|
|
|
|
|
|
if (hpriv->flags & AHCI_HFLAG_NO_MSI)
|
|
if (hpriv->flags & AHCI_HFLAG_NO_MSI)
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
|
|
|
|
- nvec = pci_msi_vec_count(pdev);
|
|
|
|
- if (nvec < 0)
|
|
|
|
- return nvec;
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* If number of MSIs is less than number of ports then Sharing Last
|
|
* If number of MSIs is less than number of ports then Sharing Last
|
|
* Message mode could be enforced. In this case assume that advantage
|
|
* Message mode could be enforced. In this case assume that advantage
|
|
* of multipe MSIs is negated and use single MSI mode instead.
|
|
* of multipe MSIs is negated and use single MSI mode instead.
|
|
*/
|
|
*/
|
|
- if (nvec < n_ports)
|
|
|
|
- goto single_msi;
|
|
|
|
-
|
|
|
|
- rc = pci_enable_msi_exact(pdev, nvec);
|
|
|
|
- if (rc == -ENOSPC)
|
|
|
|
- goto single_msi;
|
|
|
|
- if (rc < 0)
|
|
|
|
- return rc;
|
|
|
|
|
|
+ nvec = pci_alloc_irq_vectors(pdev, n_ports, INT_MAX,
|
|
|
|
+ PCI_IRQ_MSIX | PCI_IRQ_MSI);
|
|
|
|
+ if (nvec > 0) {
|
|
|
|
+ if (!(readl(hpriv->mmio + HOST_CTL) & HOST_MRSM)) {
|
|
|
|
+ hpriv->get_irq_vector = ahci_get_irq_vector;
|
|
|
|
+ hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
|
|
|
|
+ return nvec;
|
|
|
|
+ }
|
|
|
|
|
|
- /* fallback to single MSI mode if the controller enforced MRSM mode */
|
|
|
|
- if (readl(hpriv->mmio + HOST_CTL) & HOST_MRSM) {
|
|
|
|
- pci_disable_msi(pdev);
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Fallback to single MSI mode if the controller enforced MRSM
|
|
|
|
+ * mode.
|
|
|
|
+ */
|
|
printk(KERN_INFO "ahci: MRSM is on, fallback to single MSI\n");
|
|
printk(KERN_INFO "ahci: MRSM is on, fallback to single MSI\n");
|
|
- goto single_msi;
|
|
|
|
|
|
+ pci_free_irq_vectors(pdev);
|
|
}
|
|
}
|
|
|
|
|
|
- if (nvec > 1)
|
|
|
|
- hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
|
|
|
|
-
|
|
|
|
- goto out;
|
|
|
|
-
|
|
|
|
-single_msi:
|
|
|
|
- nvec = 1;
|
|
|
|
-
|
|
|
|
- rc = pci_enable_msi(pdev);
|
|
|
|
- if (rc < 0)
|
|
|
|
- return rc;
|
|
|
|
-out:
|
|
|
|
- hpriv->irq = pdev->irq;
|
|
|
|
-
|
|
|
|
- return nvec;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
|
|
|
|
- struct ahci_host_priv *hpriv)
|
|
|
|
-{
|
|
|
|
- int nvec;
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
- * Try to enable per-port MSI-X. If the host is not capable
|
|
|
|
- * fall back to single MSI before finally attempting single
|
|
|
|
- * MSI-X.
|
|
|
|
|
|
+ * -ENOSPC indicated we don't have enough vectors. Don't bother trying
|
|
|
|
+ * a single vectors for any other error:
|
|
*/
|
|
*/
|
|
- nvec = ahci_init_msix(pdev, n_ports, hpriv, AHCI_HFLAG_MULTI_MSIX);
|
|
|
|
- if (nvec >= 0)
|
|
|
|
|
|
+ if (nvec < 0 && nvec != -ENOSPC)
|
|
return nvec;
|
|
return nvec;
|
|
|
|
|
|
- nvec = ahci_init_msi(pdev, n_ports, hpriv);
|
|
|
|
- if (nvec >= 0)
|
|
|
|
- return nvec;
|
|
|
|
-
|
|
|
|
- /* try single-msix */
|
|
|
|
- nvec = ahci_init_msix(pdev, n_ports, hpriv, 0);
|
|
|
|
- if (nvec >= 0)
|
|
|
|
|
|
+ /*
|
|
|
|
+ * If the host is not capable of supporting per-port vectors, fall
|
|
|
|
+ * back to single MSI before finally attempting single MSI-X.
|
|
|
|
+ */
|
|
|
|
+ nvec = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
|
|
|
|
+ if (nvec == 1)
|
|
return nvec;
|
|
return nvec;
|
|
-
|
|
|
|
- /* legacy intx interrupts */
|
|
|
|
- pci_intx(pdev, 1);
|
|
|
|
- hpriv->irq = pdev->irq;
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
|
|
+ return pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX);
|
|
}
|
|
}
|
|
|
|
|
|
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
@@ -1698,11 +1612,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
if (!host)
|
|
if (!host)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
host->private_data = hpriv;
|
|
host->private_data = hpriv;
|
|
- hpriv->msix = devm_kzalloc(&pdev->dev,
|
|
|
|
- sizeof(struct msix_entry) * n_ports, GFP_KERNEL);
|
|
|
|
- if (!hpriv->msix)
|
|
|
|
- return -ENOMEM;
|
|
|
|
- ahci_init_interrupts(pdev, n_ports, hpriv);
|
|
|
|
|
|
+
|
|
|
|
+ if (ahci_init_msi(pdev, n_ports, hpriv) < 0) {
|
|
|
|
+ /* legacy intx interrupts */
|
|
|
|
+ pci_intx(pdev, 1);
|
|
|
|
+ }
|
|
|
|
+ hpriv->irq = pdev->irq;
|
|
|
|
|
|
if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
|
|
if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
|
|
host->flags |= ATA_HOST_PARALLEL_SCAN;
|
|
host->flags |= ATA_HOST_PARALLEL_SCAN;
|