summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorDaniel Mack2014-02-14 20:49:46 +0100
committerGreg Kroah-Hartman2014-02-18 21:08:15 +0100
commit73926db33b277c4c371edbc9fe9093adc9b2803b (patch)
treed07829e0d191fa95840f62a0ffb2e1f18753e34e /drivers/usb
parentUSB: serial: option: blacklist interface 4 for Cinterion PHS8 and PXS8 (diff)
downloadkernel-qcow2-linux-73926db33b277c4c371edbc9fe9093adc9b2803b.tar.gz
kernel-qcow2-linux-73926db33b277c4c371edbc9fe9093adc9b2803b.tar.xz
kernel-qcow2-linux-73926db33b277c4c371edbc9fe9093adc9b2803b.zip
usb: musb: do not sleep in atomic context
musb_port_reset() is called from musb_hub_control() which in turn holds a spinlock, so musb_port_reset() is not allowed to call msleep(). With the asynchronous work helpers in place, this is fortunately easy to fix by rescheduling the reset deassertion function to after the time when the wait period is finished. Note, however, that the MUSB_POWER_RESUME bit is only set on AM33xx processors under rare conditions such as when to another driver reporting an error during suspend. Hence, this didn't hit me yet in normal operation. Signed-off-by: Daniel Mack <zonque@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/musb/musb_core.c2
-rw-r--r--drivers/usb/musb/musb_virthub.c20
2 files changed, 17 insertions, 5 deletions
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index fc192ad9cc6a..1a377cac4d4c 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -477,6 +477,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);
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index eb634433ef09..32f49e1efa9c 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -158,7 +158,6 @@ void musb_port_reset(struct musb *musb, bool do_reset)
*/
power = musb_readb(mbase, MUSB_POWER);
if (do_reset) {
-
/*
* If RESUME is set, we must make sure it stays minimum 20 ms.
* Then we must clear RESUME and wait a bit to let musb start
@@ -167,11 +166,22 @@ void musb_port_reset(struct musb *musb, bool do_reset)
* detected".
*/
if (power & MUSB_POWER_RESUME) {
- while (time_before(jiffies, musb->rh_timer))
- msleep(1);
+ long remain = (unsigned long) musb->rh_timer - jiffies;
+
+ if (musb->rh_timer > 0 && remain > 0) {
+ /* take into account the minimum delay after resume */
+ schedule_delayed_work(
+ &musb->deassert_reset_work,
+ jiffies_to_msecs(remain));
+ return;
+ }
+
musb_writeb(mbase, MUSB_POWER,
- power & ~MUSB_POWER_RESUME);
- msleep(1);
+ power & ~MUSB_POWER_RESUME);
+
+ /* Give the core 1 ms to clear MUSB_POWER_RESUME */
+ schedule_delayed_work(&musb->deassert_reset_work, 1);
+ return;
}
power &= 0xf0;