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>
This commit is contained in:
parent
94d2666c58
commit
c5c403dc43
|
@ -64,7 +64,7 @@
|
||||||
*
|
*
|
||||||
* @hsotg: Programming view of the DWC_otg controller
|
* @hsotg: Programming view of the DWC_otg controller
|
||||||
*/
|
*/
|
||||||
static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
|
int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
|
||||||
{
|
{
|
||||||
struct dwc2_gregs_backup *gr;
|
struct dwc2_gregs_backup *gr;
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
|
||||||
*
|
*
|
||||||
* @hsotg: Programming view of the DWC_otg controller
|
* @hsotg: Programming view of the DWC_otg controller
|
||||||
*/
|
*/
|
||||||
static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
|
int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
|
||||||
{
|
{
|
||||||
struct dwc2_gregs_backup *gr;
|
struct dwc2_gregs_backup *gr;
|
||||||
|
|
||||||
|
|
|
@ -1155,6 +1155,8 @@ void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd);
|
||||||
|
|
||||||
void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup,
|
void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup,
|
||||||
int is_host);
|
int is_host);
|
||||||
|
int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg);
|
||||||
|
int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg);
|
||||||
|
|
||||||
void dwc2_enable_acg(struct dwc2_hsotg *hsotg);
|
void dwc2_enable_acg(struct dwc2_hsotg *hsotg);
|
||||||
|
|
||||||
|
@ -1224,6 +1226,9 @@ int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
|
||||||
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
|
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
|
||||||
int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg);
|
int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg);
|
||||||
int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup);
|
int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup);
|
||||||
|
int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg);
|
||||||
|
int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
|
||||||
|
int rem_wakeup, int reset);
|
||||||
int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg);
|
int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg);
|
||||||
int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg);
|
int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg);
|
||||||
int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg);
|
int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg);
|
||||||
|
@ -1250,6 +1255,11 @@ static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
|
||||||
static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg,
|
static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg,
|
||||||
int remote_wakeup)
|
int remote_wakeup)
|
||||||
{ return 0; }
|
{ return 0; }
|
||||||
|
static inline int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
|
||||||
|
{ return 0; }
|
||||||
|
static inline int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
|
||||||
|
int rem_wakeup, int reset)
|
||||||
|
{ return 0; }
|
||||||
static inline int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg)
|
static inline int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg)
|
||||||
{ return 0; }
|
{ return 0; }
|
||||||
static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg)
|
static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg)
|
||||||
|
@ -1267,6 +1277,9 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
|
||||||
void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
|
void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
|
||||||
int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg);
|
int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg);
|
||||||
int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg);
|
int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg);
|
||||||
|
int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg);
|
||||||
|
int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg,
|
||||||
|
int rem_wakeup, int reset);
|
||||||
#else
|
#else
|
||||||
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
|
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
|
||||||
{ return 0; }
|
{ return 0; }
|
||||||
|
@ -1283,6 +1296,11 @@ static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
|
||||||
{ return 0; }
|
{ return 0; }
|
||||||
static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
|
static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
|
||||||
{ return 0; }
|
{ return 0; }
|
||||||
|
static inline int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
|
||||||
|
{ return 0; }
|
||||||
|
static inline int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg,
|
||||||
|
int rem_wakeup, int reset)
|
||||||
|
{ return 0; }
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -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
|
dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg->regs
|
||||||
+ GLPMCFG));
|
+ 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;
|
||||||
|
}
|
||||||
|
|
|
@ -5360,3 +5360,226 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dwc2_host_enter_hibernation() - Put controller in Hibernation.
|
||||||
|
*
|
||||||
|
* @hsotg: Programming view of the DWC_otg controller
|
||||||
|
*/
|
||||||
|
int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int ret = 0;
|
||||||
|
u32 hprt0;
|
||||||
|
u32 pcgcctl;
|
||||||
|
u32 gusbcfg;
|
||||||
|
u32 gpwrdn;
|
||||||
|
|
||||||
|
dev_dbg(hsotg->dev, "Preparing host for hibernation\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_host_registers(hsotg);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(hsotg->dev, "%s: failed to backup host registers\n",
|
||||||
|
__func__);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enter USB Suspend Mode */
|
||||||
|
hprt0 = dwc2_readl(hsotg->regs + HPRT0);
|
||||||
|
hprt0 |= HPRT0_SUSP;
|
||||||
|
hprt0 &= ~HPRT0_ENA;
|
||||||
|
dwc2_writel(hprt0, hsotg->regs + HPRT0);
|
||||||
|
|
||||||
|
/* Wait for the HPRT0.PrtSusp register field to be set */
|
||||||
|
if (dwc2_hsotg_wait_bit_set(hsotg, HPRT0, HPRT0_SUSP, 300))
|
||||||
|
dev_warn(hsotg->dev, "Suspend wasn't genereted\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to disable interrupts to prevent servicing of any IRQ
|
||||||
|
* during going to hibernation
|
||||||
|
*/
|
||||||
|
spin_lock_irqsave(&hsotg->lock, flags);
|
||||||
|
hsotg->lx_state = DWC2_L2;
|
||||||
|
|
||||||
|
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||||
|
if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) {
|
||||||
|
/* ULPI interface */
|
||||||
|
/* Suspend the Phy Clock */
|
||||||
|
pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
|
||||||
|
pcgcctl |= PCGCTL_STOPPCLK;
|
||||||
|
dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
|
||||||
|
udelay(10);
|
||||||
|
|
||||||
|
gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
|
||||||
|
gpwrdn |= GPWRDN_PMUACTV;
|
||||||
|
dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
|
||||||
|
udelay(10);
|
||||||
|
} else {
|
||||||
|
/* UTMI+ Interface */
|
||||||
|
gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
|
||||||
|
gpwrdn |= GPWRDN_PMUACTV;
|
||||||
|
dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
|
||||||
|
udelay(10);
|
||||||
|
|
||||||
|
pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
|
||||||
|
pcgcctl |= PCGCTL_STOPPCLK;
|
||||||
|
dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
|
||||||
|
udelay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable interrupts from wake up logic */
|
||||||
|
gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
|
||||||
|
gpwrdn |= GPWRDN_PMUINTSEL;
|
||||||
|
dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
|
||||||
|
udelay(10);
|
||||||
|
|
||||||
|
/* Unmask host mode interrupts in GPWRDN */
|
||||||
|
gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
|
||||||
|
gpwrdn |= GPWRDN_DISCONN_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);
|
||||||
|
|
||||||
|
hsotg->hibernated = 1;
|
||||||
|
hsotg->bus_suspended = 1;
|
||||||
|
dev_dbg(hsotg->dev, "Host hibernation completed\n");
|
||||||
|
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* dwc2_host_exit_hibernation()
|
||||||
|
*
|
||||||
|
* @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 enter to hibernation.
|
||||||
|
*
|
||||||
|
* This function is for exiting from Host mode hibernation by
|
||||||
|
* Host Initiated Resume/Reset and Device Initiated Remote-Wakeup.
|
||||||
|
*/
|
||||||
|
int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
|
||||||
|
int reset)
|
||||||
|
{
|
||||||
|
u32 gpwrdn;
|
||||||
|
u32 hprt0;
|
||||||
|
int ret = 0;
|
||||||
|
struct dwc2_gregs_backup *gr;
|
||||||
|
struct dwc2_hregs_backup *hr;
|
||||||
|
|
||||||
|
gr = &hsotg->gr_backup;
|
||||||
|
hr = &hsotg->hr_backup;
|
||||||
|
|
||||||
|
dev_dbg(hsotg->dev,
|
||||||
|
"%s: called with rem_wakeup = %d reset = %d\n",
|
||||||
|
__func__, rem_wakeup, reset);
|
||||||
|
|
||||||
|
dwc2_hib_restore_common(hsotg, rem_wakeup, 1);
|
||||||
|
hsotg->hibernated = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This step is not described in functional spec but if not wait for
|
||||||
|
* this delay, mismatch interrupts occurred because just after restore
|
||||||
|
* core is in Device mode(gintsts.curmode == 0)
|
||||||
|
*/
|
||||||
|
mdelay(100);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* Restore GUSBCFG, HCFG */
|
||||||
|
dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG);
|
||||||
|
dwc2_writel(hr->hcfg, hsotg->regs + HCFG);
|
||||||
|
|
||||||
|
/* De-assert Wakeup Logic */
|
||||||
|
gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
|
||||||
|
gpwrdn &= ~GPWRDN_PMUACTV;
|
||||||
|
dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
|
||||||
|
udelay(10);
|
||||||
|
|
||||||
|
hprt0 = hr->hprt0;
|
||||||
|
hprt0 |= HPRT0_PWR;
|
||||||
|
hprt0 &= ~HPRT0_ENA;
|
||||||
|
hprt0 &= ~HPRT0_SUSP;
|
||||||
|
dwc2_writel(hprt0, hsotg->regs + HPRT0);
|
||||||
|
|
||||||
|
hprt0 = hr->hprt0;
|
||||||
|
hprt0 |= HPRT0_PWR;
|
||||||
|
hprt0 &= ~HPRT0_ENA;
|
||||||
|
hprt0 &= ~HPRT0_SUSP;
|
||||||
|
|
||||||
|
if (reset) {
|
||||||
|
hprt0 |= HPRT0_RST;
|
||||||
|
dwc2_writel(hprt0, hsotg->regs + HPRT0);
|
||||||
|
|
||||||
|
/* Wait for Resume time and then program HPRT again */
|
||||||
|
mdelay(60);
|
||||||
|
hprt0 &= ~HPRT0_RST;
|
||||||
|
dwc2_writel(hprt0, hsotg->regs + HPRT0);
|
||||||
|
} else {
|
||||||
|
hprt0 |= HPRT0_RES;
|
||||||
|
dwc2_writel(hprt0, hsotg->regs + HPRT0);
|
||||||
|
|
||||||
|
/* Wait for Resume time and then program HPRT again */
|
||||||
|
mdelay(100);
|
||||||
|
hprt0 &= ~HPRT0_RES;
|
||||||
|
dwc2_writel(hprt0, hsotg->regs + HPRT0);
|
||||||
|
}
|
||||||
|
/* Clear all interrupt status */
|
||||||
|
hprt0 = dwc2_readl(hsotg->regs + HPRT0);
|
||||||
|
hprt0 |= HPRT0_CONNDET;
|
||||||
|
hprt0 |= HPRT0_ENACHG;
|
||||||
|
hprt0 &= ~HPRT0_ENA;
|
||||||
|
dwc2_writel(hprt0, hsotg->regs + HPRT0);
|
||||||
|
|
||||||
|
hprt0 = dwc2_readl(hsotg->regs + HPRT0);
|
||||||
|
|
||||||
|
/* 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 host registers */
|
||||||
|
ret = dwc2_restore_host_registers(hsotg);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(hsotg->dev, "%s: failed to restore host registers\n",
|
||||||
|
__func__);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
hsotg->hibernated = 0;
|
||||||
|
hsotg->bus_suspended = 0;
|
||||||
|
hsotg->lx_state = DWC2_L0;
|
||||||
|
dev_dbg(hsotg->dev, "Host hibernation restore complete\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue