From 2e44cb93873215a996571fec4990f7918892a0ba Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Wed, 6 Nov 2024 17:34:02 +0800 Subject: [PATCH] feat(mcpwm): support sleep retention --- .../include/driver/mcpwm_cap.h | 7 +- .../include/driver/mcpwm_timer.h | 3 + components/esp_driver_mcpwm/src/mcpwm_cap.c | 11 + components/esp_driver_mcpwm/src/mcpwm_com.c | 57 +++++ .../esp_driver_mcpwm/src/mcpwm_private.h | 10 +- components/esp_driver_mcpwm/src/mcpwm_timer.c | 13 +- .../test_apps/mcpwm/main/CMakeLists.txt | 5 + .../test_apps/mcpwm/main/test_mcpwm_sleep.c | 229 ++++++++++++++++++ .../test_apps/mcpwm/sdkconfig.ci.release | 1 + .../test_apps/mcpwm/sdkconfig.defaults | 3 + .../esp32c5/include/soc/Kconfig.soc_caps.in | 4 + .../include/soc/retention_periph_defs.h | 3 + components/soc/esp32c5/include/soc/soc_caps.h | 1 + components/soc/esp32c5/mcpwm_periph.c | 44 ++++ .../esp32c6/include/soc/Kconfig.soc_caps.in | 4 + .../include/soc/retention_periph_defs.h | 3 + components/soc/esp32c6/include/soc/soc_caps.h | 1 + components/soc/esp32c6/mcpwm_periph.c | 46 +++- .../esp32h2/include/soc/Kconfig.soc_caps.in | 4 + .../include/soc/retention_periph_defs.h | 3 + components/soc/esp32h2/include/soc/soc_caps.h | 1 + components/soc/esp32h2/mcpwm_periph.c | 46 +++- components/soc/esp32p4/include/soc/soc_caps.h | 1 + components/soc/esp32p4/mcpwm_periph.c | 57 ++++- components/soc/include/soc/mcpwm_periph.h | 17 +- components/soc/include/soc/regdma.h | 2 + docs/en/api-reference/peripherals/mcpwm.rst | 5 + .../api-reference/system/power_management.rst | 2 +- .../zh_CN/api-reference/peripherals/mcpwm.rst | 5 + .../api-reference/system/power_management.rst | 2 +- 30 files changed, 581 insertions(+), 9 deletions(-) create mode 100644 components/esp_driver_mcpwm/test_apps/mcpwm/main/test_mcpwm_sleep.c diff --git a/components/esp_driver_mcpwm/include/driver/mcpwm_cap.h b/components/esp_driver_mcpwm/include/driver/mcpwm_cap.h index 601454b82a..a56d903bc3 100644 --- a/components/esp_driver_mcpwm/include/driver/mcpwm_cap.h +++ b/components/esp_driver_mcpwm/include/driver/mcpwm_cap.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -22,6 +22,11 @@ typedef struct { int group_id; /*!< Specify from which group to allocate the capture timer */ mcpwm_capture_clock_source_t clk_src; /*!< MCPWM capture timer clock source */ uint32_t resolution_hz; /*!< Resolution of capture timer */ + struct { + uint32_t allow_pd: 1; /*!< Set to allow power down. When this flag set, the driver will backup/restore the MCPWM registers before/after entering/exist sleep mode. + By this approach, the system can power off MCPWM's power domain. + This can save power, but at the expense of more RAM being consumed.*/ + } flags; /*!< Extra configuration flags for timer */ } mcpwm_capture_timer_config_t; /** diff --git a/components/esp_driver_mcpwm/include/driver/mcpwm_timer.h b/components/esp_driver_mcpwm/include/driver/mcpwm_timer.h index dcf6b96e83..69a8b7a3b6 100644 --- a/components/esp_driver_mcpwm/include/driver/mcpwm_timer.h +++ b/components/esp_driver_mcpwm/include/driver/mcpwm_timer.h @@ -40,6 +40,9 @@ typedef struct { struct { uint32_t update_period_on_empty: 1; /*!< Whether to update period when timer counts to zero */ uint32_t update_period_on_sync: 1; /*!< Whether to update period on sync event */ + uint32_t allow_pd: 1; /*!< Set to allow power down. When this flag set, the driver will backup/restore the MCPWM registers before/after entering/exist sleep mode. + By this approach, the system can power off MCPWM's power domain. + This can save power, but at the expense of more RAM being consumed. */ } flags; /*!< Extra configuration flags for timer */ } mcpwm_timer_config_t; diff --git a/components/esp_driver_mcpwm/src/mcpwm_cap.c b/components/esp_driver_mcpwm/src/mcpwm_cap.c index c80eedd681..7d68cf74fc 100644 --- a/components/esp_driver_mcpwm/src/mcpwm_cap.c +++ b/components/esp_driver_mcpwm/src/mcpwm_cap.c @@ -94,6 +94,10 @@ esp_err_t mcpwm_new_capture_timer(const mcpwm_capture_timer_config_t *config, mc ESP_GOTO_ON_FALSE(config->group_id < SOC_MCPWM_GROUPS && config->group_id >= 0, ESP_ERR_INVALID_ARG, err, TAG, "invalid group ID:%d", config->group_id); +#if !SOC_MCPWM_SUPPORT_SLEEP_RETENTION + 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 + 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"); @@ -139,6 +143,13 @@ esp_err_t mcpwm_new_capture_timer(const mcpwm_capture_timer_config_t *config, mc cap_timer->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; cap_timer->fsm = MCPWM_CAP_TIMER_FSM_INIT; *ret_cap_timer = cap_timer; + +#if MCPWM_USE_RETENTION_LINK + if (config->flags.allow_pd != 0) { + mcpwm_create_retention_module(group); + } +#endif // MCPWM_USE_RETENTION_LINK + ESP_LOGD(TAG, "new capture timer at %p, in group (%d), resolution %"PRIu32, cap_timer, group_id, cap_timer->resolution_hz); return ESP_OK; diff --git a/components/esp_driver_mcpwm/src/mcpwm_com.c b/components/esp_driver_mcpwm/src/mcpwm_com.c index a3bdd38269..ae57a8510a 100644 --- a/components/esp_driver_mcpwm/src/mcpwm_com.c +++ b/components/esp_driver_mcpwm/src/mcpwm_com.c @@ -21,6 +21,7 @@ #include "soc/soc_caps.h" #include "hal/mcpwm_ll.h" #include "mcpwm_private.h" +#include "esp_private/rtc_clk.h" #if SOC_PERIPH_CLK_CTRL_SHARED #define MCPWM_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC() @@ -34,6 +35,10 @@ #define MCPWM_RCC_ATOMIC() #endif +#if MCPWM_USE_RETENTION_LINK +static esp_err_t mcpwm_create_sleep_retention_link_cb(void *arg); +#endif + static const char *TAG = "mcpwm"; typedef struct { @@ -59,6 +64,23 @@ mcpwm_group_t *mcpwm_acquire_group_handle(int group_id) group->group_id = group_id; group->intr_priority = -1; group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; +#if MCPWM_USE_RETENTION_LINK + sleep_retention_module_t module = mcpwm_reg_retention_info[group_id].retention_module; + sleep_retention_module_init_param_t init_param = { + .cbs = { + .create = { + .handle = mcpwm_create_sleep_retention_link_cb, + .arg = group, + }, + }, + .depends = SLEEP_RETENTION_MODULE_BM_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 MCPWM_RCC_ATOMIC() { mcpwm_ll_enable_bus_clock(group_id, true); @@ -116,6 +138,15 @@ void mcpwm_release_group_handle(mcpwm_group_t *group) if (group->pm_lock) { esp_pm_lock_delete(group->pm_lock); } +#if MCPWM_USE_RETENTION_LINK + const periph_retention_module_t module_id = mcpwm_reg_retention_info[group_id].retention_module; + if (sleep_retention_get_created_modules() & BIT(module_id)) { + sleep_retention_module_free(module_id); + } + if (sleep_retention_get_inited_modules() & BIT(module_id)) { + sleep_retention_module_deinit(module_id); + } +#endif // MCPWM_USE_RETENTION_LINK free(group); } _lock_release(&s_platform.mutex); @@ -257,3 +288,29 @@ esp_err_t mcpwm_set_prescale(mcpwm_group_t *group, uint32_t expect_module_resolu return ESP_OK; } + +#if MCPWM_USE_RETENTION_LINK +static esp_err_t mcpwm_create_sleep_retention_link_cb(void *arg) +{ + mcpwm_group_t *group = (mcpwm_group_t *)arg; + int group_id = group->group_id; + sleep_retention_module_t module_id = mcpwm_reg_retention_info[group_id].retention_module; + esp_err_t err = sleep_retention_entries_create(mcpwm_reg_retention_info[group_id].regdma_entry_array, + mcpwm_reg_retention_info[group_id].array_size, + REGDMA_LINK_PRI_MCPWM, module_id); + return err; +} +void mcpwm_create_retention_module(mcpwm_group_t *group) +{ + int group_id = group->group_id; + sleep_retention_module_t module_id = mcpwm_reg_retention_info[group_id].retention_module; + _lock_acquire(&s_platform.mutex); + if ((sleep_retention_get_inited_modules() & BIT(module_id)) && !(sleep_retention_get_created_modules() & BIT(module_id))) { + if (sleep_retention_module_allocate(module_id) != ESP_OK) { + // even though the sleep retention module create failed, MCPWM driver should still work, so just warning here + ESP_LOGW(TAG, "create retention module failed, power domain can't turn off"); + } + } + _lock_release(&s_platform.mutex); +} +#endif // MCPWM_USE_RETENTION_LINK diff --git a/components/esp_driver_mcpwm/src/mcpwm_private.h b/components/esp_driver_mcpwm/src/mcpwm_private.h index e299d3dd80..0a51d6e070 100644 --- a/components/esp_driver_mcpwm/src/mcpwm_private.h +++ b/components/esp_driver_mcpwm/src/mcpwm_private.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,6 +17,7 @@ #include "hal/mcpwm_hal.h" #include "hal/mcpwm_types.h" #include "driver/mcpwm_types.h" +#include "esp_private/sleep_retention.h" #ifdef __cplusplus extern "C" { @@ -34,6 +35,9 @@ extern "C" { #define MCPWM_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_INTRDISABLED) #endif +// Use retention link only when the target supports sleep retention is enabled +#define MCPWM_USE_RETENTION_LINK (SOC_MCPWM_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP) + #define MCPWM_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED #define MCPWM_GROUP_CLOCK_DEFAULT_PRESCALE 2 @@ -259,6 +263,10 @@ int mcpwm_get_intr_priority_flag(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); +#if MCPWM_USE_RETENTION_LINK +void mcpwm_create_retention_module(mcpwm_group_t *group); +#endif + #ifdef __cplusplus } #endif diff --git a/components/esp_driver_mcpwm/src/mcpwm_timer.c b/components/esp_driver_mcpwm/src/mcpwm_timer.c index 19dc756af1..8acedf36ab 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-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -102,6 +102,10 @@ esp_err_t mcpwm_new_timer(const mcpwm_timer_config_t *config, mcpwm_timer_handle } ESP_GOTO_ON_FALSE(peak_ticks > 0 && peak_ticks < MCPWM_LL_MAX_COUNT_VALUE, ESP_ERR_INVALID_ARG, err, TAG, "invalid period ticks"); +#if !SOC_MCPWM_SUPPORT_SLEEP_RETENTION + 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 + 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"); @@ -143,6 +147,13 @@ esp_err_t mcpwm_new_timer(const mcpwm_timer_config_t *config, mcpwm_timer_handle timer->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; timer->fsm = MCPWM_TIMER_FSM_INIT; *ret_timer = timer; + +#if MCPWM_USE_RETENTION_LINK + if (config->flags.allow_pd != 0) { + mcpwm_create_retention_module(group); + } +#endif // MCPWM_USE_RETENTION_LINK + ESP_LOGD(TAG, "new timer(%d,%d) at %p, resolution:%"PRIu32"Hz, peak:%"PRIu32", count_mod:%c", group_id, timer_id, timer, timer->resolution_hz, timer->peak_ticks, "SUDB"[timer->count_mode]); return ESP_OK; diff --git a/components/esp_driver_mcpwm/test_apps/mcpwm/main/CMakeLists.txt b/components/esp_driver_mcpwm/test_apps/mcpwm/main/CMakeLists.txt index d4f59e540d..42208fabf3 100644 --- a/components/esp_driver_mcpwm/test_apps/mcpwm/main/CMakeLists.txt +++ b/components/esp_driver_mcpwm/test_apps/mcpwm/main/CMakeLists.txt @@ -17,6 +17,11 @@ if(CONFIG_SOC_ETM_SUPPORTED AND CONFIG_SOC_MCPWM_SUPPORT_ETM) list(APPEND srcs "test_mcpwm_etm.c") endif() +# TODO: IDF-9928 support ESP32P4 +if(CONFIG_SOC_LIGHT_SLEEP_SUPPORTED AND CONFIG_PM_ENABLE AND NOT CONFIG_IDF_TARGET_ESP32P4) + list(APPEND srcs "test_mcpwm_sleep.c") +endif() + # In order for the cases defined by `TEST_CASE` to be linked into the final elf, # the component can be registered as WHOLE_ARCHIVE idf_component_register(SRCS ${srcs} diff --git a/components/esp_driver_mcpwm/test_apps/mcpwm/main/test_mcpwm_sleep.c b/components/esp_driver_mcpwm/test_apps/mcpwm/main/test_mcpwm_sleep.c new file mode 100644 index 0000000000..e1b0840eb1 --- /dev/null +++ b/components/esp_driver_mcpwm/test_apps/mcpwm/main/test_mcpwm_sleep.c @@ -0,0 +1,229 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "unity.h" +#include "unity_test_utils.h" +#include "esp_attr.h" +#include "esp_clk_tree.h" +#include "driver/gpio.h" +#include "driver/mcpwm_prelude.h" +#include "esp_private/sleep_cpu.h" +#include "esp_private/esp_sleep_internal.h" +#include "esp_private/sleep_retention.h" +#include "esp_private/esp_pmu.h" +#include "test_mcpwm_utils.h" + +/** + * @brief Test the MCPWM timer can still work after light sleep + * + * @param allow_pd Whether to allow power down the peripheral in light sleep + */ +static void test_mcpwm_timer_sleep_retention(bool allow_pd) +{ + const int generator_gpio = TEST_PWMA_GPIO; + printf("init a gpio to read generator output\r\n"); + gpio_config_t gpio_conf = { + .mode = GPIO_MODE_INPUT, + .pin_bit_mask = BIT(generator_gpio), + }; + TEST_ESP_OK(gpio_config(&gpio_conf)); + + printf("create timer and operator\r\n"); + mcpwm_timer_config_t timer_config = { + .group_id = 0, + .clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT, + .resolution_hz = 1000000, + .count_mode = MCPWM_TIMER_COUNT_MODE_UP, + .period_ticks = 1000, + .flags.allow_pd = allow_pd, + }; + mcpwm_timer_handle_t timer = NULL; + TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer)); + + mcpwm_operator_config_t oper_config = { + .group_id = 0, + }; + mcpwm_oper_handle_t oper = NULL; + TEST_ESP_OK(mcpwm_new_operator(&oper_config, &oper)); + + printf("connect timer and operator\r\n"); + TEST_ESP_OK(mcpwm_operator_connect_timer(oper, timer)); + + printf("create generator\r\n"); + mcpwm_generator_config_t gen_config = { + .gen_gpio_num = generator_gpio, + .flags.io_loop_back = true, + }; + mcpwm_gen_handle_t gen = NULL; + TEST_ESP_OK(mcpwm_new_generator(oper, &gen_config, &gen)); + + printf("set generator to output high on timer full\r\n"); + TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(gen, + MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_FULL, MCPWM_GEN_ACTION_HIGH), + MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_KEEP), + MCPWM_GEN_TIMER_EVENT_ACTION_END())); + + // go to sleep + esp_sleep_context_t sleep_ctx; + esp_sleep_set_sleep_context(&sleep_ctx); + printf("go to light sleep for 1 seconds\r\n"); +#if ESP_SLEEP_POWER_DOWN_CPU + TEST_ESP_OK(sleep_cpu_configure(true)); +#endif + TEST_ESP_OK(esp_sleep_enable_timer_wakeup(1 * 1000 * 1000)); + TEST_ESP_OK(esp_light_sleep_start()); + + printf("Waked up! Let's see if MCPWM driver can still work...\r\n"); +#if ESP_SLEEP_POWER_DOWN_CPU + TEST_ESP_OK(sleep_cpu_configure(false)); +#endif + + printf("check if the sleep happened as expected\r\n"); + TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result); +#if SOC_MCPWM_SUPPORT_SLEEP_RETENTION + // check if the power domain also is powered down + TEST_ASSERT_EQUAL(allow_pd ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP); +#endif + esp_sleep_set_sleep_context(NULL); + + printf("enable timer\r\n"); + TEST_ESP_OK(mcpwm_timer_enable(timer)); + printf("start timer\r\n"); + TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP)); + vTaskDelay(pdMS_TO_TICKS(100)); + printf("stop timer on full\r\n"); + TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_FULL)); + vTaskDelay(pdMS_TO_TICKS(100)); + TEST_ASSERT_EQUAL(1, gpio_get_level(generator_gpio)); + + printf("delete timer, operator, generator\r\n"); + TEST_ESP_OK(mcpwm_timer_disable(timer)); + TEST_ESP_OK(mcpwm_del_generator(gen)); + TEST_ESP_OK(mcpwm_del_operator(oper)); + TEST_ESP_OK(mcpwm_del_timer(timer)); + TEST_ESP_OK(gpio_reset_pin(generator_gpio)); +} + +TEST_CASE("mcpwm_timer_sleep_retention", "[mcpwm]") +{ + test_mcpwm_timer_sleep_retention(false); +#if SOC_MCPWM_SUPPORT_SLEEP_RETENTION + test_mcpwm_timer_sleep_retention(true); +#endif +} + +TEST_MCPWM_CALLBACK_ATTR +static bool test_capture_callback(mcpwm_cap_channel_handle_t cap_channel, const mcpwm_capture_event_data_t *edata, void *user_data) +{ + uint32_t *cap_value = (uint32_t *)user_data; + if (edata->cap_edge == MCPWM_CAP_EDGE_NEG) { + cap_value[1] = edata->cap_value; + } else { + cap_value[0] = edata->cap_value; + } + return false; +} + +/** + * @brief Test the MCPWM capture timer can still work after light sleep + * + * @param allow_pd Whether to allow power down the peripheral in light sleep + */ +static void test_mcpwm_capture_timer_sleep_retention(bool allow_pd) +{ + const int cap_gpio = TEST_CAP_GPIO; + printf("init a gpio to simulate the external capture signal\r\n"); + gpio_config_t gpio_conf = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = BIT(cap_gpio), + }; + TEST_ESP_OK(gpio_config(&gpio_conf)); + // reset the gpio + TEST_ESP_OK(gpio_set_level(cap_gpio, 0)); + // install mcpwm capture timer + mcpwm_cap_timer_handle_t cap_timer = NULL; + uint32_t clk_src_freq_hz; + esp_clk_tree_src_get_freq_hz(MCPWM_CAPTURE_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz); + mcpwm_capture_timer_config_t cap_timer_config = { + .clk_src = MCPWM_CAPTURE_CLK_SRC_DEFAULT, + .group_id = 0, + .resolution_hz = clk_src_freq_hz / 2, + .flags.allow_pd = allow_pd, + }; + TEST_ESP_OK(mcpwm_new_capture_timer(&cap_timer_config, &cap_timer)); + // install mcpwm capture channel + mcpwm_cap_channel_handle_t cap_channel; + mcpwm_capture_channel_config_t cap_chan_config = { + .gpio_num = cap_gpio, + .prescale = 1, + .flags.pos_edge = true, + .flags.neg_edge = true, + }; + TEST_ESP_OK(mcpwm_new_capture_channel(cap_timer, &cap_chan_config, &cap_channel)); + + // install callback for capture channel + mcpwm_capture_event_callbacks_t cbs = { + .on_cap = test_capture_callback, + }; + uint32_t cap_value[2] = {0}; + TEST_ESP_OK(mcpwm_capture_channel_register_event_callbacks(cap_channel, &cbs, cap_value)); + + // go to sleep + esp_sleep_context_t sleep_ctx; + esp_sleep_set_sleep_context(&sleep_ctx); + printf("go to light sleep for 1 seconds\r\n"); +#if ESP_SLEEP_POWER_DOWN_CPU + TEST_ESP_OK(sleep_cpu_configure(true)); +#endif + TEST_ESP_OK(esp_sleep_enable_timer_wakeup(1 * 1000 * 1000)); + TEST_ESP_OK(esp_light_sleep_start()); + + printf("Waked up! Let's see if MCPWM driver can still work...\r\n"); +#if ESP_SLEEP_POWER_DOWN_CPU + TEST_ESP_OK(sleep_cpu_configure(false)); +#endif + + printf("check if the sleep happened as expected\r\n"); + TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result); +#if SOC_MCPWM_SUPPORT_SLEEP_RETENTION + // check if the power domain also is powered down + TEST_ASSERT_EQUAL(allow_pd ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP); +#endif + esp_sleep_set_sleep_context(NULL); + + printf("enable capture\r\n"); + TEST_ESP_OK(mcpwm_capture_channel_enable(cap_channel)); + TEST_ESP_OK(mcpwm_capture_timer_enable(cap_timer)); + + printf("simulate GPIO capture signal\r\n"); + TEST_ESP_OK(mcpwm_capture_timer_start(cap_timer)); + TEST_ESP_OK(gpio_set_level(cap_gpio, 1)); + esp_rom_delay_us(10 * 1000); + TEST_ESP_OK(gpio_set_level(cap_gpio, 0)); + + uint32_t clk_src_res; + TEST_ESP_OK(mcpwm_capture_timer_get_resolution(cap_timer, &clk_src_res)); + clk_src_res /= 1000; // convert to kHz + uint32_t capture_value = (cap_value[1] - cap_value[0]) * 1000 / clk_src_res; + TEST_ASSERT_UINT_WITHIN(1000, 10000, capture_value); + + printf("delete timer, operator, generator\r\n"); + TEST_ESP_OK(mcpwm_capture_channel_disable(cap_channel)); + TEST_ESP_OK(mcpwm_del_capture_channel(cap_channel)); + TEST_ESP_OK(mcpwm_capture_timer_disable(cap_timer)); + TEST_ESP_OK(mcpwm_del_capture_timer(cap_timer)); + TEST_ESP_OK(gpio_reset_pin(cap_gpio)); +} + +TEST_CASE("mcpwm_capture_timer_sleep_retention", "[mcpwm]") +{ + test_mcpwm_capture_timer_sleep_retention(false); +#if SOC_MCPWM_SUPPORT_SLEEP_RETENTION + test_mcpwm_capture_timer_sleep_retention(true); +#endif +} diff --git a/components/esp_driver_mcpwm/test_apps/mcpwm/sdkconfig.ci.release b/components/esp_driver_mcpwm/test_apps/mcpwm/sdkconfig.ci.release index 91d93f163e..17aaee1e8e 100644 --- a/components/esp_driver_mcpwm/test_apps/mcpwm/sdkconfig.ci.release +++ b/components/esp_driver_mcpwm/test_apps/mcpwm/sdkconfig.ci.release @@ -1,5 +1,6 @@ CONFIG_PM_ENABLE=y CONFIG_FREERTOS_USE_TICKLESS_IDLE=y +CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y CONFIG_COMPILER_OPTIMIZATION_SIZE=y CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y diff --git a/components/esp_driver_mcpwm/test_apps/mcpwm/sdkconfig.defaults b/components/esp_driver_mcpwm/test_apps/mcpwm/sdkconfig.defaults index 8e326e32e1..d7d65b1665 100644 --- a/components/esp_driver_mcpwm/test_apps/mcpwm/sdkconfig.defaults +++ b/components/esp_driver_mcpwm/test_apps/mcpwm/sdkconfig.defaults @@ -1,2 +1,5 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_ESP_TASK_WDT_INIT=n + +# primitives for checking sleep internal state +CONFIG_ESP_SLEEP_DEBUG=y diff --git a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in index e1ceec5368..f2cb4fe872 100644 --- a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in @@ -887,6 +887,10 @@ config SOC_MCPWM_CAPTURE_CLK_FROM_GROUP bool default y +config SOC_MCPWM_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_PARLIO_GROUPS int default 1 diff --git a/components/soc/esp32c5/include/soc/retention_periph_defs.h b/components/soc/esp32c5/include/soc/retention_periph_defs.h index 2396ca2c13..768dddce2f 100644 --- a/components/soc/esp32c5/include/soc/retention_periph_defs.h +++ b/components/soc/esp32c5/include/soc/retention_periph_defs.h @@ -44,6 +44,7 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_GPSPI2 = 20, SLEEP_RETENTION_MODULE_LEDC = 21, SLEEP_RETENTION_MODULE_PCNT0 = 22, + SLEEP_RETENTION_MODULE_MCPWM0 = 23, /* modem module, which includes WiFi, BLE and 802.15.4 */ SLEEP_RETENTION_MODULE_WIFI_MAC = 26, @@ -91,6 +92,7 @@ typedef enum periph_retention_module_bitmap { SLEEP_RETENTION_MODULE_BM_GPSPI2 = BIT(SLEEP_RETENTION_MODULE_GPSPI2), SLEEP_RETENTION_MODULE_BM_LEDC = BIT(SLEEP_RETENTION_MODULE_LEDC), SLEEP_RETENTION_MODULE_BM_PCNT0 = BIT(SLEEP_RETENTION_MODULE_PCNT0), + SLEEP_RETENTION_MODULE_BM_MCPWM0 = BIT(SLEEP_RETENTION_MODULE_MCPWM0), SLEEP_RETENTION_MODULE_BM_GDMA_CH0 = BIT(SLEEP_RETENTION_MODULE_GDMA_CH0), SLEEP_RETENTION_MODULE_BM_GDMA_CH1 = BIT(SLEEP_RETENTION_MODULE_GDMA_CH1), @@ -118,6 +120,7 @@ typedef enum periph_retention_module_bitmap { | SLEEP_RETENTION_MODULE_BM_GPSPI2 \ | SLEEP_RETENTION_MODULE_BM_LEDC \ | SLEEP_RETENTION_MODULE_BM_PCNT0 \ + | SLEEP_RETENTION_MODULE_BM_MCPWM0 \ | SLEEP_RETENTION_MODULE_BM_NULL \ ) #ifdef __cplusplus diff --git a/components/soc/esp32c5/include/soc/soc_caps.h b/components/soc/esp32c5/include/soc/soc_caps.h index 928e92e844..c80973323c 100644 --- a/components/soc/esp32c5/include/soc/soc_caps.h +++ b/components/soc/esp32c5/include/soc/soc_caps.h @@ -358,6 +358,7 @@ #define SOC_MCPWM_SUPPORT_ETM 1 ///< Support ETM (Event Task Matrix) #define SOC_MCPWM_SUPPORT_EVENT_COMPARATOR 1 ///< Support event comparator (based on ETM) #define SOC_MCPWM_CAPTURE_CLK_FROM_GROUP 1 ///< Capture timer shares clock with other PWM timers +#define SOC_MCPWM_SUPPORT_SLEEP_RETENTION 1 ///< Support back up registers before sleep /*------------------------ USB SERIAL JTAG CAPS ------------------------------*/ // #define SOC_USB_SERIAL_JTAG_SUPPORT_LIGHT_SLEEP (1) /*!< Support to maintain minimum usb communication during light sleep */ // TODO: IDF-6395 diff --git a/components/soc/esp32c5/mcpwm_periph.c b/components/soc/esp32c5/mcpwm_periph.c index 87ac9a7eb3..b7f1d71308 100644 --- a/components/soc/esp32c5/mcpwm_periph.c +++ b/components/soc/esp32c5/mcpwm_periph.c @@ -6,6 +6,7 @@ #include "soc/soc.h" #include "soc/mcpwm_periph.h" +#include "soc/mcpwm_reg.h" #include "soc/gpio_sig_map.h" const mcpwm_signal_conn_t mcpwm_periph_signals = { @@ -81,3 +82,46 @@ const mcpwm_signal_conn_t mcpwm_periph_signals = { } } }; + +/** + * MCPWM Registers to be saved during sleep retention + * - Clk Configuration registers, e.g.: MCPWM_CLK_CFG_REG + * - Timer Configuration registers, e.g.: MCPWM_TIMER_SYNCI_CFG_REG, MCPWM_TIMER0_CFG0_REG, MCPWM_TIMER0_CFG1_REG, MCPWM_TIMER0_CFG1_REG + * - Operator Configuration registers, e.g.: MCPWM_OPERATOR_TIMERSEL_REG + * |- Generator Configuration registers, e.g.: MCPWM_GEN0_STMP_CFG_REG, MCPWM_GEN0_TSTMP_A_REG, MCPWM_GEN0_TSTMP_B_REG, MCPWM_GEN0_CFG0_REG, MCPWM_GEN0_FORCE_REG, MCPWM_GEN0_A_REG, MCPWM_GEN0_B_REG + * |- Dead Time Configuration registers, e.g.: MCPWM_DT0_CFG_REG, MCPWM_DT0_FED_CFG_REG, MCPWM_DT0_RED_CFG_REG + * |- Carrier Configuration registers, e.g.: MCPWM_CARRIER0_CFG_REG + * └- Fault Handle Configuration registers, e.g.: MCPWM_FAULT_DETECT_REG, MCPWM_FH0_CFG0_REG, MCPWM_FH0_CFG1_REG + * - Capture Timer Configuration registers, e.g.: MCPWM_CAP_TIMER_CFG_REG, MCPWM_CAP_TIMER_PHASE_REG, MCPWM_CAP_CH0_CFG_REG, MCPWM_CAP_CH1_CFG_REG, MCPWM_CAP_CH2_CFG_REG + * - Interrupt enable registers, e.g.: MCPWM_INT_ENA_REG + * - ETM Configurations, e.g.: MCPWM_EVT_EN_REG, MCPWM_EVT_EN2_REG, MCPWM_TASK_EN_REG, MCPWM_OP0_TSTMP_E1_REG, MCPWM_OP0_TSTMP_E2_REG, MCPWM_OP1_TSTMP_E1_REG, MCPWM_OP1_TSTMP_E2_REG, MCPWM_OP2_TSTMP_E1_REG, MCPWM_OP2_TSTMP_E2_REG + * - Misc Configurations, e.g.: MCPWM_UPDATE_CFG_REG +*/ +#define MCPWM_RETENTION_REGS_CNT 68 +#define MCPWM_RETENTION_REGS_BASE (DR_REG_MCPWM_BASE + 0x0) +static const uint32_t mcpwm_regs_map[4] = {0xefffeeef, 0x7efffbff, 0x1ff18, 0x0}; +static const regdma_entries_config_t mcpwm_regs_retention[] = { + // backup stage: save configuration registers + // restore stage: restore the configuration registers + [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_MCPWM_LINK(0x00), + MCPWM_RETENTION_REGS_BASE, MCPWM_RETENTION_REGS_BASE, + MCPWM_RETENTION_REGS_CNT, 0, 0, + mcpwm_regs_map[0], mcpwm_regs_map[1], + mcpwm_regs_map[2], mcpwm_regs_map[3]), + .owner = ENTRY(0) | ENTRY(2) }, + // restore stage: trigger a forced update of all active registers + [1] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_MCPWM_LINK(0x01), + MCPWM_UPDATE_CFG_REG(0), MCPWM_GLOBAL_FORCE_UP, MCPWM_GLOBAL_FORCE_UP_M, 1, 0), + .owner = ENTRY(0) | ENTRY(2) }, + [2] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_MCPWM_LINK(0x02), + MCPWM_UPDATE_CFG_REG(0), 0, MCPWM_GLOBAL_FORCE_UP_M, 1, 0), + .owner = ENTRY(0) | ENTRY(2) }, +}; + +const mcpwm_reg_retention_info_t mcpwm_reg_retention_info[SOC_MCPWM_GROUPS] = { + [0] = { + .regdma_entry_array = mcpwm_regs_retention, + .array_size = ARRAY_SIZE(mcpwm_regs_retention), + .retention_module = SLEEP_RETENTION_MODULE_MCPWM0 + }, +}; diff --git a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in index a7626d57a7..49977427fb 100644 --- a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in @@ -895,6 +895,10 @@ config SOC_MCPWM_CAPTURE_CLK_FROM_GROUP bool default y +config SOC_MCPWM_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_PARLIO_GROUPS int default 1 diff --git a/components/soc/esp32c6/include/soc/retention_periph_defs.h b/components/soc/esp32c6/include/soc/retention_periph_defs.h index 13140b7a4b..89380da152 100644 --- a/components/soc/esp32c6/include/soc/retention_periph_defs.h +++ b/components/soc/esp32c6/include/soc/retention_periph_defs.h @@ -46,6 +46,7 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_GPSPI2 = 22, SLEEP_RETENTION_MODULE_LEDC = 23, SLEEP_RETENTION_MODULE_PCNT0 = 24, + SLEEP_RETENTION_MODULE_MCPWM0 = 25, /* Modem module, which includes WiFi, BLE and 802.15.4 */ SLEEP_RETENTION_MODULE_WIFI_MAC = 26, @@ -89,6 +90,7 @@ typedef enum periph_retention_module_bitmap { SLEEP_RETENTION_MODULE_BM_GPSPI2 = BIT(SLEEP_RETENTION_MODULE_GPSPI2), SLEEP_RETENTION_MODULE_BM_LEDC = BIT(SLEEP_RETENTION_MODULE_LEDC), SLEEP_RETENTION_MODULE_BM_PCNT0 = BIT(SLEEP_RETENTION_MODULE_PCNT0), + SLEEP_RETENTION_MODULE_BM_MCPWM0 = BIT(SLEEP_RETENTION_MODULE_MCPWM0), /* modem module, which includes WiFi, BLE and 802.15.4 */ SLEEP_RETENTION_MODULE_BM_WIFI_MAC = BIT(SLEEP_RETENTION_MODULE_WIFI_MAC), SLEEP_RETENTION_MODULE_BM_WIFI_BB = BIT(SLEEP_RETENTION_MODULE_WIFI_BB), @@ -120,6 +122,7 @@ typedef enum periph_retention_module_bitmap { | SLEEP_RETENTION_MODULE_BM_GPSPI2 \ | SLEEP_RETENTION_MODULE_BM_LEDC \ | SLEEP_RETENTION_MODULE_BM_PCNT0 \ + | SLEEP_RETENTION_MODULE_BM_MCPWM0 \ | SLEEP_RETENTION_MODULE_BM_NULL \ ) diff --git a/components/soc/esp32c6/include/soc/soc_caps.h b/components/soc/esp32c6/include/soc/soc_caps.h index 0750f12c3f..1a70e23152 100644 --- a/components/soc/esp32c6/include/soc/soc_caps.h +++ b/components/soc/esp32c6/include/soc/soc_caps.h @@ -341,6 +341,7 @@ #define SOC_MCPWM_SWSYNC_CAN_PROPAGATE (1) ///< Software sync event can be routed to its output #define SOC_MCPWM_SUPPORT_ETM (1) ///< Support ETM (Event Task Matrix) #define SOC_MCPWM_CAPTURE_CLK_FROM_GROUP (1) ///< Capture timer shares clock with other PWM timers +#define SOC_MCPWM_SUPPORT_SLEEP_RETENTION (1) ///< Support back up registers before sleep /*------------------------ USB SERIAL JTAG CAPS ------------------------------*/ // #define SOC_USB_SERIAL_JTAG_SUPPORT_LIGHT_SLEEP (1) /*!< Support to maintain minimum usb communication during light sleep */ // TODO: IDF-6395 diff --git a/components/soc/esp32c6/mcpwm_periph.c b/components/soc/esp32c6/mcpwm_periph.c index 25e1b86cc1..ff6b5f993c 100644 --- a/components/soc/esp32c6/mcpwm_periph.c +++ b/components/soc/esp32c6/mcpwm_periph.c @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "soc/soc.h" #include "soc/mcpwm_periph.h" +#include "soc/mcpwm_reg.h" #include "soc/gpio_sig_map.h" const mcpwm_signal_conn_t mcpwm_periph_signals = { @@ -81,3 +82,46 @@ const mcpwm_signal_conn_t mcpwm_periph_signals = { }, } }; + +/** + * MCPWM Registers to be saved during sleep retention + * - Clk Configuration registers, e.g.: MCPWM_CLK_CFG_REG + * - Timer Configuration registers, e.g.: MCPWM_TIMER_SYNCI_CFG_REG, MCPWM_TIMER0_CFG0_REG, MCPWM_TIMER0_CFG1_REG, MCPWM_TIMER0_CFG1_REG + * - Operator Configuration registers, e.g.: MCPWM_OPERATOR_TIMERSEL_REG + * |- Generator Configuration registers, e.g.: MCPWM_GEN0_STMP_CFG_REG, MCPWM_GEN0_TSTMP_A_REG, MCPWM_GEN0_TSTMP_B_REG, MCPWM_GEN0_CFG0_REG, MCPWM_GEN0_FORCE_REG, MCPWM_GEN0_A_REG, MCPWM_GEN0_B_REG + * |- Dead Time Configuration registers, e.g.: MCPWM_DT0_CFG_REG, MCPWM_DT0_FED_CFG_REG, MCPWM_DT0_RED_CFG_REG + * |- Carrier Configuration registers, e.g.: MCPWM_CARRIER0_CFG_REG + * └- Fault Handle Configuration registers, e.g.: MCPWM_FAULT_DETECT_REG, MCPWM_FH0_CFG0_REG, MCPWM_FH0_CFG1_REG + * - Capture Timer Configuration registers, e.g.: MCPWM_CAP_TIMER_CFG_REG, MCPWM_CAP_TIMER_PHASE_REG, MCPWM_CAP_CH0_CFG_REG, MCPWM_CAP_CH1_CFG_REG, MCPWM_CAP_CH2_CFG_REG + * - Interrupt enable registers, e.g.: MCPWM_INT_ENA_REG + * - ETM Configurations, e.g.: MCPWM_EVT_EN_REG, MCPWM_TASK_EN_REG + * - Misc Configurations, e.g.: MCPWM_UPDATE_CFG_REG +*/ +#define MCPWM_RETENTION_REGS_CNT 61 +#define MCPWM_RETENTION_REGS_BASE (DR_REG_MCPWM_BASE + 0x0) +static const uint32_t mcpwm_regs_map[4] = {0xefffeeef, 0x7efffbff, 0x318, 0x0}; +static const regdma_entries_config_t mcpwm_regs_retention[] = { + // backup stage: save configuration registers + // restore stage: restore the configuration registers + [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_MCPWM_LINK(0x00), + MCPWM_RETENTION_REGS_BASE, MCPWM_RETENTION_REGS_BASE, + MCPWM_RETENTION_REGS_CNT, 0, 0, + mcpwm_regs_map[0], mcpwm_regs_map[1], + mcpwm_regs_map[2], mcpwm_regs_map[3]), + .owner = ENTRY(0) | ENTRY(2) }, + // restore stage: trigger a forced update of all active registers + [1] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_MCPWM_LINK(0x01), + MCPWM_UPDATE_CFG_REG(0), MCPWM_GLOBAL_FORCE_UP, MCPWM_GLOBAL_FORCE_UP_M, 1, 0), + .owner = ENTRY(0) | ENTRY(2) }, + [2] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_MCPWM_LINK(0x02), + MCPWM_UPDATE_CFG_REG(0), 0, MCPWM_GLOBAL_FORCE_UP_M, 1, 0), + .owner = ENTRY(0) | ENTRY(2) }, +}; + +const mcpwm_reg_retention_info_t mcpwm_reg_retention_info[SOC_MCPWM_GROUPS] = { + [0] = { + .regdma_entry_array = mcpwm_regs_retention, + .array_size = ARRAY_SIZE(mcpwm_regs_retention), + .retention_module = SLEEP_RETENTION_MODULE_MCPWM0 + }, +}; diff --git a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in index f655023a99..1c4bd2fb6e 100644 --- a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in @@ -879,6 +879,10 @@ config SOC_MCPWM_CAPTURE_CLK_FROM_GROUP bool default y +config SOC_MCPWM_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_PARLIO_GROUPS int default 1 diff --git a/components/soc/esp32h2/include/soc/retention_periph_defs.h b/components/soc/esp32h2/include/soc/retention_periph_defs.h index a6c0bb37ee..de9a8c16bf 100644 --- a/components/soc/esp32h2/include/soc/retention_periph_defs.h +++ b/components/soc/esp32h2/include/soc/retention_periph_defs.h @@ -46,6 +46,7 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_GPSPI2 = 22, SLEEP_RETENTION_MODULE_LEDC = 23, SLEEP_RETENTION_MODULE_PCNT0 = 24, + SLEEP_RETENTION_MODULE_MCPWM0 = 25, /* Modem module, which includes BLE and 802.15.4 */ SLEEP_RETENTION_MODULE_BLE_MAC = 28, @@ -87,6 +88,7 @@ typedef enum periph_retention_module_bitmap { SLEEP_RETENTION_MODULE_BM_GPSPI2 = BIT(SLEEP_RETENTION_MODULE_GPSPI2), SLEEP_RETENTION_MODULE_BM_LEDC = BIT(SLEEP_RETENTION_MODULE_LEDC), SLEEP_RETENTION_MODULE_BM_PCNT0 = BIT(SLEEP_RETENTION_MODULE_PCNT0), + SLEEP_RETENTION_MODULE_BM_MCPWM0 = BIT(SLEEP_RETENTION_MODULE_MCPWM0), /* modem module, which includes BLE and 802.15.4 */ SLEEP_RETENTION_MODULE_BM_BLE_MAC = BIT(SLEEP_RETENTION_MODULE_BLE_MAC), SLEEP_RETENTION_MODULE_BM_BT_BB = BIT(SLEEP_RETENTION_MODULE_BT_BB), @@ -116,6 +118,7 @@ typedef enum periph_retention_module_bitmap { | SLEEP_RETENTION_MODULE_BM_GPSPI2 \ | SLEEP_RETENTION_MODULE_BM_LEDC \ | SLEEP_RETENTION_MODULE_BM_PCNT0 \ + | SLEEP_RETENTION_MODULE_BM_MCPWM0 \ | SLEEP_RETENTION_MODULE_BM_NULL \ ) diff --git a/components/soc/esp32h2/include/soc/soc_caps.h b/components/soc/esp32h2/include/soc/soc_caps.h index 26252ef134..9dfa74c6e4 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -335,6 +335,7 @@ #define SOC_MCPWM_SWSYNC_CAN_PROPAGATE (1) ///< Software sync event can be routed to its output #define SOC_MCPWM_SUPPORT_ETM (1) ///< Support ETM (Event Task Matrix) #define SOC_MCPWM_CAPTURE_CLK_FROM_GROUP (1) ///< Capture timer shares clock with other PWM timers +#define SOC_MCPWM_SUPPORT_SLEEP_RETENTION (1) ///< Support back up registers before sleep /*------------------------ USB SERIAL JTAG CAPS ------------------------------*/ // #define SOC_USB_SERIAL_JTAG_SUPPORT_LIGHT_SLEEP (1) /*!< Support to maintain minimum usb communication during light sleep */ // TODO: IDF-6395 diff --git a/components/soc/esp32h2/mcpwm_periph.c b/components/soc/esp32h2/mcpwm_periph.c index f8806c57f1..9879e557f4 100644 --- a/components/soc/esp32h2/mcpwm_periph.c +++ b/components/soc/esp32h2/mcpwm_periph.c @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "soc/soc.h" #include "soc/mcpwm_periph.h" +#include "soc/mcpwm_reg.h" #include "soc/gpio_sig_map.h" const mcpwm_signal_conn_t mcpwm_periph_signals = { @@ -81,3 +82,46 @@ const mcpwm_signal_conn_t mcpwm_periph_signals = { }, } }; + +/** + * MCPWM Registers to be saved during sleep retention + * - Clk Configuration registers, e.g.: MCPWM_CLK_CFG_REG + * - Timer Configuration registers, e.g.: MCPWM_TIMER_SYNCI_CFG_REG, MCPWM_TIMER0_CFG0_REG, MCPWM_TIMER0_CFG1_REG, MCPWM_TIMER0_CFG1_REG + * - Operator Configuration registers, e.g.: MCPWM_OPERATOR_TIMERSEL_REG + * |- Generator Configuration registers, e.g.: MCPWM_GEN0_STMP_CFG_REG, MCPWM_GEN0_TSTMP_A_REG, MCPWM_GEN0_TSTMP_B_REG, MCPWM_GEN0_CFG0_REG, MCPWM_GEN0_FORCE_REG, MCPWM_GEN0_A_REG, MCPWM_GEN0_B_REG + * |- Dead Time Configuration registers, e.g.: MCPWM_DT0_CFG_REG, MCPWM_DT0_FED_CFG_REG, MCPWM_DT0_RED_CFG_REG + * |- Carrier Configuration registers, e.g.: MCPWM_CARRIER0_CFG_REG + * └- Fault Handle Configuration registers, e.g.: MCPWM_FAULT_DETECT_REG, MCPWM_FH0_CFG0_REG, MCPWM_FH0_CFG1_REG + * - Capture Timer Configuration registers, e.g.: MCPWM_CAP_TIMER_CFG_REG, MCPWM_CAP_TIMER_PHASE_REG, MCPWM_CAP_CH0_CFG_REG, MCPWM_CAP_CH1_CFG_REG, MCPWM_CAP_CH2_CFG_REG + * - Interrupt enable registers, e.g.: MCPWM_INT_ENA_REG + * - ETM Configurations, e.g.: MCPWM_EVT_EN_REG, MCPWM_TASK_EN_REG + * - Misc Configurations, e.g.: MCPWM_UPDATE_CFG_REG +*/ +#define MCPWM_RETENTION_REGS_CNT 61 +#define MCPWM_RETENTION_REGS_BASE (DR_REG_MCPWM_BASE + 0x0) +static const uint32_t mcpwm_regs_map[4] = {0xefffeeef, 0x7efffbff, 0x318, 0x0}; +static const regdma_entries_config_t mcpwm_regs_retention[] = { + // backup stage: save configuration registers + // restore stage: restore the configuration registers + [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_MCPWM_LINK(0x00), + MCPWM_RETENTION_REGS_BASE, MCPWM_RETENTION_REGS_BASE, + MCPWM_RETENTION_REGS_CNT, 0, 0, + mcpwm_regs_map[0], mcpwm_regs_map[1], + mcpwm_regs_map[2], mcpwm_regs_map[3]), + .owner = ENTRY(0) | ENTRY(2) }, + // restore stage: trigger a forced update of all active registers + [1] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_MCPWM_LINK(0x01), + MCPWM_UPDATE_CFG_REG, MCPWM_GLOBAL_FORCE_UP, MCPWM_GLOBAL_FORCE_UP_M, 1, 0), + .owner = ENTRY(0) | ENTRY(2) }, + [2] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_MCPWM_LINK(0x02), + MCPWM_UPDATE_CFG_REG, 0, MCPWM_GLOBAL_FORCE_UP_M, 1, 0), + .owner = ENTRY(0) | ENTRY(2) }, +}; + +const mcpwm_reg_retention_info_t mcpwm_reg_retention_info[SOC_MCPWM_GROUPS] = { + [0] = { + .regdma_entry_array = mcpwm_regs_retention, + .array_size = ARRAY_SIZE(mcpwm_regs_retention), + .retention_module = SLEEP_RETENTION_MODULE_MCPWM0 + }, +}; diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index ee00d3083d..436d2f150c 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -454,6 +454,7 @@ #define SOC_MCPWM_SUPPORT_ETM (1) ///< Support ETM (Event Task Matrix) #define SOC_MCPWM_SUPPORT_EVENT_COMPARATOR (1) ///< Support event comparator (based on ETM) #define SOC_MCPWM_CAPTURE_CLK_FROM_GROUP (1) ///< Capture timer shares clock with other PWM timers +// #define SOC_MCPWM_SUPPORT_SLEEP_RETENTION (1) // TODO: IDF-9928 Waiting for expansion of module ID ///< Support back up registers before sleep /*-------------------------- USB CAPS ----------------------------------------*/ // USB Serial JTAG Caps diff --git a/components/soc/esp32p4/mcpwm_periph.c b/components/soc/esp32p4/mcpwm_periph.c index 9fe408099c..8c0cb3e444 100644 --- a/components/soc/esp32p4/mcpwm_periph.c +++ b/components/soc/esp32p4/mcpwm_periph.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -150,3 +150,58 @@ const mcpwm_signal_conn_t mcpwm_periph_signals = { } } }; + +#if SOC_MCPWM_SUPPORT_SLEEP_RETENTION +/** + * MCPWM Registers to be saved during sleep retention + * - Clk Configuration registers, e.g.: MCPWM_CLK_CFG_REG + * - Timer Configuration registers, e.g.: MCPWM_TIMER_SYNCI_CFG_REG, MCPWM_TIMER0_CFG0_REG, MCPWM_TIMER0_CFG1_REG, MCPWM_TIMER0_CFG1_REG + * - Operator Configuration registers, e.g.: MCPWM_OPERATOR_TIMERSEL_REG + * |- Generator Configuration registers, e.g.: MCPWM_GEN0_STMP_CFG_REG, MCPWM_GEN0_TSTMP_A_REG, MCPWM_GEN0_TSTMP_B_REG, MCPWM_GEN0_CFG0_REG, MCPWM_GEN0_FORCE_REG, MCPWM_GEN0_A_REG, MCPWM_GEN0_B_REG + * |- Dead Time Configuration registers, e.g.: MCPWM_DT0_CFG_REG, MCPWM_DT0_FED_CFG_REG, MCPWM_DT0_RED_CFG_REG + * |- Carrier Configuration registers, e.g.: MCPWM_CARRIER0_CFG_REG + * └- Fault Handle Configuration registers, e.g.: MCPWM_FAULT_DETECT_REG, MCPWM_FH0_CFG0_REG, MCPWM_FH0_CFG1_REG + * - Capture Timer Configuration registers, e.g.: MCPWM_CAP_TIMER_CFG_REG, MCPWM_CAP_TIMER_PHASE_REG, MCPWM_CAP_CH0_CFG_REG, MCPWM_CAP_CH1_CFG_REG, MCPWM_CAP_CH2_CFG_REG + * - Interrupt enable registers, e.g.: MCPWM_INT_ENA_REG + * - ETM Configurations, e.g.: MCPWM_EVT_EN_REG, MCPWM_TASK_EN_REG + * - Misc Configurations, e.g.: MCPWM_UPDATE_CFG_REG +*/ +#define MCPWM_RETENTION_REGS_CNT 68 +#define MCPWM_RETENTION_REGS_BASE(i) REG_MCPWM_BASE(i) +static const uint32_t mcpwm_regs_map[4] = {0xefffeeef, 0x7efffbff, 0x1ff18, 0x0}; +#define MCPWM_SLEEP_RETENTION_ENTRIES(mcpwm_port) { \ + /* backup stage: save configuration registers \ + restore stage: restore the configuration registers */ \ + [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT( \ + REGDMA_MCPWM_LINK(0x00), \ + MCPWM_RETENTION_REGS_BASE(mcpwm_port), \ + MCPWM_RETENTION_REGS_BASE(mcpwm_port), \ + MCPWM_RETENTION_REGS_CNT, 0, 0, \ + mcpwm_regs_map[0], mcpwm_regs_map[1], \ + mcpwm_regs_map[2], mcpwm_regs_map[3]), \ + .owner = ENTRY(0)}, \ + /* restore stage: trigger a forced update of all active registers*/ \ + [1] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_MCPWM_LINK(0x01), \ + MCPWM_UPDATE_CFG_REG(mcpwm_port), MCPWM_GLOBAL_FORCE_UP, MCPWM_GLOBAL_FORCE_UP_M, 1, 0), \ + .owner = ENTRY(0) }, \ + [2] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_MCPWM_LINK(0x02), \ + MCPWM_UPDATE_CFG_REG(mcpwm_port), 0, MCPWM_GLOBAL_FORCE_UP_M, 1, 0), \ + .owner = ENTRY(0) }, \ +}; + +static const regdma_entries_config_t mcpwm0_regs_retention[] = MCPWM_SLEEP_RETENTION_ENTRIES(0); +static const regdma_entries_config_t mcpwm1_regs_retention[] = MCPWM_SLEEP_RETENTION_ENTRIES(1); + +const mcpwm_reg_retention_info_t mcpwm_reg_retention_info[SOC_MCPWM_GROUPS] = { + [0] = { + .regdma_entry_array = mcpwm0_regs_retention, + .array_size = ARRAY_SIZE(mcpwm0_regs_retention), + .retention_module = SLEEP_RETENTION_MODULE_MCPWM0 + }, + [1] = { + .regdma_entry_array = mcpwm1_regs_retention, + .array_size = ARRAY_SIZE(mcpwm1_regs_retention), + .retention_module = SLEEP_RETENTION_MODULE_MCPWM1 + }, +}; +#endif //SOC_MCPWM_SUPPORT_SLEEP_RETENTION diff --git a/components/soc/include/soc/mcpwm_periph.h b/components/soc/include/soc/mcpwm_periph.h index 9ce4e06592..12309f208f 100644 --- a/components/soc/include/soc/mcpwm_periph.h +++ b/components/soc/include/soc/mcpwm_periph.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,6 +9,10 @@ #include #include "soc/soc_caps.h" #include "soc/periph_defs.h" +#include "soc/regdma.h" +#if SOC_MCPWM_SUPPORT_SLEEP_RETENTION +#include "soc/retention_periph_defs.h" +#endif #ifdef __cplusplus extern "C" { @@ -37,6 +41,17 @@ typedef struct { } mcpwm_signal_conn_t; extern const mcpwm_signal_conn_t mcpwm_periph_signals; + +#if SOC_MCPWM_SUPPORT_SLEEP_RETENTION +typedef struct { + const regdma_entries_config_t *regdma_entry_array; + uint32_t array_size; + const periph_retention_module_t retention_module; +} mcpwm_reg_retention_info_t; + +extern const mcpwm_reg_retention_info_t mcpwm_reg_retention_info[SOC_MCPWM_GROUPS]; +#endif // SOC_MCPWM_SUPPORT_SLEEP_RETENTION + #endif // SOC_MCPWM_SUPPORTED #ifdef __cplusplus diff --git a/components/soc/include/soc/regdma.h b/components/soc/include/soc/regdma.h index 7ee06f0095..80b7cf301f 100644 --- a/components/soc/include/soc/regdma.h +++ b/components/soc/include/soc/regdma.h @@ -61,6 +61,7 @@ extern "C" { #define REGDMA_GPSPI_LINK(_pri) ((0x23 << 8) | _pri) #define REGDMA_LEDC_LINK(_pri) ((0x24 << 8) | _pri) #define REGDMA_PCNT_LINK(_pri) ((0x25 << 8) | _pri) +#define REGDMA_MCPWM_LINK(_pri) ((0x26 << 8) | _pri) #define REGDMA_MODEM_FE_LINK(_pri) ((0xFF << 8) | _pri) #define REGDMA_LINK_PRI_SYS_CLK REGDMA_LINK_PRI_0 @@ -86,6 +87,7 @@ extern "C" { #define REGDMA_LINK_PRI_TWAI REGDMA_LINK_PRI_GENERAL_PERIPH #define REGDMA_LINK_PRI_GPSPI REGDMA_LINK_PRI_GENERAL_PERIPH #define REGDMA_LINK_PRI_LEDC REGDMA_LINK_PRI_GENERAL_PERIPH +#define REGDMA_LINK_PRI_MCPWM REGDMA_LINK_PRI_GENERAL_PERIPH typedef enum { REGDMA_LINK_PRI_0 = 0, diff --git a/docs/en/api-reference/peripherals/mcpwm.rst b/docs/en/api-reference/peripherals/mcpwm.rst index 7e80c519c5..d6fd611d53 100644 --- a/docs/en/api-reference/peripherals/mcpwm.rst +++ b/docs/en/api-reference/peripherals/mcpwm.rst @@ -976,6 +976,11 @@ However, the driver can prevent the system from going into Light-sleep by acquir Likewise, whenever the driver creates an MCPWM capture timer instance, the driver guarantees that the power management lock is acquired when enabling the timer by :cpp:func:`mcpwm_capture_timer_enable`. And releases the lock in :cpp:func:`mcpwm_capture_timer_disable`. +.. only:: SOC_MCPWM_SUPPORT_SLEEP_RETENTION + + {IDF_TARGET_NAME} supports to retain the MCPWM register context before entering **Light-sleep** and restore them after woke up. Which means you don't have to re-init the MCPWM driver after the **Light-sleep**. + + This feature can be enabled by setting the flag :cpp:member:`mcpwm_timer_config_t::allow_pd` or :cpp:member:`mcpwm_capture_timer_config_t::allow_pd`. It will allow the system to power down the MCPWM in Light-sleep, meanwhile save the MCPWM register context. It can help to save more power consumption with some extra cost of the memory. .. _mcpwm-iram-safe: diff --git a/docs/en/api-reference/system/power_management.rst b/docs/en/api-reference/system/power_management.rst index 2c203fbd65..98434d8c6c 100644 --- a/docs/en/api-reference/system/power_management.rst +++ b/docs/en/api-reference/system/power_management.rst @@ -156,6 +156,7 @@ The following peripheral drivers are not aware of DFS yet. Applications need to :SOC_LEDC_SUPPORT_SLEEP_RETENTION: - LEDC :SOC_I2C_SUPPORT_SLEEP_RETENTION: - I2C :SOC_I2S_SUPPORT_SLEEP_RETENTION: - I2S + :SOC_MCPWM_SUPPORT_SLEEP_RETENTION: - MCPWM :SOC_UART_SUPPORT_SLEEP_RETENTION: - All UARTs :SOC_TEMPERATURE_SENSOR_SUPPORT_SLEEP_RETENTION: - Temperature Sensor :SOC_TWAI_SUPPORT_SLEEP_RETENTION: - All TWAIs @@ -177,7 +178,6 @@ The following peripheral drivers are not aware of DFS yet. Applications need to - Crypto: AES/ECC/HMAC/RSA/SHA/DS/XTA_AES/ECDSA - PCNT - USB-Serial-JTAG - - MCPWM - SARADC .. note:: diff --git a/docs/zh_CN/api-reference/peripherals/mcpwm.rst b/docs/zh_CN/api-reference/peripherals/mcpwm.rst index 78f29d6983..e91e906205 100644 --- a/docs/zh_CN/api-reference/peripherals/mcpwm.rst +++ b/docs/zh_CN/api-reference/peripherals/mcpwm.rst @@ -976,6 +976,11 @@ MCPWM 捕获通道支持在信号上检测到有效边沿时发送通知。须 同理,每当驱动创建 MCPWM 捕获定时器实例时,都会在通过 :cpp:func:`mcpwm_capture_timer_enable` 启用定时器时获取电源管理锁,并在调用 :cpp:func:`mcpwm_capture_timer_disable` 时释放锁。 +.. only:: SOC_MCPWM_SUPPORT_SLEEP_RETENTION + + {IDF_TARGET_NAME} 支持在进入 **Light-sleep** 之前保留 MCPWM 寄存器中的内容,并在唤醒后恢复。也就是说程序不需要在 **Light-sleep** 唤醒后重新配置 MCPWM。 + + 该特性可以通过置位配置中的 :cpp:member:`mcpwm_timer_config_t::allow_pd` 或 :cpp:member:`mcpwm_capture_timer_config_t::allow_pd` 标志位启用。启用后驱动允许系统在 Light-sleep 时对 MCPWM 掉电,同时保存 MCPWM 的寄存器内容。它可以帮助降低 Light-sleep 时的功耗,但需要花费一些额外的存储来保存寄存器的配置。 .. _mcpwm-iram-safe: diff --git a/docs/zh_CN/api-reference/system/power_management.rst b/docs/zh_CN/api-reference/system/power_management.rst index 4ca140e21e..e6d2b211de 100644 --- a/docs/zh_CN/api-reference/system/power_management.rst +++ b/docs/zh_CN/api-reference/system/power_management.rst @@ -156,6 +156,7 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求, :SOC_I2C_SUPPORT_SLEEP_RETENTION: - I2C :SOC_I2S_SUPPORT_SLEEP_RETENTION: - I2S :SOC_ETM_SUPPORT_SLEEP_RETENTION: - ETM + :SOC_MCPWM_SUPPORT_SLEEP_RETENTION: - MCPWM :SOC_UART_SUPPORT_SLEEP_RETENTION: - All UARTs :SOC_TEMPERATURE_SENSOR_SUPPORT_SLEEP_RETENTION: - Temperature Sensor :SOC_TWAI_SUPPORT_SLEEP_RETENTION: - All TWAIs @@ -177,7 +178,6 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求, - Crypto: AES/ECC/HMAC/RSA/SHA/DS/XTA_AES/ECDSA - PCNT - USB-Serial-JTAG - - MCPWM - SARADC .. note::