Skip to content

Commit 8619248

Browse files
mathieuchopstmcfriedt
authored andcommitted
drivers: usb: dc: stm32: fix usb_disable() deadlock on certain series
During a teardown sequence performed by the USB DC stack's "usb_disable()", the controller is first disabled by calling "usb_dc_detach()", which turns off the USB controller clock in the STM32 implementation. "usb_disable()" then disables endpoints by calling "usb_dc_ep_disable()" on each of them, which is merely forwarded to HAL_PCD_EP_Close() by the STM32 driver. This order of operations means that the latter operation is actually operating on the no-longer-clocks USB controller! Up until recently, memory accesses to MMIO of unclocked peripherals in STM32 SoCs would not cause issues, even if the resulting access was a no-op (read returns zero, write is ignored), so everything worked fine even if the access was *technically* illegal... However, on newer series with a different bus fabric, accesses to unclocked peripherals will instead deadlock the SoC! Prevent illegal accesses inside "usb_dc_stm32_ep_disable()" by checking if the USB controller clock is enabled before calling HAL_PCD_EP_Close(), and skipping the call if it isn't. This allows "usb_disable()" to complete on series such as STM32N6. Signed-off-by: Mathieu Choplain <[email protected]>
1 parent 8b31d2c commit 8619248

File tree

1 file changed

+21
-0
lines changed

1 file changed

+21
-0
lines changed

drivers/usb/device/usb_dc_stm32.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,16 @@ static struct usb_dc_stm32_state usb_dc_stm32_state;
201201

202202
/* Internal functions */
203203

204+
static bool usb_dc_stm32_clock_enabled(void)
205+
{
206+
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
207+
208+
enum clock_control_status status = clock_control_get_status(
209+
clk, (clock_control_subsys_t)&pclken[0]);
210+
211+
return status == CLOCK_CONTROL_STATUS_ON;
212+
}
213+
204214
static struct usb_dc_stm32_ep_state *usb_dc_stm32_get_ep_state(uint8_t ep)
205215
{
206216
struct usb_dc_stm32_ep_state *ep_state_base;
@@ -940,6 +950,17 @@ int usb_dc_ep_disable(const uint8_t ep)
940950
return -EINVAL;
941951
}
942952

953+
if (!usb_dc_stm32_clock_enabled()) {
954+
/*
955+
* During device teardown, EP disable is attempted after
956+
* the USB controller clock has been turned off. Accessing
957+
* it would do nothing and even deadlocks the SoC on certain
958+
* series: don't even bother trying. Return success as all
959+
* endpoints are disabled anyways when USB controller is off.
960+
*/
961+
return 0;
962+
}
963+
943964
status = HAL_PCD_EP_Close(&usb_dc_stm32_state.pcd, ep);
944965
if (status != HAL_OK) {
945966
LOG_ERR("HAL_PCD_EP_Close failed(0x%02x), %d", ep,

0 commit comments

Comments
 (0)