fc34fb983e8792d33fd76ecf43ffce06cf4cc076
[openembedded.git] /
1 From 4288b7df4ae6629a4fb14aca2c489da01d4d19c3 Mon Sep 17 00:00:00 2001
2 From: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
3 Date: Tue, 31 Mar 2009 12:35:09 -0700
4 Subject: [PATCH] musb: support disconnect after HNP roleswitch
5
6 Adjust HNP state machines in MUSB driver so that they handle the
7 case where the cable is disconnected.  The A-side machine was
8 very wrong (unrecoverable); the B-Side was much less so.
9
10  - A_PERIPHERAL ... as usual, the non-observability of the ID
11    pin through Mentor's registers makes trouble.  We can't go
12    directly to A_WAIT_VFALL to end the session and start the
13    disconnect processing.  We can however sense link suspending,
14    go to A_WAIT_BCON, and from there use OTG timeouts to finally
15    trigger that A_WAIT_VFALL transition.  (Hoping that nobody
16    reconnects quickly to that port and notices the wrong state.)
17
18  - B_HOST ... actually clear the Host Request (HR) bit as the
19    messages say, disconnect the peripheral from the root hub,
20    and don't detour through a suspend state.  (In some cases
21    this would eventually have cleaned up.)
22
23 Also adjust the A_SUSPEND transition to respect the A_AIDL_BDIS
24 timeout, so if HNP doesn't trigger quickly enough the A_WAIT_VFALL
25 transition happens as it should.
26
27 Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
28 ---
29  drivers/usb/musb/musb_core.c    |   41 +++++++++++++++++++++++++++-----------
30  drivers/usb/musb/musb_gadget.c  |    2 +
31  drivers/usb/musb/musb_virthub.c |    4 +++
32  3 files changed, 35 insertions(+), 12 deletions(-)
33
34 diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
35 index 9dc995a..5770ccb 100644
36 --- a/drivers/usb/musb/musb_core.c
37 +++ b/drivers/usb/musb/musb_core.c
38 @@ -304,9 +304,11 @@ void musb_otg_timer_func(unsigned long data)
39                 musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
40                 musb->is_active = 0;
41                 break;
42 +       case OTG_STATE_A_SUSPEND:
43         case OTG_STATE_A_WAIT_BCON:
44 -               DBG(1, "HNP: a_wait_bcon timeout; back to a_host\n");
45 -               musb_hnp_stop(musb);
46 +               DBG(1, "HNP: %s timeout\n", otg_state_string(musb));
47 +               musb_set_vbus(musb, 0);
48 +               musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
49                 break;
50         default:
51                 DBG(1, "HNP: Unhandled mode %s\n", otg_state_string(musb));
52 @@ -324,15 +326,12 @@ void musb_hnp_stop(struct musb *musb)
53         void __iomem    *mbase = musb->mregs;
54         u8      reg;
55  
56 +       DBG(1, "HNP: stop from %s\n", otg_state_string(musb));
57 +
58         switch (musb->xceiv->state) {
59         case OTG_STATE_A_PERIPHERAL:
60 -       case OTG_STATE_A_WAIT_VFALL:
61 -       case OTG_STATE_A_WAIT_BCON:
62 -               DBG(1, "HNP: Switching back to A-host\n");
63                 musb_g_disconnect(musb);
64 -               musb->xceiv->state = OTG_STATE_A_IDLE;
65 -               MUSB_HST_MODE(musb);
66 -               musb->is_active = 0;
67 +               DBG(1, "HNP: back to %s\n", otg_state_string(musb));
68                 break;
69         case OTG_STATE_B_HOST:
70                 DBG(1, "HNP: Disabling HR\n");
71 @@ -775,7 +774,16 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
72  #endif /* HOST */
73  #ifdef CONFIG_USB_MUSB_OTG
74                 case OTG_STATE_B_HOST:
75 -                       musb_hnp_stop(musb);
76 +                       /* REVISIT this behaves for "real disconnect"
77 +                        * cases; make sure the other transitions from
78 +                        * from B_HOST act right too.  The B_HOST code
79 +                        * in hnp_stop() is currently not used...
80 +                        */
81 +                       musb_root_disconnect(musb);
82 +                       musb_to_hcd(musb)->self.is_b_host = 0;
83 +                       musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
84 +                       MUSB_DEV_MODE(musb);
85 +                       musb_g_disconnect(musb);
86                         break;
87                 case OTG_STATE_A_PERIPHERAL:
88                         musb_hnp_stop(musb);
89 @@ -807,10 +815,19 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
90                 switch (musb->xceiv->state) {
91  #ifdef CONFIG_USB_MUSB_OTG
92                 case OTG_STATE_A_PERIPHERAL:
93 -                       /*
94 -                        * We cannot stop HNP here, devctl BDEVICE might be
95 -                        * still set.
96 +                       /* We also come here if the cable is removed, since
97 +                        * this silicon doesn't report ID-no-longer-grounded.
98 +                        *
99 +                        * We depend on T(a_wait_bcon) to shut us down, and
100 +                        * hope users don't do anything dicey during this
101 +                        * undesired detour through A_WAIT_BCON.
102                          */
103 +                       musb_hnp_stop(musb);
104 +                       usb_hcd_resume_root_hub(musb_to_hcd(musb));
105 +                       musb_root_disconnect(musb);
106 +                       musb_platform_try_idle(musb, jiffies
107 +                                       + msecs_to_jiffies(musb->a_wait_bcon
108 +                                               ? : OTG_TIME_A_WAIT_BCON));
109                         break;
110  #endif
111                 case OTG_STATE_B_PERIPHERAL:
112 diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
113 index 7dd3d59..8b3c4e2 100644
114 --- a/drivers/usb/musb/musb_gadget.c
115 +++ b/drivers/usb/musb/musb_gadget.c
116 @@ -1962,9 +1962,11 @@ void musb_g_disconnect(struct musb *musb)
117                 DBG(2, "Unhandled disconnect %s, setting a_idle\n",
118                         otg_state_string(musb));
119                 musb->xceiv->state = OTG_STATE_A_IDLE;
120 +               MUSB_HST_MODE(musb);
121                 break;
122         case OTG_STATE_A_PERIPHERAL:
123                 musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
124 +               MUSB_HST_MODE(musb);
125                 break;
126         case OTG_STATE_B_WAIT_ACON:
127         case OTG_STATE_B_HOST:
128 diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
129 index 14f7cf3..e8ef925 100644
130 --- a/drivers/usb/musb/musb_virthub.c
131 +++ b/drivers/usb/musb/musb_virthub.c
132 @@ -83,6 +83,10 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend)
133                         musb->xceiv->state = OTG_STATE_A_SUSPEND;
134                         musb->is_active = is_otg_enabled(musb)
135                                         && musb->xceiv->host->b_hnp_enable;
136 +                       if (musb->is_active)
137 +                               mod_timer(&musb->otg_timer, jiffies
138 +                                       + msecs_to_jiffies(
139 +                                               OTG_TIME_A_AIDL_BDIS));
140                         musb_platform_try_idle(musb, 0);
141                         break;
142  #ifdef CONFIG_USB_MUSB_OTG
143 -- 
144 1.6.0.4
145