Commit c46c85b2 authored by P33M's avatar P33M
Browse files

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.
parent 4e36213a
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -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);