diff options
author | Suman Tripathi <stripathi@apm.com> | 2016-02-06 00:55:24 -0500 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2016-02-11 09:54:15 -0500 |
commit | 32aea2680de01f539d928112150279fdeeabca00 (patch) | |
tree | 37da2247e5c7b86b5d91a262716bdca5518a2d55 | |
parent | d867b95f965457b9e85fb061ef8e3fdc029116ed (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.c | 47 |
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 | */ | ||
575 | static 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 | |||
551 | static irqreturn_t xgene_ahci_irq_intr(int irq, void *dev_instance) | 596 | static 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 | ||