|
@@ -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)
|
|
@@ -1807,6 +1808,34 @@ static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
|
+irqreturn_t ahci_thread_fn(int irq, void *dev_instance)
|
|
|
+{
|
|
|
+ struct ata_host *host = dev_instance;
|
|
|
+ struct ahci_host_priv *hpriv = host->private_data;
|
|
|
+ u32 irq_masked = hpriv->port_map;
|
|
|
+ unsigned int i;
|
|
|
+
|
|
|
+ for (i = 0; i < host->n_ports; i++) {
|
|
|
+ struct ata_port *ap;
|
|
|
+
|
|
|
+ if (!(irq_masked & (1 << i)))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ ap = host->ports[i];
|
|
|
+ if (ap) {
|
|
|
+ ahci_port_thread_fn(irq, ap);
|
|
|
+ VPRINTK("port %u\n", i);
|
|
|
+ } else {
|
|
|
+ VPRINTK("port %u (no irq)\n", i);
|
|
|
+ if (ata_ratelimit())
|
|
|
+ dev_warn(host->dev,
|
|
|
+ "interrupt on disabled port %u\n", i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return IRQ_HANDLED;
|
|
|
+}
|
|
|
+
|
|
|
static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
|
|
|
{
|
|
|
struct ata_port *ap = dev_instance;
|
|
@@ -1856,7 +1885,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);
|
|
@@ -1883,7 +1912,7 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
|
|
|
|
|
|
VPRINTK("EXIT\n");
|
|
|
|
|
|
- return IRQ_RETVAL(handled);
|
|
|
+ return handled ? IRQ_WAKE_THREAD : IRQ_NONE;
|
|
|
}
|
|
|
|
|
|
unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
|
|
@@ -2295,13 +2324,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;
|
|
|
|
|
@@ -2462,6 +2486,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
|
|
@@ -2487,8 +2536,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);
|