summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorTarun Gupta <tarung@codeaurora.org>2015-06-10 19:28:49 +0530
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 11:07:08 -0700
commit8d8d2bea771e1ecef2ea1cb136088e72053c9067 (patch)
tree89281c7d34dde62ba36874893e8e4c98fea8ec45 /drivers/usb
parent2c455f7be19ae4deb0884cef204746b386a7e199 (diff)
usb: dwc3: gadget: Avoid unclocked access due to pm_runtime_get failure
If remote wakeup request is received during system resume where resume_early is still processing, pm_runtime_get_sync returns -EACCES error as pm_runtime framework is disabled between late_suspend and early_resume. Due to failure of this API controller did not exit LPM. This leads to unclocked access of registers as part of remote wakeup processing causing target to crash. Fix this by checking for return value of pm_runtime_get_sync, and queuing dwc3_gadget_wakeup_work again with intermediate delay of 100ms for maximum of 20 times. Change-Id: I8e2215ef9ee708e86356622e85fd2f23a18f7944 Signed-off-by: Tarun Gupta <tarung@codeaurora.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/dwc3/gadget.c26
1 files changed, 25 insertions, 1 deletions
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index c615d5917867..63f90080cb49 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1505,14 +1505,38 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)
return DWC3_DSTS_SOFFN(reg);
}
+#define DWC3_PM_RESUME_RETRIES 20 /* Max Number of retries */
+#define DWC3_PM_RESUME_DELAY 100 /* 100 msec */
+
static void dwc3_gadget_wakeup_work(struct work_struct *w)
{
struct dwc3 *dwc;
int ret;
+ static int retry_count;
dwc = container_of(w, struct dwc3, wakeup_work);
- pm_runtime_get_sync(dwc->dev);
+ ret = pm_runtime_get_sync(dwc->dev);
+ if (ret) {
+ /* pm_runtime_get_sync returns -EACCES error between
+ * late_suspend and early_resume, wait for system resume to
+ * finish and queue work again
+ */
+ pr_debug("PM runtime get sync failed, ret %d\n", ret);
+ if (ret == -EACCES) {
+ pm_runtime_put_noidle(dwc->dev);
+ if (retry_count == DWC3_PM_RESUME_RETRIES) {
+ retry_count = 0;
+ pr_err("pm_runtime_get_sync timed out\n");
+ return;
+ }
+ msleep(DWC3_PM_RESUME_DELAY);
+ retry_count++;
+ schedule_work(&dwc->wakeup_work);
+ return;
+ }
+ }
+ retry_count = 0;
dbg_event(0xFF, "Gdgwake gsyn",
atomic_read(&dwc->dev->power.usage_count));