From 162c595a0b4803505796349466f75167db2d6c88 Mon Sep 17 00:00:00 2001 From: Dominic Clifton Date: Tue, 20 Jan 2026 22:37:00 +0100 Subject: [PATCH] KINETIS - Fix second USB stop and disconnect from crashing. in qmk, there was startup code: ``` usbDisconnectBus(usbp); usbStop(usbp); wait_ms(50); usbStart(usbp, &usbcfg); usbConnectBus(usbp); ``` then later: ``` usbDisconnectBus(&USB_DRIVER); usbStop(&USB_DRIVER); ``` This commit prevents crashes in the start/stop/connect/disconnect and ISRs for this sequence of calls. --- .../ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c | 70 ++++++++++++++++--- .../ports/KINETIS/LLD/USBHSv1/hal_usb_lld.h | 20 +++++- 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c b/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c index 0fe261c5d0..65f1b5500d 100644 --- a/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c +++ b/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c @@ -211,6 +211,26 @@ void usb_packet_receive(USBDriver *usbp, usbep_t ep, size_t n) */ OSAL_IRQ_HANDLER(KINETIS_USB_IRQ_VECTOR) { USBDriver *usbp = &USBD1; + + /* Bail out if driver is stopped or being torn down */ + if (usbp->state == USB_UNINIT || usbp->state == USB_STOP) { + uint8_t enabled = +#if KINETIS_USB0_IS_USBOTG + (SIM->SCGC4 & SIM_SCGC4_USBOTG); +#else + (SIM->SCGC4 & SIM_SCGC4_USBFS); +#endif + + if (enabled) { + /* Clear all pending USB IRQ flags */ + USB0->ISTAT = 0xFF; + USB0->ERRSTAT = 0xFF; + } + OSAL_IRQ_EPILOGUE(); + return; + } + + uint8_t istat = USB0->ISTAT; OSAL_IRQ_PROLOGUE(); @@ -527,18 +547,50 @@ void usb_lld_start(USBDriver *usbp) { * @notapi */ void usb_lld_stop(USBDriver *usbp) { - /* TODO: If in ready state then disables the USB clock.*/ - if (usbp->state == USB_STOP) { #if KINETIS_USB_USE_USB0 - if (&USBD1 == usbp) { + if (&USBD1 != usbp) return; + + /* If the driver was never started, bail out */ + if (usbp->state == USB_UNINIT || usbp->state == USB_STOP) { + return; + } + + /* Mask all peripheral interrupts */ + USB0->INTEN = 0; + USB0->ERREN = 0; + + uint8_t enabled = #if KINETIS_USB0_IS_USBOTG - nvicDisableVector(USB_OTG_IRQn); -#else /* KINETIS_USB0_IS_USBOTG */ - nvicDisableVector(USB_IRQn); -#endif /* KINETIS_USB0_IS_USBOTG */ + (SIM->SCGC4 & SIM_SCGC4_USBOTG); +#else + (SIM->SCGC4 & SIM_SCGC4_USBFS); +#endif + if (enabled) { + /* Disconnect pull-up while clock is still on */ + usb_lld_disconnect_bus(usbp); + + /* Only disable USB engine if the clock is enabled */ + USB0->CTL = 0; + USB0->CTL = USBx_CTL_ODDRST; /* reset odd/even PID */ + USB0->ISTAT = 0xFF; + USB0->ERRSTAT = 0xFF; } -#endif /* KINETIS_USB_USE_USB0 */ - } + + /* Disable NVIC IRQ */ +#if KINETIS_USB0_IS_USBOTG + nvicDisableVector(USB_OTG_IRQn); +#else + nvicDisableVector(USB_IRQn); +#endif + + /* Gate the USB clock */ +#if KINETIS_USB0_IS_USBOTG + SIM->SCGC4 &= ~SIM_SCGC4_USBOTG; +#else + SIM->SCGC4 &= ~SIM_SCGC4_USBFS; +#endif + +#endif } /** diff --git a/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.h b/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.h index 05dd9c8c7f..38d16c7bfa 100644 --- a/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.h +++ b/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.h @@ -397,14 +397,28 @@ struct USBDriver { * @api */ #if !defined(usb_lld_disconnect_bus) -/* Writing to USB0->CONTROL causes an unhandled exception when USB module is not clocked. */ + +/* Writing USB0->CONTROL with clock off causes hard fault on MK20 */ #if KINETIS_USB0_IS_USBOTG -#define usb_lld_disconnect_bus(usbp) if(SIM->SCGC4 & SIM_SCGC4_USBOTG) {USB0->CONTROL &= ~USBx_CONTROL_DPPULLUPNONOTG;} else {} +#define usb_lld_disconnect_bus(usbp) \ + do { \ + (void)(usbp); \ + if (SIM->SCGC4 & SIM_SCGC4_USBOTG) { \ + USB0->CONTROL &= ~USBx_CONTROL_DPPULLUPNONOTG; \ + } \ + } while (false) #else /* KINETIS_USB0_IS_USBOTG */ -#define usb_lld_disconnect_bus(usbp) if(SIM->SCGC4 & SIM_SCGC4_USBFS) {USB0->CONTROL &= ~USBx_CONTROL_DPPULLUPNONOTG;} else {} +#define usb_lld_disconnect_bus(usbp) \ + do { \ + (void)(usbp); \ + if (SIM->SCGC4 & SIM_SCGC4_USBFS) { \ + USB0->CONTROL &= ~USBx_CONTROL_DPPULLUPNONOTG; \ + } \ + } while (false) #endif /* KINETIS_USB0_IS_USBOTG */ #endif + /** * @brief Start of host wake-up procedure. *