aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/brcm2708/patches-3.10/0079-dwc_otg-fiq-prevent-FIQ-thrash-and-incorrect-state-p.patch
blob: d869941e5a90f90facbd6825d69f32bae0e4df2c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
From 6a276a4e4878b5b3e92c8975a87eb641d57669c4 Mon Sep 17 00:00:00 2001
From: P33M <P33M@github.com>
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