Skip to content

Commit

Permalink
ahci_xgene: Implement the workaround to fix the missing of the edge i…
Browse files Browse the repository at this point in the history
…nterrupt 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>
  • Loading branch information
Suman Tripathi authored and Tejun Heo committed Feb 11, 2016
1 parent d867b95 commit 32aea26
Showing 1 changed file with 46 additions and 1 deletion.
47 changes: 46 additions & 1 deletion drivers/ata/ahci_xgene.c
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,51 @@ static int xgene_ahci_softreset(struct ata_link *link, unsigned int *class,
return rc;
}

/**
* xgene_ahci_handle_broken_edge_irq - Handle the broken irq.
* @ata_host: Host that recieved the irq
* @irq_masked: HOST_IRQ_STAT value
*
* For hardware with broken edge trigger latch
* the HOST_IRQ_STAT register misses the edge interrupt
* when clearing of HOST_IRQ_STAT register and hardware
* reporting the PORT_IRQ_STAT register at the
* same clock cycle.
* As such, the algorithm below outlines the workaround.
*
* 1. Read HOST_IRQ_STAT register and save the state.
* 2. Clear the HOST_IRQ_STAT register.
* 3. Read back the HOST_IRQ_STAT register.
* 4. If HOST_IRQ_STAT register equals to zero, then
* traverse the rest of port's PORT_IRQ_STAT register
* to check if an interrupt is triggered at that point else
* go to step 6.
* 5. If PORT_IRQ_STAT register of rest ports is not equal to zero
* then update the state of HOST_IRQ_STAT saved in step 1.
* 6. Handle port interrupts.
* 7. Exit
*/
static int xgene_ahci_handle_broken_edge_irq(struct ata_host *host,
u32 irq_masked)
{
struct ahci_host_priv *hpriv = host->private_data;
void __iomem *port_mmio;
int i;

if (!readl(hpriv->mmio + HOST_IRQ_STAT)) {
for (i = 0; i < host->n_ports; i++) {
if (irq_masked & (1 << i))
continue;

port_mmio = ahci_port_base(host->ports[i]);
if (readl(port_mmio + PORT_IRQ_STAT))
irq_masked |= (1 << i);
}
}

return ahci_handle_port_intr(host, irq_masked);
}

static irqreturn_t xgene_ahci_irq_intr(int irq, void *dev_instance)
{
struct ata_host *host = dev_instance;
Expand Down Expand Up @@ -576,7 +621,7 @@ static irqreturn_t xgene_ahci_irq_intr(int irq, void *dev_instance)
*/
writel(irq_stat, mmio + HOST_IRQ_STAT);

rc = ahci_handle_port_intr(host, irq_masked);
rc = xgene_ahci_handle_broken_edge_irq(host, irq_masked);

spin_unlock(&host->lock);

Expand Down

0 comments on commit 32aea26

Please sign in to comment.