OSDN Git Service

Merge branch 'for-4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata
[uclinux-h8/linux.git] / drivers / ata / ahci.c
index 65ee944..7e62751 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/device.h>
 #include <linux/dmi.h>
 #include <linux/gfp.h>
+#include <linux/msi.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_cmnd.h>
 #include <linux/libata.h>
@@ -52,6 +53,7 @@
 
 enum {
        AHCI_PCI_BAR_STA2X11    = 0,
+       AHCI_PCI_BAR_CAVIUM     = 0,
        AHCI_PCI_BAR_ENMOTUS    = 2,
        AHCI_PCI_BAR_STANDARD   = 5,
 };
@@ -1288,17 +1290,60 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host)
 {}
 #endif
 
-static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
-                               struct ahci_host_priv *hpriv)
+/*
+ * ahci_init_msix() only implements single MSI-X support, not multiple
+ * MSI-X per-port interrupts. This is needed for host controllers that only
+ * have MSI-X support implemented, but no MSI or intx.
+ */
+static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports,
+                         struct ahci_host_priv *hpriv)
 {
        int rc, nvec;
+       struct msix_entry entry = {};
 
+       /* Do not init MSI-X if MSI is disabled for the device */
        if (hpriv->flags & AHCI_HFLAG_NO_MSI)
-               goto intx;
+               return -ENODEV;
+
+       nvec = pci_msix_vec_count(pdev);
+       if (nvec < 0)
+               return nvec;
+
+       if (!nvec) {
+               rc = -ENODEV;
+               goto fail;
+       }
+
+       /*
+        * There can be more than one vector (e.g. for error detection or
+        * hdd hotplug). Only the first vector (entry.entry = 0) is used.
+        */
+       rc = pci_enable_msix_exact(pdev, &entry, 1);
+       if (rc < 0)
+               goto fail;
+
+       hpriv->irq = entry.vector;
+
+       return 1;
+fail:
+       dev_err(&pdev->dev,
+               "failed to enable MSI-X with error %d, # of vectors: %d\n",
+               rc, nvec);
+
+       return rc;
+}
+
+static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports,
+                       struct ahci_host_priv *hpriv)
+{
+       int rc, nvec;
+
+       if (hpriv->flags & AHCI_HFLAG_NO_MSI)
+               return -ENODEV;
 
        nvec = pci_msi_vec_count(pdev);
        if (nvec < 0)
-               goto intx;
+               return nvec;
 
        /*
         * If number of MSIs is less than number of ports then Sharing Last
@@ -1311,8 +1356,8 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
        rc = pci_enable_msi_exact(pdev, nvec);
        if (rc == -ENOSPC)
                goto single_msi;
-       else if (rc < 0)
-               goto intx;
+       if (rc < 0)
+               return rc;
 
        /* fallback to single MSI mode if the controller enforced MRSM mode */
        if (readl(hpriv->mmio + HOST_CTL) & HOST_MRSM) {
@@ -1324,15 +1369,42 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
        if (nvec > 1)
                hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
 
-       return nvec;
+       goto out;
 
 single_msi:
-       if (pci_enable_msi(pdev))
-               goto intx;
-       return 1;
+       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;
+
+       nvec = ahci_init_msi(pdev, n_ports, hpriv);
+       if (nvec >= 0)
+               return nvec;
+
+       /*
+        * Currently, MSI-X support only implements single IRQ mode and
+        * exists for controllers which can't do other types of IRQ. Only
+        * set it up if MSI fails.
+        */
+       nvec = ahci_init_msix(pdev, n_ports, hpriv);
+       if (nvec >= 0)
+               return nvec;
 
-intx:
+       /* lagacy intx interrupts */
        pci_intx(pdev, 1);
+       hpriv->irq = pdev->irq;
+
        return 0;
 }
 
@@ -1371,11 +1443,13 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                dev_info(&pdev->dev,
                         "PDC42819 can only drive SATA devices with this driver\n");
 
-       /* Both Connext and Enmotus devices use non-standard BARs */
+       /* Some devices use non-standard BARs */
        if (pdev->vendor == PCI_VENDOR_ID_STMICRO && pdev->device == 0xCC06)
                ahci_pci_bar = AHCI_PCI_BAR_STA2X11;
        else if (pdev->vendor == 0x1c44 && pdev->device == 0x8000)
                ahci_pci_bar = AHCI_PCI_BAR_ENMOTUS;
+       else if (pdev->vendor == 0x177d && pdev->device == 0xa01c)
+               ahci_pci_bar = AHCI_PCI_BAR_CAVIUM;
 
        /*
         * The JMicron chip 361/363 contains one SATA controller and one
@@ -1497,13 +1571,13 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
         */
        n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
 
-       ahci_init_interrupts(pdev, n_ports, hpriv);
-
        host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
        if (!host)
                return -ENOMEM;
        host->private_data = hpriv;
 
+       ahci_init_interrupts(pdev, n_ports, hpriv);
+
        if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
                host->flags |= ATA_HOST_PARALLEL_SCAN;
        else
@@ -1549,7 +1623,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        pci_set_master(pdev);
 
-       return ahci_host_activate(host, pdev->irq, &ahci_sht);
+       return ahci_host_activate(host, &ahci_sht);
 }
 
 module_pci_driver(ahci_pci_driver);