Merge branch 'x86-boot-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[firefly-linux-kernel-4.4.55.git] / drivers / ata / libahci.c
index 48175e5bd0011ebd0b1ecb218e75bf440a4c0afd..5eb61c9e63da95cbd24a54ee0604262288c8a9f6 100644 (file)
@@ -1778,15 +1778,16 @@ static void ahci_handle_port_interrupt(struct ata_port *ap,
        }
 }
 
-static void ahci_port_intr(struct ata_port *ap)
+static void ahci_update_intr_status(struct ata_port *ap)
 {
        void __iomem *port_mmio = ahci_port_base(ap);
+       struct ahci_port_priv *pp = ap->private_data;
        u32 status;
 
        status = readl(port_mmio + PORT_IRQ_STAT);
        writel(status, port_mmio + PORT_IRQ_STAT);
 
-       ahci_handle_port_interrupt(ap, port_mmio, status);
+       atomic_or(status, &pp->intr_status);
 }
 
 static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
@@ -1794,14 +1795,11 @@ static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
        struct ata_port *ap = dev_instance;
        struct ahci_port_priv *pp = ap->private_data;
        void __iomem *port_mmio = ahci_port_base(ap);
-       unsigned long flags;
        u32 status;
 
-       spin_lock_irqsave(&ap->host->lock, flags);
-       status = pp->intr_status;
-       if (status)
-               pp->intr_status = 0;
-       spin_unlock_irqrestore(&ap->host->lock, flags);
+       status = atomic_xchg(&pp->intr_status, 0);
+       if (!status)
+               return IRQ_NONE;
 
        spin_lock_bh(ap->lock);
        ahci_handle_port_interrupt(ap, port_mmio, status);
@@ -1810,45 +1808,12 @@ static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
        return IRQ_HANDLED;
 }
 
-static void ahci_update_intr_status(struct ata_port *ap)
+irqreturn_t ahci_thread_fn(int irq, void *dev_instance)
 {
-       void __iomem *port_mmio = ahci_port_base(ap);
-       struct ahci_port_priv *pp = ap->private_data;
-       u32 status;
-
-       status = readl(port_mmio + PORT_IRQ_STAT);
-       writel(status, port_mmio + PORT_IRQ_STAT);
-
-       pp->intr_status |= status;
-}
-
-static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
-{
-       struct ata_port *ap_this = dev_instance;
-       struct ahci_port_priv *pp = ap_this->private_data;
-       struct ata_host *host = ap_this->host;
+       struct ata_host *host = dev_instance;
        struct ahci_host_priv *hpriv = host->private_data;
-       void __iomem *mmio = hpriv->mmio;
+       u32 irq_masked = hpriv->port_map;
        unsigned int i;
-       u32 irq_stat, irq_masked;
-
-       VPRINTK("ENTER\n");
-
-       spin_lock(&host->lock);
-
-       irq_stat = readl(mmio + HOST_IRQ_STAT);
-
-       if (!irq_stat) {
-               u32 status = pp->intr_status;
-
-               spin_unlock(&host->lock);
-
-               VPRINTK("EXIT\n");
-
-               return status ? IRQ_WAKE_THREAD : IRQ_NONE;
-       }
-
-       irq_masked = irq_stat & hpriv->port_map;
 
        for (i = 0; i < host->n_ports; i++) {
                struct ata_port *ap;
@@ -1858,7 +1823,7 @@ static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
 
                ap = host->ports[i];
                if (ap) {
-                       ahci_update_intr_status(ap);
+                       ahci_port_thread_fn(irq, ap);
                        VPRINTK("port %u\n", i);
                } else {
                        VPRINTK("port %u (no irq)\n", i);
@@ -1868,9 +1833,22 @@ static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
                }
        }
 
-       writel(irq_stat, mmio + HOST_IRQ_STAT);
+       return IRQ_HANDLED;
+}
 
-       spin_unlock(&host->lock);
+static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
+{
+       struct ata_port *ap = dev_instance;
+       void __iomem *port_mmio = ahci_port_base(ap);
+       struct ahci_port_priv *pp = ap->private_data;
+       u32 status;
+
+       VPRINTK("ENTER\n");
+
+       status = readl(port_mmio + PORT_IRQ_STAT);
+       writel(status, port_mmio + PORT_IRQ_STAT);
+
+       atomic_or(status, &pp->intr_status);
 
        VPRINTK("EXIT\n");
 
@@ -1897,8 +1875,6 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
 
        irq_masked = irq_stat & hpriv->port_map;
 
-       spin_lock(&host->lock);
-
        for (i = 0; i < host->n_ports; i++) {
                struct ata_port *ap;
 
@@ -1907,7 +1883,7 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
 
                ap = host->ports[i];
                if (ap) {
-                       ahci_port_intr(ap);
+                       ahci_update_intr_status(ap);
                        VPRINTK("port %u\n", i);
                } else {
                        VPRINTK("port %u (no irq)\n", i);
@@ -1930,11 +1906,9 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
         */
        writel(irq_stat, mmio + HOST_IRQ_STAT);
 
-       spin_unlock(&host->lock);
-
        VPRINTK("EXIT\n");
 
-       return IRQ_RETVAL(handled);
+       return handled ? IRQ_WAKE_THREAD : IRQ_NONE;
 }
 
 unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
@@ -2346,13 +2320,8 @@ static int ahci_port_start(struct ata_port *ap)
         */
        pp->intr_mask = DEF_PORT_IRQ;
 
-       /*
-        * Switch to per-port locking in case each port has its own MSI vector.
-        */
-       if ((hpriv->flags & AHCI_HFLAG_MULTI_MSI)) {
-               spin_lock_init(&pp->lock);
-               ap->lock = &pp->lock;
-       }
+       spin_lock_init(&pp->lock);
+       ap->lock = &pp->lock;
 
        ap->private_data = pp;
 
@@ -2513,6 +2482,31 @@ out_free_irqs:
        return rc;
 }
 
+static int ahci_host_activate_single_irq(struct ata_host *host, int irq,
+                                        struct scsi_host_template *sht)
+{
+       int i, rc;
+
+       rc = ata_host_start(host);
+       if (rc)
+               return rc;
+
+       rc = devm_request_threaded_irq(host->dev, irq, ahci_single_irq_intr,
+                                      ahci_thread_fn, IRQF_SHARED,
+                                      dev_driver_string(host->dev), host);
+       if (rc)
+               return rc;
+
+       for (i = 0; i < host->n_ports; i++)
+               ata_port_desc(host->ports[i], "irq %d", irq);
+
+       rc = ata_host_register(host, sht);
+       if (rc)
+               devm_free_irq(host->dev, irq, host);
+
+       return rc;
+}
+
 /**
  *     ahci_host_activate - start AHCI host, request IRQs and register it
  *     @host: target ATA host
@@ -2538,8 +2532,7 @@ int ahci_host_activate(struct ata_host *host, int irq,
        if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
                rc = ahci_host_activate_multi_irqs(host, irq, sht);
        else
-               rc = ata_host_activate(host, irq, ahci_single_irq_intr,
-                                      IRQF_SHARED, sht);
+               rc = ahci_host_activate_single_irq(host, irq, sht);
        return rc;
 }
 EXPORT_SYMBOL_GPL(ahci_host_activate);