diff options
| author | Tarun Gupta <tarung@codeaurora.org> | 2015-06-10 19:28:49 +0530 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 11:07:08 -0700 |
| commit | 8d8d2bea771e1ecef2ea1cb136088e72053c9067 (patch) | |
| tree | 89281c7d34dde62ba36874893e8e4c98fea8ec45 /drivers/usb | |
| parent | 2c455f7be19ae4deb0884cef204746b386a7e199 (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.c | 26 |
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)); |
