summaryrefslogtreecommitdiffstats
path: root/drivers/usb/musb/musb_core.c
diff options
context:
space:
mode:
authorDaniel Mack2013-12-18 20:23:46 +0100
committerFelipe Balbi2013-12-19 17:31:11 +0100
commit8ed1fb790ea24bb223e3b30e2b22bccf5b0a76c9 (patch)
treeeaa291bdb62bc8773df7f62d760c8ecf6fa25a65 /drivers/usb/musb/musb_core.c
parentusb: gadget: should use u16 type variable to store MaxPower (diff)
downloadkernel-qcow2-linux-8ed1fb790ea24bb223e3b30e2b22bccf5b0a76c9.tar.gz
kernel-qcow2-linux-8ed1fb790ea24bb223e3b30e2b22bccf5b0a76c9.tar.xz
kernel-qcow2-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_core.c')
-rw-r--r--drivers/usb/musb/musb_core.c25
1 files changed, 23 insertions, 2 deletions
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 1ecdb94f3812..0e7f4a00ca7b 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -478,8 +478,8 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
musb->port1_status |=
(USB_PORT_STAT_C_SUSPEND << 16)
| MUSB_PORT_STAT_RESUME;
- musb->rh_timer = jiffies
- + msecs_to_jiffies(20);
+ schedule_delayed_work(
+ &musb->finish_resume_work, 20);
musb->xceiv->state = OTG_STATE_A_HOST;
musb->is_active = 1;
@@ -1813,6 +1813,21 @@ static void musb_free(struct musb *musb)
musb_host_free(musb);
}
+static void musb_deassert_reset(struct work_struct *work)
+{
+ struct musb *musb;
+ unsigned long flags;
+
+ musb = container_of(work, struct musb, deassert_reset_work.work);
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ if (musb->port1_status & USB_PORT_STAT_RESET)
+ musb_port_reset(musb, false);
+
+ spin_unlock_irqrestore(&musb->lock, flags);
+}
+
/*
* Perform generic per-controller initialization.
*
@@ -1897,6 +1912,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
/* Init IRQ workqueue before request_irq */
INIT_WORK(&musb->irq_work, musb_irq_work);
+ INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset);
+ INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume);
/* setup musb parts of the core (especially endpoints) */
status = musb_core_init(plat->config->multipoint
@@ -1990,6 +2007,8 @@ fail4:
fail3:
cancel_work_sync(&musb->irq_work);
+ cancel_delayed_work_sync(&musb->finish_resume_work);
+ cancel_delayed_work_sync(&musb->deassert_reset_work);
if (musb->dma_controller)
dma_controller_destroy(musb->dma_controller);
fail2_5:
@@ -2053,6 +2072,8 @@ static int musb_remove(struct platform_device *pdev)
dma_controller_destroy(musb->dma_controller);
cancel_work_sync(&musb->irq_work);
+ cancel_delayed_work_sync(&musb->finish_resume_work);
+ cancel_delayed_work_sync(&musb->deassert_reset_work);
musb_free(musb);
device_init_wakeup(dev, 0);
return 0;