aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/dwc2/core_intr.c
diff options
context:
space:
mode:
authorGregory Herrero <gregory.herrero@intel.com>2015-04-29 22:09:02 +0200
committerFelipe Balbi <balbi@ti.com>2015-04-29 15:18:43 -0500
commitf81f46e1f530900323b6e32eba1af7244ca69537 (patch)
tree00397ede9a442a5233b0b6d3a14f277ca954231e /drivers/usb/dwc2/core_intr.c
parentusb: dwc2: add controller hibernation support (diff)
downloadlinux-dev-f81f46e1f530900323b6e32eba1af7244ca69537.tar.xz
linux-dev-f81f46e1f530900323b6e32eba1af7244ca69537.zip
usb: dwc2: implement hibernation during bus suspend/resume
Allow controller to enter in hibernation during usb bus suspend and inform both phy and gadget about the suspended state. While in hibernation, the controller can't detect the resume condition. An external mechanism must call usb_phy_set_suspend on resume. Exit hibernation when controller gets the resume interrupt and inform only gadget driver about it. Acked-by: John Youn <johnyoun@synopsys.com> Signed-off-by: Gregory Herrero <gregory.herrero@intel.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/dwc2/core_intr.c')
-rw-r--r--drivers/usb/dwc2/core_intr.c32
1 files changed, 32 insertions, 0 deletions
diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c
index 6cf047878dba..6ffb5a9c385e 100644
--- a/drivers/usb/dwc2/core_intr.c
+++ b/drivers/usb/dwc2/core_intr.c
@@ -334,6 +334,7 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
*/
static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
{
+ int ret;
dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
@@ -345,6 +346,11 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
/* Clear Remote Wakeup Signaling */
dctl &= ~DCTL_RMTWKUPSIG;
writel(dctl, hsotg->regs + DCTL);
+ ret = dwc2_exit_hibernation(hsotg, true);
+ if (ret)
+ dev_err(hsotg->dev, "exit hibernation failed\n");
+
+ call_gadget(hsotg, resume);
}
/* Change to L0 state */
hsotg->lx_state = DWC2_L0;
@@ -397,6 +403,7 @@ static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
{
u32 dsts;
+ int ret;
dev_dbg(hsotg->dev, "USB SUSPEND\n");
@@ -411,6 +418,30 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
"DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n",
!!(dsts & DSTS_SUSPSTS),
hsotg->hw_params.power_optimized);
+ if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) {
+ /* Ignore suspend request before enumeration */
+ if (!dwc2_is_device_connected(hsotg)) {
+ dev_dbg(hsotg->dev,
+ "ignore suspend request before enumeration\n");
+ goto clear_int;
+ }
+
+ ret = dwc2_enter_hibernation(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev,
+ "enter hibernation failed\n");
+ goto skip_power_saving;
+ }
+
+ udelay(100);
+
+ /* Ask phy to be suspended */
+ if (!IS_ERR_OR_NULL(hsotg->uphy))
+ usb_phy_set_suspend(hsotg->uphy, true);
+skip_power_saving:
+ /* Call gadget suspend callback */
+ call_gadget(hsotg, suspend);
+ }
} else {
if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) {
dev_dbg(hsotg->dev, "a_peripheral->a_host\n");
@@ -426,6 +457,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
/* Change to L2 (suspend) state */
hsotg->lx_state = DWC2_L2;
+clear_int:
/* Clear interrupt */
writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
}