diff options
Diffstat (limited to 'drivers/ata/libahci.c')
-rw-r--r-- | drivers/ata/libahci.c | 105 |
1 files changed, 76 insertions, 29 deletions
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 287c4ba0219f..d256a66158be 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -1825,11 +1825,38 @@ static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance) return IRQ_WAKE_THREAD; } -static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance) +static u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked) +{ + unsigned int i, handled = 0; + + 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_intr(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); + } + + handled = 1; + } + + return handled; +} + +static irqreturn_t ahci_single_edge_irq_intr(int irq, void *dev_instance) { struct ata_host *host = dev_instance; struct ahci_host_priv *hpriv; - unsigned int i, handled = 0; + unsigned int rc = 0; void __iomem *mmio; u32 irq_stat, irq_masked; @@ -1847,25 +1874,44 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance) spin_lock(&host->lock); - for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap; + /* + * HOST_IRQ_STAT behaves as edge triggered latch meaning that + * it should be cleared before all the port events are cleared. + */ + writel(irq_stat, mmio + HOST_IRQ_STAT); - if (!(irq_masked & (1 << i))) - continue; + rc = ahci_handle_port_intr(host, irq_masked); - ap = host->ports[i]; - if (ap) { - ahci_port_intr(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); - } + spin_unlock(&host->lock); - handled = 1; - } + VPRINTK("EXIT\n"); + + return IRQ_RETVAL(rc); +} + +static irqreturn_t ahci_single_level_irq_intr(int irq, void *dev_instance) +{ + struct ata_host *host = dev_instance; + struct ahci_host_priv *hpriv; + unsigned int rc = 0; + void __iomem *mmio; + u32 irq_stat, irq_masked; + + VPRINTK("ENTER\n"); + + hpriv = host->private_data; + mmio = hpriv->mmio; + + /* sigh. 0xffffffff is a valid return from h/w */ + irq_stat = readl(mmio + HOST_IRQ_STAT); + if (!irq_stat) + return IRQ_NONE; + + irq_masked = irq_stat & hpriv->port_map; + + spin_lock(&host->lock); + + rc = ahci_handle_port_intr(host, irq_masked); /* HOST_IRQ_STAT behaves as level triggered latch meaning that * it should be cleared after all the port events are cleared; @@ -1882,7 +1928,7 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance) VPRINTK("EXIT\n"); - return IRQ_RETVAL(handled); + return IRQ_RETVAL(rc); } unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) @@ -2297,7 +2343,7 @@ static int ahci_port_start(struct ata_port *ap) /* * Switch to per-port locking in case each port has its own MSI vector. */ - if ((hpriv->flags & AHCI_HFLAG_MULTI_MSI)) { + if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) { spin_lock_init(&pp->lock); ap->lock = &pp->lock; } @@ -2425,7 +2471,10 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq, rc = ata_host_start(host); if (rc) return rc; - + /* + * Requests IRQs according to AHCI-1.1 when multiple MSIs were + * allocated. That is one MSI per port, starting from @irq. + */ for (i = 0; i < host->n_ports; i++) { struct ahci_port_priv *pp = host->ports[i]->private_data; @@ -2464,29 +2513,27 @@ out_free_irqs: /** * ahci_host_activate - start AHCI host, request IRQs and register it * @host: target ATA host - * @irq: base IRQ number to request * @sht: scsi_host_template to use when registering the host * - * Similar to ata_host_activate, but requests IRQs according to AHCI-1.1 - * when multiple MSIs were allocated. That is one MSI per port, starting - * from @irq. - * * LOCKING: * Inherited from calling layer (may sleep). * * RETURNS: * 0 on success, -errno otherwise. */ -int ahci_host_activate(struct ata_host *host, int irq, - struct scsi_host_template *sht) +int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht) { struct ahci_host_priv *hpriv = host->private_data; + int irq = hpriv->irq; int rc; if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) rc = ahci_host_activate_multi_irqs(host, irq, sht); + else if (hpriv->flags & AHCI_HFLAG_EDGE_IRQ) + rc = ata_host_activate(host, irq, ahci_single_edge_irq_intr, + IRQF_SHARED, sht); else - rc = ata_host_activate(host, irq, ahci_single_irq_intr, + rc = ata_host_activate(host, irq, ahci_single_level_irq_intr, IRQF_SHARED, sht); return rc; } |