]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
usb: musb: do not sleep in atomic context
authorDaniel Mack <zonque@gmail.com>
Fri, 14 Feb 2014 19:49:46 +0000 (20:49 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 18 Feb 2014 20:08:15 +0000 (12:08 -0800)
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>
drivers/usb/musb/musb_core.c
drivers/usb/musb/musb_virthub.c

index fc192ad9cc6a60eb4a7f00d124eb0a0d1bca73fe..1a377cac4d4c5795e7937175b87e83ba7b70490d 100644 (file)
@@ -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);
 
index eb634433ef095b631cd7e70df77453334cf48ddc..32f49e1efa9c50116415ea1f105064adc9ca3ac8 100644 (file)
@@ -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;