[PATCH 1/2] libahci: Implement the workaround to fix the missing of edge interrupt for HOST_IRQ_STAT
Tejun Heo
tj at kernel.org
Mon Jan 11 08:16:48 PST 2016
Hello,
On Mon, Jan 11, 2016 at 03:36:02PM +0530, Suman Tripathi wrote:
> 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 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 void ahci_handle_broken_edge_irq(struct ata_host *host,
> + u32 *irq_masked)
> +{
> + struct ahci_host_priv *hpriv = host->private_data;
> + void __iomem *mmio = hpriv->mmio;
> + unsigned int i, temp_irq_masked;
> + struct ata_port *next_ap;
> + void __iomem *port_mmio;
> + int j;
> +
> + if (!readl(mmio + HOST_IRQ_STAT)) {
> + temp_irq_masked = *irq_masked;
> +
> + for (i = 0; i < __sw_hweight32(hpriv->port_map);
> + i++) {
^^^^^^
Doesn't this fit on the same line?
> + if (*irq_masked & (1 << i)) {
> + for (j = 0;
> + j < __sw_hweight32(hpriv->port_map);
> + j++) {
Heh, if (!COND) continue; can be your friend.
> + if (i == j)
> + continue;
> +
> + next_ap = host->ports[j];
> + port_mmio = ahci_port_base(next_ap);
> + if (readl(port_mmio + PORT_IRQ_STAT))
> + temp_irq_masked |= (1 << j);
> + }
> + }
> + }
> + *irq_masked = temp_irq_masked;
> + }
> +
> +}
So, this is really specific to the controller.
> static u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked)
> {
> + struct ahci_host_priv *hpriv = host->private_data;
> unsigned int i, handled = 0;
>
> + /*
> + * 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.
> + */
> + if (hpriv->flags & AHCI_HFLAG_EDGE_IRQ_BROKEN)
> + ahci_handle_broken_edge_irq(host, &irq_masked);
> +
And all it needs is doing some extra processing at the beginning of
port interrupt handling. I think it'd be better to implement
ahci_xgene specific IRQ handler which does the special processing and
call the generic ahci_handle_port_intr().
Thanks.
--
tejun
More information about the linux-arm-kernel
mailing list