{arm,xtensa}/otg: unmask WKUP so USB resume reaches the class driver.

Every DWC2-derived USB device driver enables USBSUSP in GINTMSK but not
WKUP, and every one of them ANDs GINTSTS with GINTMSK before dispatch.
The resume handler is therefore unreachable: CLASS_SUSPEND is delivered
on suspend, CLASS_RESUME never is.

For CDC/ACM that is fatal. cdcacm_suspend() calls uart_connected(false),
after which serial.c refuses every open() and write() with -ENOTCONN,
and the cdcacm_resume() that would clear it never runs. On a Linux host
with the default USB autosuspend (power/control=auto, 2000 ms) simply
closing the tty is enough to trip it, and the port stays dead for the
rest of the boot while the device remains enumerated.

Verified on STM32H7 (ARK FMU v6X): before, one host suspend leaves the
CDC/ACM port permanently -ENOTCONN; after, ten forced suspend/resume
cycles all recover with the MAVLink stream intact. The remaining
drivers carry a line-for-line copy of the same initialisation.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
This commit is contained in:
Jacob Dahl 2026-08-22 10:48:54 -06:00 committed by Xiang Xiao
parent 21a99f9a23
commit ce0038f2bc
8 changed files with 25 additions and 17 deletions

View file

@ -5539,8 +5539,9 @@ static void at32_hwinitialize(struct at32_usbdev_s *priv)
/* Enable the interrupts in the INTMSK */
regval = (OTGFS_GINT_RXFLVL | OTGFS_GINT_USBSUSP | OTGFS_GINT_ENUMDNE |
OTGFS_GINT_IEP | OTGFS_GINT_OEP | OTGFS_GINT_USBRST);
regval = (OTGFS_GINT_RXFLVL | OTGFS_GINT_USBSUSP | OTGFS_GINT_WKUP |
OTGFS_GINT_ENUMDNE | OTGFS_GINT_IEP | OTGFS_GINT_OEP |
OTGFS_GINT_USBRST);
#ifdef CONFIG_USBDEV_ISOCHRONOUS
regval |= (OTGFS_GINT_IISOIXFR | OTGFS_GINT_IISOOXFR);

View file

@ -5533,8 +5533,9 @@ static void stm32_hwinitialize(struct stm32_usbdev_s *priv)
/* Enable the interrupts in the INTMSK */
regval = (OTGFS_GINT_RXFLVL | OTGFS_GINT_USBSUSP | OTGFS_GINT_ENUMDNE |
OTGFS_GINT_IEP | OTGFS_GINT_OEP | OTGFS_GINT_USBRST);
regval = (OTGFS_GINT_RXFLVL | OTGFS_GINT_USBSUSP | OTGFS_GINT_WKUP |
OTGFS_GINT_ENUMDNE | OTGFS_GINT_IEP | OTGFS_GINT_OEP |
OTGFS_GINT_USBRST);
#ifdef CONFIG_USBDEV_ISOCHRONOUS
regval |= (OTGFS_GINT_IISOIXFR | OTGFS_GINT_IISOOXFR);

View file

@ -5426,8 +5426,9 @@ static void stm32_hwinitialize(struct stm32_usbdev_s *priv)
/* Enable the interrupts in the INTMSK */
regval = (OTGHS_GINT_RXFLVL | OTGHS_GINT_USBSUSP | OTGHS_GINT_ENUMDNE |
OTGHS_GINT_IEP | OTGHS_GINT_OEP | OTGHS_GINT_USBRST);
regval = (OTGHS_GINT_RXFLVL | OTGHS_GINT_USBSUSP | OTGHS_GINT_WKUP |
OTGHS_GINT_ENUMDNE | OTGHS_GINT_IEP | OTGHS_GINT_OEP |
OTGHS_GINT_USBRST);
#ifdef CONFIG_USBDEV_ISOCHRONOUS
regval |= (OTGHS_GINT_IISOIXFR | OTGHS_GINT_IISOOXFR);

View file

