diff options
author | Li Jun <jun.li@freescale.com> | 2015-01-15 21:00:34 +0800 |
---|---|---|
committer | Jason Liu <jason.hui.liu@nxp.com> | 2019-02-12 10:21:33 +0800 |
commit | d25ca997938184ddee35d78ddbb044513248aff3 (patch) | |
tree | 801815d6e6af6536133e3e1fef69606f05b678e0 /drivers/usb/chipidea/core.c | |
parent | 6c737a401471bf5503da119bfbd3f7c08e001b91 (diff) |
MLK-10102-8 usb: chipidea: support role change after power lost
This patch is to complete support usb resume from power lost in non-otg
fsm mode:
- Re-init usb phy.
- Support role changes during system sleep with power lost.
Acked-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Li Jun <b47624@freescale.com>
Diffstat (limited to 'drivers/usb/chipidea/core.c')
-rw-r--r-- | drivers/usb/chipidea/core.c | 79 |
1 files changed, 60 insertions, 19 deletions
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 0542f548d1ae..3644f868acc8 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -891,6 +891,50 @@ static const struct attribute_group ci_attr_group = { .attrs = ci_attrs, }; +static enum ci_role ci_get_role(struct ci_hdrc *ci) +{ + if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { + if (ci->is_otg) { + hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); + return ci_otg_role(ci); + } else { + /* + * If the controller is not OTG capable, but support + * role switch, the defalt role is gadget, and the + * user can switch it through debugfs. + */ + return CI_ROLE_GADGET; + } + } else { + return ci->roles[CI_ROLE_HOST] + ? CI_ROLE_HOST + : CI_ROLE_GADGET; + } +} + +static void ci_start_new_role(struct ci_hdrc *ci) +{ + enum ci_role role = ci_get_role(ci); + + if (ci->role != role) + ci_handle_id_switch(ci); + + if (role == CI_ROLE_GADGET) + ci_handle_vbus_connected(ci); +} + +static void ci_power_lost_work(struct work_struct *work) +{ + struct ci_hdrc *ci = container_of(work, struct ci_hdrc, + power_lost_work); + + pm_runtime_get_sync(ci->dev); + if (!ci_otg_is_fsm_mode(ci)) + ci_start_new_role(ci); + pm_runtime_put_sync(ci->dev); + enable_irq(ci->irq); +} + static int ci_hdrc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1012,25 +1056,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) } } - if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { - if (ci->is_otg) { - ci->role = ci_otg_role(ci); - /* Enable ID change irq */ - hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); - } else { - /* - * If the controller is not OTG capable, but support - * role switch, the defalt role is gadget, and the - * user can switch it through debugfs. - */ - ci->role = CI_ROLE_GADGET; - } - } else { - ci->role = ci->roles[CI_ROLE_HOST] - ? CI_ROLE_HOST - : CI_ROLE_GADGET; - } - + ci->role = ci_get_role(ci); /* only update vbus status for peripheral */ if (ci->role == CI_ROLE_GADGET) ci_handle_vbus_connected(ci); @@ -1065,6 +1091,10 @@ static int ci_hdrc_probe(struct platform_device *pdev) ci_hdrc_otg_fsm_start(ci); device_set_wakeup_capable(&pdev->dev, true); + + /* Init workqueue for controller power lost handling */ + INIT_WORK(&ci->power_lost_work, ci_power_lost_work); + ret = dbg_create_files(ci); if (ret) goto stop; @@ -1249,10 +1279,21 @@ static int ci_resume(struct device *dev) if (ret) return ret; + if (power_lost) { + /* shutdown and re-init for phy */ + ci_usb_phy_exit(ci); + ci_usb_phy_init(ci); + } + /* Extra routine per role after system resume */ if (ci->role != CI_ROLE_END && ci_role(ci)->resume) ci_role(ci)->resume(ci, power_lost); + if (power_lost) { + disable_irq_nosync(ci->irq); + schedule_work(&ci->power_lost_work); + } + if (ci->supports_runtime_pm) { pm_runtime_disable(dev); pm_runtime_set_active(dev); |