summaryrefslogtreecommitdiff
path: root/drivers/usb/chipidea/core.c
diff options
context:
space:
mode:
authorLi Jun <b47624@freescale.com>2015-02-11 04:45:03 (GMT)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-03-18 15:19:12 (GMT)
commit961ea496facda611eeb153d8133a4d40055e56ca (patch)
treeda1267b53586d89f2510dcdd27b636315ac7eb6e /drivers/usb/chipidea/core.c
parent6594591741883e004aaba105951337878698b054 (diff)
downloadlinux-961ea496facda611eeb153d8133a4d40055e56ca.tar.xz
usb: chipidea: support runtime power management for otg fsm mode
This patch adds runtime power management support for otg fsm mode, since A-device in a_idle state cannot detect data pulse irq after suspended, here enable wakeup by connection before suspend to make it can be resumed by DP; and handle wakeup from that state like SRP. Signed-off-by: Li Jun <jun.li@freescale.com> Signed-off-by: Peter Chen <peter.chen@freescale.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/chipidea/core.c')
-rw-r--r--drivers/usb/chipidea/core.c43
1 files changed, 39 insertions, 4 deletions
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 4b22d7c..74fea4f 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -798,11 +798,11 @@ static int ci_hdrc_probe(struct platform_device *pdev)
: CI_ROLE_GADGET;
}
- /* only update vbus status for peripheral */
- if (ci->role == CI_ROLE_GADGET)
- ci_handle_vbus_change(ci);
-
if (!ci_otg_is_fsm_mode(ci)) {
+ /* only update vbus status for peripheral */
+ if (ci->role == CI_ROLE_GADGET)
+ ci_handle_vbus_change(ci);
+
ret = ci_role_start(ci, ci->role);
if (ret) {
dev_err(dev, "can't start %s role\n",
@@ -861,6 +861,33 @@ static int ci_hdrc_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
+/* Prepare wakeup by SRP before suspend */
+static void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci)
+{
+ if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) &&
+ !hw_read_otgsc(ci, OTGSC_ID)) {
+ hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP,
+ PORTSC_PP);
+ hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_WKCN,
+ PORTSC_WKCN);
+ }
+}
+
+/* Handle SRP when wakeup by data pulse */
+static void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci)
+{
+ if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) &&
+ (ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) {
+ if (!hw_read_otgsc(ci, OTGSC_ID)) {
+ ci->fsm.a_srp_det = 1;
+ ci->fsm.a_bus_drop = 0;
+ } else {
+ ci->fsm.id = 1;
+ }
+ ci_otg_queue_work(ci);
+ }
+}
+
static void ci_controller_suspend(struct ci_hdrc *ci)
{
disable_irq(ci->irq);
@@ -894,6 +921,8 @@ static int ci_controller_resume(struct device *dev)
pm_runtime_mark_last_busy(ci->dev);
pm_runtime_put_autosuspend(ci->dev);
enable_irq(ci->irq);
+ if (ci_otg_is_fsm_mode(ci))
+ ci_otg_fsm_wakeup_by_srp(ci);
}
return 0;
@@ -921,6 +950,9 @@ static int ci_suspend(struct device *dev)
}
if (device_may_wakeup(dev)) {
+ if (ci_otg_is_fsm_mode(ci))
+ ci_otg_fsm_suspend_for_srp(ci);
+
usb_phy_set_wakeup(ci->usb_phy, true);
enable_irq_wake(ci->irq);
}
@@ -963,6 +995,9 @@ static int ci_runtime_suspend(struct device *dev)
return 0;
}
+ if (ci_otg_is_fsm_mode(ci))
+ ci_otg_fsm_suspend_for_srp(ci);
+
usb_phy_set_wakeup(ci->usb_phy, true);
ci_controller_suspend(ci);