From 994d2b4203152ecf77523a1819d050460eee8340 Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Mon, 17 Aug 2026 15:04:56 +0200 Subject: [PATCH] fix(usb_phy): Fixed USB PHY clock during low-power modes On ESP32-S3, USB PHY is clocked from BBPLL, however if the BBPLL is not used as CPU clock source, the power management unit can turn it off. This commit registers USB as BBPLL consumer so it is not turned off. Closes https://github.com/espressif/esp-idf/issues/15451 --- .../esp32s3/include/hal/usb_wrap_ll.h | 1 + .../include/esp_private/rtc_clk.h | 5 ++-- .../esp_hw_support/port/esp32s3/rtc_clk.c | 20 +++++++++++++--- components/esp_hw_support/usb_phy/usb_phy.c | 23 +++++++++++++------ 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/components/esp_hal_usb/esp32s3/include/hal/usb_wrap_ll.h b/components/esp_hal_usb/esp32s3/include/hal/usb_wrap_ll.h index 23a02a83441..c011f5e397e 100644 --- a/components/esp_hal_usb/esp32s3/include/hal/usb_wrap_ll.h +++ b/components/esp_hal_usb/esp32s3/include/hal/usb_wrap_ll.h @@ -17,6 +17,7 @@ /* ----------------------------- Macros & Types ----------------------------- */ #define USB_WRAP_LL_EXT_PHY_SUPPORTED 1 // Can route to an external FSLS PHY +#define USB_WRAP_LL_DEPENDS_ON_BBPLL 1 // USB PHY depends on BBPLL #ifdef __cplusplus extern "C" { diff --git a/components/esp_hw_support/include/esp_private/rtc_clk.h b/components/esp_hw_support/include/esp_private/rtc_clk.h index 4d4402c92a5..c1f650c7eda 100644 --- a/components/esp_hw_support/include/esp_private/rtc_clk.h +++ b/components/esp_hw_support/include/esp_private/rtc_clk.h @@ -45,9 +45,10 @@ void rtc_clk_cpu_freq_set_xtal_for_sleep(void); /** * @brief Notify that the BBPLL has a new in-use consumer * - * Currently, this function is only used for tracking whether USB Serial/JTAG is using the 48MHz PHY clock + * Currently, this function is used for tracking whether USB PHY is using the 48MHz PHY clock. * - * Note: Calling this function only helps to not disable the BBPLL clock in `rtc_clk_cpu_freq_set_config`. + * The first consumer enables BBPLL analog power. Later `rtc_clk_cpu_freq_set_config` will not + * disable BBPLL while any consumer remains. */ void rtc_clk_bbpll_add_consumer(void); diff --git a/components/esp_hw_support/port/esp32s3/rtc_clk.c b/components/esp_hw_support/port/esp32s3/rtc_clk.c index 13169a76e83..21cfe5f46de 100644 --- a/components/esp_hw_support/port/esp32s3/rtc_clk.c +++ b/components/esp_hw_support/port/esp32s3/rtc_clk.c @@ -35,22 +35,36 @@ static uint32_t s_apb_freq; void rtc_clk_cpu_freq_to_xtal(int freq, int div); static void rtc_clk_cpu_freq_to_rc_fast(void); +static void rtc_clk_bbpll_enable(void); +static void rtc_clk_bbpll_configure(soc_xtal_freq_t xtal_freq, int pll_freq); extern uint32_t g_dig_dbias_pvt_240m; extern uint32_t g_rtc_dbias_pvt_240m; extern uint32_t g_dig_dbias_pvt_non_240m; extern uint32_t g_rtc_dbias_pvt_non_240m; -static uint32_t s_bbpll_digi_consumers_ref_count = 0; // Currently, it only tracks whether the 48MHz PHY clock is in-use by USB Serial/JTAG +static uint32_t s_bbpll_digi_consumers_ref_count = 0; // Currently, it tracks whether the 48MHz PHY clock is in-use by USB void rtc_clk_bbpll_add_consumer(void) { - s_bbpll_digi_consumers_ref_count += 1; + // Should be refactored in PM-653 + if (s_bbpll_digi_consumers_ref_count == 0) { + if (s_cur_pll_freq == 0) { + rtc_clk_bbpll_enable(); + rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), CLK_LL_PLL_480M_FREQ_MHZ); + } + } + s_bbpll_digi_consumers_ref_count++; } void rtc_clk_bbpll_remove_consumer(void) { - s_bbpll_digi_consumers_ref_count -= 1; + // Should be refactored in PM-653 + if (s_bbpll_digi_consumers_ref_count > 0) { + s_bbpll_digi_consumers_ref_count--; + } else { + ESP_HW_LOGW(TAG, "bbpll clk ref cnt mismatched!"); + } } void rtc_clk_32k_enable(bool enable) diff --git a/components/esp_hw_support/usb_phy/usb_phy.c b/components/esp_hw_support/usb_phy/usb_phy.c index 57fc7fca215..eedfde1a71d 100644 --- a/components/esp_hw_support/usb_phy/usb_phy.c +++ b/components/esp_hw_support/usb_phy/usb_phy.c @@ -19,6 +19,9 @@ #include "hal/usb_utmi_hal.h" #include "hal/gpio_ll.h" #include "soc/soc_caps.h" +#if USB_WRAP_LL_DEPENDS_ON_BBPLL +#include "esp_private/rtc_clk.h" +#endif #if (SOC_USB_FSLS_PHY_NUM > 0) #define USB_PHY_FSLS_EXT_PHY_SUPPORTED USB_WRAP_LL_EXT_PHY_SUPPORTED @@ -327,13 +330,6 @@ esp_err_t usb_new_phy(const usb_phy_config_t *config, usb_phy_handle_t *handle_r } #endif - // For FSLS PHY that shares pads with GPIO peripheral, we must set drive capability to 3 (40mA) - if (phy_target == USB_PHY_TARGET_INT) { - assert(usb_dwc_info.controllers[otg11_index].internal_phy_io); - gpio_ll_set_drive_capability(GPIO_LL_GET_HW(0), usb_dwc_info.controllers[otg11_index].internal_phy_io->dm, GPIO_DRIVE_CAP_3); - gpio_ll_set_drive_capability(GPIO_LL_GET_HW(0), usb_dwc_info.controllers[otg11_index].internal_phy_io->dp, GPIO_DRIVE_CAP_3); - } - *handle_ret = (usb_phy_handle_t) phy_context; if (phy_target == USB_PHY_TARGET_EXT) { phy_context->iopins = (usb_phy_ext_io_conf_t *) calloc(1, sizeof(usb_phy_ext_io_conf_t)); @@ -349,6 +345,16 @@ esp_err_t usb_new_phy(const usb_phy_config_t *config, usb_phy_handle_t *handle_r const usb_otg_signal_conn_t *otg_sig = usb_dwc_info.controllers[otg11_index].otg_signals; ESP_ERROR_CHECK(phy_otg_iopins_configure(config->otg_io_conf, otg_sig)); } + if (phy_target == USB_PHY_TARGET_INT) { +#if USB_WRAP_LL_DEPENDS_ON_BBPLL + // PHY clock is derived from BBPLL. Do not turn off BBPLL during low-power modes + rtc_clk_bbpll_add_consumer(); +#endif + // For FSLS internal PHY that shares pads with GPIO peripheral, we must set drive capability to 3 (40mA) + assert(usb_dwc_info.controllers[otg11_index].internal_phy_io); + gpio_ll_set_drive_capability(GPIO_LL_GET_HW(0), usb_dwc_info.controllers[otg11_index].internal_phy_io->dm, GPIO_DRIVE_CAP_3); + gpio_ll_set_drive_capability(GPIO_LL_GET_HW(0), usb_dwc_info.controllers[otg11_index].internal_phy_io->dp, GPIO_DRIVE_CAP_3); + } return ESP_OK; cleanup: @@ -397,6 +403,9 @@ esp_err_t usb_del_phy(usb_phy_handle_t handle) // Clear pullup and pulldown loads on D+ / D-, and disable the pads usb_wrap_hal_phy_disable_pull_override(&handle->wrap_hal); p_phy_ctrl_obj->fsls_phy = NULL; +#if USB_WRAP_LL_DEPENDS_ON_BBPLL + rtc_clk_bbpll_remove_consumer(); +#endif #endif } else { // USB_PHY_TARGET_UTMI p_phy_ctrl_obj->utmi_phy = NULL;