From: Gregory Herrero <gregory.herr...@intel.com>

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 <johny...@synopsys.com>
Signed-off-by: Gregory Herrero <gregory.herr...@intel.com>
---
 drivers/usb/dwc2/core.h      |  2 ++
 drivers/usb/dwc2/core_intr.c | 32 ++++++++++++++++++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index b0ee951..e6abc28 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -1088,6 +1088,7 @@ extern void s3c_hsotg_core_init_disconnected(struct 
dwc2_hsotg *dwc2,
 extern void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg);
 extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2);
 extern int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
+#define dwc2_is_device_connected(hsotg) (hsotg->connected)
 #else
 static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2)
 { return 0; }
@@ -1104,6 +1105,7 @@ static inline void s3c_hsotg_disconnect(struct dwc2_hsotg 
*dwc2) {}
 static inline int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
                                                        int testmode)
 { return 0; }
+#define dwc2_is_device_connected(hsotg) (0)
 #endif
 
 #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c
index 6cf0478..6ffb5a9 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);
 }
-- 
2.3.3

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to