aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuman Tripathi <stripathi@apm.com>2016-02-06 00:55:24 -0500
committerTejun Heo <tj@kernel.org>2016-02-11 09:54:15 -0500
commit32aea2680de01f539d928112150279fdeeabca00 (patch)
tree37da2247e5c7b86b5d91a262716bdca5518a2d55
parentd867b95f965457b9e85fb061ef8e3fdc029116ed (diff)
ahci_xgene: Implement the workaround to fix the missing of the edge interrupt for the HOST_IRQ_STAT.
Due to H/W errata, the HOST_IRQ_STAT register misses the edge interrupt when clearing the HOST_IRQ_STAT register and hardware reporting the PORT_IRQ_STAT register happens to be at the same clock cycle. Signed-off-by: Suman Tripathi <stripathi@apm.com> Signed-off-by: Tejun Heo <tj@kernel.org>
-rw-r--r--drivers/ata/ahci_xgene.c47
1 files changed, 46 insertions, 1 deletions
diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c
index 8b8ccb6434e2..8e3f7faf00d3 100644
--- a/drivers/ata/ahci_xgene.c
+++ b/drivers/ata/ahci_xgene.c
@@ -548,6 +548,51 @@ softreset_retry:
548 return rc; 548 return rc;
549} 549}
550 550
551/**
552 * xgene_ahci_handle_broken_edge_irq - Handle the broken irq.
553 * @ata_host: Host that recieved the irq
554 * @irq_masked: HOST_IRQ_STAT value
555 *
556 * For hardware with broken edge trigger latch
557 * the HOST_IRQ_STAT register misses the edge interrupt
558 * when clearing of HOST_IRQ_STAT register and hardware
559 * reporting the PORT_IRQ_STAT register at the
560 * same clock cycle.
561 * As such, the algorithm below outlines the workaround.
562 *
563 * 1. Read HOST_IRQ_STAT register and save the state.
564 * 2. Clear the HOST_IRQ_STAT register.
565 * 3. Read back the HOST_IRQ_STAT register.
566 * 4. If HOST_IRQ_STAT register equals to zero, then
567 * traverse the rest of port's PORT_IRQ_STAT register
568 * to check if an interrupt is triggered at that point else
569 * go to step 6.
570 * 5. If PORT_IRQ_STAT register of rest ports is not equal to zero
571 * then update the state of HOST_IRQ_STAT saved in step 1.
572 * 6. Handle port interrupts.
573 * 7. Exit
574 */
575static int xgene_ahci_handle_broken_edge_irq(struct ata_host *host,
576 u32 irq_masked)
577{
578 struct ahci_host_priv *hpriv = host->private_data;
579 void __iomem *port_mmio;
580 int i;
581
582 if (!readl(hpriv->mmio + HOST_IRQ_STAT)) {
583 for (i = 0; i < host->n_ports; i++) {
584 if (irq_masked & (1 << i))
585 continue;
586
587 port_mmio = ahci_port_base(host->ports[i]);
588 if (readl(port_mmio + PORT_IRQ_STAT))
589 irq_masked |= (1 << i);
590 }
591 }
592
593 return ahci_handle_port_intr(host, irq_masked);
594}
595
551static irqreturn_t xgene_ahci_irq_intr(int irq, void *dev_instance) 596static irqreturn_t xgene_ahci_irq_intr(int irq, void *dev_instance)
552{ 597{
553 struct ata_host *host = dev_instance; 598 struct ata_host *host = dev_instance;
@@ -576,7 +621,7 @@ static irqreturn_t xgene_ahci_irq_intr(int irq, void *dev_instance)
576 */ 621 */
577 writel(irq_stat, mmio + HOST_IRQ_STAT); 622 writel(irq_stat, mmio + HOST_IRQ_STAT);
578 623
579 rc = ahci_handle_port_intr(host, irq_masked); 624 rc = xgene_ahci_handle_broken_edge_irq(host, irq_masked);
580 625
581 spin_unlock(&host->lock); 626 spin_unlock(&host->lock);
582 627