summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chip/stm32/usb.c29
1 files changed, 28 insertions, 1 deletions
diff --git a/chip/stm32/usb.c b/chip/stm32/usb.c
index 4aec16f8d3..57e629a3d1 100644
--- a/chip/stm32/usb.c
+++ b/chip/stm32/usb.c
@@ -460,13 +460,34 @@ void board_usb_wake(void)
/* Side-band USB wake, do nothing by default. */
}
+/* Called 10ms after usb_wake started. */
+static void usb_wake_deferred(void)
+{
+ if (esof_count == 3) {
+ /*
+ * If we reach here, it means that we are not counting ESOF/SOF
+ * properly (either of these interrupts should occur every 1ms).
+ * This should never happen if we implemented the resume logic
+ * correctly.
+ *
+ * We reset the controller in that case, which recovers the
+ * interface.
+ */
+ CPRINTF("USB stuck\n");
+ STM32_RCC_APB1RSTR |= STM32_RCC_PB1_USB;
+ STM32_RCC_APB1RSTR &= ~STM32_RCC_PB1_USB;
+ usb_init();
+ }
+}
+DECLARE_DEFERRED(usb_wake_deferred);
+
void usb_wake(void)
{
if (!remote_wakeup_enabled ||
!(STM32_USB_CNTR & STM32_USB_CNTR_FSUSP)) {
/*
* USB wake not enabled, or already woken up, or already waking
- * up,nothing to do.
+ * up, nothing to do.
*/
return;
}
@@ -478,6 +499,12 @@ void usb_wake(void)
CPRINTF("WAKE\n");
/*
+ * Sometimes the USB controller gets stuck, and does not count SOF/ESOF
+ * frames anymore, detect that.
+ */
+ hook_call_deferred(&usb_wake_deferred_data, 10 * MSEC);
+
+ /*
* Set RESUME bit for 1 to 15 ms, then clear it. We ask the interrupt
* routine to count 3 ESOF interrupts, which should take between
* 2 and 3 ms.