Skip to content

Commit

Permalink
Merge branch 'feat/etm_sleep_retention' into 'master'
Browse files Browse the repository at this point in the history
feat(etm): support sleep retention

Closes IDF-8462

See merge request espressif/esp-idf!33787
  • Loading branch information
suda-morris committed Oct 11, 2024
2 parents 5f0a71a + a4f783d commit d38e30e
Show file tree
Hide file tree
Showing 42 changed files with 566 additions and 63 deletions.
30 changes: 13 additions & 17 deletions components/esp_driver_gptimer/src/gptimer_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 0 additions & 4 deletions components/esp_driver_gptimer/src/gptimer_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
30 changes: 13 additions & 17 deletions components/esp_driver_rmt/src/rmt_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ rmt_group_t *rmt_acquire_group_handle(int group_id)
rmt_ll_reset_register(group_id);
}
#if RMT_USE_RETENTION_LINK
sleep_retention_module_t module = RMT_LL_SLEEP_RETENTION_MODULE_ID(group_id);
sleep_retention_module_t module = rmt_reg_retention_info[group_id].module;
sleep_retention_module_init_param_t init_param = {
.cbs = {
.create = {
Expand All @@ -85,9 +85,7 @@ rmt_group_t *rmt_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);
}
Expand Down Expand Up @@ -147,11 +145,12 @@ void rmt_release_group_handle(rmt_group_t *group)

if (do_deinitialize) {
#if RMT_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 = rmt_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);
Expand Down Expand Up @@ -303,24 +302,21 @@ static esp_err_t rmt_create_sleep_retention_link_cb(void *arg)
{
rmt_group_t *group = (rmt_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(rmt_reg_retention_info[group_id].regdma_entry_array,
rmt_reg_retention_info[group_id].array_size,
REGDMA_LINK_PRI_RMT, module);
REGDMA_LINK_PRI_RMT, rmt_reg_retention_info[group_id].module);
return err;
}

void rmt_create_retention_module(rmt_group_t *group)
{
sleep_retention_module_t module = group->sleep_retention_module;

int group_id = group->group_id;
sleep_retention_module_t module = rmt_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, RMT driver should still work, so just warning here
ESP_LOGW(TAG, "create retention link failed, power domain can't be turned off");
} else {
group->retention_link_created = true;
ESP_LOGW(TAG, "create retention link failed, power domain won't be turned off during sleep");
}
}
_lock_release(&s_platform.mutex);
Expand Down
4 changes: 0 additions & 4 deletions components/esp_driver_rmt/src/rmt_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,6 @@ struct rmt_group_t {
rmt_rx_channel_t *rx_channels[SOC_RMT_RX_CANDIDATES_PER_GROUP]; // array of RMT RX channels
rmt_sync_manager_t *sync_manager; // sync manager, this can be extended into an array if there're more sync controllers in one RMT group
int intr_priority; // RMT interrupt priority
#if RMT_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
};

struct rmt_channel_t {
Expand Down
Loading

0 comments on commit d38e30e

Please sign in to comment.