Skip to content

Commit 32aea26

Browse files
suman-tripathihtejun
authored andcommitted
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>
1 parent d867b95 commit 32aea26

File tree

1 file changed

+46
-1
lines changed

1 file changed

+46
-1
lines changed

drivers/ata/ahci_xgene.c

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,51 @@ static int xgene_ahci_softreset(struct ata_link *link, unsigned int *class,
548548
return rc;
549549
}
550550

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+
551596
static irqreturn_t xgene_ahci_irq_intr(int irq, void *dev_instance)
552597
{
553598
struct ata_host *host = dev_instance;
@@ -576,7 +621,7 @@ static irqreturn_t xgene_ahci_irq_intr(int irq, void *dev_instance)
576621
*/
577622
writel(irq_stat, mmio + HOST_IRQ_STAT);
578623

579-
rc = ahci_handle_port_intr(host, irq_masked);
624+
rc = xgene_ahci_handle_broken_edge_irq(host, irq_masked);
580625

581626
spin_unlock(&host->lock);
582627

0 commit comments

Comments
 (0)