Skip to content

Commit c46c85b

Browse files
P33MP33M
authored andcommitted
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.
1 parent 4e36213 commit c46c85b

File tree

1 file changed

+21
-0
lines changed

1 file changed

+21
-0
lines changed

drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,27 @@ int fiq_hcintr_handle(int channel, hfnum_data_t hfnum)
324324
}
325325
}
326326
}
327+
else
328+
{
329+
/*
330+
* If we have any of NAK, ACK, Datatlgerr active on a
331+
* non-split channel, the sole reason is to reset error
332+
* counts for a previously broken transaction. The FIQ
333+
* will thrash on NAK IN and ACK OUT in particular so
334+
* handle it "once" and allow the IRQ to do the rest.
335+
*/
336+
hcint.d32 &= hcintmsk.d32;
337+
if(hcint.b.nak)
338+
{
339+
hcintmsk.b.nak = 0;
340+
FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32);
341+
}
342+
if (hcint.b.ack)
343+
{
344+
hcintmsk.b.ack = 0;
345+
FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32);
346+
}
347+
}
327348

328349
// Clear the interrupt, this will also clear the HAINT bit
329350
FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x8), hcint.d32);

0 commit comments

Comments
 (0)