diff options
author | David Brownell <dbrownell@users.sourceforge.net> | 2009-03-31 15:35:09 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-06-16 00:44:41 -0400 |
commit | ab983f2a1be582b00f706013f40f658769d0823a (patch) | |
tree | 95dce5821b73726351f323d2d1bd4a80bea9ceb3 | |
parent | 1de00dae8036dfee44ebea2c38f942fb6072e0b7 (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.c | 41 | ||||
-rw-r--r-- | drivers/usb/musb/musb_gadget.c | 2 | ||||
-rw-r--r-- | 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 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 |