Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 74b8a97e authored by Ajay Agarwal's avatar Ajay Agarwal Committed by Gerrit - the friendly Code Review server
Browse files

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: default avatarAjay Agarwal <ajaya@codeaurora.org>
Signed-off-by: default avatarHarsh Agarwal <harshq@codeaurora.org>
parent 28255001
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -2005,7 +2005,8 @@ static int dwc3_resume(struct device *dev)
		 * state as active to reflect actual state of device which
		 * state as active to reflect actual state of device which
		 * is now out of LPM. This allows runtime_suspend later.
		 * 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);
			pm_runtime_resume(dev);


		return 0;
		return 0;
+6 −4
Original line number Original line Diff line number Diff line
@@ -1387,11 +1387,13 @@ struct dwc3 {
	/* Indicate if software connect was issued by the usb_gadget_driver */
	/* Indicate if software connect was issued by the usb_gadget_driver */
	unsigned int		softconnect:1;
	unsigned int		softconnect:1;
	/*
	/*
	 * If true, PM suspend allowed irrespective of host runtimePM state
	 * If true, PM suspend/freeze allowed irrespective of host runtimePM
	 * and core will power collapse. This also leads to reset-resume of
	 * state. In PM suspend/resume case, core will stay powered and
	 * connected devices on PM resume.
	 * 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;
	int			retries_on_error;
	u32			gen2_tx_de_emph;
	u32			gen2_tx_de_emph;
	u32			gen2_tx_de_emph1;
	u32			gen2_tx_de_emph1;
+76 −14
Original line number Original line Diff line number Diff line
@@ -4446,6 +4446,12 @@ static int dwc3_msm_probe(struct platform_device *pdev)
		goto put_dwc3;
		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
	 * 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.
	 * 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;
		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);
	mutex_init(&mdwc->suspend_resume_mutex);


	mdwc->ss_redriver_node = of_parse_phandle(node, "ssusb_redriver", 0);
	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
	 * Check if pm_suspend can proceed irrespective of runtimePM state of
	 * host.
	 * 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)) {
		if (!atomic_read(&dwc->in_lpm)) {
			dev_err(mdwc->dev, "Abort PM suspend!! (USB is outside LPM)\n");
			dev_err(mdwc->dev, "Abort PM suspend!! (USB is outside LPM)\n");
			return -EBUSY;
			return -EBUSY;
@@ -5313,6 +5313,66 @@ static int dwc3_msm_pm_suspend(struct device *dev)
		return 0;
		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
	 * PHYs also need to be power collapsed, so call notify_disconnect
	 * before suspend to ensure it.
	 * before suspend to ensure it.
@@ -5333,13 +5393,13 @@ static int dwc3_msm_pm_suspend(struct device *dev)
	return ret;
	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_msm *mdwc = dev_get_drvdata(dev);
	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);


	dev_dbg(dev, "dwc3-msm PM resume\n");
	dev_dbg(dev, "dwc3-msm PM restore\n");
	dbg_event(0xFF, "PM Res", 0);
	dbg_event(0xFF, "PM Restore", 0);


	atomic_set(&mdwc->pm_suspended, 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 */
	/* Resume dwc to avoid unclocked access by xhci_plat_resume */
	dwc3_msm_resume(mdwc);
	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 */
	/* Restore PHY flags if hibernated in host mode */
	mdwc->hs_phy->flags |= PHY_HOST_MODE;
	mdwc->hs_phy->flags |= PHY_HOST_MODE;
@@ -5408,7 +5465,12 @@ static int dwc3_msm_runtime_resume(struct device *dev)
#endif
#endif


static const struct dev_pm_ops dwc3_msm_dev_pm_ops = {
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,
	SET_RUNTIME_PM_OPS(dwc3_msm_runtime_suspend, dwc3_msm_runtime_resume,
				dwc3_msm_runtime_idle)
				dwc3_msm_runtime_idle)
};
};
+2 −2
Original line number Original line Diff line number Diff line
@@ -115,9 +115,9 @@ int dwc3_host_init(struct dwc3 *dwc)
	if (dwc->revision <= DWC3_REVISION_300A)
	if (dwc->revision <= DWC3_REVISION_300A)
		props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped");
		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++] =
		props[prop_idx++] =
			PROPERTY_ENTRY_BOOL("host-poweroff-in-pm-suspend");
			PROPERTY_ENTRY_BOOL("ignore-wakeup-src-in-hostmode");


	if (prop_idx) {
	if (prop_idx) {
		ret = platform_device_add_properties(xhci, props);
		ret = platform_device_add_properties(xhci, props);
+66 −23
Original line number Original line Diff line number Diff line
@@ -391,24 +391,7 @@ static int xhci_plat_remove(struct platform_device *dev)
	return 0;
	return 0;
}
}


