diff --git a/components/esp_driver_gptimer/src/gptimer_common.c b/components/esp_driver_gptimer/src/gptimer_common.c index 928ce3c84a..8338ebb2f4 100644 --- a/components/esp_driver_gptimer/src/gptimer_common.c +++ b/components/esp_driver_gptimer/src/gptimer_common.c @@ -27,24 +27,21 @@ static esp_err_t gptimer_create_sleep_retention_link_cb(void *arg) { gptimer_group_t *group = (gptimer_group_t *)arg; int group_id = group->group_id; - sleep_retention_module_t module = group->sleep_retention_module; esp_err_t err = sleep_retention_entries_create(tg_timer_reg_retention_info[group_id].regdma_entry_array, tg_timer_reg_retention_info[group_id].array_size, - REGDMA_LINK_PRI_GPTIMER, module); - ESP_RETURN_ON_ERROR(err, TAG, "create retention link failed"); - return ESP_OK; + REGDMA_LINK_PRI_GPTIMER, tg_timer_reg_retention_info[group_id].module); + return err; } void gptimer_create_retention_module(gptimer_group_t *group) { - sleep_retention_module_t module = group->sleep_retention_module; + int group_id = group->group_id; + sleep_retention_module_t module = tg_timer_reg_retention_info[group_id].module; _lock_acquire(&s_platform.mutex); - if (group->retention_link_created == false) { + if ((sleep_retention_get_inited_modules() & BIT(module)) && !(sleep_retention_get_created_modules() & BIT(module))) { if (sleep_retention_module_allocate(module) != ESP_OK) { // even though the sleep retention module create failed, GPTimer driver should still work, so just warning here - ESP_LOGW(TAG, "create retention module for group %d retention, power domain can't turn off", group->group_id); - } else { - group->retention_link_created = true; + ESP_LOGW(TAG, "create retention link failed %d, power domain won't be turned off during sleep", group_id); } } _lock_release(&s_platform.mutex); @@ -97,9 +94,7 @@ gptimer_group_t *gptimer_acquire_group_handle(int group_id) }, .depends = BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM) }; - if (sleep_retention_module_init(module, &init_param) == ESP_OK) { - group->sleep_retention_module = module; - } else { + if (sleep_retention_module_init(module, &init_param) != ESP_OK) { // even though the sleep retention module init failed, RMT 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); } @@ -132,11 +127,12 @@ void gptimer_release_group_handle(gptimer_group_t *group) } } #if GPTIMER_USE_RETENTION_LINK - if (group->sleep_retention_module) { - if (group->retention_link_created) { - sleep_retention_module_free(group->sleep_retention_module); - } - sleep_retention_module_deinit(group->sleep_retention_module); + sleep_retention_module_t module = tg_timer_reg_retention_info[group_id].module; + if (sleep_retention_get_created_modules() & BIT(module)) { + sleep_retention_module_free(module); + } + if (sleep_retention_get_inited_modules() & BIT(module)) { + sleep_retention_module_deinit(module); } #endif free(group); diff --git a/components/esp_driver_gptimer/src/gptimer_priv.h b/components/esp_driver_gptimer/src/gptimer_priv.h index 3c117af44f..b5d4d1c0e6 100644 --- a/components/esp_driver_gptimer/src/gptimer_priv.h +++ b/components/esp_driver_gptimer/src/gptimer_priv.h @@ -59,10 +59,6 @@ typedef struct gptimer_group_t { int group_id; portMUX_TYPE spinlock; // to protect per-group register level concurrent access gptimer_t *timers[SOC_TIMER_GROUP_TIMERS_PER_GROUP]; -#if GPTIMER_USE_RETENTION_LINK - sleep_retention_module_t sleep_retention_module; // sleep retention module - bool retention_link_created; // mark if the retention link is created -#endif } gptimer_group_t; typedef enum { diff --git a/components/esp_driver_gptimer/test_apps/gptimer/main/test_gptimer_sleep.c b/components/esp_driver_gptimer/test_apps/gptimer/main/test_gptimer_sleep.c index 34a4371598..5b7e5879d5 100644 --- a/components/esp_driver_gptimer/test_apps/gptimer/main/test_gptimer_sleep.c +++ b/components/esp_driver_gptimer/test_apps/gptimer/main/test_gptimer_sleep.c @@ -10,6 +10,8 @@ #include "freertos/task.h" #include "unity.h" #include "driver/gptimer.h" +#include "driver/gpio_etm.h" +#include "driver/gpio.h" #include "soc/soc_caps.h" #include "esp_sleep.h" #include "esp_private/sleep_cpu.h" @@ -117,3 +119,135 @@ TEST_CASE("gptimer can work after light sleep", "[gptimer]") test_gptimer_sleep_retention(true); #endif } + +#if SOC_TIMER_SUPPORT_ETM +/** + * @brief Test the GPTimer and ETM subsystem can still work after light sleep + * + * @param back_up_before_sleep Whether to back up GPTimer registers before sleep + */ +static void test_gptimer_etm_sleep_retention(bool back_up_before_sleep) +{ + const uint32_t output_gpio = 1; + // GPTimer alarm ---> ETM channel A ---> GPTimer alarm re-enable + // GPTimer alarm ---> ETM channel B ---> GPIO toggle + printf("allocate etm channel\r\n"); + esp_etm_channel_config_t etm_config = { + .flags.allow_pd = back_up_before_sleep, + }; + esp_etm_channel_handle_t etm_channel_a, etm_channel_b; + TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel_a)); + TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel_b)); + + printf("initialize gpio\r\n"); + gpio_config_t task_gpio_config = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << output_gpio, + }; + TEST_ESP_OK(gpio_config(&task_gpio_config)); + + printf("create a gptimer\r\n"); + gptimer_handle_t gptimer = NULL; + gptimer_config_t timer_config = { + .clk_src = GPTIMER_CLK_SRC_DEFAULT, + .direction = GPTIMER_COUNT_UP, + .resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us + .flags.backup_before_sleep = back_up_before_sleep, + }; + TEST_ESP_OK(gptimer_new_timer(&timer_config, &gptimer)); + + printf("get gptimer etm event and task handle\r\n"); + esp_etm_event_handle_t gptimer_event = NULL; + gptimer_etm_event_config_t gptimer_etm_event_conf = { + .event_type = GPTIMER_ETM_EVENT_ALARM_MATCH, + }; + TEST_ESP_OK(gptimer_new_etm_event(gptimer, &gptimer_etm_event_conf, &gptimer_event)); + esp_etm_task_handle_t gptimer_task = NULL; + gptimer_etm_task_config_t gptimer_etm_task_conf = { + .task_type = GPTIMER_ETM_TASK_EN_ALARM, + }; + TEST_ESP_OK(gptimer_new_etm_task(gptimer, &gptimer_etm_task_conf, & gptimer_task)); + + printf("connect event and task to the channel a\r\n"); + TEST_ESP_OK(esp_etm_channel_connect(etm_channel_a, gptimer_event, gptimer_task)); + + printf("enable etm channels\r\n"); + TEST_ESP_OK(esp_etm_channel_enable(etm_channel_a)); + TEST_ESP_OK(esp_etm_channel_enable(etm_channel_b)); + + printf("set timer alarm action\r\n"); + gptimer_alarm_config_t alarm_config = { + .reload_count = 0, + .alarm_count = 100, // 100us per alarm event + .flags.auto_reload_on_alarm = true, + }; + TEST_ESP_OK(gptimer_set_alarm_action(gptimer, &alarm_config)); + + // before going to sleep, ensure the gptimer is not enabled yet, otherwise it will acquire power management lock + + esp_sleep_context_t sleep_ctx; + esp_sleep_set_sleep_context(&sleep_ctx); + printf("go to light sleep for 2 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(2 * 1000 * 1000)); + TEST_ESP_OK(esp_light_sleep_start()); + + printf("Waked up! Let's see if GPTimer and ETM 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_RMT_SUPPORT_SLEEP_RETENTION + // check if the power domain also is powered down + TEST_ASSERT_EQUAL(back_up_before_sleep ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP); +#endif + esp_sleep_set_sleep_context(NULL); + + printf("enable and start timer\r\n"); + TEST_ESP_OK(gptimer_enable(gptimer)); + TEST_ESP_OK(gptimer_start(gptimer)); + + printf("allocate GPIO etm task\r\n"); + esp_etm_task_handle_t gpio_task = NULL; + gpio_etm_task_config_t gpio_task_config = { + .action = GPIO_ETM_TASK_ACTION_TOG, + }; + TEST_ESP_OK(gpio_new_etm_task(&gpio_task_config, &gpio_task)); + // set gpio number for the gpio etm primitives + TEST_ESP_OK(gpio_etm_task_add_gpio(gpio_task, output_gpio)); + printf("connect event and task to the channel b\r\n"); + TEST_ESP_OK(esp_etm_channel_connect(etm_channel_b, gptimer_event, gpio_task)); + + // delay sometime for us to view the waveform, should see a 5KHz square waveform + vTaskDelay(pdMS_TO_TICKS(1000)); + + // delete gptimer + TEST_ESP_OK(gptimer_stop(gptimer)); + TEST_ESP_OK(gptimer_disable(gptimer)); + TEST_ESP_OK(gptimer_del_timer(gptimer)); + + // delete etm primitives + TEST_ESP_OK(gpio_etm_task_rm_gpio(gpio_task, output_gpio)); + TEST_ESP_OK(esp_etm_del_task(gpio_task)); + TEST_ESP_OK(esp_etm_del_task(gptimer_task)); + TEST_ESP_OK(esp_etm_del_event(gptimer_event)); + TEST_ESP_OK(esp_etm_channel_disable(etm_channel_a)); + TEST_ESP_OK(esp_etm_channel_disable(etm_channel_b)); + TEST_ESP_OK(esp_etm_del_channel(etm_channel_a)); + TEST_ESP_OK(esp_etm_del_channel(etm_channel_b)); +} + +TEST_CASE("gptimer and ETM can work after light sleep", "[gptimer]") +{ + test_gptimer_etm_sleep_retention(false); +#if SOC_TIMER_SUPPORT_SLEEP_RETENTION && SOC_ETM_SUPPORT_SLEEP_RETENTION + test_gptimer_etm_sleep_retention(true); +#endif +} + +#endif // SOC_TIMER_SUPPORT_ETM