55 lines
2.0 KiB
Diff
55 lines
2.0 KiB
Diff
From 4d44d8447b13ded9c6931583938183c76b1846ed Mon Sep 17 00:00:00 2001
|
|
From: P33M <P33M@github.com>
|
|
Date: Sat, 13 Jul 2013 21:48:41 +0100
|
|
Subject: [PATCH 079/174] dwc_otg: fiq: prevent FIQ thrash and incorrect state
|
|
passing to IRQ
|
|
|
|
In the case of a transaction to a device that had previously aborted
|
|
due to an error, several interrupts are enabled to reset the error
|
|
count when a device responds. This has the side-effect of making the
|
|
FIQ thrash because the hardware will generate multiple instances of
|
|
a NAK on an IN bulk/interrupt endpoint and multiple instances of ACK
|
|
on an OUT bulk/interrupt endpoint. Make the FIQ mask and clear the
|
|
associated interrupts.
|
|
|
|
Additionally, on non-split transactions make sure that only unmasked
|
|
interrupts are cleared. This caused a hard-to-trigger but serious
|
|
race condition when you had the combination of an endpoint awaiting
|
|
error recovery and a transaction completed on an endpoint - due to
|
|
the sequencing and timing of interrupts generated by the dwc_otg core,
|
|
it was possible to confuse the IRQ handler.
|
|
---
|
|
drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 21 +++++++++++++++++++++
|
|
1 file changed, 21 insertions(+)
|
|
|
|
--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
|
|
+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
|
|
@@ -324,6 +324,27 @@ int fiq_hcintr_handle(int channel, hfnum
|
|
}
|
|
}
|
|
}
|
|
+ else
|
|
+ {
|
|
+ /*
|
|
+ * If we have any of NAK, ACK, Datatlgerr active on a
|
|
+ * non-split channel, the sole reason is to reset error
|
|
+ * counts for a previously broken transaction. The FIQ
|
|
+ * will thrash on NAK IN and ACK OUT in particular so
|
|
+ * handle it "once" and allow the IRQ to do the rest.
|
|
+ */
|
|
+ hcint.d32 &= hcintmsk.d32;
|
|
+ if(hcint.b.nak)
|
|
+ {
|
|
+ hcintmsk.b.nak = 0;
|
|
+ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32);
|
|
+ }
|
|
+ if (hcint.b.ack)
|
|
+ {
|
|
+ hcintmsk.b.ack = 0;
|
|
+ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32);
|
|
+ }
|
|
+ }
|
|
|
|
// Clear the interrupt, this will also clear the HAINT bit
|
|
FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x8), hcint.d32);
|