aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Brownell <dbrownell@users.sourceforge.net>2009-03-31 15:35:09 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-06-16 00:44:41 -0400
commitab983f2a1be582b00f706013f40f658769d0823a (patch)
tree95dce5821b73726351f323d2d1bd4a80bea9ceb3
parent1de00dae8036dfee44ebea2c38f942fb6072e0b7 (diff)
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@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/musb/musb_core.c41
-rw-r--r--drivers/usb/musb/musb_gadget.c2
-rw-r--r--drivers/usb/musb/musb_virthub.c4
3 files changed, 35 insertions, 12 deletions
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 93dd23a7eb6c..b49859623f8d 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)
304 musb->xceiv->state = OTG_STATE_B_PERIPHERAL; 304 musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
305 musb->is_active = 0; 305 musb->is_active = 0;
306 break; 306 break;
307 case OTG_STATE_A_SUSPEND:
307 case OTG_STATE_A_WAIT_BCON: 308 case OTG_STATE_A_WAIT_BCON:
308 DBG(1, "HNP: a_wait_bcon timeout; back to a_host\n"); 309 DBG(1, "HNP: %s timeout\n", otg_state_string(musb));
309 musb_hnp_stop(musb); 310 musb_set_vbus(musb, 0);
311 musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
310 break; 312 break;
311 default: 313 default:
312 DBG(1, "HNP: Unhandled mode %s\n", otg_state_string(musb)); 314 DBG(1, "HNP: Unhandled mode %s\n", otg_state_string(musb));
@@ -324,15 +326,12 @@ void musb_hnp_stop(struct musb *musb)
324 void __iomem *mbase = musb->mregs; 326 void __iomem *mbase = musb->mregs;
325 u8 reg; 327 u8 reg;
326 328
329 DBG(1, "HNP: stop from %s\n", otg_state_string(musb));
330
327 switch (musb->xceiv->state) { 331 switch (musb->xceiv->state) {
328 case OTG_STATE_A_PERIPHERAL: 332 case OTG_STATE_A_PERIPHERAL:
329 case OTG_STATE_A_WAIT_VFALL:
330 case OTG_STATE_A_WAIT_BCON:
331 DBG(1, "HNP: Switching back to A-host\n");
332 musb_g_disconnect(musb); 333 musb_g_disconnect(musb);
333 musb->xceiv->state = OTG_STATE_A_IDLE; 334 DBG(1, "HNP: back to %s\n", otg_state_string(musb));
334 MUSB_HST_MODE(musb);
335 musb->is_active = 0;
336 break; 335 break;
337 case OTG_STATE_B_HOST: 336 case OTG_STATE_B_HOST:
338 DBG(1, "HNP: Disabling HR\n"); 337 DBG(1, "HNP: Disabling HR\n");
@@ -775,7 +774,16 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
775#endif /* HOST */ 774#endif /* HOST */
776#ifdef CONFIG_USB_MUSB_OTG 775#ifdef CONFIG_USB_MUSB_OTG
777 case OTG_STATE_B_HOST: 776 case OTG_STATE_B_HOST:
778 musb_hnp_stop(musb); 777 /* REVISIT this behaves for "real disconnect"
778 * cases; make sure the other transitions from
779 * from B_HOST act right too. The B_HOST code
780 * in hnp_stop() is currently not used...
781 */
782 musb_root_disconnect(musb);
783 musb_to_hcd(musb)->self.is_b_host = 0;
784 musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
785 MUSB_DEV_MODE(musb);
786 musb_g_disconnect(musb);
779 break; 787 break;
780 case OTG_STATE_A_PERIPHERAL: 788 case OTG_STATE_A_PERIPHERAL:
781 musb_hnp_stop(musb); 789 musb_hnp_stop(musb);
@@ -807,10 +815,19 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
807 switch (musb->xceiv->state) { 815 switch (musb->xceiv->state) {
808#ifdef CONFIG_USB_MUSB_OTG 816#ifdef CONFIG_USB_MUSB_OTG
809 case OTG_STATE_A_PERIPHERAL: 817 case OTG_STATE_A_PERIPHERAL:
810 /* 818 /* We also come here if the cable is removed, since
811 * We cannot stop HNP here, devctl BDEVICE might be 819 * this silicon doesn't report ID-no-longer-grounded.
812 * still set. 820 *
821 * We depend on T(a_wait_bcon) to shut us down, and
822 * hope users don't do anything dicey during this
823 * undesired detour through A_WAIT_BCON.
813 */ 824 */
825 musb_hnp_stop(musb);
826 usb_hcd_resume_root_hub(musb_to_hcd(musb));
827 musb_root_disconnect(musb);
828 musb_platform_try_idle(musb, jiffies
829 + msecs_to_jiffies(musb->a_wait_bcon
830 ? : OTG_TIME_A_WAIT_BCON));
814 break; 831 break;
815#endif 832#endif
816 case OTG_STATE_B_PERIPHERAL: 833 case OTG_STATE_B_PERIPHERAL:
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 3c4da75fbc7b..858d005fae6d 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)
1962 DBG(2, "Unhandled disconnect %s, setting a_idle\n", 1962 DBG(2, "Unhandled disconnect %s, setting a_idle\n",
1963 otg_state_string(musb)); 1963 otg_state_string(musb));
1964 musb->xceiv->state = OTG_STATE_A_IDLE; 1964 musb->xceiv->state = OTG_STATE_A_IDLE;
1965 MUSB_HST_MODE(musb);
1965 break; 1966 break;
1966 case OTG_STATE_A_PERIPHERAL: 1967 case OTG_STATE_A_PERIPHERAL:
1967 musb->xceiv->state = OTG_STATE_A_WAIT_BCON; 1968 musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
1969 MUSB_HST_MODE(musb);
1968 break; 1970 break;
1969 case OTG_STATE_B_WAIT_ACON: 1971 case OTG_STATE_B_WAIT_ACON:
1970 case OTG_STATE_B_HOST: 1972 case OTG_STATE_B_HOST:
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index c85a82a41d5c..bfe5fe4ebfee 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)
83 musb->xceiv->state = OTG_STATE_A_SUSPEND; 83 musb->xceiv->state = OTG_STATE_A_SUSPEND;
84 musb->is_active = is_otg_enabled(musb) 84 musb->is_active = is_otg_enabled(musb)
85 && musb->xceiv->host->b_hnp_enable; 85 && musb->xceiv->host->b_hnp_enable;
86 if (musb->is_active)
87 mod_timer(&musb->otg_timer, jiffies
88 + msecs_to_jiffies(
89 OTG_TIME_A_AIDL_BDIS));
86 musb_platform_try_idle(musb, 0); 90 musb_platform_try_idle(musb, 0);
87 break; 91 break;
88#ifdef CONFIG_USB_MUSB_OTG 92#ifdef CONFIG_USB_MUSB_OTG