summaryrefslogtreecommitdiff
path: root/chip/stm32/usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/stm32/usb.c')
-rw-r--r--chip/stm32/usb.c41
1 files changed, 34 insertions, 7 deletions
diff --git a/chip/stm32/usb.c b/chip/stm32/usb.c
index dfec42b5f8..4aec16f8d3 100644
--- a/chip/stm32/usb.c
+++ b/chip/stm32/usb.c
@@ -384,11 +384,17 @@ static void usb_suspend(void)
{
CPRINTF("SUS\n");
+ /*
+ * usb_suspend can be called from hook task, make sure no interrupt is
+ * modifying CNTR at the same time.
+ */
+ interrupt_disable();
/* Set FSUSP bit to activate suspend mode */
STM32_USB_CNTR |= STM32_USB_CNTR_FSUSP;
/* Set USB low power mode */
STM32_USB_CNTR |= STM32_USB_CNTR_LP_MODE;
+ interrupt_enable();
clock_enable_module(MODULE_USB, 0);
@@ -396,15 +402,20 @@ static void usb_suspend(void)
enable_sleep(SLEEP_MASK_USB_DEVICE);
}
-static void usb_resume(void)
+static void usb_resume_deferred(void)
{
- CPRINTF("RSM\n");
+ uint32_t state = (STM32_USB_FNR & STM32_USB_FNR_RXDP_RXDM_MASK)
+ >> STM32_USB_FNR_RXDP_RXDM_SHIFT;
- /*
- * TODO(crosbug.com/p/63273): Reference manual suggests going back to
- * sleep if state is 10 or 11, but this seems to cause other problems
- * (see bug). Ignore them for now.
- */
+ CPRINTF("RSMd %d %04x\n", state, STM32_USB_CNTR);
+ if (state == 2 || state == 3)
+ usb_suspend();
+}
+DECLARE_DEFERRED(usb_resume_deferred);
+
+static void usb_resume(void)
+{
+ uint32_t state;
clock_enable_module(MODULE_USB, 1);
@@ -413,6 +424,22 @@ static void usb_resume(void)
/* USB is in use again */
disable_sleep(SLEEP_MASK_USB_DEVICE);
+
+ state = (STM32_USB_FNR & STM32_USB_FNR_RXDP_RXDM_MASK)
+ >> STM32_USB_FNR_RXDP_RXDM_SHIFT;
+
+ CPRINTF("RSM %d %04x\n", state, STM32_USB_CNTR);
+
+ /*
+ * Reference manual tells we should go back to sleep if state is 10 or
+ * 11. However, setting FSUSP and LP_MODE in this interrupt routine
+ * seems to lock the USB controller (see b/35775088 and b/71688150).
+ * Instead, we do it in a deferred routine. The host must assert the
+ * 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)
+ hook_call_deferred(&usb_resume_deferred_data, 3 * MSEC);
}
#ifdef CONFIG_USB_REMOTE_WAKEUP