diff --git a/drivers/usb/common/CMakeLists.txt b/drivers/usb/common/CMakeLists.txt index 56e4c734ced97..621e848d6e0c0 100644 --- a/drivers/usb/common/CMakeLists.txt +++ b/drivers/usb/common/CMakeLists.txt @@ -1,3 +1,4 @@ # SPDX-License-Identifier: Apache-2.0 add_subdirectory_ifdef(CONFIG_HAS_NRFX nrf_usbd_common) +add_subdirectory_ifdef(CONFIG_SOC_FAMILY_STM32 stm32) diff --git a/drivers/usb/common/Kconfig b/drivers/usb/common/Kconfig index d5a0d4dc1b4a6..d80a784c7e483 100644 --- a/drivers/usb/common/Kconfig +++ b/drivers/usb/common/Kconfig @@ -4,5 +4,6 @@ menu "USB common" rsource "nrf_usbd_common/Kconfig" +rsource "stm32/Kconfig" endmenu diff --git a/drivers/usb/common/stm32/CMakeLists.txt b/drivers/usb/common/stm32/CMakeLists.txt new file mode 100644 index 0000000000000..5012b9acf8dc3 --- /dev/null +++ b/drivers/usb/common/stm32/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2025 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +if(CONFIG_STM32_USB_COMMON) + zephyr_library() + + zephyr_include_directories(.) + + zephyr_library_sources(stm32_usb_pwr.c) +endif() diff --git a/drivers/usb/common/stm32/Kconfig b/drivers/usb/common/stm32/Kconfig new file mode 100644 index 0000000000000..67d6365427f0d --- /dev/null +++ b/drivers/usb/common/stm32/Kconfig @@ -0,0 +1,17 @@ +# Copyright (c) 2025 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +if SOC_FAMILY_STM32 + +config STM32_USB_COMMON + bool + help + Enable compilation of STM32 USB common code. + + This option is selected by the USB drivers when appropriate. + +module = STM32_USB_COMMON +module-str = STM32 USB common code +source "subsys/logging/Kconfig.template.log_config" + +endif # SOC_FAMILY_STM32 diff --git a/drivers/usb/common/stm32/stm32_usb_common.h b/drivers/usb/common/stm32/stm32_usb_common.h new file mode 100644 index 0000000000000..5c8b224d1dcf9 --- /dev/null +++ b/drivers/usb/common/stm32/stm32_usb_common.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_USB_COMMON_STM32_STM32_USB_COMMON_H_ +#define ZEPHYR_DRIVERS_USB_COMMON_STM32_STM32_USB_COMMON_H_ + +/** + * @brief Configures the Power Controller as necessary + * for proper operation of the USB controllers + */ +int stm32_usb_pwr_enable(void); + +/** + * @brief Configures the Power Controller to disable + * USB-related regulators/etc if no controller is + * still active (refcounted). + */ +int stm32_usb_pwr_disable(void); + +#endif /* ZEPHYR_DRIVERS_USB_COMMON_STM32_STM32_USB_COMMON_H_ */ diff --git a/drivers/usb/common/stm32/stm32_usb_pwr.c b/drivers/usb/common/stm32/stm32_usb_pwr.c new file mode 100644 index 0000000000000..391821b3ffc37 --- /dev/null +++ b/drivers/usb/common/stm32/stm32_usb_pwr.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(stm32_usb_pwr, CONFIG_STM32_USB_COMMON_LOG_LEVEL); + +/* + * Keep track of whether power is already + * enabled here to simplify the USB drivers. + */ +static atomic_t usb_pwr_refcount = ATOMIC_INIT(0); + +int stm32_usb_pwr_enable(void) +{ + atomic_val_t old = atomic_inc(&usb_pwr_refcount); + + if (old > 0) { + /* Already enabled - nothing to do */ + return 0; + } + +#if defined(CONFIG_SOC_SERIES_STM32H7X) + LL_PWR_EnableUSBVoltageDetector(); + + /* Per AN2606: USBREGEN not supported when running in FS mode. */ + LL_PWR_DisableUSBReg(); + while (!LL_PWR_IsActiveFlag_USB()) { + LOG_INF("PWR not active yet"); + k_msleep(100); + } +#elif defined(CONFIG_SOC_SERIES_STM32U5X) + /* Sequence to enable the power of the OTG HS on a stm32U5 serie : Enable VDDUSB */ + __ASSERT_NO_MSG(LL_AHB3_GRP1_IsEnabledClock(LL_AHB3_GRP1_PERIPH_PWR)); + + /* Check that power range is 1 or 2 */ + if (LL_PWR_GetRegulVoltageScaling() < LL_PWR_REGU_VOLTAGE_SCALE2) { + LOG_ERR("Wrong Power range to use USB OTG HS"); + return -EIO; + } + + LL_PWR_EnableVddUSB(); + + #if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs) + /* Configure VOSR register of USB HSTransceiverSupply(); */ + LL_PWR_EnableUSBPowerSupply(); + LL_PWR_EnableUSBEPODBooster(); + while (LL_PWR_IsActiveFlag_USBBOOST() != 1) { + /* Wait for USB EPOD BOOST ready */ + } + #endif /* DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs) */ +#elif defined(CONFIG_SOC_SERIES_STM32N6X) + /* Enable Vdd33USB voltage monitoring */ + LL_PWR_EnableVddUSBMonitoring(); + while (!LL_PWR_IsActiveFlag_USB33RDY()) { + /* Wait for Vdd33USB ready */ + } + + /* Enable VDDUSB */ + LL_PWR_EnableVddUSB(); +#elif defined(CONFIG_SOC_SERIES_STM32WBAX) + /* Remove VDDUSB power isolation */ + LL_PWR_EnableVddUSB(); + + /* Make sure that voltage scaling is Range 1 */ + __ASSERT_NO_MSG(LL_PWR_GetRegulCurrentVOS() == LL_PWR_REGU_VOLTAGE_SCALE1); + + /* Enable VDD11USB */ + LL_PWR_EnableVdd11USB(); + + /* Enable USB OTG internal power */ + LL_PWR_EnableUSBPWR(); + + while (!LL_PWR_IsActiveFlag_VDD11USBRDY()) { + /* Wait for VDD11USB supply to be ready */ + } + + /* Enable USB OTG booster */ + LL_PWR_EnableUSBBooster(); + + while (!LL_PWR_IsActiveFlag_USBBOOSTRDY()) { + /* Wait for USB OTG booster to be ready */ + } +#elif defined(PWR_USBSCR_USB33SV) || defined(PWR_SVMCR_USV) + /* + * VDDUSB independent USB supply (PWR clock is on) + * with LL_PWR_EnableVDDUSB function (higher case) + */ + LL_PWR_EnableVDDUSB(); +#elif defined(PWR_CR2_USV) + /*cd + * Required for at least STM32L4 devices as they electrically + * isolate USB features from VDDUSB. It must be enabled before + * USB can function. Refer to section 5.1.3 in DM00083560 or + * DM00310109. + */ + LL_PWR_EnableVddUSB(); +#endif + return 0; +} + +int stm32_usb_pwr_disable(void) +{ + atomic_val_t old = atomic_dec(&usb_pwr_refcount); + + if (old > 1) { + /* There are other users - don't disable now */ + return 0; + } + +#if defined(CONFIG_SOC_SERIES_STM32H7X) + LL_PWR_DisableUSBVoltageDetector(); +#elif defined(CONFIG_SOC_SERIES_STM32U5X) + #if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs) + LL_PWR_DisableUSBEPODBooster(); + while (LL_PWR_IsActiveFlag_USBBOOST() != 0) { + /* Wait for USB EPOD BOOST off */ + } + + LL_PWR_DisableUSBPowerSupply(); + #endif /* DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs) */ + + LL_PWR_DisableVddUSB(); +#elif defined(CONFIG_SOC_SERIES_STM32N6X) + /* Disable Vdd33USB voltage monitoring */ + LL_PWR_DisableVddUSBMonitoring(); + + /* Disable VDDUSB */ + LL_PWR_DisableVddUSB(); +#elif defined(CONFIG_SOC_SERIES_STM32WBAX) + /* Disable USB OTG booster */ + LL_PWR_DisableUSBBooster(); + + while (LL_PWR_IsActiveFlag_USBBOOSTRDY()) { + /* Wait until USB OTG booster is off */ + } + + /* Disable USB OTG internal power */ + LL_PWR_DisableUSBPWR(); + + /* Disable VDD11USB */ + LL_PWR_DisableVdd11USB(); + + while (LL_PWR_IsActiveFlag_VDD11USBRDY()) { + /* Wait until VDD11USB supply is off */ + } + + /* Enable VDDUSB power isolation */ + LL_PWR_DisableVddUSB(); +#elif defined(PWR_USBSCR_USB33SV) || defined(PWR_SVMCR_USV) + /* Enable VDDUSB power isolation */ + LL_PWR_DisableVDDUSB(); +#elif defined(PWR_CR2_USV) + /* Enable VDDUSB power isolation */ + LL_PWR_DisableVddUSB(); +#endif + + return 0; +} diff --git a/drivers/usb/udc/Kconfig.stm32 b/drivers/usb/udc/Kconfig.stm32 index e99839ca92b98..b3648b6cd195e 100644 --- a/drivers/usb/udc/Kconfig.stm32 +++ b/drivers/usb/udc/Kconfig.stm32 @@ -10,6 +10,7 @@ config UDC_STM32 select USE_STM32_HAL_PCD select USE_STM32_HAL_PCD_EX select UDC_DRIVER_HAS_HIGH_SPEED_SUPPORT + select STM32_USB_COMMON select PINCTRL default y help diff --git a/drivers/usb/udc/udc_stm32.c b/drivers/usb/udc/udc_stm32.c index 243683258bd55..f0296bad8eb39 100644 --- a/drivers/usb/udc/udc_stm32.c +++ b/drivers/usb/udc/udc_stm32.c @@ -22,6 +22,7 @@ #include #include "udc_common.h" +#include #include LOG_MODULE_REGISTER(udc_stm32, CONFIG_UDC_DRIVER_LOG_LEVEL); @@ -720,6 +721,13 @@ int udc_stm32_init(const struct device *dev) struct udc_stm32_data *priv = udc_get_private(dev); const struct udc_stm32_config *cfg = dev->config; HAL_StatusTypeDef status; + int err; + + err = stm32_usb_pwr_enable(); + if (err < 0) { + LOG_ERR("Error enabling USB power: %d", err); + return err; + } if (udc_stm32_clock_enable(dev) < 0) { LOG_ERR("Error enabling clock(s)"); @@ -946,6 +954,7 @@ static int udc_stm32_shutdown(const struct device *dev) struct udc_stm32_data *priv = udc_get_private(dev); const struct udc_stm32_config *cfg = dev->config; HAL_StatusTypeDef status; + int err; status = HAL_PCD_DeInit(&priv->pcd); if (status != HAL_OK) { @@ -958,6 +967,12 @@ static int udc_stm32_shutdown(const struct device *dev) /* continue anyway */ } + err = stm32_usb_pwr_disable(); + if (err < 0) { + LOG_ERR("Error disabling USB power: %d", err); + /* continue anyway */ + } + if (irq_is_enabled(cfg->irqn)) { irq_disable(cfg->irqn); } @@ -1296,76 +1311,6 @@ static int udc_stm32_clock_enable(const struct device *dev) return -ENODEV; } - /* Power configuration */ -#if defined(CONFIG_SOC_SERIES_STM32H7X) - LL_PWR_EnableUSBVoltageDetector(); - - /* Per AN2606: USBREGEN not supported when running in FS mode. */ - LL_PWR_DisableUSBReg(); - while (!LL_PWR_IsActiveFlag_USB()) { - LOG_INF("PWR not active yet"); - k_msleep(100); - } -#elif defined(CONFIG_SOC_SERIES_STM32U5X) - /* Sequence to enable the power of the OTG HS on a stm32U5 serie : Enable VDDUSB */ - __ASSERT_NO_MSG(LL_AHB3_GRP1_IsEnabledClock(LL_AHB3_GRP1_PERIPH_PWR)); - - /* Check that power range is 1 or 2 */ - if (LL_PWR_GetRegulVoltageScaling() < LL_PWR_REGU_VOLTAGE_SCALE2) { - LOG_ERR("Wrong Power range to use USB OTG HS"); - return -EIO; - } - - LL_PWR_EnableVddUSB(); - - #if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs) - /* Configure VOSR register of USB HSTransceiverSupply(); */ - LL_PWR_EnableUSBPowerSupply(); - LL_PWR_EnableUSBEPODBooster(); - while (LL_PWR_IsActiveFlag_USBBOOST() != 1) { - /* Wait for USB EPOD BOOST ready */ - } - #endif /* DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs) */ -#elif defined(CONFIG_SOC_SERIES_STM32N6X) - /* Enable Vdd33USB voltage monitoring */ - LL_PWR_EnableVddUSBMonitoring(); - while (!LL_PWR_IsActiveFlag_USB33RDY()) { - /* Wait for Vdd33USB ready */ - } - - /* Enable VDDUSB */ - LL_PWR_EnableVddUSB(); -#elif defined(CONFIG_SOC_SERIES_STM32WBAX) - /* Remove VDDUSB power isolation */ - LL_PWR_EnableVddUSB(); - - /* Make sure that voltage scaling is Range 1 */ - __ASSERT_NO_MSG(LL_PWR_GetRegulCurrentVOS() == LL_PWR_REGU_VOLTAGE_SCALE1); - - /* Enable VDD11USB */ - LL_PWR_EnableVdd11USB(); - - /* Enable USB OTG internal power */ - LL_PWR_EnableUSBPWR(); - - while (!LL_PWR_IsActiveFlag_VDD11USBRDY()) { - /* Wait for VDD11USB supply to be ready */ - } - - /* Enable USB OTG booster */ - LL_PWR_EnableUSBBooster(); - - while (!LL_PWR_IsActiveFlag_USBBOOSTRDY()) { - /* Wait for USB OTG booster to be ready */ - } -#elif defined(PWR_USBSCR_USB33SV) || defined(PWR_SVMCR_USV) - /* - * VDDUSB independent USB supply (PWR clock is on) - * with LL_PWR_EnableVDDUSB function (higher case) - */ - LL_PWR_EnableVDDUSB(); -#endif - if (cfg->num_clocks > 1) { if (clock_control_configure(clk, &cfg->pclken[1], NULL) != 0) { LOG_ERR("Could not select USB domain clock"); @@ -1618,26 +1563,6 @@ static int udc_stm32_driver_init0(const struct device *dev) } } - /*cd - * Required for at least STM32L4 devices as they electrically - * isolate USB features from VDDUSB. It must be enabled before - * USB can function. Refer to section 5.1.3 in DM00083560 or - * DM00310109. - */ -#ifdef PWR_CR2_USV -#if defined(LL_APB1_GRP1_PERIPH_PWR) - if (LL_APB1_GRP1_IsEnabledClock(LL_APB1_GRP1_PERIPH_PWR)) { - LL_PWR_EnableVddUSB(); - } else { - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR); - LL_PWR_EnableVddUSB(); - LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_PWR); - } - #else - LL_PWR_EnableVddUSB(); -#endif /* defined(LL_APB1_GRP1_PERIPH_PWR) */ -#endif /* PWR_CR2_USV */ - return 0; }