}
}
+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;
}
}
- 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);
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)
return 0;
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);