Skip to content

Commit

Permalink
usb: dwc2: Add host/device hibernation functions
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Vardan Mikayelyan authored and Felipe Balbi committed Mar 13, 2018
1 parent 94d2666 commit c5c403d
Show file tree
Hide file tree
Showing 4 changed files with 419 additions and 2 deletions.
4 changes: 2 additions & 2 deletions drivers/usb/dwc2/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
*
* @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;

Expand Down Expand Up @@ -96,7 +96,7 @@ static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
*
* @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;

Expand Down
18 changes: 18 additions & 0 deletions drivers/usb/dwc2/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
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);

Expand Down Expand Up @@ -1224,6 +1226,9 @@ int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg);
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_total_depth(struct dwc2_hsotg *hsotg);
int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg);
Expand All @@ -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,
int remote_wakeup)
{ 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)
{ return 0; }
static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg)
Expand All @@ -1267,6 +1277,9 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
int dwc2_backup_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
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
{ return 0; }
Expand All @@ -1283,6 +1296,11 @@ static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
{ 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

Expand Down
176 changes: 176 additions & 0 deletions drivers/usb/dwc2/gadget.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

0 comments on commit c5c403d

Please sign in to comment.