Skip to content

Commit c74c26f

Browse files
Artur Petrosyangregkh
Artur Petrosyan
authored andcommitted
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: 6f6d705 ("usb: dwc2: bus suspend/resume for hosts with DWC2_POWER_DOWN_PARAM_NONE") Cc: <stable@vger.kernel.org> Acked-by: Minas Harutyunyan <Minas.Harutyunyan@synopsys.com> Signed-off-by: Artur Petrosyan <Arthur.Petrosyan@synopsys.com> Link: https://lore.kernel.org/r/20210408094607.1A9BAA0094@mailhost.synopsys.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 113f86d commit c74c26f

File tree

1 file changed

+46
-44
lines changed

1 file changed

+46
-44
lines changed

drivers/usb/dwc2/hcd.c

+46-44
Original file line numberDiff line numberDiff line change
@@ -4427,7 +4427,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
44274427
{
44284428
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
44294429
unsigned long flags;
4430-
u32 pcgctl;
4430+
u32 hprt0;
44314431
int ret = 0;
44324432

44334433
spin_lock_irqsave(&hsotg->lock, flags);
@@ -4438,11 +4438,40 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
44384438
if (hsotg->lx_state != DWC2_L2)
44394439
goto unlock;
44404440

4441-
if (hsotg->params.power_down > DWC2_POWER_DOWN_PARAM_PARTIAL) {
4441+
hprt0 = dwc2_read_hprt0(hsotg);
4442+
4443+
/*
4444+
* Added port connection status checking which prevents exiting from
4445+
* Partial Power Down mode from _dwc2_hcd_resume() if not in Partial
4446+
* Power Down mode.
4447+
*/
4448+
if (hprt0 & HPRT0_CONNSTS) {
4449+
hsotg->lx_state = DWC2_L0;
4450+
goto unlock;
4451+
}
4452+
4453+
switch (hsotg->params.power_down) {
4454+
case DWC2_POWER_DOWN_PARAM_PARTIAL:
4455+
ret = dwc2_exit_partial_power_down(hsotg, 0, true);
4456+
if (ret)
4457+
dev_err(hsotg->dev,
4458+
"exit partial_power_down failed\n");
4459+
/*
4460+
* Set HW accessible bit before powering on the controller
4461+
* since an interrupt may rise.
4462+
*/
4463+
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
4464+
break;
4465+
case DWC2_POWER_DOWN_PARAM_HIBERNATION:
4466+
case DWC2_POWER_DOWN_PARAM_NONE:
4467+
default:
44424468
hsotg->lx_state = DWC2_L0;
44434469
goto unlock;
44444470
}
44454471

4472+
/* Change Root port status, as port status change occurred after resume.*/
4473+
hsotg->flags.b.port_suspend_change = 1;
4474+
44464475
/*
44474476
* Enable power if not already done.
44484477
* This must not be spinlocked since duration
@@ -4454,52 +4483,25 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
44544483
spin_lock_irqsave(&hsotg->lock, flags);
44554484
}
44564485

4457-
if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) {
4458-
/*
4459-
* Set HW accessible bit before powering on the controller
4460-
* since an interrupt may rise.
4461-
*/
4462-
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
4463-
4464-
4465-
/* Exit partial_power_down */
4466-
ret = dwc2_exit_partial_power_down(hsotg, 0, true);
4467-
if (ret && (ret != -ENOTSUPP))
4468-
dev_err(hsotg->dev, "exit partial_power_down failed\n");
4469-
} else {
4470-
pcgctl = readl(hsotg->regs + PCGCTL);
4471-
pcgctl &= ~PCGCTL_STOPPCLK;
4472-
writel(pcgctl, hsotg->regs + PCGCTL);
4473-
}
4474-
4475-
hsotg->lx_state = DWC2_L0;
4476-
4486+
/* Enable external vbus supply after resuming the port. */
44774487
spin_unlock_irqrestore(&hsotg->lock, flags);
4488+
dwc2_vbus_supply_init(hsotg);
44784489

4479-
if (hsotg->bus_suspended) {
4480-
spin_lock_irqsave(&hsotg->lock, flags);
4481-
hsotg->flags.b.port_suspend_change = 1;
4482-
spin_unlock_irqrestore(&hsotg->lock, flags);
4483-
dwc2_port_resume(hsotg);
4484-
} else {
4485-
if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) {
4486-
dwc2_vbus_supply_init(hsotg);
4487-
4488-
/* Wait for controller to correctly update D+/D- level */
4489-
usleep_range(3000, 5000);
4490-
}
4490+
/* Wait for controller to correctly update D+/D- level */
4491+
usleep_range(3000, 5000);
4492+
spin_lock_irqsave(&hsotg->lock, flags);
44914493

4492-
/*
4493-
* Clear Port Enable and Port Status changes.
4494-
* Enable Port Power.
4495-
*/
4496-
dwc2_writel(hsotg, HPRT0_PWR | HPRT0_CONNDET |
4497-
HPRT0_ENACHG, HPRT0);
4498-
/* Wait for controller to detect Port Connect */
4499-
usleep_range(5000, 7000);
4500-
}
4494+
/*
4495+
* Clear Port Enable and Port Status changes.
4496+
* Enable Port Power.
4497+
*/
4498+
dwc2_writel(hsotg, HPRT0_PWR | HPRT0_CONNDET |
4499+
HPRT0_ENACHG, HPRT0);
45014500

4502-
return ret;
4501+
/* Wait for controller to detect Port Connect */
4502+
spin_unlock_irqrestore(&hsotg->lock, flags);
4503+
usleep_range(5000, 7000);
4504+
spin_lock_irqsave(&hsotg->lock, flags);
45034505
unlock:
45044506
spin_unlock_irqrestore(&hsotg->lock, flags);
45054507

0 commit comments

Comments
 (0)