[PATCH v1 14/14] usb: dwc2: Add enter/exit hibernation from system issued suspend/resume

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Added a new flow of entering and exiting hibernation when PC is
hibernated or suspended.

Signed-off-by: Artur Petrosyan <arturp@xxxxxxxxxxxx>
---
 drivers/usb/dwc2/hcd.c | 128 +++++++++++++++++++++++++++++++------------------
 1 file changed, 81 insertions(+), 47 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 45d4a3e1ebd2..f1e92a287cb1 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -4510,35 +4510,54 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
 	if (hsotg->op_state == OTG_STATE_B_PERIPHERAL)
 		goto unlock;
 
-	if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL ||
+	if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE ||
 	    hsotg->flags.b.port_connect_status == 0)
 		goto skip_power_saving;
 
-	/*
-	 * Drive USB suspend and disable port Power
-	 * if usb bus is not suspended.
-	 */
-	if (!hsotg->bus_suspended) {
-		hprt0 = dwc2_read_hprt0(hsotg);
-		hprt0 |= HPRT0_SUSP;
-		hprt0 &= ~HPRT0_PWR;
-		dwc2_writel(hsotg, hprt0, HPRT0);
-		spin_unlock_irqrestore(&hsotg->lock, flags);
-		dwc2_vbus_supply_exit(hsotg);
-		spin_lock_irqsave(&hsotg->lock, flags);
-	}
+	switch (hsotg->params.power_down) {
+	case DWC2_POWER_DOWN_PARAM_PARTIAL:
+		/*
+		 * Drive USB suspend and disable port Power
+		 * if usb bus is not suspended.
+		 */
+		if (!hsotg->bus_suspended) {
+			hprt0 = dwc2_read_hprt0(hsotg);
+			hprt0 |= HPRT0_SUSP;
+			hprt0 &= ~HPRT0_PWR;
+			dwc2_writel(hsotg, hprt0, HPRT0);
+			spin_unlock_irqrestore(&hsotg->lock, flags);
+			dwc2_vbus_supply_exit(hsotg);
+			spin_lock_irqsave(&hsotg->lock, flags);
+		}
 
-	/* Enter partial_power_down */
-	ret = dwc2_enter_partial_power_down(hsotg);
-	if (ret) {
-		if (ret != -ENOTSUPP)
-			dev_err(hsotg->dev,
-				"enter partial_power_down failed\n");
+		/* Enter partial_power_down */
+		ret = dwc2_enter_partial_power_down(hsotg);
+		if (ret) {
+			if (ret != -ENOTSUPP)
+				dev_err(hsotg->dev,
+					"enter partial_power_down failed\n");
+			goto skip_power_saving;
+		}
+		hsotg->bus_suspended = true;
+		break;
+	case DWC2_POWER_DOWN_PARAM_HIBERNATION:
+		if (!hsotg->bus_suspended) {
+			/* Enter hibernation */
+			spin_unlock_irqrestore(&hsotg->lock, flags);
+			ret = dwc2_enter_hibernation(hsotg, 1);
+			spin_lock_irqsave(&hsotg->lock, flags);
+			if (ret && ret != -ENOTSUPP)
+				dev_err(hsotg->dev,
+					"%s: enter hibernation failed\n",
+					__func__);
+		} else {
+			goto skip_power_saving;
+		}
+		break;
+	default:
 		goto skip_power_saving;
 	}
 
-	hsotg->bus_suspended = true;
-
 	/* Ask phy to be suspended */
 	if (!IS_ERR_OR_NULL(hsotg->uphy)) {
 		spin_unlock_irqrestore(&hsotg->lock, flags);
@@ -4564,17 +4583,17 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
 	int ret = 0;
 	u32 hprt0;
 
-	hprt0 = dwc2_read_hprt0(hsotg);
-
 	spin_lock_irqsave(&hsotg->lock, flags);
 
-	if (dwc2_is_device_mode(hsotg))
+	if (!hsotg->bus_suspended)
 		goto unlock;
 
 	if (hsotg->lx_state != DWC2_L2)
 		goto unlock;
 
-	if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL ||
+	hprt0 = dwc2_read_hprt0(hsotg);
+
+	if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE ||
 	    hprt0 & HPRT0_CONNSTS) {
 		hsotg->lx_state = DWC2_L0;
 		goto unlock;
@@ -4597,36 +4616,51 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
 		spin_lock_irqsave(&hsotg->lock, flags);
 	}
 
-	/* Exit partial_power_down */
-	ret = dwc2_exit_partial_power_down(hsotg, true);
-	if (ret && (ret != -ENOTSUPP))
-		dev_err(hsotg->dev, "exit partial_power_down failed\n");
+	switch (hsotg->params.power_down) {
+	case DWC2_POWER_DOWN_PARAM_PARTIAL:
 
-	hsotg->lx_state = DWC2_L0;
+		/* Exit partial_power_down */
+		ret = dwc2_exit_partial_power_down(hsotg, true);
+		if (ret && (ret != -ENOTSUPP))
+			dev_err(hsotg->dev, "exit partial_power_down failed\n");
 
-	spin_unlock_irqrestore(&hsotg->lock, flags);
+		hsotg->lx_state = DWC2_L0;
 
-	if (hsotg->bus_suspended) {
-		spin_lock_irqsave(&hsotg->lock, flags);
-		hsotg->flags.b.port_suspend_change = 1;
 		spin_unlock_irqrestore(&hsotg->lock, flags);
-		dwc2_port_resume(hsotg);
-	} else {
-		dwc2_vbus_supply_init(hsotg);
 
-		/* Wait for controller to correctly update D+/D- level */
-		usleep_range(3000, 5000);
+		if (hsotg->bus_suspended) {
+			spin_lock_irqsave(&hsotg->lock, flags);
+			hsotg->flags.b.port_suspend_change = 1;
+			spin_unlock_irqrestore(&hsotg->lock, flags);
+			dwc2_port_resume(hsotg);
+		} else {
+			dwc2_vbus_supply_init(hsotg);
 
-		/*
-		 * Clear Port Enable and Port Status changes.
-		 * Enable Port Power.
-		 */
-		dwc2_writel(hsotg, HPRT0_PWR | HPRT0_CONNDET |
+			/* Wait for controller to correctly update D+/D- level */
+			usleep_range(3000, 5000);
+
+			/*
+			 * Clear Port Enable and Port Status changes.
+			 * Enable Port Power.
+			 */
+			dwc2_writel(hsotg, HPRT0_PWR | HPRT0_CONNDET |
 				HPRT0_ENACHG, HPRT0);
-		/* Wait for controller to detect Port Connect */
-		usleep_range(5000, 7000);
+			/* Wait for controller to detect Port Connect */
+			usleep_range(5000, 7000);
+		}
+		break;
+	case DWC2_POWER_DOWN_PARAM_HIBERNATION:
+
+		/* Exit host hibernation. */
+		if (hsotg->hibernated)
+			dwc2_exit_hibernation(hsotg, 0, 0, 1);
+		break;
+	default:
+		goto unlock;
 	}
 
+	spin_unlock_irqrestore(&hsotg->lock, flags);
+
 	return ret;
 unlock:
 	spin_unlock_irqrestore(&hsotg->lock, flags);
-- 
2.11.0





[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux