feat(mcpwm): fix mcpwm clock init failure

Closes https://github.com/espressif/esp-idf/issues/18666
Closes https://github.com/espressif/esp-idf/issues/18777
This commit is contained in:
Chen Chen
2026-06-23 14:20:13 +08:00
parent cadfa9c096
commit 5a3f6eccc5
7 changed files with 238 additions and 161 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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)

View File

@@ -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)

View File

@@ -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);

View File

@@ -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)

View File

@@ -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