static int __maybe_unused xhci_plat_runtime_idle(struct device *dev)
static int xhci_plat_suspend(struct device *dev)
{
	/*
	 * When pm_runtime_put_autosuspend() is called on this device,
	 * after this idle callback returns the PM core will schedule the
	 * autosuspend if there is any remaining time until expiry. However,
	 * when reaching this point because the child_count becomes 0, the
	 * core does not honor autosuspend in that case and results in
	 * idle/suspend happening immediately. In order to have a delay
	 * before suspend we have to call pm_runtime_autosuspend() manually.
	 */

	pm_runtime_mark_last_busy(dev);
	pm_runtime_autosuspend(dev);
	return -EBUSY;
}

static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev)
{
{
	struct usb_hcd  *hcd = dev_get_drvdata(dev);
	struct usb_hcd  *hcd = dev_get_drvdata(dev);
	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
@@ -416,9 +399,10 @@ static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev)
	if (!xhci)
	if (!xhci)
		return 0;
		return 0;


	dev_dbg(dev, "xhci-plat runtime suspend\n");
	dev_dbg(dev, "xhci-plat PM suspend\n");


	return xhci_suspend(xhci, true);
	/* Disable wakeup capability */
	return xhci_suspend(xhci, false);
}
}


static int xhci_plat_resume(struct device *dev)
static int xhci_plat_resume(struct device *dev)
@@ -436,13 +420,68 @@ static int xhci_plat_resume(struct device *dev)
	if (ret)
	if (ret)
		return ret;
		return ret;


	if (pm_runtime_status_suspended(dev))
		ret = pm_runtime_resume(dev);
		ret = pm_runtime_resume(dev);
	else
		ret = xhci_resume(xhci, false);

	if (ret)
	if (ret)
		dev_err(dev, "failed to resume xhci-plat (%d)\n", ret);
		dev_err(dev, "failed to resume xhci-plat (%d)\n", ret);


	return 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)
{
	/*
	 * When pm_runtime_put_autosuspend() is called on this device,
	 * after this idle callback returns the PM core will schedule the
	 * autosuspend if there is any remaining time until expiry. However,
	 * when reaching this point because the child_count becomes 0, the
	 * core does not honor autosuspend in that case and results in
	 * idle/suspend happening immediately. In order to have a delay
	 * before suspend we have to call pm_runtime_autosuspend() manually.
	 */

	pm_runtime_mark_last_busy(dev);
	pm_runtime_autosuspend(dev);
	return -EBUSY;
}

static int __maybe_unused xhci_plat_runtime_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 runtime suspend\n");

	return xhci_suspend(xhci, true);
}

static int __maybe_unused xhci_plat_runtime_resume(struct device *dev)
static int __maybe_unused xhci_plat_runtime_resume(struct device *dev)
{
{
	struct usb_hcd  *hcd = dev_get_drvdata(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 = {
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,
	SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend,
			   xhci_plat_runtime_resume,
			   xhci_plat_runtime_resume,
			   xhci_plat_runtime_idle)
			   xhci_plat_runtime_idle)
Loading