From a637c5056ef52fbb7c41eb7537a9ec3d150231ad Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 2 Apr 2009 10:16:11 -0700 Subject: [PATCH] musb: make initial HNP roleswitch work (v2) Minor HNP bugfixes, so the initial role switch works: - A-Device: * disconnect-during-suspend enters A_PERIPHERAL state * kill OTG timer after reset as A_PERIPHERAL ... * ... and also pass that reset to the gadget * once HNP succeeds, clear the "ignore_disconnect" flag * from A_PERIPHERAL, disconnect transitions to A_WAIT_BCON - B-Device: * kill OTG timer on entry to B_HOST state (HNP succeeded) * once HNP succeeds, clear "ignore_disconnect" flag * kick the root hub only _after_ the state is adjusted Other state transitions are left alone. Notably, exit paths from the "roles have switched" state ... A_PERIPHERAL handling of that stays seriously broken. Signed-off-by: David Brownell --- drivers/usb/musb/musb_core.c | 27 ++++++++++++++++----------- drivers/usb/musb/musb_gadget.c | 2 +- drivers/usb/musb/musb_virthub.c | 11 ++++++++++- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 05c5dd3..9dc995a 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -587,28 +587,23 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, if (devctl & MUSB_DEVCTL_LSDEV) musb->port1_status |= USB_PORT_STAT_LOW_SPEED; - if (hcd->status_urb) - usb_hcd_poll_rh_status(hcd); - else - usb_hcd_resume_root_hub(hcd); - - MUSB_HST_MODE(musb); - /* indicate new connection to OTG machine */ switch (musb->xceiv->state) { case OTG_STATE_B_PERIPHERAL: if (int_usb & MUSB_INTR_SUSPEND) { DBG(1, "HNP: SUSPEND+CONNECT, now b_host\n"); - musb->xceiv->state = OTG_STATE_B_HOST; - hcd->self.is_b_host = 1; int_usb &= ~MUSB_INTR_SUSPEND; + goto b_host; } else DBG(1, "CONNECT as b_peripheral???\n"); break; case OTG_STATE_B_WAIT_ACON: - DBG(1, "HNP: Waiting to switch to b_host state\n"); + DBG(1, "HNP: CONNECT, now b_host\n"); +b_host: musb->xceiv->state = OTG_STATE_B_HOST; hcd->self.is_b_host = 1; + musb->ignore_disconnect = 0; + del_timer(&musb->otg_timer); break; default: if ((devctl & MUSB_DEVCTL_VBUS) @@ -618,6 +613,14 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, } break; } + + /* poke the root hub */ + MUSB_HST_MODE(musb); + if (hcd->status_urb) + usb_hcd_poll_rh_status(hcd); + else + usb_hcd_resume_root_hub(hcd); + DBG(1, "CONNECT (%s) devctl %02x\n", otg_state_string(musb), devctl); } @@ -662,7 +665,9 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, + msecs_to_jiffies(TA_WAIT_BCON(musb))); break; case OTG_STATE_A_PERIPHERAL: - musb_hnp_stop(musb); + musb->ignore_disconnect = 0; + del_timer(&musb->otg_timer); + musb_g_reset(musb); break; case OTG_STATE_B_WAIT_ACON: DBG(1, "HNP: RESET (%s), to b_peripheral\n", diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 2fbfba5..7dd3d59 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1964,7 +1964,7 @@ void musb_g_disconnect(struct musb *musb) musb->xceiv->state = OTG_STATE_A_IDLE; break; case OTG_STATE_A_PERIPHERAL: - musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; + musb->xceiv->state = OTG_STATE_A_WAIT_BCON; 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 7e7900f..14f7cf3 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -187,8 +187,17 @@ void musb_root_disconnect(struct musb *musb) musb->is_active = 0; switch (musb->xceiv->state) { - case OTG_STATE_A_HOST: case OTG_STATE_A_SUSPEND: +#ifdef CONFIG_USB_MUSB_OTG + if (is_otg_enabled(musb) + && musb->xceiv->host->b_hnp_enable) { + musb->xceiv->state = OTG_STATE_A_PERIPHERAL; + musb->g.is_a_peripheral = 1; + break; + } +#endif + /* FALLTHROUGH */ + case OTG_STATE_A_HOST: musb->xceiv->state = OTG_STATE_A_WAIT_BCON; musb->is_active = 0; break; -- 1.6.0.4