USB: dwc3_otg: Retry if vbus regulator get fail with -EPROBE_DEFER
As part of ID clear, OTG driver tries to start host mode. This might
fail if regulator driver is not ready and returns -EPROBE_DEFER for
VBUS regulator_get. Hence schedule sm_work for one second delay in
case of -EPROBEDEFER so that it can retry start_host again.
CRs-Fixed: 546709
Change-Id: Iab443e7984619d474b15b695fbf5b7639a41ddd0
Signed-off-by: Vijayavardhan Vennapusa <vvreddy@codeaurora.org>
diff --git a/drivers/usb/dwc3/dwc3_otg.c b/drivers/usb/dwc3/dwc3_otg.c
index e912e86..5348eb5 100644
--- a/drivers/usb/dwc3/dwc3_otg.c
+++ b/drivers/usb/dwc3/dwc3_otg.c
@@ -24,6 +24,7 @@
#include "io.h"
#include "xhci.h"
+#define VBUS_REG_CHECK_DELAY (jiffies + msecs_to_jiffies(1000))
#define MAX_INVALID_CHRGR_RETRY 3
static int max_chgr_retry_count = MAX_INVALID_CHRGR_RETRY;
module_param(max_chgr_retry_count, int, S_IRUGO | S_IWUSR);
@@ -336,13 +337,13 @@
dev_dbg(otg->phy->dev, "%s: set gadget %s\n",
__func__, gadget->name);
otg->gadget = gadget;
- schedule_work(&dotg->sm_work);
+ queue_delayed_work(system_nrt_wq, &dotg->sm_work, 0);
} else {
if (otg->phy->state == OTG_STATE_B_PERIPHERAL) {
dwc3_otg_start_peripheral(otg, 0);
otg->gadget = NULL;
otg->phy->state = OTG_STATE_UNDEFINED;
- schedule_work(&dotg->sm_work);
+ queue_delayed_work(system_nrt_wq, &dotg->sm_work, 0);
} else {
otg->gadget = NULL;
}
@@ -367,7 +368,7 @@
* STOP chg_det as part of !BSV handling would reset the chg_det flags
*/
if (test_bit(B_SESS_VLD, &dotg->inputs))
- schedule_work(&dotg->sm_work);
+ queue_delayed_work(system_nrt_wq, &dotg->sm_work, 0);
}
/**
@@ -406,7 +407,7 @@
/* Flush processing any pending events before handling new ones */
if (init)
- flush_work(&dotg->sm_work);
+ flush_delayed_work(&dotg->sm_work);
if (event == DWC3_EVENT_PHY_RESUME) {
if (!pm_runtime_status_suspended(phy->dev)) {
@@ -453,15 +454,16 @@
if (!init) {
init = true;
- if (!work_busy(&dotg->sm_work))
- schedule_work(&dotg->sm_work);
+ if (!work_busy(&dotg->sm_work.work))
+ queue_delayed_work(system_nrt_wq,
+ &dotg->sm_work, 0);
complete(&dotg->dwc3_xcvr_vbus_init);
dev_dbg(phy->dev, "XCVR: BSV init complete\n");
return;
}
- schedule_work(&dotg->sm_work);
+ queue_delayed_work(system_nrt_wq, &dotg->sm_work, 0);
}
}
@@ -610,7 +612,7 @@
handled_irqs |= DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT;
}
- schedule_work(&dotg->sm_work);
+ queue_delayed_work(system_nrt_wq, &dotg->sm_work, 0);
ret = IRQ_HANDLED;
@@ -671,10 +673,12 @@
*/
static void dwc3_otg_sm_work(struct work_struct *w)
{
- struct dwc3_otg *dotg = container_of(w, struct dwc3_otg, sm_work);
+ struct dwc3_otg *dotg = container_of(w, struct dwc3_otg, sm_work.work);
struct usb_phy *phy = dotg->otg.phy;
struct dwc3_charger *charger = dotg->charger;
bool work = 0;
+ int ret = 0;
+ int delay = 0;
pm_runtime_resume(phy->dev);
dev_dbg(phy->dev, "%s state\n", otg_state_string(phy->state));
@@ -817,10 +821,23 @@
if (test_bit(ID, &dotg->inputs)) {
dev_dbg(phy->dev, "id\n");
phy->state = OTG_STATE_B_IDLE;
+ dotg->vbus_retry_count = 0;
work = 1;
} else {
phy->state = OTG_STATE_A_HOST;
- if (dwc3_otg_start_host(&dotg->otg, 1)) {
+ ret = dwc3_otg_start_host(&dotg->otg, 1);
+ if ((ret == -EPROBE_DEFER) &&
+ dotg->vbus_retry_count < 3) {
+ /*
+ * Get regulator failed as regulator driver is
+ * not up yet. Will try to start host after 1sec
+ */
+ phy->state = OTG_STATE_A_IDLE;
+ dev_dbg(phy->dev, "Unable to get vbus regulator. Retrying...\n");
+ delay = VBUS_REG_CHECK_DELAY;
+ work = 1;
+ dotg->vbus_retry_count++;
+ } else if (ret) {
/*
* Probably set_host was not called yet.
* We will re-try as soon as it will be called
@@ -839,6 +856,7 @@
dev_dbg(phy->dev, "id\n");
dwc3_otg_start_host(&dotg->otg, 0);
phy->state = OTG_STATE_B_IDLE;
+ dotg->vbus_retry_count = 0;
work = 1;
}
break;
@@ -849,7 +867,7 @@
}
if (work)
- schedule_work(&dotg->sm_work);
+ queue_delayed_work(system_nrt_wq, &dotg->sm_work, delay);
}
@@ -973,7 +991,7 @@
dotg->otg.phy->state = OTG_STATE_UNDEFINED;
init_completion(&dotg->dwc3_xcvr_vbus_init);
- INIT_WORK(&dotg->sm_work, dwc3_otg_sm_work);
+ INIT_DELAYED_WORK(&dotg->sm_work, dwc3_otg_sm_work);
ret = request_irq(dotg->irq, dwc3_otg_interrupt, IRQF_SHARED,
"dwc3_otg", dotg);
@@ -988,7 +1006,7 @@
return 0;
err3:
- cancel_work_sync(&dotg->sm_work);
+ cancel_delayed_work_sync(&dotg->sm_work);
usb_set_transceiver(NULL);
err2:
kfree(dotg->otg.phy);
@@ -1013,7 +1031,7 @@
if (dotg) {
if (dotg->charger)
dotg->charger->start_detection(dotg->charger, false);
- cancel_work_sync(&dotg->sm_work);
+ cancel_delayed_work_sync(&dotg->sm_work);
usb_set_transceiver(NULL);
pm_runtime_put(dwc->dev);
free_irq(dotg->irq, dotg);