summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r--drivers/usb/dwc3/dwc3-msm.c54
1 files changed, 50 insertions, 4 deletions
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 5ad68df298cd..010331bb80a5 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -58,7 +58,7 @@
/* time out to wait for USB cable status notification (in ms)*/
#define SM_INIT_TIMEOUT 30000
-
+#define DWC3_WAKEUP_SRC_TIMEOUT 5000
/* AHB2PHY register offsets */
#define PERIPH_SS_AHB2PHY_TOP_CFG 0x10
@@ -216,6 +216,7 @@ struct dwc3_msm {
struct notifier_block usbdev_nb;
bool hc_died;
bool xhci_ss_compliance_enable;
+ bool no_wakeup_src_in_hostmode;
struct extcon_dev *extcon_vbus;
struct extcon_dev *extcon_id;
@@ -2338,6 +2339,7 @@ static void dwc3_ext_event_notify(struct dwc3_msm *mdwc)
clear_bit(B_SUSPEND, &mdwc->inputs);
}
+ pm_stay_awake(mdwc->dev);
schedule_delayed_work(&mdwc->sm_work, 0);
}
@@ -2626,6 +2628,7 @@ static int dwc3_msm_id_notifier(struct notifier_block *nb,
if (mdwc->id_state != id) {
mdwc->id_state = id;
dbg_event(0xFF, "id_state", mdwc->id_state);
+ pm_stay_awake(mdwc->dev);
queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
}
@@ -2688,6 +2691,7 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb,
mdwc->vbus_active = event;
if (dwc->is_drd && !mdwc->in_restart) {
dbg_event(0xFF, "Q RW (vbus)", mdwc->vbus_active);
+ pm_stay_awake(mdwc->dev);
queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
}
done:
@@ -3087,6 +3091,11 @@ static int dwc3_msm_probe(struct platform_device *pdev)
mdwc->disable_host_mode_pm = of_property_read_bool(node,
"qcom,disable-host-mode-pm");
+ mdwc->no_wakeup_src_in_hostmode = of_property_read_bool(node,
+ "qcom,no-wakeup-src-in-hostmode");
+ if (mdwc->no_wakeup_src_in_hostmode)
+ dev_dbg(&pdev->dev, "dwc3 host not using wakeup source\n");
+
dwc3_set_notifier(&dwc3_msm_notify_event);
/* Assumes dwc3 is the first DT child of dwc3-msm */
@@ -3879,12 +3888,14 @@ static void dwc3_otg_sm_work(struct work_struct *w)
mdwc->otg_state = OTG_STATE_A_IDLE;
goto ret;
}
+ pm_wakeup_event(mdwc->dev, DWC3_WAKEUP_SRC_TIMEOUT);
}
break;
case OTG_STATE_A_HOST:
if (test_bit(ID, &mdwc->inputs) || mdwc->hc_died) {
- dev_dbg(mdwc->dev, "id || hc_died\n");
+ dbg_event(0xFF, "id || hc_died", 0);
+ dev_dbg(mdwc->dev, "%s state id || hc_died\n", state);
dwc3_otg_start_host(mdwc, 0);
mdwc->otg_state = OTG_STATE_B_IDLE;
mdwc->vbus_retry_count = 0;
@@ -3895,6 +3906,7 @@ static void dwc3_otg_sm_work(struct work_struct *w)
dbg_event(0xFF, "XHCIResume", 0);
if (dwc)
pm_runtime_resume(&dwc->xhci->dev);
+ pm_wakeup_event(mdwc->dev, DWC3_WAKEUP_SRC_TIMEOUT);
}
break;
@@ -3910,6 +3922,34 @@ ret:
return;
}
+static int dwc3_msm_pm_prepare(struct device *dev)
+{
+ struct dwc3_msm *mdwc = dev_get_drvdata(dev);
+ struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
+ struct usb_hcd *hcd;
+ struct xhci_hcd *xhci;
+
+ dev_dbg(dev, "dwc3-msm PM prepare,lpm:%u\n", atomic_read(&dwc->in_lpm));
+ dbg_event(0xFF, "PM Prep", 0);
+ if (!mdwc->in_host_mode || !mdwc->no_wakeup_src_in_hostmode)
+ return 0;
+
+ hcd = dev_get_drvdata(&dwc->xhci->dev);
+ xhci = hcd_to_xhci(hcd);
+ flush_delayed_work(&mdwc->sm_work);
+
+ /* If in lpm then prevent usb core to runtime_resume from pm_suspend */
+ if (atomic_read(&dwc->in_lpm)) {
+ hcd_to_bus(hcd)->skip_resume = true;
+ hcd_to_bus(xhci->shared_hcd)->skip_resume = true;
+ } else {
+ hcd_to_bus(hcd)->skip_resume = false;
+ hcd_to_bus(xhci->shared_hcd)->skip_resume = false;
+ }
+
+ return 0;
+}
+
#ifdef CONFIG_PM_SLEEP
static int dwc3_msm_pm_suspend(struct device *dev)
{
@@ -3921,7 +3961,7 @@ static int dwc3_msm_pm_suspend(struct device *dev)
dbg_event(0xFF, "PM Sus", 0);
flush_workqueue(mdwc->dwc3_wq);
- if (!atomic_read(&dwc->in_lpm)) {
+ if (!atomic_read(&dwc->in_lpm) && !mdwc->no_wakeup_src_in_hostmode) {
dev_err(mdwc->dev, "Abort PM suspend!! (USB is outside LPM)\n");
return -EBUSY;
}
@@ -3945,8 +3985,13 @@ static int dwc3_msm_pm_resume(struct device *dev)
flush_workqueue(mdwc->dwc3_wq);
atomic_set(&mdwc->pm_suspended, 0);
+ /* Resume h/w in host mode as it may not be runtime suspended */
+ if (mdwc->no_wakeup_src_in_hostmode && !test_bit(ID, &mdwc->inputs))
+ dwc3_msm_resume(mdwc);
+
/* kick in otg state machine */
- queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
+ if (mdwc->vbus_active || !mdwc->id_state)
+ queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
return 0;
}
@@ -3983,6 +4028,7 @@ static int dwc3_msm_runtime_resume(struct device *dev)
#endif
static const struct dev_pm_ops dwc3_msm_dev_pm_ops = {
+ .prepare = dwc3_msm_pm_prepare,
SET_SYSTEM_SLEEP_PM_OPS(dwc3_msm_pm_suspend, dwc3_msm_pm_resume)
SET_RUNTIME_PM_OPS(dwc3_msm_runtime_suspend, dwc3_msm_runtime_resume,
dwc3_msm_runtime_idle)