diff --git a/components/esp_driver_mcpwm/src/mcpwm_cap.c b/components/esp_driver_mcpwm/src/mcpwm_cap.c index e85ec9b4119..a195e7eccce 100644 --- a/components/esp_driver_mcpwm/src/mcpwm_cap.c +++ b/components/esp_driver_mcpwm/src/mcpwm_cap.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,11 +14,12 @@ static void mcpwm_capture_default_isr(void *args); -static esp_err_t mcpwm_cap_timer_register_to_group(mcpwm_cap_timer_t *cap_timer, int group_id) +static esp_err_t mcpwm_cap_timer_register_to_group(mcpwm_cap_timer_t *cap_timer, int group_id, soc_module_clk_t clk_src) { - mcpwm_group_t *group = mcpwm_acquire_group_handle(group_id); - ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", group_id); + mcpwm_group_t *group = NULL; + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_ERROR(mcpwm_acquire_group_handle(group_id, clk_src, &group), err, TAG, "acquire group failed"); bool new_timer = false; portENTER_CRITICAL(&group->spinlock); if (!group->cap_timer) { @@ -27,14 +28,16 @@ static esp_err_t mcpwm_cap_timer_register_to_group(mcpwm_cap_timer_t *cap_timer, } portEXIT_CRITICAL(&group->spinlock); - if (!new_timer) { - mcpwm_release_group_handle(group); - group = NULL; - } else { - cap_timer->group = group; - } - ESP_RETURN_ON_FALSE(new_timer, ESP_ERR_NOT_FOUND, TAG, "no free cap timer in group (%d)", group_id); + ESP_GOTO_ON_FALSE(new_timer, ESP_ERR_NOT_FOUND, err, TAG, "no free cap timer in group (%d)", group_id); + + cap_timer->group = group; return ESP_OK; + +err: + if (group) { + mcpwm_release_group_handle(group); + } + return ret; } static void mcpwm_cap_timer_unregister_from_group(mcpwm_cap_timer_t *cap_timer) @@ -75,17 +78,21 @@ esp_err_t mcpwm_new_capture_timer(const mcpwm_capture_timer_config_t *config, mc ESP_RETURN_ON_FALSE(config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported"); #endif // SOC_MCPWM_SUPPORT_SLEEP_RETENTION + mcpwm_capture_clock_source_t clk_src = config->clk_src ? config->clk_src : MCPWM_CAPTURE_CLK_SRC_DEFAULT; + soc_module_clk_t group_clk_src = 0; +#if SOC_MCPWM_CAPTURE_CLK_FROM_GROUP + group_clk_src = clk_src; +#endif + cap_timer = heap_caps_calloc(1, sizeof(mcpwm_cap_timer_t), MCPWM_MEM_ALLOC_CAPS); ESP_GOTO_ON_FALSE(cap_timer, ESP_ERR_NO_MEM, err, TAG, "no mem for capture timer"); - ESP_GOTO_ON_ERROR(mcpwm_cap_timer_register_to_group(cap_timer, config->group_id), err, TAG, "register timer failed"); + ESP_GOTO_ON_ERROR(mcpwm_cap_timer_register_to_group(cap_timer, config->group_id, group_clk_src), err, TAG, "register timer failed"); mcpwm_group_t *group = cap_timer->group; int group_id = group->group_id; - mcpwm_capture_clock_source_t clk_src = config->clk_src ? config->clk_src : MCPWM_CAPTURE_CLK_SRC_DEFAULT; #if SOC_MCPWM_CAPTURE_CLK_FROM_GROUP - // capture timer clock source is same as the MCPWM group - ESP_GOTO_ON_ERROR(mcpwm_select_periph_clock(group, (soc_module_clk_t)clk_src), err, TAG, "set group clock failed"); + // capture timer clock source is same as the MCPWM group, already committed in mcpwm_acquire_group_handle() #if CONFIG_PM_ENABLE cap_timer->pm_lock = group->pm_lock; #endif diff --git a/components/esp_driver_mcpwm/src/mcpwm_com.c b/components/esp_driver_mcpwm/src/mcpwm_com.c index 49a4492c5d6..d81af691794 100644 --- a/components/esp_driver_mcpwm/src/mcpwm_com.c +++ b/components/esp_driver_mcpwm/src/mcpwm_com.c @@ -12,7 +12,11 @@ #if MCPWM_USE_RETENTION_LINK static esp_err_t mcpwm_create_sleep_retention_link_cb(void *arg); +static void mcpwm_release_sleep_retention_module(int group_id); #endif +// NOTE: caller MUST hold s_platform.mutex. This function does not take any lock by itself. +static esp_err_t mcpwm_create_group_unsafe(int group_id, soc_module_clk_t clk_src, mcpwm_group_t **ret_group); +static esp_err_t mcpwm_select_periph_clock_unsafe(mcpwm_group_t *group, soc_module_clk_t clk_src); typedef struct { _lock_t mutex; // platform level mutex lock @@ -22,72 +26,102 @@ typedef struct { static mcpwm_platform_t s_platform; // singleton platform -mcpwm_group_t *mcpwm_acquire_group_handle(int group_id) +esp_err_t mcpwm_acquire_group_handle(int group_id, soc_module_clk_t clk_src, mcpwm_group_t **ret_group) { - bool new_group = false; - mcpwm_group_t *group = NULL; + esp_err_t ret = ESP_OK; + + ESP_RETURN_ON_FALSE(ret_group, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + *ret_group = NULL; // prevent install mcpwm group concurrently _lock_acquire(&s_platform.mutex); - if (!s_platform.groups[group_id]) { - group = heap_caps_calloc(1, sizeof(mcpwm_group_t), MCPWM_MEM_ALLOC_CAPS); - if (group) { - new_group = true; - s_platform.groups[group_id] = group; - group->group_id = group_id; - group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; -#if MCPWM_USE_RETENTION_LINK - sleep_retention_module_t module = mcpwm_retention_infos[group_id].retention_module; - sleep_retention_module_init_param_t init_param = { - .cbs = { - .create = { - .handle = mcpwm_create_sleep_retention_link_cb, - .arg = group, - }, - }, - .attribute = SLEEP_RETENTION_MODULE_ATTR_ATTACH, - .depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM) - }; - // we only do retention init here. Allocate retention module in the unit initialization - if (sleep_retention_module_init(module, &init_param) != ESP_OK) { - // even though the sleep retention module init failed, MCPWM driver should still work, so just warning here - ESP_LOGW(TAG, "init sleep retention failed %d, power domain may be turned off during sleep", group_id); - } -#endif // MCPWM_USE_RETENTION_LINK - // enable APB to access MCPWM registers - PERIPH_RCC_ATOMIC() { - mcpwm_ll_enable_bus_clock(group_id, true); - mcpwm_ll_reset_register(group_id); - } - // enable function clock before initialize HAL context - // MCPWM registers are in the core clock domain, there's a bridge between APB and the Core clock domain - // if the core clock is not enabled, then even the APB clock is enabled, the MCPWM registers are still not accessible - PERIPH_RCC_ATOMIC() { - mcpwm_ll_group_enable_clock(group_id, true); - } - // initialize HAL context - mcpwm_hal_init_config_t hal_config = { - .group_id = group_id - }; - mcpwm_hal_context_t *hal = &group->hal; - mcpwm_hal_init(hal, &hal_config); - // disable all interrupts and clear pending status - mcpwm_ll_intr_enable(hal->dev, UINT32_MAX, false); - mcpwm_ll_intr_clear_status(hal->dev, UINT32_MAX); - } - } else { // group already install - group = s_platform.groups[group_id]; - } + mcpwm_group_t *group = s_platform.groups[group_id]; if (group) { + // commit or verify the group clock source while holding the platform mutex + ret = mcpwm_select_periph_clock_unsafe(group, clk_src); + } else { + ret = mcpwm_create_group_unsafe(group_id, clk_src, &group); + if (ret == ESP_OK) { + s_platform.groups[group_id] = group; + } + } + if (ret == ESP_OK) { // someone acquired the group handle means we have a new object that refer to this group s_platform.group_ref_counts[group_id]++; + *ret_group = group; } _lock_release(&s_platform.mutex); + return ret; +} - if (new_group) { - ESP_LOGD(TAG, "new group(%d) at %p", group_id, group); +// NOTE: caller MUST hold s_platform.mutex. This function does not take any lock by itself. +static esp_err_t mcpwm_create_group_unsafe(int group_id, soc_module_clk_t clk_src, mcpwm_group_t **ret_group) +{ + esp_err_t ret = ESP_OK; + *ret_group = NULL; + mcpwm_group_t *group = heap_caps_calloc(1, sizeof(mcpwm_group_t), MCPWM_MEM_ALLOC_CAPS); + ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", group_id); + + group->group_id = group_id; + group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; +#if MCPWM_USE_RETENTION_LINK + sleep_retention_module_t module = mcpwm_retention_infos[group_id].retention_module; + sleep_retention_module_init_param_t init_param = { + .cbs = { + .create = { + .handle = mcpwm_create_sleep_retention_link_cb, + .arg = group, + }, + }, + .attribute = SLEEP_RETENTION_MODULE_ATTR_ATTACH, + .depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM) + }; + // we only do retention init here. Allocate retention module in the unit initialization + if (sleep_retention_module_init(module, &init_param) != ESP_OK) { + // even though the sleep retention module init failed, MCPWM driver should still work, so just warning here + ESP_LOGW(TAG, "init sleep retention failed %d, power domain may be turned off during sleep", group_id); } - return group; +#endif // MCPWM_USE_RETENTION_LINK + // enable APB to access MCPWM registers + PERIPH_RCC_ATOMIC() { + mcpwm_ll_enable_bus_clock(group_id, true); + mcpwm_ll_reset_register(group_id); + } + // enable function clock before initialize HAL context + // MCPWM registers are in the core clock domain, there's a bridge between APB and the Core clock domain + // if the core clock is not enabled, then even the APB clock is enabled, the MCPWM registers are still not accessible + ESP_GOTO_ON_ERROR(mcpwm_select_periph_clock_unsafe(group, clk_src), err, TAG, "set group clock failed"); + PERIPH_RCC_ATOMIC() { +#if !CONFIG_IDF_TARGET_ESP32 && !CONFIG_IDF_TARGET_ESP32S3 + if (!group->clk_src) { + // Use an always-on source only for safe register bring-up; it is not committed as the group source. + mcpwm_ll_group_set_clock_source(group_id, MCPWM_TIMER_CLK_SRC_XTAL); + } +#endif + mcpwm_ll_group_enable_clock(group_id, true); + } + // initialize HAL context + mcpwm_hal_init_config_t hal_config = { + .group_id = group_id + }; + mcpwm_hal_context_t *hal = &group->hal; + mcpwm_hal_init(hal, &hal_config); + // disable all interrupts and clear pending status + mcpwm_ll_intr_enable(hal->dev, UINT32_MAX, false); + mcpwm_ll_intr_clear_status(hal->dev, UINT32_MAX); + + *ret_group = group; + return ESP_OK; + +err: + PERIPH_RCC_ATOMIC() { + mcpwm_ll_enable_bus_clock(group_id, false); + } +#if MCPWM_USE_RETENTION_LINK + mcpwm_release_sleep_retention_module(group_id); +#endif // MCPWM_USE_RETENTION_LINK + free(group); + return ret; } void mcpwm_release_group_handle(mcpwm_group_t *group) @@ -108,20 +142,17 @@ void mcpwm_release_group_handle(mcpwm_group_t *group) PERIPH_RCC_ATOMIC() { mcpwm_ll_enable_bus_clock(group_id, false); } + // release the group clock source acquired in mcpwm_select_periph_clock_unsafe() + if (group->clk_src) { + esp_clk_tree_enable_src(group->clk_src, false); + } #if CONFIG_PM_ENABLE if (group->pm_lock) { esp_pm_lock_delete(group->pm_lock); } #endif #if MCPWM_USE_RETENTION_LINK - const periph_retention_module_t module_id = mcpwm_retention_infos[group_id].retention_module; - sleep_retention_module_detach(module_id); - if (sleep_retention_is_module_created(module_id)) { - sleep_retention_module_free(module_id); - } - if (sleep_retention_is_module_inited(module_id)) { - sleep_retention_module_deinit(module_id); - } + mcpwm_release_sleep_retention_module(group_id); #endif // MCPWM_USE_RETENTION_LINK free(group); } @@ -132,43 +163,57 @@ void mcpwm_release_group_handle(mcpwm_group_t *group) } } +// The group clock source (group->clk_src / group->pm_lock and the clock source enable/disable) is owned +// exclusively by s_platform.mutex. The caller must hold s_platform.mutex. +static esp_err_t mcpwm_select_periph_clock_unsafe(mcpwm_group_t *group, soc_module_clk_t clk_src) +{ + esp_err_t ret = ESP_OK; + int group_id = group->group_id; + if (!clk_src) { + return ESP_OK; + } + // check if we need to update the group clock source, group clock source is shared by all mcpwm modules + if (group->clk_src) { + ESP_RETURN_ON_FALSE(group->clk_src == clk_src, ESP_ERR_INVALID_STATE, TAG, + "group clock conflict, already is %d but attempt to %d", group->clk_src, clk_src); + return ESP_OK; + } + + group->clk_src = clk_src; + ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true), + err, TAG, "clock source enable failed"); +#if CONFIG_PM_ENABLE + // to make the mcpwm works reliable, the source clock must stay alive and unchanged + esp_pm_lock_type_t pm_lock_type = ESP_PM_NO_LIGHT_SLEEP; +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S3 + // on ESP32 and ESP32S3, MCPWM's clock source (PLL_160M) frequency is automatically reduced during DFS, resulting in an inaccurate time base + // thus we want to use the APB_MAX lock + pm_lock_type = ESP_PM_APB_FREQ_MAX; +#endif + ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_lock_type, 0, soc_mcpwm_signals[group_id].module_name, &group->pm_lock), + err_clock_enabled, TAG, "create pm lock failed"); +#endif // CONFIG_PM_ENABLE + PERIPH_RCC_ATOMIC() { + mcpwm_ll_group_set_clock_source(group_id, clk_src); + } + return ret; + +#if CONFIG_PM_ENABLE +err_clock_enabled: + esp_clk_tree_enable_src((soc_module_clk_t)clk_src, false); +#endif +err: + group->clk_src = 0; + return ret; +} + esp_err_t mcpwm_select_periph_clock(mcpwm_group_t *group, soc_module_clk_t clk_src) { esp_err_t ret = ESP_OK; - bool clock_selection_conflict = false; - bool do_clock_init = false; - int group_id = group->group_id; - // check if we need to update the group clock source, group clock source is shared by all mcpwm modules - portENTER_CRITICAL(&group->spinlock); - if (group->clk_src == 0) { - group->clk_src = clk_src; - do_clock_init = true; - } else { - clock_selection_conflict = (group->clk_src != clk_src); - } - portEXIT_CRITICAL(&group->spinlock); - ESP_RETURN_ON_FALSE(!clock_selection_conflict, ESP_ERR_INVALID_STATE, TAG, - "group clock conflict, already is %d but attempt to %d", group->clk_src, clk_src); - if (do_clock_init) { - -#if CONFIG_PM_ENABLE - // to make the mcpwm works reliable, the source clock must stay alive and unchanged - esp_pm_lock_type_t pm_lock_type = ESP_PM_NO_LIGHT_SLEEP; -#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S3 - // on ESP32 and ESP32S3, MCPWM's clock source (PLL_160M) frequency is automatically reduced during DFS, resulting in an inaccurate time base - // thus we want to use the APB_MAX lock - pm_lock_type = ESP_PM_APB_FREQ_MAX; -#endif - ret = esp_pm_lock_create(pm_lock_type, 0, soc_mcpwm_signals[group_id].module_name, &group->pm_lock); - ESP_RETURN_ON_ERROR(ret, TAG, "create pm lock failed"); -#endif // CONFIG_PM_ENABLE - - ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true), TAG, "clock source enable failed"); - PERIPH_RCC_ATOMIC() { - mcpwm_ll_group_set_clock_source(group_id, clk_src); - } - } + _lock_acquire(&s_platform.mutex); + ret = mcpwm_select_periph_clock_unsafe(group, clk_src); + _lock_release(&s_platform.mutex); return ret; } @@ -256,6 +301,19 @@ static esp_err_t mcpwm_create_sleep_retention_link_cb(void *arg) REGDMA_LINK_PRI_MCPWM, module_id); return err; } + +static void mcpwm_release_sleep_retention_module(int group_id) +{ + const periph_retention_module_t module_id = mcpwm_retention_infos[group_id].retention_module; + sleep_retention_module_detach(module_id); + if (sleep_retention_is_module_created(module_id)) { + sleep_retention_module_free(module_id); + } + if (sleep_retention_is_module_inited(module_id)) { + sleep_retention_module_deinit(module_id); + } +} + void mcpwm_create_retention_module(mcpwm_group_t *group) { int group_id = group->group_id; diff --git a/components/esp_driver_mcpwm/src/mcpwm_fault.c b/components/esp_driver_mcpwm/src/mcpwm_fault.c index b8ae3514486..281ee8fb176 100644 --- a/components/esp_driver_mcpwm/src/mcpwm_fault.c +++ b/components/esp_driver_mcpwm/src/mcpwm_fault.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,9 +16,10 @@ static esp_err_t mcpwm_del_soft_fault(mcpwm_fault_handle_t fault); static esp_err_t mcpwm_gpio_fault_register_to_group(mcpwm_gpio_fault_t *fault, int group_id) { - mcpwm_group_t *group = mcpwm_acquire_group_handle(group_id); - ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", group_id); + mcpwm_group_t *group = NULL; + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_ERROR(mcpwm_acquire_group_handle(group_id, 0, &group), err, TAG, "acquire group failed"); int fault_id = -1; portENTER_CRITICAL(&group->spinlock); for (int i = 0; i < MCPWM_LL_GET(GPIO_FAULTS_PER_GROUP); i++) { @@ -29,15 +30,17 @@ static esp_err_t mcpwm_gpio_fault_register_to_group(mcpwm_gpio_fault_t *fault, i } } portEXIT_CRITICAL(&group->spinlock); - if (fault_id < 0) { - mcpwm_release_group_handle(group); - group = NULL; - } else { - fault->base.group = group; - fault->fault_id = fault_id; - } - ESP_RETURN_ON_FALSE(fault_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free gpio fault in group (%d)", group_id); + ESP_GOTO_ON_FALSE(fault_id >= 0, ESP_ERR_NOT_FOUND, err, TAG, "no free gpio fault in group (%d)", group_id); + + fault->base.group = group; + fault->fault_id = fault_id; return ESP_OK; + +err: + if (group) { + mcpwm_release_group_handle(group); + } + return ret; } static void mcpwm_gpio_fault_unregister_from_group(mcpwm_gpio_fault_t *fault) diff --git a/components/esp_driver_mcpwm/src/mcpwm_oper.c b/components/esp_driver_mcpwm/src/mcpwm_oper.c index f1c4c0325c7..2d255165545 100644 --- a/components/esp_driver_mcpwm/src/mcpwm_oper.c +++ b/components/esp_driver_mcpwm/src/mcpwm_oper.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,9 +12,10 @@ static void mcpwm_operator_default_isr(void *args); static esp_err_t mcpwm_operator_register_to_group(mcpwm_oper_t *oper, int group_id) { - mcpwm_group_t *group = mcpwm_acquire_group_handle(group_id); - ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", group_id); + mcpwm_group_t *group = NULL; + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_ERROR(mcpwm_acquire_group_handle(group_id, 0, &group), err, TAG, "acquire group failed"); int oper_id = -1; portENTER_CRITICAL(&group->spinlock); for (int i = 0; i < MCPWM_LL_GET(OPERATORS_PER_GROUP); i++) { @@ -25,15 +26,17 @@ static esp_err_t mcpwm_operator_register_to_group(mcpwm_oper_t *oper, int group_ } } portEXIT_CRITICAL(&group->spinlock); - if (oper_id < 0) { - mcpwm_release_group_handle(group); - group = NULL; - } else { - oper->group = group; - oper->oper_id = oper_id; - } - ESP_RETURN_ON_FALSE(oper_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free operators in group (%d)", group_id); + ESP_GOTO_ON_FALSE(oper_id >= 0, ESP_ERR_NOT_FOUND, err, TAG, "no free operators in group (%d)", group_id); + + oper->group = group; + oper->oper_id = oper_id; return ESP_OK; + +err: + if (group) { + mcpwm_release_group_handle(group); + } + return ret; } static void mcpwm_operator_unregister_from_group(mcpwm_oper_t *oper) diff --git a/components/esp_driver_mcpwm/src/mcpwm_private.h b/components/esp_driver_mcpwm/src/mcpwm_private.h index 7f3079e159d..063ef6ee22a 100644 --- a/components/esp_driver_mcpwm/src/mcpwm_private.h +++ b/components/esp_driver_mcpwm/src/mcpwm_private.h @@ -285,7 +285,7 @@ struct mcpwm_cap_channel_t { void *user_data; // user data which would be passed to the capture callback }; -mcpwm_group_t *mcpwm_acquire_group_handle(int group_id); +esp_err_t mcpwm_acquire_group_handle(int group_id, soc_module_clk_t clk_src, mcpwm_group_t **ret_group); void mcpwm_release_group_handle(mcpwm_group_t *group); esp_err_t mcpwm_select_periph_clock(mcpwm_group_t *group, soc_module_clk_t clk_src); esp_err_t mcpwm_set_prescale(mcpwm_group_t *group, uint32_t expect_module_resolution_hz, uint32_t module_prescale_max, uint32_t *ret_module_prescale); diff --git a/components/esp_driver_mcpwm/src/mcpwm_sync.c b/components/esp_driver_mcpwm/src/mcpwm_sync.c index 0d5c99d62f2..8be1bdf5864 100644 --- a/components/esp_driver_mcpwm/src/mcpwm_sync.c +++ b/components/esp_driver_mcpwm/src/mcpwm_sync.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -105,9 +105,10 @@ static esp_err_t mcpwm_del_timer_sync_src(mcpwm_sync_t *sync_src) static esp_err_t mcpwm_gpio_sync_src_register_to_group(mcpwm_gpio_sync_src_t *gpio_sync_src, int group_id) { - mcpwm_group_t *group = mcpwm_acquire_group_handle(group_id); - ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", group_id); + mcpwm_group_t *group = NULL; + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_ERROR(mcpwm_acquire_group_handle(group_id, 0, &group), err, TAG, "acquire group failed"); int sync_id = -1; portENTER_CRITICAL(&group->spinlock); for (int i = 0; i < MCPWM_LL_GET(GPIO_SYNCHROS_PER_GROUP); i++) { @@ -119,16 +120,17 @@ static esp_err_t mcpwm_gpio_sync_src_register_to_group(mcpwm_gpio_sync_src_t *gp } portEXIT_CRITICAL(&group->spinlock); - if (sync_id < 0) { - mcpwm_release_group_handle(group); - group = NULL; - } else { - gpio_sync_src->base.group = group; - gpio_sync_src->sync_id = sync_id; - } - ESP_RETURN_ON_FALSE(sync_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free gpio sync_src in group (%d)", group_id); + ESP_GOTO_ON_FALSE(sync_id >= 0, ESP_ERR_NOT_FOUND, err, TAG, "no free gpio sync_src in group (%d)", group_id); + gpio_sync_src->base.group = group; + gpio_sync_src->sync_id = sync_id; return ESP_OK; + +err: + if (group) { + mcpwm_release_group_handle(group); + } + return ret; } static void mcpwm_gpio_sync_src_unregister_from_group(mcpwm_gpio_sync_src_t *gpio_sync_src) diff --git a/components/esp_driver_mcpwm/src/mcpwm_timer.c b/components/esp_driver_mcpwm/src/mcpwm_timer.c index 4ca9c68ebd6..b44ad756b6b 100644 --- a/components/esp_driver_mcpwm/src/mcpwm_timer.c +++ b/components/esp_driver_mcpwm/src/mcpwm_timer.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,11 +12,12 @@ static void mcpwm_timer_default_isr(void *args); -static esp_err_t mcpwm_timer_register_to_group(mcpwm_timer_t *timer, int group_id) +static esp_err_t mcpwm_timer_register_to_group(mcpwm_timer_t *timer, int group_id, soc_module_clk_t clk_src) { - mcpwm_group_t *group = mcpwm_acquire_group_handle(group_id); - ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", group_id); + mcpwm_group_t *group = NULL; + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_ERROR(mcpwm_acquire_group_handle(group_id, clk_src, &group), err, TAG, "acquire group failed"); int timer_id = -1; portENTER_CRITICAL(&group->spinlock); for (int i = 0; i < MCPWM_LL_GET(TIMERS_PER_GROUP); i++) { @@ -27,15 +28,17 @@ static esp_err_t mcpwm_timer_register_to_group(mcpwm_timer_t *timer, int group_i } } portEXIT_CRITICAL(&group->spinlock); - if (timer_id < 0) { - mcpwm_release_group_handle(group); - group = NULL; - } else { - timer->group = group; - timer->timer_id = timer_id; - } - ESP_RETURN_ON_FALSE(timer_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free timer in group (%d)", group_id); + ESP_GOTO_ON_FALSE(timer_id >= 0, ESP_ERR_NOT_FOUND, err, TAG, "no free timer in group (%d)", group_id); + + timer->group = group; + timer->timer_id = timer_id; return ESP_OK; + +err: + if (group) { + mcpwm_release_group_handle(group); + } + return ret; } static void mcpwm_timer_unregister_from_group(mcpwm_timer_t *timer) @@ -85,18 +88,19 @@ esp_err_t mcpwm_new_timer(const mcpwm_timer_config_t *config, mcpwm_timer_handle ESP_RETURN_ON_FALSE(config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported"); #endif // SOC_MCPWM_SUPPORT_SLEEP_RETENTION + // select the clock source before group HAL initialization if this timer creates the group + mcpwm_timer_clock_source_t clk_src = config->clk_src ? config->clk_src : MCPWM_TIMER_CLK_SRC_DEFAULT; + timer = heap_caps_calloc(1, sizeof(mcpwm_timer_t), MCPWM_MEM_ALLOC_CAPS); ESP_GOTO_ON_FALSE(timer, ESP_ERR_NO_MEM, err, TAG, "no mem for timer"); - ESP_GOTO_ON_ERROR(mcpwm_timer_register_to_group(timer, config->group_id), err, TAG, "register timer failed"); + ESP_GOTO_ON_ERROR(mcpwm_timer_register_to_group(timer, config->group_id, (soc_module_clk_t)clk_src), err, TAG, "register timer failed"); mcpwm_group_t *group = timer->group; int group_id = group->group_id; mcpwm_hal_context_t *hal = &group->hal; int timer_id = timer->timer_id; - // select the clock source - mcpwm_timer_clock_source_t clk_src = config->clk_src ? config->clk_src : MCPWM_TIMER_CLK_SRC_DEFAULT; - ESP_GOTO_ON_ERROR(mcpwm_select_periph_clock(group, (soc_module_clk_t)clk_src), err, TAG, "set group clock failed"); + // group clock source has already been committed in mcpwm_acquire_group_handle() // reset the timer to a determined state mcpwm_hal_timer_reset(hal, timer_id); // set timer resolution