Merge "usb: dwc3-msm: Allow PM suspend in host mode irrespective of runtimePM"
This commit is contained in:
commit
258407248b
5 changed files with 86 additions and 7 deletions
|
@ -64,6 +64,8 @@ Optional properties :
|
||||||
device provides both "USB" and "USB-HOST" events.
|
device provides both "USB" and "USB-HOST" events.
|
||||||
- qcom,pm-qos-latency: This represents max tolerable CPU latency in microsecs,
|
- qcom,pm-qos-latency: This represents max tolerable CPU latency in microsecs,
|
||||||
which is used as a vote by driver to get max performance in perf mode.
|
which is used as a vote by driver to get max performance in perf mode.
|
||||||
|
- qcom,no-wakeup-src-in-hostmode: If present then driver doesn't use wakeup_source APIs
|
||||||
|
in host mode. This allows PM suspend to happen irrespective of runtimePM state of host.
|
||||||
|
|
||||||
Sub nodes:
|
Sub nodes:
|
||||||
- Sub node for "DWC3- USB3 controller".
|
- Sub node for "DWC3- USB3 controller".
|
||||||
|
|
|
@ -58,7 +58,7 @@
|
||||||
|
|
||||||
/* time out to wait for USB cable status notification (in ms)*/
|
/* time out to wait for USB cable status notification (in ms)*/
|
||||||
#define SM_INIT_TIMEOUT 30000
|
#define SM_INIT_TIMEOUT 30000
|
||||||
|
#define DWC3_WAKEUP_SRC_TIMEOUT 5000
|
||||||
/* AHB2PHY register offsets */
|
/* AHB2PHY register offsets */
|
||||||
#define PERIPH_SS_AHB2PHY_TOP_CFG 0x10
|
#define PERIPH_SS_AHB2PHY_TOP_CFG 0x10
|
||||||
|
|
||||||
|
@ -216,6 +216,7 @@ struct dwc3_msm {
|
||||||
struct notifier_block usbdev_nb;
|
struct notifier_block usbdev_nb;
|
||||||
bool hc_died;
|
bool hc_died;
|
||||||
bool xhci_ss_compliance_enable;
|
bool xhci_ss_compliance_enable;
|
||||||
|
bool no_wakeup_src_in_hostmode;
|
||||||
|
|
||||||
struct extcon_dev *extcon_vbus;
|
struct extcon_dev *extcon_vbus;
|
||||||
struct extcon_dev *extcon_id;
|
struct extcon_dev *extcon_id;
|
||||||
|
@ -2350,6 +2351,7 @@ static void dwc3_ext_event_notify(struct dwc3_msm *mdwc)
|
||||||
clear_bit(B_SUSPEND, &mdwc->inputs);
|
clear_bit(B_SUSPEND, &mdwc->inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pm_stay_awake(mdwc->dev);
|
||||||
schedule_delayed_work(&mdwc->sm_work, 0);
|
schedule_delayed_work(&mdwc->sm_work, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2638,6 +2640,7 @@ static int dwc3_msm_id_notifier(struct notifier_block *nb,
|
||||||
if (mdwc->id_state != id) {
|
if (mdwc->id_state != id) {
|
||||||
mdwc->id_state = id;
|
mdwc->id_state = id;
|
||||||
dbg_event(0xFF, "id_state", mdwc->id_state);
|
dbg_event(0xFF, "id_state", mdwc->id_state);
|
||||||
|
pm_stay_awake(mdwc->dev);
|
||||||
queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
|
queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2700,6 +2703,7 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb,
|
||||||
mdwc->vbus_active = event;
|
mdwc->vbus_active = event;
|
||||||
if (dwc->is_drd && !mdwc->in_restart) {
|
if (dwc->is_drd && !mdwc->in_restart) {
|
||||||
dbg_event(0xFF, "Q RW (vbus)", mdwc->vbus_active);
|
dbg_event(0xFF, "Q RW (vbus)", mdwc->vbus_active);
|
||||||
|
pm_stay_awake(mdwc->dev);
|
||||||
queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
|
queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
|
@ -3099,6 +3103,11 @@ static int dwc3_msm_probe(struct platform_device *pdev)
|
||||||
mdwc->disable_host_mode_pm = of_property_read_bool(node,
|
mdwc->disable_host_mode_pm = of_property_read_bool(node,
|
||||||
"qcom,disable-host-mode-pm");
|
"qcom,disable-host-mode-pm");
|
||||||
|
|
||||||
|
mdwc->no_wakeup_src_in_hostmode = of_property_read_bool(node,
|
||||||
|
"qcom,no-wakeup-src-in-hostmode");
|
||||||
|
if (mdwc->no_wakeup_src_in_hostmode)
|
||||||
|
dev_dbg(&pdev->dev, "dwc3 host not using wakeup source\n");
|
||||||
|
|
||||||
dwc3_set_notifier(&dwc3_msm_notify_event);
|
dwc3_set_notifier(&dwc3_msm_notify_event);
|
||||||
|
|
||||||
/* Assumes dwc3 is the first DT child of dwc3-msm */
|
/* Assumes dwc3 is the first DT child of dwc3-msm */
|
||||||
|
@ -3892,12 +3901,14 @@ static void dwc3_otg_sm_work(struct work_struct *w)
|
||||||
mdwc->otg_state = OTG_STATE_A_IDLE;
|
mdwc->otg_state = OTG_STATE_A_IDLE;
|
||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
|
pm_wakeup_event(mdwc->dev, DWC3_WAKEUP_SRC_TIMEOUT);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OTG_STATE_A_HOST:
|
case OTG_STATE_A_HOST:
|
||||||
if (test_bit(ID, &mdwc->inputs) || mdwc->hc_died) {
|
if (test_bit(ID, &mdwc->inputs) || mdwc->hc_died) {
|
||||||
dev_dbg(mdwc->dev, "id || hc_died\n");
|
dbg_event(0xFF, "id || hc_died", 0);
|
||||||
|
dev_dbg(mdwc->dev, "%s state id || hc_died\n", state);
|
||||||
dwc3_otg_start_host(mdwc, 0);
|
dwc3_otg_start_host(mdwc, 0);
|
||||||
mdwc->otg_state = OTG_STATE_B_IDLE;
|
mdwc->otg_state = OTG_STATE_B_IDLE;
|
||||||
mdwc->vbus_retry_count = 0;
|
mdwc->vbus_retry_count = 0;
|
||||||
|
@ -3908,6 +3919,7 @@ static void dwc3_otg_sm_work(struct work_struct *w)
|
||||||
dbg_event(0xFF, "XHCIResume", 0);
|
dbg_event(0xFF, "XHCIResume", 0);
|
||||||
if (dwc)
|
if (dwc)
|
||||||
pm_runtime_resume(&dwc->xhci->dev);
|
pm_runtime_resume(&dwc->xhci->dev);
|
||||||
|
pm_wakeup_event(mdwc->dev, DWC3_WAKEUP_SRC_TIMEOUT);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -3923,6 +3935,34 @@ ret:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dwc3_msm_pm_prepare(struct device *dev)
|
||||||
|
{
|
||||||
|
struct dwc3_msm *mdwc = dev_get_drvdata(dev);
|
||||||
|
struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
|
||||||
|
struct usb_hcd *hcd;
|
||||||
|
struct xhci_hcd *xhci;
|
||||||
|
|
||||||
|
dev_dbg(dev, "dwc3-msm PM prepare,lpm:%u\n", atomic_read(&dwc->in_lpm));
|
||||||
|
dbg_event(0xFF, "PM Prep", 0);
|
||||||
|
if (!mdwc->in_host_mode || !mdwc->no_wakeup_src_in_hostmode)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
hcd = dev_get_drvdata(&dwc->xhci->dev);
|
||||||
|
xhci = hcd_to_xhci(hcd);
|
||||||
|
flush_delayed_work(&mdwc->sm_work);
|
||||||
|
|
||||||
|
/* If in lpm then prevent usb core to runtime_resume from pm_suspend */
|
||||||
|
if (atomic_read(&dwc->in_lpm)) {
|
||||||
|
hcd_to_bus(hcd)->skip_resume = true;
|
||||||
|
hcd_to_bus(xhci->shared_hcd)->skip_resume = true;
|
||||||
|
} else {
|
||||||
|
hcd_to_bus(hcd)->skip_resume = false;
|
||||||
|
hcd_to_bus(xhci->shared_hcd)->skip_resume = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
static int dwc3_msm_pm_suspend(struct device *dev)
|
static int dwc3_msm_pm_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
|
@ -3934,7 +3974,7 @@ static int dwc3_msm_pm_suspend(struct device *dev)
|
||||||
dbg_event(0xFF, "PM Sus", 0);
|
dbg_event(0xFF, "PM Sus", 0);
|
||||||
|
|
||||||
flush_workqueue(mdwc->dwc3_wq);
|
flush_workqueue(mdwc->dwc3_wq);
|
||||||
if (!atomic_read(&dwc->in_lpm)) {
|
if (!atomic_read(&dwc->in_lpm) && !mdwc->no_wakeup_src_in_hostmode) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
@ -3958,8 +3998,13 @@ static int dwc3_msm_pm_resume(struct device *dev)
|
||||||
flush_workqueue(mdwc->dwc3_wq);
|
flush_workqueue(mdwc->dwc3_wq);
|
||||||
atomic_set(&mdwc->pm_suspended, 0);
|
atomic_set(&mdwc->pm_suspended, 0);
|
||||||
|
|
||||||
|
/* Resume h/w in host mode as it may not be runtime suspended */
|
||||||
|
if (mdwc->no_wakeup_src_in_hostmode && !test_bit(ID, &mdwc->inputs))
|
||||||
|
dwc3_msm_resume(mdwc);
|
||||||
|
|
||||||
/* kick in otg state machine */
|
/* kick in otg state machine */
|
||||||
queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
|
if (mdwc->vbus_active || !mdwc->id_state)
|
||||||
|
queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -3996,6 +4041,7 @@ 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 = {
|
||||||
|
.prepare = dwc3_msm_pm_prepare,
|
||||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_msm_pm_suspend, dwc3_msm_pm_resume)
|
SET_SYSTEM_SLEEP_PM_OPS(dwc3_msm_pm_suspend, dwc3_msm_pm_resume)
|
||||||
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)
|
||||||
|
|
|
@ -325,6 +325,34 @@ static int xhci_plat_remove(struct platform_device *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
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");
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (!xhci)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
dev_dbg(dev, "xhci-plat PM resume\n");
|
||||||
|
|
||||||
|
return xhci_resume(xhci, false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static int xhci_plat_runtime_idle(struct device *dev)
|
static int xhci_plat_runtime_idle(struct device *dev)
|
||||||
{
|
{
|
||||||
|
@ -373,7 +401,7 @@ static int 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, NULL)
|
SET_SYSTEM_SLEEP_PM_OPS(xhci_plat_suspend, xhci_plat_resume)
|
||||||
SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend, xhci_plat_runtime_resume,
|
SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend, xhci_plat_runtime_resume,
|
||||||
xhci_plat_runtime_idle)
|
xhci_plat_runtime_idle)
|
||||||
};
|
};
|
||||||
|
|
|
@ -918,7 +918,7 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
|
||||||
struct usb_hcd *hcd = xhci_to_hcd(xhci);
|
struct usb_hcd *hcd = xhci_to_hcd(xhci);
|
||||||
u32 command;
|
u32 command;
|
||||||
|
|
||||||
if (!hcd->state)
|
if (!hcd->state || xhci->suspended)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (hcd->state != HC_STATE_SUSPENDED ||
|
if (hcd->state != HC_STATE_SUSPENDED ||
|
||||||
|
@ -988,6 +988,7 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
|
||||||
/* step 5: remove core well power */
|
/* step 5: remove core well power */
|
||||||
/* synchronize irq when using MSI-X */
|
/* synchronize irq when using MSI-X */
|
||||||
xhci_msix_sync_irqs(xhci);
|
xhci_msix_sync_irqs(xhci);
|
||||||
|
xhci->suspended = true;
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -1007,7 +1008,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
bool comp_timer_running = false;
|
bool comp_timer_running = false;
|
||||||
|
|
||||||
if (!hcd->state)
|
if (!hcd->state || !xhci->suspended)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Wait a bit if either of the roothubs need to settle from the
|
/* Wait a bit if either of the roothubs need to settle from the
|
||||||
|
@ -1141,6 +1142,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
|
||||||
|
|
||||||
/* Re-enable port polling. */
|
/* Re-enable port polling. */
|
||||||
xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
|
xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
|
||||||
|
xhci->suspended = false;
|
||||||
set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
|
set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
|
||||||
usb_hcd_poll_rh_status(xhci->shared_hcd);
|
usb_hcd_poll_rh_status(xhci->shared_hcd);
|
||||||
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||||
|
|
|
@ -1667,6 +1667,7 @@ struct xhci_hcd {
|
||||||
/* Compliance Mode Recovery Data */
|
/* Compliance Mode Recovery Data */
|
||||||
struct timer_list comp_mode_recovery_timer;
|
struct timer_list comp_mode_recovery_timer;
|
||||||
u32 port_status_u0;
|
u32 port_status_u0;
|
||||||
|
bool suspended;
|
||||||
/* Compliance Mode Timer Triggered every 2 seconds */
|
/* Compliance Mode Timer Triggered every 2 seconds */
|
||||||
#define COMP_MODE_RCVRY_MSECS 2000
|
#define COMP_MODE_RCVRY_MSECS 2000
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue