diff options
author | Vardan Mikayelyan <mvardan@synopsys.com> | 2018-02-16 14:10:13 +0400 |
---|---|---|
committer | Felipe Balbi <felipe.balbi@linux.intel.com> | 2018-03-13 10:47:56 +0200 |
commit | c5c403dc43365d1669e5a36829356b1bfddbd39e (patch) | |
tree | 243faaa0d9990bd9188c8ac1d4ce9bc584ccd312 /drivers/usb/dwc2/gadget.c | |
parent | 94d2666c588cefc86709822153fa11ab770ada54 (diff) | |
download | lwn-c5c403dc43365d1669e5a36829356b1bfddbd39e.tar.gz lwn-c5c403dc43365d1669e5a36829356b1bfddbd39e.zip |
usb: dwc2: Add host/device hibernation functions
Add host/device hibernation functions which must be wrapped
by core's dwc2_enter_hibernation()/dwc2_exit_hibernation()
functions.
Make dwc2_backup_global_registers dwc2_restore_global_register
non-static to use them in both host/gadget sides.
Added function names:
dwc2_gadget_enter_hibernation()
dwc2_gadget_exit_hibernation()
dwc2_host_enter_hibernation()
dwc2_host_exit_hibernation()
Signed-off-by: Vardan Mikayelyan <mvardan@synopsys.com>
Signed-off-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Artur Petrosyan <arturp@synopsys.com>
Signed-off-by: Minas Harutyunyan <hminas@synopsys.com>
Signed-off-by: Grigor Tovmasyan <tovmasya@synopsys.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
Diffstat (limited to 'drivers/usb/dwc2/gadget.c')
-rw-r--r-- | drivers/usb/dwc2/gadget.c | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 838e0929db59..6c32bf26e48e 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -4913,3 +4913,179 @@ void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg->regs + GLPMCFG)); } + +/** + * dwc2_gadget_enter_hibernation() - Put controller in Hibernation. + * + * @hsotg: Programming view of the DWC_otg controller + * + * Return non-zero if failed to enter to hibernation. + */ +int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) +{ + u32 gpwrdn; + int ret = 0; + + /* Change to L2(suspend) state */ + hsotg->lx_state = DWC2_L2; + dev_dbg(hsotg->dev, "Start of hibernation completed\n"); + ret = dwc2_backup_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup global registers\n", + __func__); + return ret; + } + ret = dwc2_backup_device_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup device registers\n", + __func__); + return ret; + } + + gpwrdn = GPWRDN_PWRDNRSTN; + gpwrdn |= GPWRDN_PMUACTV; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Set flag to indicate that we are in hibernation */ + hsotg->hibernated = 1; + + /* Enable interrupts from wake up logic */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PMUINTSEL; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Unmask device mode interrupts in GPWRDN */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_RST_DET_MSK; + gpwrdn |= GPWRDN_LNSTSCHG_MSK; + gpwrdn |= GPWRDN_STS_CHGINT_MSK; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Enable Power Down Clamp */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PWRDNCLMP; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Switch off VDD */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PWRDNSWTCH; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Save gpwrdn register for further usage if stschng interrupt */ + hsotg->gr_backup.gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + dev_dbg(hsotg->dev, "Hibernation completed\n"); + + return ret; +} + +/** + * dwc2_gadget_exit_hibernation() + * This function is for exiting from Device mode hibernation by host initiated + * resume/reset and device initiated remote-wakeup. + * + * @hsotg: Programming view of the DWC_otg controller + * @rem_wakeup: indicates whether resume is initiated by Device or Host. + * @param reset: indicates whether resume is initiated by Reset. + * + * Return non-zero if failed to exit from hibernation. + */ +int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, + int rem_wakeup, int reset) +{ + u32 pcgcctl; + u32 gpwrdn; + u32 dctl; + int ret = 0; + struct dwc2_gregs_backup *gr; + struct dwc2_dregs_backup *dr; + + gr = &hsotg->gr_backup; + dr = &hsotg->dr_backup; + + if (!hsotg->hibernated) { + dev_dbg(hsotg->dev, "Already exited from Hibernation\n"); + return 1; + } + dev_dbg(hsotg->dev, + "%s: called with rem_wakeup = %d reset = %d\n", + __func__, rem_wakeup, reset); + + dwc2_hib_restore_common(hsotg, rem_wakeup, 0); + + if (!reset) { + /* Clear all pending interupts */ + dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + } + + /* De-assert Restore */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn &= ~GPWRDN_RESTORE; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + if (!rem_wakeup) { + pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl &= ~PCGCTL_RSTPDWNMODULE; + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + } + + /* Restore GUSBCFG, DCFG and DCTL */ + dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(dr->dcfg, hsotg->regs + DCFG); + dwc2_writel(dr->dctl, hsotg->regs + DCTL); + + /* De-assert Wakeup Logic */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn &= ~GPWRDN_PMUACTV; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + + if (rem_wakeup) { + udelay(10); + /* Start Remote Wakeup Signaling */ + dwc2_writel(dr->dctl | DCTL_RMTWKUPSIG, hsotg->regs + DCTL); + } else { + udelay(50); + /* Set Device programming done bit */ + dctl = dwc2_readl(hsotg->regs + DCTL); + dctl |= DCTL_PWRONPRGDONE; + dwc2_writel(dctl, hsotg->regs + DCTL); + } + /* Wait for interrupts which must be cleared */ + mdelay(2); + /* Clear all pending interupts */ + dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + + /* Restore global registers */ + ret = dwc2_restore_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore registers\n", + __func__); + return ret; + } + + /* Restore device registers */ + ret = dwc2_restore_device_registers(hsotg, rem_wakeup); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore device registers\n", + __func__); + return ret; + } + + if (rem_wakeup) { + mdelay(10); + dctl = dwc2_readl(hsotg->regs + DCTL); + dctl &= ~DCTL_RMTWKUPSIG; + dwc2_writel(dctl, hsotg->regs + DCTL); + } + + hsotg->hibernated = 0; + hsotg->lx_state = DWC2_L0; + dev_dbg(hsotg->dev, "Hibernation recovery completes here\n"); + + return ret; +} |