Commit c74c26f6 authored by Artur Petrosyan's avatar Artur Petrosyan Committed by Greg Kroah-Hartman

usb: dwc2: Fix partial power down exiting by system resume

Fixes the implementation of exiting from partial power down
power saving mode when PC is resumed.

Added port connection status checking which prevents exiting from
Partial Power Down mode from _dwc2_hcd_resume() if not in Partial
Power Down mode.

Rearranged the implementation to get rid of many "if"
statements.

NOTE: Switch case statement is used for hibernation partial
power down and clock gating mode determination. In this patch
only Partial Power Down is implemented the Hibernation and
clock gating implementations are planned to be added.

Fixes: 6f6d7059 ("usb: dwc2: bus suspend/resume for hosts with DWC2_POWER_DOWN_PARAM_NONE")
Cc: <stable@vger.kernel.org>
Acked-by: default avatarMinas Harutyunyan <Minas.Harutyunyan@synopsys.com>
Signed-off-by: default avatarArtur Petrosyan <Arthur.Petrosyan@synopsys.com>
Link: https://lore.kernel.org/r/20210408094607.1A9BAA0094@mailhost.synopsys.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 113f86d0
...@@ -4427,7 +4427,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) ...@@ -4427,7 +4427,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
{ {
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
unsigned long flags; unsigned long flags;
u32 pcgctl; u32 hprt0;
int ret = 0; int ret = 0;
spin_lock_irqsave(&hsotg->lock, flags); spin_lock_irqsave(&hsotg->lock, flags);
...@@ -4438,56 +4438,58 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) ...@@ -4438,56 +4438,58 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
if (hsotg->lx_state != DWC2_L2) if (hsotg->lx_state != DWC2_L2)
goto unlock; goto unlock;
if (hsotg->params.power_down > DWC2_POWER_DOWN_PARAM_PARTIAL) { hprt0 = dwc2_read_hprt0(hsotg);
hsotg->lx_state = DWC2_L0;
goto unlock;
}
/* /*
* Enable power if not already done. * Added port connection status checking which prevents exiting from
* This must not be spinlocked since duration * Partial Power Down mode from _dwc2_hcd_resume() if not in Partial
* of this call is unknown. * Power Down mode.
*/ */
if (!IS_ERR_OR_NULL(hsotg->uphy)) { if (hprt0 & HPRT0_CONNSTS) {
spin_unlock_irqrestore(&hsotg->lock, flags); hsotg->lx_state = DWC2_L0;
usb_phy_set_suspend(hsotg->uphy, false); goto unlock;
spin_lock_irqsave(&hsotg->lock, flags);
} }
if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { switch (hsotg->params.power_down) {
case DWC2_POWER_DOWN_PARAM_PARTIAL:
ret = dwc2_exit_partial_power_down(hsotg, 0, true);
if (ret)
dev_err(hsotg->dev,
"exit partial_power_down failed\n");
/* /*
* Set HW accessible bit before powering on the controller * Set HW accessible bit before powering on the controller
* since an interrupt may rise. * since an interrupt may rise.
*/ */
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
break;
case DWC2_POWER_DOWN_PARAM_HIBERNATION:
/* Exit partial_power_down */ case DWC2_POWER_DOWN_PARAM_NONE:
ret = dwc2_exit_partial_power_down(hsotg, 0, true); default:
if (ret && (ret != -ENOTSUPP)) hsotg->lx_state = DWC2_L0;
dev_err(hsotg->dev, "exit partial_power_down failed\n"); goto unlock;
} else {
pcgctl = readl(hsotg->regs + PCGCTL);
pcgctl &= ~PCGCTL_STOPPCLK;
writel(pcgctl, hsotg->regs + PCGCTL);
} }
hsotg->lx_state = DWC2_L0; /* Change Root port status, as port status change occurred after resume.*/
hsotg->flags.b.port_suspend_change = 1;
/*
* Enable power if not already done.
* This must not be spinlocked since duration
* of this call is unknown.
*/
if (!IS_ERR_OR_NULL(hsotg->uphy)) {
spin_unlock_irqrestore(&hsotg->lock, flags); spin_unlock_irqrestore(&hsotg->lock, flags);
usb_phy_set_suspend(hsotg->uphy, false);
if (hsotg->bus_suspended) {
spin_lock_irqsave(&hsotg->lock, flags); spin_lock_irqsave(&hsotg->lock, flags);
hsotg->flags.b.port_suspend_change = 1; }
/* Enable external vbus supply after resuming the port. */
spin_unlock_irqrestore(&hsotg->lock, flags); spin_unlock_irqrestore(&hsotg->lock, flags);
dwc2_port_resume(hsotg);
} else {
if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) {
dwc2_vbus_supply_init(hsotg); dwc2_vbus_supply_init(hsotg);
/* Wait for controller to correctly update D+/D- level */ /* Wait for controller to correctly update D+/D- level */
usleep_range(3000, 5000); usleep_range(3000, 5000);
} spin_lock_irqsave(&hsotg->lock, flags);
/* /*
* Clear Port Enable and Port Status changes. * Clear Port Enable and Port Status changes.
...@@ -4495,11 +4497,11 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) ...@@ -4495,11 +4497,11 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
*/ */
dwc2_writel(hsotg, HPRT0_PWR | HPRT0_CONNDET | dwc2_writel(hsotg, HPRT0_PWR | HPRT0_CONNDET |
HPRT0_ENACHG, HPRT0); HPRT0_ENACHG, HPRT0);
/* Wait for controller to detect Port Connect */ /* Wait for controller to detect Port Connect */
spin_unlock_irqrestore(&hsotg->lock, flags);
usleep_range(5000, 7000); usleep_range(5000, 7000);
} spin_lock_irqsave(&hsotg->lock, flags);
return ret;
unlock: unlock:
spin_unlock_irqrestore(&hsotg->lock, flags); spin_unlock_irqrestore(&hsotg->lock, flags);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment