]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
ENGR00320099 usb: chipidea: add vbus glitch handling
authorLi Jun <b47624@freescale.com>
Fri, 20 Jun 2014 07:52:14 +0000 (15:52 +0800)
committerLi Jun <jun.li@freescale.com>
Wed, 28 Jan 2015 08:42:07 +0000 (16:42 +0800)
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)

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

index c20edf83aa0f70d32cc302590bfdeb8965ef48bb..8d2b74ad36cb88fac72c0e6d1249eda630be4a08 100644 (file)
@@ -194,6 +194,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
@@ -243,6 +244,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 bcc111a5dfe4104098667187bafb54b244e4feae..db558402add987172cba11028201bae0b350ed08 100644 (file)
@@ -578,7 +578,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 f0b260428bc9c195f6571c7e23434f543a9ffa4b..7b9e417060e800b43dff407b9fa0097804e95f7f 100644 (file)
@@ -57,6 +57,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)
 {
        /*
@@ -67,7 +88,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);
 }
 
@@ -104,6 +125,33 @@ void ci_handle_id_switch(struct ci_hdrc *ci)
                ci_role_start(ci, role);
        }
 }
+
+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
@@ -112,6 +160,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 7ae4d0269d2dc5f222a606ee1cff43cd63065583..34a4f3c53a6107f6fa768fa8f89f61ad6d0c0845 100644 (file)
@@ -807,13 +807,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);