diff options
author | Nicolas Boichat <drinkcat@chromium.org> | 2020-02-04 16:05:16 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-02-04 20:19:47 +0000 |
commit | 0cfd0ba9b84621a42ae24ca88a4df17bd82da2c1 (patch) | |
tree | f954c997b2cb3630e166f125191c08d69eec28a4 /chip/stm32/usb.c | |
parent | b149d72f08edb528914d17d36435500cf497e792 (diff) | |
download | chrome-ec-0cfd0ba9b84621a42ae24ca88a4df17bd82da2c1.tar.gz |
chip/stm32/usb: Detect SOF when resume state is not expected
When the resume interrupt handler is called, but the state is not
the expected 0 or 1, let's detect if we receive any SOF from the
host. If we do, this means that the link is active and that we
shouldn't go back to sleep (which would put us in an infinite
loop).
BRANCH=kukui
BUG=b:144808697
TEST=Type usbresume in console (with other HACK CL), see that we
can recover whether or not the interface is autosuspended.
TEST=With device autosuspended, short D+/D- on the base, that
generates a spurious resume event (similar to what we
observed doing ESD testing on Soraka), and check that in that
can we don't get an SOF and we go back to sleep.
Change-Id: Ibd5c5f7e5f5c324622df5169bb317afa118edc45
Signed-off-by: Nicolas Boichat <drinkcat@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2035436
Reviewed-by: Ting Shen <phoenixshen@chromium.org>
Reviewed-by: Eric Yilun Lin <yllin@chromium.org>
Diffstat (limited to 'chip/stm32/usb.c')
-rw-r--r-- | chip/stm32/usb.c | 32 |
1 files changed, 27 insertions, 5 deletions
diff --git a/chip/stm32/usb.c b/chip/stm32/usb.c index c901c293f7..54cd90dd46 100644 --- a/chip/stm32/usb.c +++ b/chip/stm32/usb.c @@ -409,13 +409,19 @@ static void usb_suspend(void) hook_call_deferred(&usb_pm_change_notify_hooks_data, 0); } +/* + * SOF was received (set in interrupt), reset in usb_resume in the + * unexpected state case. + */ +static volatile int sof_received; + static void usb_resume_deferred(void) { uint32_t state = (STM32_USB_FNR & STM32_USB_FNR_RXDP_RXDM_MASK) >> STM32_USB_FNR_RXDP_RXDM_SHIFT; - CPRINTF("RSMd %d %04x\n", state, STM32_USB_CNTR); - if (state == 2 || state == 3) + CPRINTF("RSMd %d %04x %d\n", state, STM32_USB_CNTR, sof_received); + if (sof_received == 0 && (state == 2 || state == 3)) usb_suspend(); else hook_call_deferred(&usb_pm_change_notify_hooks_data, 0); @@ -447,10 +453,17 @@ static void usb_resume(void) * reset condition for 20ms, so reading D+/D- after ~3ms should be safe * (there is no chance we end up sampling during a bus transaction). */ - if (state == 2 || state == 3) + if (state == 2 || state == 3) { + /* + * This function is already called from interrupt context so + * there is no risk of race here. + */ + sof_received = 0; + STM32_USB_CNTR |= STM32_USB_CNTR_SOFM; hook_call_deferred(&usb_resume_deferred_data, 3 * MSEC); - else + } else { hook_call_deferred(&usb_pm_change_notify_hooks_data, 0); + } } #ifdef CONFIG_USB_REMOTE_WAKEUP @@ -593,7 +606,7 @@ static void usb_interrupt_handle_wake(uint16_t status) if (good || state == 3 || esof_count <= -USB_RESUME_TIMEOUT_MS) { int ep; - STM32_USB_CNTR &= ~(STM32_USB_CNTR_ESOFM | STM32_USB_CNTR_SOFM); + STM32_USB_CNTR &= ~STM32_USB_CNTR_ESOFM; usb_wake_done = 1; if (!good) { CPRINTF("wake error: cnt=%d state=%d\n", @@ -618,6 +631,15 @@ void usb_interrupt(void) usb_reset(); #ifdef CONFIG_USB_SUSPEND + if (status & STM32_USB_ISTR_SOF) { + sof_received = 1; + /* + * The wake handler also only cares about the _first_ SOF that + * is received, so we can disable that interrupt. + */ + STM32_USB_CNTR &= ~STM32_USB_CNTR_SOFM; + } + #ifdef CONFIG_USB_REMOTE_WAKEUP if (status & (STM32_USB_ISTR_ESOF | STM32_USB_ISTR_SOF) && !usb_wake_done) |