MLK-11340-41 usb: chipidea: add vbus glitch handling
authorLi Jun <b47624@freescale.com>
Fri, 20 Jun 2014 07:52:14 +0000 (15:52 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 19:47:03 +0000 (14:47 -0500)
We add vbus glitch handling for both BSV rise and drop interruptes.
If it is a vbus glitch (higher than BSV but cannot reach AVV), ignore it.

Signed-off-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Li Jun <b47624@freescale.com>
(cherry picked from commit 827f2fe71e6222882930db7e89460087cb3bce5b)
(cherry picked from commit 4f9cefe4cf4bb093c8c1766409320dc86dd9a596)

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

index b125a65..081a501 100644 (file)
@@ -197,6 +197,7 @@ struct hw_bank {
  * @debugfs: root dentry for this controller in debugfs
  * @id_event: indicates there is an id event, and handled at ci_otg_work
  * @b_sess_valid_event: indicates there is a vbus event, and handled
+ * @vbus_glitch_check_event: check if vbus change is a glitch
  * at ci_otg_work
  * @imx28_write_fix: Freescale imx28 needs swp instruction for writing
  * @supports_runtime_pm: if runtime pm is supported
@@ -247,6 +248,7 @@ struct ci_hdrc {
        struct dentry                   *debugfs;
        bool                            id_event;
        bool                            b_sess_valid_event;
+       bool                            vbus_glitch_check_event;
        bool                            imx28_write_fix;
        bool                            supports_runtime_pm;
        bool                            in_lpm;
index 2af01ee..3c7831d 100644 (file)
@@ -555,7 +555,7 @@ static irqreturn_t ci_irq(int irq, void *data)
         * and disconnection events.
         */
        if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
-               ci->b_sess_valid_event = true;
+               ci->vbus_glitch_check_event = true;
                /* Clear BSV irq */
                hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS);
                ci_otg_queue_work(ci);
index 2896df0..d8285cc 100644 (file)
@@ -130,6 +130,27 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci)
        return role;
 }
 
+#define CI_VBUS_CONNECT_TIMEOUT_MS 500
+static int ci_is_vbus_glitch(struct ci_hdrc *ci)
+{
+       /*
+        * Handling vbus glitch
+        *
+        * We only need to consider glitch for without usb connection,
+        * With usb connection, we consider it as real disconnection.
+        *
+        * If the vbus can't higher than AVV in timeout value, we think
+        * it is a vbus glitch
+        */
+       if (hw_wait_reg(ci, OP_OTGSC, OTGSC_AVV, OTGSC_AVV,
+                       CI_VBUS_CONNECT_TIMEOUT_MS)) {
+               dev_warn(ci->dev, "there is a vbus glitch\n");
+               return 1;
+       }
+
+       return 0;
+}
+
 void ci_handle_vbus_connected(struct ci_hdrc *ci)
 {
        /*
@@ -140,7 +161,7 @@ void ci_handle_vbus_connected(struct ci_hdrc *ci)
        if (!ci->is_otg)
                return;
 
-       if (hw_read_otgsc(ci, OTGSC_BSV))
+       if (hw_read_otgsc(ci, OTGSC_BSV) && !ci_is_vbus_glitch(ci))
                usb_gadget_vbus_connect(&ci->gadget);
 }
 
@@ -213,6 +234,33 @@ void ci_handle_id_switch(struct ci_hdrc *ci)
                        ci_handle_vbus_change(ci);
        }
 }
+
+static void ci_handle_vbus_glitch(struct ci_hdrc *ci)
+{
+       bool valid_vbus_change = false;
+
+       if (hw_read_otgsc(ci, OTGSC_BSV)) {
+               if (!ci_is_vbus_glitch(ci)) {
+                       if (ci_otg_is_fsm_mode(ci)) {
+                               ci->fsm.b_sess_vld = 1;
+                               ci->fsm.b_ssend_srp = 0;
+                               otg_del_timer(&ci->fsm, B_SSEND_SRP);
+                               otg_del_timer(&ci->fsm, B_SRP_FAIL);
+                       }
+                       valid_vbus_change = true;
+               }
+       } else {
+               if (ci->vbus_active || (ci_otg_is_fsm_mode(ci) &&
+                                               ci->fsm.b_sess_vld))
+                       valid_vbus_change = true;
+       }
+
+       if (valid_vbus_change) {
+               ci->b_sess_valid_event = true;
+               ci_otg_queue_work(ci);
+       }
+}
+
 /**
  * ci_otg_work - perform otg (vbus/id) event handle
  * @work: work struct
@@ -221,6 +269,15 @@ static void ci_otg_work(struct work_struct *work)
 {
        struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work);
 
+       if (ci->vbus_glitch_check_event) {
+               ci->vbus_glitch_check_event = false;
+               pm_runtime_get_sync(ci->dev);
+               ci_handle_vbus_glitch(ci);
+               pm_runtime_put_sync(ci->dev);
+               enable_irq(ci->irq);
+               return;
+       }
+
        if (ci_otg_is_fsm_mode(ci) && !ci_otg_fsm_work(ci)) {
                enable_irq(ci->irq);
                return;
index 6a80441..294b3c2 100644 (file)
@@ -786,13 +786,8 @@ irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
                        }
                } else if (otg_int_src & OTGSC_BSVIS) {
                        hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS);
-                       ci->b_sess_valid_event = true;
-                       if (otgsc & OTGSC_BSV) {
-                               fsm->b_sess_vld = 1;
-                               ci_otg_del_timer(ci, B_SSEND_SRP);
-                               ci_otg_del_timer(ci, B_SRP_FAIL);
-                               fsm->b_ssend_srp = 0;
-                       } else {
+                       ci->vbus_glitch_check_event = true;
+                       if (!(otgsc & OTGSC_BSV) && fsm->b_sess_vld) {
                                fsm->b_sess_vld = 0;
                                if (fsm->id)
                                        ci_otg_add_timer(ci, B_SSEND_SRP);