@@ -242,6 +242,8 @@ enum {
AHCI_HFLAG_NO_FBS = (1 << 18), /* no FBS */
AHCI_HFLAG_EDGE_IRQ = (1 << 19), /* HOST_IRQ_STAT behaves as
Edge Triggered */
+ AHCI_HFLAG_EDGE_IRQ_BROKEN = (1 << 20), /* HOST_IRQ_STAT miss edge
+ from PORT_IRQ_STAT */
/* ap->flags bits */
@@ -1832,10 +1832,55 @@ static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
return IRQ_WAKE_THREAD;
}
+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++) {
+ if (*irq_masked & (1 << i)) {
+ for (j = 0;
+ j < __sw_hweight32(hpriv->port_map);
+ j++) {
+ 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;
+ }
+
+}
+
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);
+
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap;
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 Signed-off-by: Suman Tripathi <stripathi@apm.com> --- drivers/ata/ahci.h | 2 ++ drivers/ata/libahci.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 0 deletions(-) -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html