usb: host: Modify PM handling for USB interconnect platforms

Currently the PM handling power collapses the core and issues
reset-resume for all the connected devices, both during system
suspend-resume and hibernation-restore scenarios. But certain USB
interconnect platforms want the connected device to not undergo
reset-resume on system resume.
Modify the operations to keep the core powered up, PHY resources
ON and xhci_resume with hibernated flag as 'false' so that
suspend-resume of the connected device is done on resume from
system suspend. Keep the hibernation operation as is.
Also change the DT flag to 'qcom,ignore-wakeup-src-in-hostmode'.

Change-Id: I661494595a3e270890174e0952a67985db58706d
Signed-off-by: Ajay Agarwal <ajaya@codeaurora.org>
Signed-off-by: Harsh Agarwal <harshq@codeaurora.org>
This commit is contained in:
Ajay Agarwal 2020-11-27 12:30:21 +05:30 • committed by Gerrit - the friendly Code Review server
commit 74b8a97e90
6 changed files with 157 additions and 44 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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)
};

View file

@ -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);

View file

@ -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)

View file

@ -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. */