From 6a276a4e4878b5b3e92c8975a87eb641d57669c4 Mon Sep 17 00:00:00 2001 From: P33M Date: Sat, 13 Jul 2013 21:48:41 +0100 Subject: [PATCH 079/196] 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(+) diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c index 27b673f..d655363 100644 --- 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_data_t 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); -- 1.9.1