summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3/dwc3-msm.c
diff options
context:
space:
mode:
authorSriharsha Allenki <sallenki@codeaurora.org>2018-04-25 19:13:44 +0530
committerAjay Agarwal <ajaya@codeaurora.org>2018-06-01 14:14:16 +0530
commit6a6d52ab38238000ad93009270ee9bec4dc9410f (patch)
treeffaf017e1f667c63cc8e26cb0b2b8802eb5e3794 /drivers/usb/dwc3/dwc3-msm.c
parentc0a42ee0ed459c22d980d9bf706775ab987e378e (diff)
usb: dwc3: Add support for USB hibernation in host mode
Add support for hibernation of USB in host mode and cable disconnected mode by adding proper pm operations freeze and restore. Change-Id: Ie1761a8afb0bb44a37d6ed4d046e8a09e1f53c5c Signed-off-by: Sriharsha Allenki <sallenki@codeaurora.org> Signed-off-by: Ajay Agarwal <ajaya@codeaurora.org>
Diffstat (limited to 'drivers/usb/dwc3/dwc3-msm.c')
-rw-r--r--drivers/usb/dwc3/dwc3-msm.c100
1 files changed, 85 insertions, 15 deletions
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 3f59a2f8b84f..f94d0ba2f966 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -1908,6 +1908,18 @@ static void dwc3_msm_power_collapse_por(struct dwc3_msm *mdwc)
dwc3_core_init(dwc);
/* Re-configure event buffers */
dwc3_event_buffers_setup(dwc);
+
+ /* Get initial P3 status and enable IN_P3 event */
+ val = dwc3_msm_read_reg_field(mdwc->base,
+ DWC3_GDBGLTSSM, DWC3_GDBGLTSSM_LINKSTATE_MASK);
+ atomic_set(&mdwc->in_p3, val == DWC3_LINK_STATE_U3);
+ dwc3_msm_write_reg_field(mdwc->base, PWR_EVNT_IRQ_MASK_REG,
+ PWR_EVNT_POWERDOWN_IN_P3_MASK, 1);
+ if (mdwc->otg_state == OTG_STATE_A_HOST) {
+ dev_dbg(mdwc->dev, "%s: set the core in host mode\n",
+ __func__);
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
+ }
}
static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc)
@@ -1993,7 +2005,7 @@ static void dwc3_set_phy_speed_flags(struct dwc3_msm *mdwc)
static void msm_dwc3_perf_vote_update(struct dwc3_msm *mdwc,
bool perf_mode);
-static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
+static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool hibernation)
{
int ret, i;
struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
@@ -2115,7 +2127,8 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
clk_disable_unprepare(mdwc->xo_clk);
/* Perform controller power collapse */
- if (!mdwc->in_host_mode && (!mdwc->vbus_active || mdwc->in_restart)) {
+ if ((!mdwc->in_host_mode && (!mdwc->vbus_active || mdwc->in_restart)) ||
+ hibernation) {
mdwc->lpm_flags |= MDWC3_POWER_COLLAPSE;
dev_dbg(mdwc->dev, "%s: power collapse\n", __func__);
dwc3_msm_config_gdsc(mdwc, 0);
@@ -2254,19 +2267,10 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
/* Recover from controller power collapse */
if (mdwc->lpm_flags & MDWC3_POWER_COLLAPSE) {
- u32 tmp;
-
dev_dbg(mdwc->dev, "%s: exit power collapse\n", __func__);
dwc3_msm_power_collapse_por(mdwc);
- /* Get initial P3 status and enable IN_P3 event */
- tmp = dwc3_msm_read_reg_field(mdwc->base,
- DWC3_GDBGLTSSM, DWC3_GDBGLTSSM_LINKSTATE_MASK);
- atomic_set(&mdwc->in_p3, tmp == DWC3_LINK_STATE_U3);
- dwc3_msm_write_reg_field(mdwc->base, PWR_EVNT_IRQ_MASK_REG,
- PWR_EVNT_POWERDOWN_IN_P3_MASK, 1);
-
mdwc->lpm_flags &= ~MDWC3_POWER_COLLAPSE;
}
@@ -4014,7 +4018,39 @@ static int dwc3_msm_pm_suspend(struct device *dev)
return -EBUSY;
}
- ret = dwc3_msm_suspend(mdwc);
+ ret = dwc3_msm_suspend(mdwc, false);
+ if (!ret)
+ atomic_set(&mdwc->pm_suspended, 1);
+
+ return ret;
+}
+
+static int dwc3_msm_pm_freeze(struct device *dev)
+{
+ int ret = 0;
+ struct dwc3_msm *mdwc = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "dwc3-msm PM freeze\n");
+ dbg_event(0xFF, "PM Freeze", 0);
+
+ flush_workqueue(mdwc->dwc3_wq);
+
+ /* Resume the core to make sure we can power collapse it */
+ ret = dwc3_msm_resume(mdwc);
+
+ /*
+ * PHYs also need to be power collapsed, so call the notify_disconnect
+ * before suspend to ensure it.
+ */
+ usb_phy_notify_disconnect(mdwc->hs_phy, USB_SPEED_HIGH);
+ if (mdwc->ss_phy->flags & PHY_HOST_MODE) {
+ usb_phy_notify_disconnect(mdwc->ss_phy, USB_SPEED_SUPER);
+ mdwc->ss_phy->flags &= ~PHY_HOST_MODE;
+ }
+
+ mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
+
+ ret = dwc3_msm_suspend(mdwc, true);
if (!ret)
atomic_set(&mdwc->pm_suspended, 1);
@@ -4043,6 +4079,35 @@ static int dwc3_msm_pm_resume(struct device *dev)
return 0;
}
+
+static int dwc3_msm_pm_restore(struct device *dev)
+{
+ struct dwc3_msm *mdwc = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "dwc3-msm PM restore\n");
+ dbg_event(0xFF, "PM Restore", 0);
+
+ atomic_set(&mdwc->pm_suspended, 0);
+
+ dwc3_msm_resume(mdwc);
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ /* Restore PHY flags if hibernated in host mode */
+ if (mdwc->otg_state == OTG_STATE_A_HOST) {
+ mdwc->hs_phy->flags |= PHY_HOST_MODE;
+ if (mdwc->ss_phy) {
+ mdwc->ss_phy->flags |= PHY_HOST_MODE;
+ usb_phy_notify_connect(mdwc->ss_phy,
+ USB_SPEED_SUPER);
+ }
+
+ usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH);
+ }
+
+ return 0;
+}
#endif
#ifdef CONFIG_PM
@@ -4061,7 +4126,7 @@ static int dwc3_msm_runtime_suspend(struct device *dev)
dev_dbg(dev, "DWC3-msm runtime suspend\n");
dbg_event(0xFF, "RT Sus", 0);
- return dwc3_msm_suspend(mdwc);
+ return dwc3_msm_suspend(mdwc, false);
}
static int dwc3_msm_runtime_resume(struct device *dev)
@@ -4076,8 +4141,13 @@ 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)
+ .prepare = dwc3_msm_pm_prepare,
+ .suspend = dwc3_msm_pm_suspend,
+ .resume = dwc3_msm_pm_resume,
+ .freeze = dwc3_msm_pm_freeze,
+ .thaw = dwc3_msm_pm_restore,
+ .poweroff = dwc3_msm_pm_suspend,
+ .restore = dwc3_msm_pm_restore,
SET_RUNTIME_PM_OPS(dwc3_msm_runtime_suspend, dwc3_msm_runtime_resume,
dwc3_msm_runtime_idle)
};