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