diff options
Diffstat (limited to 'drivers/usb/dwc2/platform.c')
-rw-r--r-- | drivers/usb/dwc2/platform.c | 66 |
1 files changed, 53 insertions, 13 deletions
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 7b84416dfc2b..12b4dc07d08a 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -469,18 +469,6 @@ static int dwc2_driver_probe(struct platform_device *dev) spin_lock_init(&hsotg->lock); - hsotg->irq = platform_get_irq(dev, 0); - if (hsotg->irq < 0) - return hsotg->irq; - - dev_dbg(hsotg->dev, "registering common handler for irq%d\n", - hsotg->irq); - retval = devm_request_irq(hsotg->dev, hsotg->irq, - dwc2_handle_common_intr, IRQF_SHARED, - dev_name(hsotg->dev), hsotg); - if (retval) - return retval; - hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus"); if (IS_ERR(hsotg->vbus_supply)) { retval = PTR_ERR(hsotg->vbus_supply); @@ -524,6 +512,20 @@ static int dwc2_driver_probe(struct platform_device *dev) if (retval) goto error; + hsotg->irq = platform_get_irq(dev, 0); + if (hsotg->irq < 0) { + retval = hsotg->irq; + goto error; + } + + dev_dbg(hsotg->dev, "registering common handler for irq%d\n", + hsotg->irq); + retval = devm_request_irq(hsotg->dev, hsotg->irq, + dwc2_handle_common_intr, IRQF_SHARED, + dev_name(hsotg->dev), hsotg); + if (retval) + goto error; + /* * For OTG cores, set the force mode bits to reflect the value * of dr_mode. Force mode bits should not be touched at any @@ -683,6 +685,14 @@ static int __maybe_unused dwc2_suspend(struct device *dev) regulator_disable(dwc2->usb33d); } + if (is_device_mode) + ret = dwc2_gadget_backup_critical_registers(dwc2); + else + ret = dwc2_host_backup_critical_registers(dwc2); + + if (ret) + return ret; + if (dwc2->ll_hw_enabled && (is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) { ret = __dwc2_lowlevel_hw_disable(dwc2); @@ -692,6 +702,24 @@ static int __maybe_unused dwc2_suspend(struct device *dev) return ret; } +static int dwc2_restore_critical_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_gregs_backup *gr; + + gr = &hsotg->gr_backup; + + if (!gr->valid) { + dev_err(hsotg->dev, "No valid register backup, failed to restore\n"); + return -EINVAL; + } + + if (gr->gintsts & GINTSTS_CURMODE_HOST) + return dwc2_host_restore_critical_registers(hsotg); + + return dwc2_gadget_restore_critical_registers(hsotg, DWC2_RESTORE_DCTL | + DWC2_RESTORE_DCFG); +} + static int __maybe_unused dwc2_resume(struct device *dev) { struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); @@ -704,6 +732,18 @@ static int __maybe_unused dwc2_resume(struct device *dev) } dwc2->phy_off_for_suspend = false; + /* + * During suspend it's possible that the power domain for the + * DWC2 controller is disabled and all register values get lost. + * In case the GUSBCFG register is not initialized, it's clear the + * registers must be restored. + */ + if (!(dwc2_readl(dwc2, GUSBCFG) & GUSBCFG_TOUTCAL_MASK)) { + ret = dwc2_restore_critical_registers(dwc2); + if (ret) + return ret; + } + if (dwc2->params.activate_stm_id_vb_detection) { unsigned long flags; u32 ggpio, gotgctl; @@ -754,7 +794,7 @@ static struct platform_driver dwc2_platform_driver = { .pm = &dwc2_dev_pm_ops, }, .probe = dwc2_driver_probe, - .remove_new = dwc2_driver_remove, + .remove = dwc2_driver_remove, .shutdown = dwc2_driver_shutdown, }; |