Commit 6f6d7059 authored by Douglas Anderson's avatar Douglas Anderson Committed by Felipe Balbi

usb: dwc2: bus suspend/resume for hosts with DWC2_POWER_DOWN_PARAM_NONE

This is an attempt to rehash commit 0cf884e8 ("usb: dwc2: add bus
suspend/resume for dwc2") on ToT.  That commit was reverted in commit
b0bb9bb6 ("Revert "usb: dwc2: add bus suspend/resume for dwc2"")
because apparently it broke the Altera SOCFPGA.

With all the changes that have happened to dwc2 in the meantime, it's
possible that the Altera SOCFPGA will just magically work with this
change now.  ...and it would be good to get bus suspend/resume
implemented.

This change is a forward port of one that's been living in the Chrome
OS 3.14 kernel tree.
Signed-off-by: default avatarDouglas Anderson <dianders@chromium.org>
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
parent c9999337
...@@ -4471,6 +4471,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) ...@@ -4471,6 +4471,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
unsigned long flags; unsigned long flags;
int ret = 0; int ret = 0;
u32 hprt0; u32 hprt0;
u32 pcgctl;
spin_lock_irqsave(&hsotg->lock, flags); spin_lock_irqsave(&hsotg->lock, flags);
...@@ -4486,7 +4487,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) ...@@ -4486,7 +4487,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
if (hsotg->op_state == OTG_STATE_B_PERIPHERAL) if (hsotg->op_state == OTG_STATE_B_PERIPHERAL)
goto unlock; goto unlock;
if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) if (hsotg->params.power_down > DWC2_POWER_DOWN_PARAM_PARTIAL)
goto skip_power_saving; goto skip_power_saving;
/* /*
...@@ -4495,21 +4496,35 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) ...@@ -4495,21 +4496,35 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
*/ */
if (!hsotg->bus_suspended) { if (!hsotg->bus_suspended) {
hprt0 = dwc2_read_hprt0(hsotg); hprt0 = dwc2_read_hprt0(hsotg);
hprt0 |= HPRT0_SUSP; if (hprt0 & HPRT0_CONNSTS) {
hprt0 &= ~HPRT0_PWR; hprt0 |= HPRT0_SUSP;
dwc2_writel(hsotg, hprt0, HPRT0); if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL)
spin_unlock_irqrestore(&hsotg->lock, flags); hprt0 &= ~HPRT0_PWR;
dwc2_vbus_supply_exit(hsotg); dwc2_writel(hsotg, hprt0, HPRT0);
spin_lock_irqsave(&hsotg->lock, flags); }
if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) {
spin_unlock_irqrestore(&hsotg->lock, flags);
dwc2_vbus_supply_exit(hsotg);
spin_lock_irqsave(&hsotg->lock, flags);
} else {
pcgctl = readl(hsotg->regs + PCGCTL);
pcgctl |= PCGCTL_STOPPCLK;
writel(pcgctl, hsotg->regs + PCGCTL);
}
} }
/* Enter partial_power_down */ if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) {
ret = dwc2_enter_partial_power_down(hsotg); /* Enter partial_power_down */
if (ret) { ret = dwc2_enter_partial_power_down(hsotg);
if (ret != -ENOTSUPP) if (ret) {
dev_err(hsotg->dev, if (ret != -ENOTSUPP)
"enter partial_power_down failed\n"); dev_err(hsotg->dev,
goto skip_power_saving; "enter partial_power_down failed\n");
goto skip_power_saving;
}
/* After entering partial_power_down, hardware is no more accessible */
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
} }
/* Ask phy to be suspended */ /* Ask phy to be suspended */
...@@ -4519,9 +4534,6 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) ...@@ -4519,9 +4534,6 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
spin_lock_irqsave(&hsotg->lock, flags); spin_lock_irqsave(&hsotg->lock, flags);
} }
/* After entering partial_power_down, hardware is no more accessible */
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
skip_power_saving: skip_power_saving:
hsotg->lx_state = DWC2_L2; hsotg->lx_state = DWC2_L2;
unlock: unlock:
...@@ -4534,6 +4546,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) ...@@ -4534,6 +4546,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;
int ret = 0; int ret = 0;
spin_lock_irqsave(&hsotg->lock, flags); spin_lock_irqsave(&hsotg->lock, flags);
...@@ -4544,17 +4557,11 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) ...@@ -4544,17 +4557,11 @@ 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) { if (hsotg->params.power_down > DWC2_POWER_DOWN_PARAM_PARTIAL) {
hsotg->lx_state = DWC2_L0; hsotg->lx_state = DWC2_L0;
goto unlock; goto unlock;
} }
/*
* Set HW accessible bit before powering on the controller
* since an interrupt may rise.
*/
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
/* /*
* Enable power if not already done. * Enable power if not already done.
* This must not be spinlocked since duration * This must not be spinlocked since duration
...@@ -4566,10 +4573,23 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) ...@@ -4566,10 +4573,23 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
spin_lock_irqsave(&hsotg->lock, flags); spin_lock_irqsave(&hsotg->lock, flags);
} }
/* Exit partial_power_down */ if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) {
ret = dwc2_exit_partial_power_down(hsotg, true); /*
if (ret && (ret != -ENOTSUPP)) * Set HW accessible bit before powering on the controller
dev_err(hsotg->dev, "exit partial_power_down failed\n"); * since an interrupt may rise.
*/
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->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");
} else {
pcgctl = readl(hsotg->regs + PCGCTL);
pcgctl &= ~PCGCTL_STOPPCLK;
writel(pcgctl, hsotg->regs + PCGCTL);
}
hsotg->lx_state = DWC2_L0; hsotg->lx_state = DWC2_L0;
...@@ -4581,10 +4601,12 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) ...@@ -4581,10 +4601,12 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
spin_unlock_irqrestore(&hsotg->lock, flags); spin_unlock_irqrestore(&hsotg->lock, flags);
dwc2_port_resume(hsotg); dwc2_port_resume(hsotg);
} else { } else {
dwc2_vbus_supply_init(hsotg); if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) {
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);
}
/* /*
* Clear Port Enable and Port Status changes. * Clear Port Enable and Port Status changes.
......
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