diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index b04bbed7f29b..cee077a956b8 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -2005,7 +2005,8 @@ static int dwc3_resume(struct device *dev) * state as active to reflect actual state of device which * is now out of LPM. This allows runtime_suspend later. */ - if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST) + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST && + pm_runtime_status_suspended(dev)) pm_runtime_resume(dev); return 0; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 2f4f9cf44ad9..5c1fec2f3934 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1387,11 +1387,13 @@ struct dwc3 { /* Indicate if software connect was issued by the usb_gadget_driver */ unsigned int softconnect:1; /* - * If true, PM suspend allowed irrespective of host runtimePM state - * and core will power collapse. This also leads to reset-resume of - * connected devices on PM resume. + * If true, PM suspend/freeze allowed irrespective of host runtimePM + * state. In PM suspend/resume case, core will stay powered and + * connected devices will just be suspended/resumed. + * In hibernation, core will power collapse and connected devices will + * reset-resume on PM restore. */ - bool host_poweroff_in_pm_suspend; + bool ignore_wakeup_src_in_hostmode; int retries_on_error; u32 gen2_tx_de_emph; u32 gen2_tx_de_emph1; diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 948c8a6f6870..4d5915a1e7ed 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -4446,6 +4446,12 @@ static int dwc3_msm_probe(struct platform_device *pdev) goto put_dwc3; } + if (of_property_read_bool(node, "qcom,ignore-wakeup-src-in-hostmode")) { + dwc->ignore_wakeup_src_in_hostmode = true; + dev_dbg(mdwc->dev, "%s: Allow system suspend irrespective of runtime suspend\n", + __func__); + } + /* * On platforms with SS PHY that do not support ss_phy_irq for wakeup * events, use pwr_event_irq for wakeup events in superspeed mode. @@ -4474,12 +4480,6 @@ static int dwc3_msm_probe(struct platform_device *pdev) mdwc->pm_qos_latency = 0; } - if (of_property_read_bool(node, "qcom,host-poweroff-in-pm-suspend")) { - dwc->host_poweroff_in_pm_suspend = true; - dev_dbg(mdwc->dev, "%s: Core power collapse on host PM suspend\n", - __func__); - } - mutex_init(&mdwc->suspend_resume_mutex); mdwc->ss_redriver_node = of_parse_phandle(node, "ssusb_redriver", 0); @@ -5302,7 +5302,7 @@ static int dwc3_msm_pm_suspend(struct device *dev) * Check if pm_suspend can proceed irrespective of runtimePM state of * host. */ - if (!dwc->host_poweroff_in_pm_suspend || !mdwc->in_host_mode) { + if (!dwc->ignore_wakeup_src_in_hostmode || !mdwc->in_host_mode) { if (!atomic_read(&dwc->in_lpm)) { dev_err(mdwc->dev, "Abort PM suspend!! (USB is outside LPM)\n"); return -EBUSY; @@ -5313,6 +5313,66 @@ static int dwc3_msm_pm_suspend(struct device *dev) return 0; } + ret = dwc3_msm_suspend(mdwc, false); + if (!ret) + atomic_set(&mdwc->pm_suspended, 1); + + return ret; +} + +static int dwc3_msm_pm_resume(struct device *dev) +{ + struct dwc3_msm *mdwc = dev_get_drvdata(dev); + struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); + + dev_dbg(dev, "dwc3-msm PM resume\n"); + dbg_event(0xFF, "PM Res", 0); + + atomic_set(&mdwc->pm_suspended, 0); + + if (!mdwc->in_host_mode) { + /* kick in otg state machine */ + queue_work(mdwc->dwc3_wq, &mdwc->resume_work); + + return 0; + } + + /* Resume dwc to avoid unclocked access by xhci_plat_resume */ + if (pm_runtime_status_suspended(dev)) + pm_runtime_resume(dev); + else + dwc3_msm_resume(mdwc); + + /* kick in otg state machine */ + queue_work(mdwc->dwc3_wq, &mdwc->resume_work); + + return 0; +} + +static int dwc3_msm_pm_freeze(struct device *dev) +{ + int ret = 0; + struct dwc3_msm *mdwc = dev_get_drvdata(dev); + struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); + + dev_dbg(dev, "dwc3-msm PM freeze\n"); + dbg_event(0xFF, "PM Freeze", 0); + + /* + * Check if pm_freeze can proceed irrespective of runtimePM state of + * host. + */ + if (!dwc->ignore_wakeup_src_in_hostmode || !mdwc->in_host_mode) { + if (!atomic_read(&dwc->in_lpm)) { + dev_err(mdwc->dev, "Abort PM freeze!! (USB is outside LPM)\n"); + return -EBUSY; + } + + atomic_set(&mdwc->pm_suspended, 1); + + return 0; + } + /* * PHYs also need to be power collapsed, so call notify_disconnect * before suspend to ensure it. @@ -5333,13 +5393,13 @@ static int dwc3_msm_pm_suspend(struct device *dev) return ret; } -static int dwc3_msm_pm_resume(struct device *dev) +static int dwc3_msm_pm_restore(struct device *dev) { struct dwc3_msm *mdwc = dev_get_drvdata(dev); struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); - dev_dbg(dev, "dwc3-msm PM resume\n"); - dbg_event(0xFF, "PM Res", 0); + dev_dbg(dev, "dwc3-msm PM restore\n"); + dbg_event(0xFF, "PM Restore", 0); atomic_set(&mdwc->pm_suspended, 0); @@ -5352,9 +5412,6 @@ static int dwc3_msm_pm_resume(struct device *dev) /* Resume dwc to avoid unclocked access by xhci_plat_resume */ dwc3_msm_resume(mdwc); - pm_runtime_disable(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); /* Restore PHY flags if hibernated in host mode */ mdwc->hs_phy->flags |= PHY_HOST_MODE; @@ -5408,7 +5465,12 @@ static int dwc3_msm_runtime_resume(struct device *dev) #endif static const struct dev_pm_ops dwc3_msm_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(dwc3_msm_pm_suspend, dwc3_msm_pm_resume) + .suspend = dwc3_msm_pm_suspend, + .resume = dwc3_msm_pm_resume, + .freeze = dwc3_msm_pm_freeze, + .thaw = dwc3_msm_pm_restore, + .poweroff = dwc3_msm_pm_suspend, + .restore = dwc3_msm_pm_restore, SET_RUNTIME_PM_OPS(dwc3_msm_runtime_suspend, dwc3_msm_runtime_resume, dwc3_msm_runtime_idle) }; diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index e0ecabf9e558..8f1463a1ba46 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -115,9 +115,9 @@ int dwc3_host_init(struct dwc3 *dwc) if (dwc->revision <= DWC3_REVISION_300A) props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped"); - if (dwc->host_poweroff_in_pm_suspend) + if (dwc->ignore_wakeup_src_in_hostmode) props[prop_idx++] = - PROPERTY_ENTRY_BOOL("host-poweroff-in-pm-suspend"); + PROPERTY_ENTRY_BOOL("ignore-wakeup-src-in-hostmode"); if (prop_idx) { ret = platform_device_add_properties(xhci, props); diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 83d088d5d457..2acf74dec14a 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -391,6 +391,67 @@ static int xhci_plat_remove(struct platform_device *dev) return 0; } +static int xhci_plat_suspend(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + if (!xhci) + return 0; + + dev_dbg(dev, "xhci-plat PM suspend\n"); + + /* Disable wakeup capability */ + return xhci_suspend(xhci, false); +} + +static int xhci_plat_resume(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + int ret; + + if (!xhci) + return 0; + + dev_dbg(dev, "xhci-plat resume\n"); + + ret = xhci_priv_resume_quirk(hcd); + if (ret) + return ret; + + if (pm_runtime_status_suspended(dev)) + ret = pm_runtime_resume(dev); + else + ret = xhci_resume(xhci, false); + + if (ret) + dev_err(dev, "failed to resume xhci-plat (%d)\n", ret); + + return ret; +} + +static int xhci_plat_restore(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + int ret; + + if (!xhci) + return 0; + + dev_dbg(dev, "xhci-plat PM restore\n"); + + ret = xhci_priv_resume_quirk(hcd); + if (ret) + return ret; + + /* resume from hibernation/power-collapse */ + ret = xhci_resume(xhci, true); + + return ret; +} + static int __maybe_unused xhci_plat_runtime_idle(struct device *dev) { /* @@ -421,28 +482,6 @@ static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev) return xhci_suspend(xhci, true); } -static int xhci_plat_resume(struct device *dev) -{ - struct usb_hcd *hcd = dev_get_drvdata(dev); - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - int ret; - - if (!xhci) - return 0; - - dev_dbg(dev, "xhci-plat resume\n"); - - ret = xhci_priv_resume_quirk(hcd); - if (ret) - return ret; - - ret = pm_runtime_resume(dev); - if (ret) - dev_err(dev, "failed to resume xhci-plat (%d)\n", ret); - - return ret; -} - static int __maybe_unused xhci_plat_runtime_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); @@ -465,8 +504,12 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev) } static const struct dev_pm_ops xhci_plat_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(NULL, xhci_plat_resume) - + .suspend = xhci_plat_suspend, + .resume = xhci_plat_resume, + .freeze = xhci_plat_suspend, + .thaw = xhci_plat_restore, + .poweroff = xhci_plat_suspend, + .restore = xhci_plat_restore, SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend, xhci_plat_runtime_resume, xhci_plat_runtime_idle) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 1c4a25d12e28..97c869c65ec1 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1011,6 +1011,11 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) xhci->shared_hcd->state != HC_STATE_SUSPENDED) return -EINVAL; + /* If XHCI is already suspended , then return */ + if ((!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || + !test_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags))) + return 0; + xhci_dbc_suspend(xhci); /* Clear root port wake on bits if wakeup not allowed. */