aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorDaniel Mack <zonque@gmail.com>2014-01-15 08:09:49 -0500
committerFelipe Balbi <balbi@ti.com>2014-02-18 11:34:07 -0500
commit30d361bf0f81ece8de42c5e0a4e560270ad72b65 (patch)
tree6c1e85c58e1d8de250f89ff9511dea6ee308fcef /drivers/usb
parentd246c9d55a90722fac9e7284201088eb62adcfc3 (diff)
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: Felipe Balbi <balbi@ti.com>
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 a501542dfeb5..b49a5945c90c 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,
477 musb->port1_status |= 477 musb->port1_status |=
478 (USB_PORT_STAT_C_SUSPEND << 16) 478 (USB_PORT_STAT_C_SUSPEND << 16)
479 | MUSB_PORT_STAT_RESUME; 479 | MUSB_PORT_STAT_RESUME;
480 musb->rh_timer = jiffies
481 + msecs_to_jiffies(20);
480 schedule_delayed_work( 482 schedule_delayed_work(
481 &musb->finish_resume_work, 20); 483 &musb->finish_resume_work, 20);
482 484
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)
158 */ 158 */
159 power = musb_readb(mbase, MUSB_POWER); 159 power = musb_readb(mbase, MUSB_POWER);
160 if (do_reset) { 160 if (do_reset) {
161
162 /* 161 /*
163 * If RESUME is set, we must make sure it stays minimum 20 ms. 162 * If RESUME is set, we must make sure it stays minimum 20 ms.
164 * Then we must clear RESUME and wait a bit to let musb start 163 * 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)
167 * detected". 166 * detected".
168 */ 167 */
169 if (power & MUSB_POWER_RESUME) { 168 if (power & MUSB_POWER_RESUME) {
170 while (time_before(jiffies, musb->rh_timer)) 169 long remain = (unsigned long) musb->rh_timer - jiffies;
171 msleep(1); 170
171 if (musb->rh_timer > 0 && remain > 0) {
172 /* take into account the minimum delay after resume */
173 schedule_delayed_work(
174 &musb->deassert_reset_work,
175 jiffies_to_msecs(remain));
176 return;
177 }
178
172 musb_writeb(mbase, MUSB_POWER, 179 musb_writeb(mbase, MUSB_POWER,
173 power & ~MUSB_POWER_RESUME); 180 power & ~MUSB_POWER_RESUME);
174 msleep(1); 181
182 /* Give the core 1 ms to clear MUSB_POWER_RESUME */
183 schedule_delayed_work(&musb->deassert_reset_work, 1);
184 return;
175 } 185 }
176 186
177 power &= 0xf0; 187 power &= 0xf0;