diff options
author | Daniel Mack <zonque@gmail.com> | 2013-12-18 20:23:46 +0100 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2013-12-19 17:31:11 +0100 |
commit | 8ed1fb790ea24bb223e3b30e2b22bccf5b0a76c9 (patch) | |
tree | eaa291bdb62bc8773df7f62d760c8ecf6fa25a65 /drivers/usb/musb/musb_virthub.c | |
parent | usb: gadget: should use u16 type variable to store MaxPower (diff) | |
download | linux-8ed1fb790ea24bb223e3b30e2b22bccf5b0a76c9.tar.xz linux-8ed1fb790ea24bb223e3b30e2b22bccf5b0a76c9.zip |
usb: musb: finish suspend/reset work independently from musb_hub_control()
Currently, resume and reset is completed when the USB core calls back
the root hub, asking for the port's state. This results in
unpredictable timing of state assertion, which in turn renders some
USB devices unusable after resume.
Fix this by moving the logic to end the reset and suspend state out of
musb_hub_control() into separate functions called from delayed workers.
GetPortStatus only reports the current state now, without taking any
real action.
The rh_timeout variable is kept in order to define a minimum time gap
between reset and resume only.
FWIW, in my case, a Verbatim "STORE N GO" mass storage device won't
resume cleanly without this patch.
Signed-off-by: Daniel Mack <zonque@gmail.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/musb/musb_virthub.c')
-rw-r--r-- | drivers/usb/musb/musb_virthub.c | 65 |
1 files changed, 33 insertions, 32 deletions
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 24e46c0c84b5..ad417fd0e1a7 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -44,6 +44,37 @@ #include "musb_core.h" +void musb_host_finish_resume(struct work_struct *work) +{ + struct musb *musb; + unsigned long flags; + u8 power; + + musb = container_of(work, struct musb, deassert_reset_work.work); + + spin_lock_irqsave(&musb->lock, flags); + + power = musb_readb(musb->mregs, MUSB_POWER); + power &= ~MUSB_POWER_RESUME; + dev_dbg(musb->controller, "root port resume stopped, power %02x\n", + power); + musb_writeb(musb->mregs, MUSB_POWER, power); + + /* + * ISSUE: DaVinci (RTL 1.300) disconnects after + * resume of high speed peripherals (but not full + * speed ones). + */ + musb->is_active = 1; + musb->port1_status &= ~(USB_PORT_STAT_SUSPEND | MUSB_PORT_STAT_RESUME); + musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; + usb_hcd_poll_rh_status(musb->hcd); + /* NOTE: it might really be A_WAIT_BCON ... */ + musb->xceiv->state = OTG_STATE_A_HOST; + + spin_unlock_irqrestore(&musb->lock, flags); +} + void musb_port_suspend(struct musb *musb, bool do_suspend) { struct usb_otg *otg = musb->xceiv->otg; @@ -105,7 +136,7 @@ void musb_port_suspend(struct musb *musb, bool do_suspend) /* later, GetPortStatus will stop RESUME signaling */ musb->port1_status |= MUSB_PORT_STAT_RESUME; - musb->rh_timer = jiffies + msecs_to_jiffies(20); + schedule_delayed_work(&musb->finish_resume_work, 20); } } @@ -150,7 +181,7 @@ void musb_port_reset(struct musb *musb, bool do_reset) musb->port1_status |= USB_PORT_STAT_RESET; musb->port1_status &= ~USB_PORT_STAT_ENABLE; - musb->rh_timer = jiffies + msecs_to_jiffies(50); + schedule_delayed_work(&musb->deassert_reset_work, 50); } else { dev_dbg(musb->controller, "root port reset stopped\n"); musb_writeb(mbase, MUSB_POWER, @@ -325,36 +356,6 @@ int musb_hub_control( if (wIndex != 1) goto error; - /* finish RESET signaling? */ - if ((musb->port1_status & USB_PORT_STAT_RESET) - && time_after_eq(jiffies, musb->rh_timer)) - musb_port_reset(musb, false); - - /* finish RESUME signaling? */ - if ((musb->port1_status & MUSB_PORT_STAT_RESUME) - && time_after_eq(jiffies, musb->rh_timer)) { - u8 power; - - power = musb_readb(musb->mregs, MUSB_POWER); - power &= ~MUSB_POWER_RESUME; - dev_dbg(musb->controller, "root port resume stopped, power %02x\n", - power); - musb_writeb(musb->mregs, MUSB_POWER, power); - - /* ISSUE: DaVinci (RTL 1.300) disconnects after - * resume of high speed peripherals (but not full - * speed ones). - */ - - musb->is_active = 1; - musb->port1_status &= ~(USB_PORT_STAT_SUSPEND - | MUSB_PORT_STAT_RESUME); - musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; - usb_hcd_poll_rh_status(musb->hcd); - /* NOTE: it might really be A_WAIT_BCON ... */ - musb->xceiv->state = OTG_STATE_A_HOST; - } - put_unaligned(cpu_to_le32(musb->port1_status & ~MUSB_PORT_STAT_RESUME), (__le32 *) buf); |