summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc2/core_intr.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc2/core_intr.c')
-rw-r--r--drivers/usb/dwc2/core_intr.c45
1 files changed, 41 insertions, 4 deletions
diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c
index 6cf047878dba..927be1e8b3dc 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 && (ret != -ENOTSUPP))
+ 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,10 +418,43 @@ 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) {
+ if (ret != -ENOTSUPP)
+ 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:
+ /*
+ * Change to L2 (suspend) state before releasing
+ * spinlock
+ */
+ hsotg->lx_state = DWC2_L2;
+
+ /* 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");
+ /* Change to L2 (suspend) state */
+ hsotg->lx_state = DWC2_L2;
/* Clear the a_peripheral flag, back to a_host */
spin_unlock(&hsotg->lock);
dwc2_hcd_start(hsotg);
@@ -423,9 +463,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);
}
@@ -522,4 +560,3 @@ out:
spin_unlock(&hsotg->lock);
return retval;
}
-EXPORT_SYMBOL_GPL(dwc2_handle_common_intr);