From: Li Jun Date: Fri, 20 Jun 2014 07:52:14 +0000 (+0800) Subject: MLK-11340-41 usb: chipidea: add vbus glitch handling X-Git-Tag: C0P2-H0.0--20200415~4693 X-Git-Url: https://git.somdevices.com/?a=commitdiff_plain;h=d417e9e1b5a1c9fc96d27c95526776bee631bb2f;p=linux.git MLK-11340-41 usb: chipidea: add vbus glitch handling 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 Signed-off-by: Li Jun (cherry picked from commit 827f2fe71e6222882930db7e89460087cb3bce5b) (cherry picked from commit 4f9cefe4cf4bb093c8c1766409320dc86dd9a596) --- diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index b125a653c497..081a501f4105 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -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; diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 2af01eee106d..3c7831db95c3 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -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); diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index 2896df089a1d..d8285ccce1c6 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -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; diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index 6a80441ceddf..294b3c264206 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -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);