summaryrefslogtreecommitdiff
path: root/recipes/linux/linux-omap-2.6.29/musb/0029-musb-support-disconnect-after-HNP-roleswitch.patch
blob: fc34fb983e8792d33fd76ecf43ffce06cf4cc076 (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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
From 4288b7df4ae6629a4fb14aca2c489da01d4d19c3 Mon Sep 17 00:00:00 2001
From: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
Date: Tue, 31 Mar 2009 12:35:09 -0700
Subject: [PATCH] musb: support disconnect after HNP roleswitch

Adjust HNP state machines in MUSB driver so that they handle the
case where the cable is disconnected.  The A-side machine was
very wrong (unrecoverable); the B-Side was much less so.

 - A_PERIPHERAL ... as usual, the non-observability of the ID
   pin through Mentor's registers makes trouble.  We can't go
   directly to A_WAIT_VFALL to end the session and start the
   disconnect processing.  We can however sense link suspending,
   go to A_WAIT_BCON, and from there use OTG timeouts to finally
   trigger that A_WAIT_VFALL transition.  (Hoping that nobody
   reconnects quickly to that port and notices the wrong state.)

 - B_HOST ... actually clear the Host Request (HR) bit as the
   messages say, disconnect the peripheral from the root hub,
   and don't detour through a suspend state.  (In some cases
   this would eventually have cleaned up.)

Also adjust the A_SUSPEND transition to respect the A_AIDL_BDIS
timeout, so if HNP doesn't trigger quickly enough the A_WAIT_VFALL
transition happens as it should.

Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
---
 drivers/usb/musb/musb_core.c    |   41 +++++++++++++++++++++++++++-----------
 drivers/usb/musb/musb_gadget.c  |    2 +
 drivers/usb/musb/musb_virthub.c |    4 +++
 3 files changed, 35 insertions(+), 12 deletions(-)

diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 9dc995a..5770ccb 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -304,9 +304,11 @@ void musb_otg_timer_func(unsigned long data)
 		musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
 		musb->is_active = 0;
 		break;
+	case OTG_STATE_A_SUSPEND:
 	case OTG_STATE_A_WAIT_BCON:
-		DBG(1, "HNP: a_wait_bcon timeout; back to a_host\n");
-		musb_hnp_stop(musb);
+		DBG(1, "HNP: %s timeout\n", otg_state_string(musb));
+		musb_set_vbus(musb, 0);
+		musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
 		break;
 	default:
 		DBG(1, "HNP: Unhandled mode %s\n", otg_state_string(musb));
@@ -324,15 +326,12 @@ void musb_hnp_stop(struct musb *musb)
 	void __iomem	*mbase = musb->mregs;
 	u8	reg;
 
+	DBG(1, "HNP: stop from %s\n", otg_state_string(musb));
+
 	switch (musb->xceiv->state) {
 	case OTG_STATE_A_PERIPHERAL:
-	case OTG_STATE_A_WAIT_VFALL:
-	case OTG_STATE_A_WAIT_BCON:
-		DBG(1, "HNP: Switching back to A-host\n");
 		musb_g_disconnect(musb);
-		musb->xceiv->state = OTG_STATE_A_IDLE;
-		MUSB_HST_MODE(musb);
-		musb->is_active = 0;
+		DBG(1, "HNP: back to %s\n", otg_state_string(musb));
 		break;
 	case OTG_STATE_B_HOST:
 		DBG(1, "HNP: Disabling HR\n");
@@ -775,7 +774,16 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
 #endif	/* HOST */
 #ifdef CONFIG_USB_MUSB_OTG
 		case OTG_STATE_B_HOST:
-			musb_hnp_stop(musb);
+			/* REVISIT this behaves for "real disconnect"
+			 * cases; make sure the other transitions from
+			 * from B_HOST act right too.  The B_HOST code
+			 * in hnp_stop() is currently not used...
+			 */
+			musb_root_disconnect(musb);
+			musb_to_hcd(musb)->self.is_b_host = 0;
+			musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
+			MUSB_DEV_MODE(musb);
+			musb_g_disconnect(musb);
 			break;
 		case OTG_STATE_A_PERIPHERAL:
 			musb_hnp_stop(musb);
@@ -807,10 +815,19 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
 		switch (musb->xceiv->state) {
 #ifdef	CONFIG_USB_MUSB_OTG
 		case OTG_STATE_A_PERIPHERAL:
-			/*
-			 * We cannot stop HNP here, devctl BDEVICE might be
-			 * still set.
+			/* We also come here if the cable is removed, since
+			 * this silicon doesn't report ID-no-longer-grounded.
+			 *
+			 * We depend on T(a_wait_bcon) to shut us down, and
+			 * hope users don't do anything dicey during this
+			 * undesired detour through A_WAIT_BCON.
 			 */
+			musb_hnp_stop(musb);
+			usb_hcd_resume_root_hub(musb_to_hcd(musb));
+			musb_root_disconnect(musb);
+			musb_platform_try_idle(musb, jiffies
+					+ msecs_to_jiffies(musb->a_wait_bcon
+						? : OTG_TIME_A_WAIT_BCON));
 			break;
 #endif
 		case OTG_STATE_B_PERIPHERAL:
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 7dd3d59..8b3c4e2 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1962,9 +1962,11 @@ void musb_g_disconnect(struct musb *musb)
 		DBG(2, "Unhandled disconnect %s, setting a_idle\n",
 			otg_state_string(musb));
 		musb->xceiv->state = OTG_STATE_A_IDLE;
+		MUSB_HST_MODE(musb);
 		break;
 	case OTG_STATE_A_PERIPHERAL:
 		musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
+		MUSB_HST_MODE(musb);
 		break;
 	case OTG_STATE_B_WAIT_ACON:
 	case OTG_STATE_B_HOST:
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index 14f7cf3..e8ef925 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -83,6 +83,10 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend)
 			musb->xceiv->state = OTG_STATE_A_SUSPEND;
 			musb->is_active = is_otg_enabled(musb)
 					&& musb->xceiv->host->b_hnp_enable;
+			if (musb->is_active)
+				mod_timer(&musb->otg_timer, jiffies
+					+ msecs_to_jiffies(
+						OTG_TIME_A_AIDL_BDIS));
 			musb_platform_try_idle(musb, 0);
 			break;
 #ifdef	CONFIG_USB_MUSB_OTG
-- 
1.6.0.4