diff options
| author | Li Jun <b47624@freescale.com> | 2014-06-20 15:52:14 +0800 |
|---|---|---|
| committer | Li Jun <jun.li@freescale.com> | 2015-01-28 16:42:07 +0800 |
| commit | 5586becd523d8b9b2315863f36cc72ec52e5c269 (patch) | |
| tree | f6fa9e60569168fa7824bf8bea32cf1af86e9533 | |
| parent | 11b7dbb125f6cbe4f07238bc15abe1f1d1b9b1c5 (diff) | |
ENGR00320099 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 <peter.chen@freescale.com>
Signed-off-by: Li Jun <b47624@freescale.com>
(cherry picked from commit 827f2fe71e6222882930db7e89460087cb3bce5b)
| -rw-r--r-- | drivers/usb/chipidea/ci.h | 2 | ||||
| -rw-r--r-- | drivers/usb/chipidea/core.c | 2 | ||||
| -rw-r--r-- | drivers/usb/chipidea/otg.c | 59 | ||||
| -rw-r--r-- | drivers/usb/chipidea/otg_fsm.c | 9 |
4 files changed, 63 insertions, 9 deletions
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index c20edf83aa0f..8d2b74ad36cb 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -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; diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index bcc111a5dfe4..db558402add9 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -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); diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index f0b260428bc9..7b9e417060e8 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -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; diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index 7ae4d0269d2d..34a4f3c53a61 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -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); |
