summaryrefslogtreecommitdiff
path: root/chip/stm32/usb.c
diff options
context:
space:
mode:
authorNicolas Boichat <drinkcat@chromium.org>2020-02-04 16:05:16 +0800
committerCommit Bot <commit-bot@chromium.org>2020-02-04 20:19:47 +0000
commit0cfd0ba9b84621a42ae24ca88a4df17bd82da2c1 (patch)
treef954c997b2cb3630e166f125191c08d69eec28a4 /chip/stm32/usb.c
parentb149d72f08edb528914d17d36435500cf497e792 (diff)
downloadchrome-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.c32
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)