diff options
| author | Jack Pham <jackp@codeaurora.org> | 2016-01-29 11:46:05 -0800 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 11:06:41 -0700 |
| commit | b0b1bb59407262e425fcb5a95f7df72a156c99ab (patch) | |
| tree | 43567fd34e95e182ce61bbf59a84bd16c40e0349 | |
| parent | c6d80a92b79d6b50129a27e8747c5a490da367e3 (diff) | |
usb: dwc3: gadget: Resolve recursive spinlock during remote wakeup
When a USB function wishes to send new data during USB suspend state, it
needs to issue USB remote wakeup and send a function wakeup notification
after then. This scenario leads to recursive spin locking inside the
_usb_func_wakeup() function, because this function gets called recursively.
This function issues remote wakeup, which internally calls the resume
interrupt callback, which calls the _usb_func_wakeup() function again.
This issue is resolved by performing the remote wakeup in a deferred work
context, and this splits the recursion loop.
CRs-fixed: 700667
Change-Id: I59c8efde098781587d29f08cd60e4aa3521949d8
Signed-off-by: Danny Segal <dsegal@codeaurora.org>
| -rw-r--r-- | drivers/usb/dwc3/gadget.c | 28 |
1 files changed, 7 insertions, 21 deletions
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 12969d2fa688..d841f09ff378 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1263,27 +1263,9 @@ out: static int dwc3_gadget_wakeup(struct usb_gadget *g) { struct dwc3 *dwc = gadget_to_dwc(g); - unsigned long flags; - int ret = 0; - spin_lock_irqsave(&dwc->lock, flags); - - if (atomic_read(&dwc->in_lpm)) { - schedule_work(&dwc->wakeup_work); - pr_debug("Core is in low-power mode. Scheduling wakeup work.\n"); - ret = -EBUSY; - } else { - pr_debug("Core is active. Initiating remote wakeup.\n"); - ret = dwc3_gadget_wakeup_int(dwc); - if (ret) - pr_err("Remote wakeup failed. ret = %d\n", ret); - else - pr_debug("Remote wake up succeeded.\n"); - } - - spin_unlock_irqrestore(&dwc->lock, flags); - - return ret; + schedule_work(&dwc->wakeup_work); + return 0; } static inline enum dwc3_link_state dwc3_get_link_state(struct dwc3 *dwc) @@ -1618,7 +1600,6 @@ static int dwc3_gadget_wakeup_int(struct dwc3 *dwc) if (!link_recover_only) dwc3_gadget_wakeup_interrupt(dwc); out: - return ret; } @@ -1639,6 +1620,11 @@ static int dwc_gadget_func_wakeup(struct usb_gadget *g, int interface_id) ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_XMIT_FUNCTION, interface_id); + if (ret) + pr_err("Function wakeup HW command failed.\n"); + else + pr_debug("Function wakeup HW command succeeded.\n"); + return ret; } |
