This is an attempt to rehash commit 0cf884e819e0 ("usb: dwc2: add bus suspend/resume for dwc2") on ToT. That commit was reverted in commit b0bb9bb6ce01 ("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. Signed-off-by: Douglas Anderson <dianders@xxxxxxxxxxxx> --- I've posted up a bunch of patches recently but tried to keep them in separate series where it makes sense. A summary of known patches: All patches can be found with: https://patchwork.kernel.org/patch/<ID>/ ACKed (thanks!) and not yet landed: 7453801 usb: dwc2: host: Fix ahbcfg for rk3066 7467521 usb: dwc2: host: Fix remote wakeup when not in DWC2_L2 Important, not yet reviewed: 7421171 [1/2] usb: dwc2: host: Fix missing device insertions Fixes no known issues, not yet reviewed: 7421181 [2/2] usb: dwc2: host: Clear interrupts before handling them Optimization to reduce probe time (may require simple rebase): 7348131 [1/5] usb: dwc2: Restore GUSBCFG in dwc2_get_hwparams() 7348221 [2/5] usb: dwc2: reset dwc2 core before dwc2_get_hwparams() 7348191 [3/5] CHROMIUM: usb: dwc2: Avoid double-reset at boot time 7348211 [4/5] usb: dwc2: Speed dwc2_get_hwparams() on some host-only ports 7348201 [5/5] usb: dwc2: reduce dwc2 driver probe time 7355241 usb: dwc2: Avoid more calls to dwc2_core_reset() Fix host port: 7529101 usb: dwc2: optionally assert phy "full reset" when waking up 7529081 ARM: dts: rockchip: Point rk3288 dwc2 usb at the full PHY reset This patch: RFT: usb: dwc2: bus suspend/resume that's not hibernate Abandoned for now (can't get wakeup to work on mainline): 6727091 [REPOST,1/3] USB: Export usb_wakeup_enabled_descendants() 6727101 [REPOST,2/3] Documentation: dt-bindings: Add snps,need-phy-for-wake for dwc2 USB 6727121 [REPOST,3/3] USB: dwc2: Don't turn off the usbphy in suspend if wakeup is enabled drivers/usb/dwc2/hcd.c | 79 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 32 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index e79baf73c234..0771fa667d0f 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -2381,6 +2381,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) unsigned long flags; int ret = 0; u32 hprt0; + u32 pcgctl; spin_lock_irqsave(&hsotg->lock, flags); @@ -2390,27 +2391,41 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) if (!HCD_HW_ACCESSIBLE(hcd)) goto unlock; - if (!hsotg->core_params->hibernation) - 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(hprt0, hsotg->regs + HPRT0); + if (hprt0 & HPRT0_CONNSTS) { + hprt0 |= HPRT0_SUSP; + if (hsotg->core_params->hibernation) + hprt0 &= ~HPRT0_PWR; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + } + + if (!hsotg->core_params->hibernation) { + pcgctl = readl(hsotg->regs + PCGCTL); + pcgctl |= PCGCTL_STOPPCLK; + writel(pcgctl, hsotg->regs + PCGCTL); + } } - /* Enter hibernation */ - ret = dwc2_enter_hibernation(hsotg); - if (ret) { - if (ret != -ENOTSUPP) - dev_err(hsotg->dev, - "enter hibernation failed\n"); - goto skip_power_saving; + if (hsotg->core_params->hibernation) { + /* Enter hibernation */ + ret = dwc2_enter_hibernation(hsotg); + if (ret) { + if (ret != -ENOTSUPP) + dev_err(hsotg->dev, + "enter hibernation failed\n"); + goto skip_power_saving; + } + + /* + * After entering hibernation, hardware is no + * more accessible + */ + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); } /* Ask phy to be suspended */ @@ -2420,9 +2435,6 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) spin_lock_irqsave(&hsotg->lock, flags); } - /* After entering hibernation, hardware is no more accessible */ - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - skip_power_saving: hsotg->lx_state = DWC2_L2; unlock: @@ -2435,6 +2447,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) { struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); unsigned long flags; + u32 pcgctl; int ret = 0; spin_lock_irqsave(&hsotg->lock, flags); @@ -2442,17 +2455,6 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) if (hsotg->lx_state != DWC2_L2) goto unlock; - if (!hsotg->core_params->hibernation) { - hsotg->lx_state = DWC2_L0; - 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. * This must not be spinlocked since duration @@ -2464,10 +2466,22 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) spin_lock_irqsave(&hsotg->lock, flags); } - /* Exit hibernation */ - ret = dwc2_exit_hibernation(hsotg, true); - if (ret && (ret != -ENOTSUPP)) - dev_err(hsotg->dev, "exit hibernation failed\n"); + if (hsotg->core_params->hibernation) { + /* + * Set HW accessible bit before powering on the controller + * since an interrupt may rise. + */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + /* Exit hibernation */ + ret = dwc2_exit_hibernation(hsotg, true); + if (ret && (ret != -ENOTSUPP)) + dev_err(hsotg->dev, "exit hibernation failed\n"); + } else { + pcgctl = readl(hsotg->regs + PCGCTL); + pcgctl &= ~PCGCTL_STOPPCLK; + writel(pcgctl, hsotg->regs + PCGCTL); + } hsotg->lx_state = DWC2_L0; @@ -2480,7 +2494,8 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) dwc2_port_resume(hsotg); } else { /* Wait for controller to correctly update D+/D- level */ - usleep_range(3000, 5000); + if (hsotg->core_params->hibernation) + usleep_range(3000, 5000); /* * Clear Port Enable and Port Status changes. -- 2.6.0.rc2.230.g3dd15c0 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html