diff options
author | Sriharsha Allenki <sallenki@codeaurora.org> | 2018-04-25 19:13:44 +0530 |
---|---|---|
committer | Ajay Agarwal <ajaya@codeaurora.org> | 2018-06-01 14:14:16 +0530 |
commit | 6a6d52ab38238000ad93009270ee9bec4dc9410f (patch) | |
tree | ffaf017e1f667c63cc8e26cb0b2b8802eb5e3794 /drivers/usb/dwc3/dwc3-msm.c | |
parent | c0a42ee0ed459c22d980d9bf706775ab987e378e (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.c | 100 |
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) }; |