@ -5488,8 +5488,9 @@ static void efm32_hwinitialize(struct efm32_usbdev_s *priv)
/* Enable the interrupts in the INTMSK */
regval = (USB_GINTMSK_RXFLVLMSK | USB_GINTMSK_USBSUSPMSK |
USB_GINTMSK_ENUMDONEMSK | USB_GINTMSK_IEPINTMSK |
USB_GINTMSK_OEPINTMSK | USB_GINTMSK_USBRSTMSK);
USB_GINTMSK_WKUPINTMSK | USB_GINTMSK_ENUMDONEMSK |
USB_GINTMSK_IEPINTMSK | USB_GINTMSK_OEPINTMSK |
USB_GINTMSK_USBRSTMSK);
#ifdef CONFIG_USBDEV_ISOCHRONOUS
regval |= (USB_GINTMSK_INCOMPISOINMSK | USB_GINTMSK_INCOMPLPMSK);

View file

@ -5666,8 +5666,9 @@ static void stm32_hwinitialize(struct stm32_usbdev_s *priv)
/* Enable the interrupts in the INTMSK */
regval = (OTG_GINT_RXFLVL | OTG_GINT_USBSUSP | OTG_GINT_ENUMDNE |
OTG_GINT_IEP | OTG_GINT_OEP | OTG_GINT_USBRST);
regval = (OTG_GINT_RXFLVL | OTG_GINT_USBSUSP | OTG_GINT_WKUP |
OTG_GINT_ENUMDNE | OTG_GINT_IEP | OTG_GINT_OEP |
OTG_GINT_USBRST);
# ifdef CONFIG_USBDEV_ISOCHRONOUS
regval |= (OTG_GINT_IISOIXFR | OTG_GINT_IISOOXFR);

View file

@ -3362,7 +3362,7 @@ static inline void stm32_rxinterrupt(struct stm32_usbdev_s *priv)
regval = stm32_getreg(STM32_OTG_DOEPCTL(0));
regval |= OTG_DOEPCTL0_CNAK;
stm32_putreg(regval, STM32_OTG_DOEPCTL(0));
}
}
}
break;
@ -5565,8 +5565,9 @@ static void stm32_hwinitialize(struct stm32_usbdev_s *priv)
/* Enable the interrupts in the INTMSK */
regval = (OTG_GINT_RXFLVL | OTG_GINT_USBSUSP | OTG_GINT_ENUMDNE |
OTG_GINT_IEP | OTG_GINT_OEP | OTG_GINT_USBRST);
regval = (OTG_GINT_RXFLVL | OTG_GINT_USBSUSP | OTG_GINT_WKUP |
OTG_GINT_ENUMDNE | OTG_GINT_IEP | OTG_GINT_OEP |
OTG_GINT_USBRST);
#ifdef CONFIG_USBDEV_ISOCHRONOUS
regval |= (OTG_GINT_IISOIXFR | OTG_GINT_IISOOXFR);

View file

@ -5594,8 +5594,9 @@ static void stm32_hwinitialize(struct stm32_usbdev_s *priv)
/* Enable the interrupts in the INTMSK */
regval = (OTGFS_GINT_RXFLVL | OTGFS_GINT_USBSUSP | OTGFS_GINT_ENUMDNE |
OTGFS_GINT_IEP | OTGFS_GINT_OEP | OTGFS_GINT_USBRST);
regval = (OTGFS_GINT_RXFLVL | OTGFS_GINT_USBSUSP | OTGFS_GINT_WKUP |
OTGFS_GINT_ENUMDNE | OTGFS_GINT_IEP | OTGFS_GINT_OEP |
OTGFS_GINT_USBRST);
#ifdef CONFIG_USBDEV_ISOCHRONOUS
regval |= (OTGFS_GINT_IISOIXFR | OTGFS_GINT_IISOOXFR);

View file

@ -5540,8 +5540,9 @@ static void esp32s3_hwinitialize(struct esp32s3_usbdev_s *priv)
/* Enable the interrupts in the INTMSK */
regval = (OTG_GINT_RXFLVL | OTG_GINT_USBSUSP | OTG_GINT_ENUMDNE |
OTG_GINT_IEP | OTG_GINT_OEP | OTG_GINT_USBRST);
regval = (OTG_GINT_RXFLVL | OTG_GINT_USBSUSP | OTG_GINT_WKUP |
OTG_GINT_ENUMDNE | OTG_GINT_IEP | OTG_GINT_OEP |
OTG_GINT_USBRST);
#ifdef CONFIG_USBDEV_ISOCHRONOUS
regval |= (OTG_GINT_IISOIXFR | OTG_GINT_IISOOXFR);