MLK-10102-8 usb: chipidea: support role change after power lost
authorLi Jun <jun.li@freescale.com>
Thu, 15 Jan 2015 13:00:34 +0000 (21:00 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 19:46:59 +0000 (14:46 -0500)
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>
(cherry picked from commit b15794487c32a6c9317f12b6bca5d2d752c9d76c)
(cherry picked from commit 3e5d97cfd2f3b350dd5b5012cf7af73ce11531ac)

drivers/usb/chipidea/ci.h
drivers/usb/chipidea/core.c

index 9e14d75..b125a65 100644 (file)
@@ -263,6 +263,7 @@ struct ci_hdrc {
        u32                             pm_configured_flag;
        u32                             pm_portsc;
        u32                             pm_usbmode;
+       struct work_struct              power_lost_work;
 };
 
 static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci)
index d907dd7..284830d 100644 (file)
@@ -859,6 +859,50 @@ static void ci_get_otg_capable(struct ci_hdrc *ci)
        }
 }
 
+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;
@@ -963,25 +1007,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);
@@ -1018,6 +1044,9 @@ static int ci_hdrc_probe(struct platform_device *pdev)
 
        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;
@@ -1181,10 +1210,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);