From 31befbfad3765316c64610ab224d94f7a8102735 Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Thu, 7 Nov 2024 11:48:16 +0800 Subject: [PATCH] feat(pcnt): add sleep retention init Currently, due to the lack of sleep callback feature. We only init sleep module but don't allocate it. Thus the power domain will be kept during the light sleep. And temporarily disable pcnt sleep retention support on P4 due to the lack of retention module ID. --- components/esp_driver_pcnt/src/pulse_cnt.c | 45 ++++++ .../test_apps/pulse_cnt/main/CMakeLists.txt | 5 + .../pulse_cnt/main/test_pulse_cnt_sleep.c | 141 ++++++++++++++++++ .../test_apps/pulse_cnt/sdkconfig.ci.release | 1 + .../test_apps/pulse_cnt/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/pcnt_periph.c | 29 ++++ .../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/pcnt_periph.c | 30 +++- .../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/pcnt_periph.c | 30 +++- components/soc/esp32p4/include/soc/soc_caps.h | 1 + components/soc/esp32p4/pcnt_periph.c | 33 +++- components/soc/include/soc/pcnt_periph.h | 16 +- components/soc/include/soc/regdma.h | 2 + 21 files changed, 356 insertions(+), 4 deletions(-) create mode 100644 components/esp_driver_pcnt/test_apps/pulse_cnt/main/test_pulse_cnt_sleep.c diff --git a/components/esp_driver_pcnt/src/pulse_cnt.c b/components/esp_driver_pcnt/src/pulse_cnt.c index 0f1148e9fd..6be76876c1 100644 --- a/components/esp_driver_pcnt/src/pulse_cnt.c +++ b/components/esp_driver_pcnt/src/pulse_cnt.c @@ -28,6 +28,7 @@ #include "hal/gpio_hal.h" #include "esp_private/esp_clk.h" #include "esp_private/periph_ctrl.h" +#include "esp_private/sleep_retention.h" #include "driver/gpio.h" #include "esp_private/gpio.h" #include "hal/gpio_ll.h" // for io_loop_back flag only @@ -65,6 +66,13 @@ typedef struct pcnt_group_t pcnt_group_t; typedef struct pcnt_unit_t pcnt_unit_t; typedef struct pcnt_chan_t pcnt_chan_t; +// Use retention link only when the target supports sleep retention +#define PCNT_USE_RETENTION_LINK (SOC_PCNT_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP) + +#if PCNT_USE_RETENTION_LINK +static esp_err_t pcnt_create_sleep_retention_link_cb(void *arg); +#endif + struct pcnt_platform_t { _lock_t mutex; // platform level mutex lock pcnt_group_t *groups[SOC_PCNT_GROUPS]; // pcnt group pool @@ -864,6 +872,23 @@ static pcnt_group_t *pcnt_acquire_group_handle(int group_id) pcnt_ll_enable_bus_clock(group_id, true); pcnt_ll_reset_register(group_id); } +#if PCNT_USE_RETENTION_LINK + sleep_retention_module_t module_id = pcnt_reg_retention_info[group_id].retention_module; + sleep_retention_module_init_param_t init_param = { + .cbs = { + .create = { + .handle = pcnt_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_id, &init_param) != ESP_OK) { + // even though the sleep retention module init failed, PCNT 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 // PCNT_USE_RETENTION_LINK // initialize HAL context pcnt_hal_init(&group->hal, group_id); } @@ -901,6 +926,12 @@ static void pcnt_release_group_handle(pcnt_group_t *group) _lock_release(&s_platform.mutex); if (do_deinitialize) { +#if PCNT_USE_RETENTION_LINK + const periph_retention_module_t module_id = pcnt_reg_retention_info[group_id].retention_module; + if (sleep_retention_get_inited_modules() & BIT(module_id)) { + sleep_retention_module_deinit(module_id); + } +#endif // PCNT_USE_RETENTION_LINK free(group); ESP_LOGD(TAG, "del group (%d)", group_id); } @@ -998,3 +1029,17 @@ IRAM_ATTR static void pcnt_default_isr(void *args) portYIELD_FROM_ISR(); } } + +#if PCNT_USE_RETENTION_LINK +static esp_err_t pcnt_create_sleep_retention_link_cb(void *arg) +{ + pcnt_group_t *group = (pcnt_group_t *)arg; + int group_id = group->group_id; + sleep_retention_module_t module_id = pcnt_reg_retention_info[group_id].retention_module; + esp_err_t err = sleep_retention_entries_create(pcnt_reg_retention_info[group_id].regdma_entry_array, + pcnt_reg_retention_info[group_id].array_size, + REGDMA_LINK_PRI_PCNT, module_id); + ESP_RETURN_ON_ERROR(err, TAG, "create retention link failed"); + return ESP_OK; +} +#endif // PCNT_USE_RETENTION_LINK diff --git a/components/esp_driver_pcnt/test_apps/pulse_cnt/main/CMakeLists.txt b/components/esp_driver_pcnt/test_apps/pulse_cnt/main/CMakeLists.txt index 67bb690c46..1fc04aa4b8 100644 --- a/components/esp_driver_pcnt/test_apps/pulse_cnt/main/CMakeLists.txt +++ b/components/esp_driver_pcnt/test_apps/pulse_cnt/main/CMakeLists.txt @@ -6,6 +6,11 @@ if(CONFIG_PCNT_ISR_IRAM_SAFE) list(APPEND srcs "test_pulse_cnt_iram.c") endif() +# TODO: IDF-9907 support ESP32P4 +if(CONFIG_SOC_LIGHT_SLEEP_SUPPORTED AND CONFIG_PM_ENABLE AND NOT CONFIG_IDF_TARGET_ESP32P4) + list(APPEND srcs "test_pulse_cnt_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_pcnt/test_apps/pulse_cnt/main/test_pulse_cnt_sleep.c b/components/esp_driver_pcnt/test_apps/pulse_cnt/main/test_pulse_cnt_sleep.c new file mode 100644 index 0000000000..2ac2dc4062 --- /dev/null +++ b/components/esp_driver_pcnt/test_apps/pulse_cnt/main/test_pulse_cnt_sleep.c @@ -0,0 +1,141 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "unity.h" +#include "unity_test_utils.h" +#include "driver/pulse_cnt.h" +#include "driver/gpio.h" +#include "spi_flash_mmap.h" +#include "esp_attr.h" +#include "esp_sleep.h" +#include "soc/soc_caps.h" +#include "soc/pcnt_struct.h" +#include "hal/pcnt_ll.h" +#include "test_pulse_cnt_board.h" +#include "esp_private/sleep_cpu.h" +#include "esp_private/esp_sleep_internal.h" +#include "esp_private/esp_pmu.h" + +/** + * @brief Test the PCNT driver can still work after light sleep + */ +static void test_pcnt_sleep_retention(void) +{ + test_gpio_init_for_simulation(TEST_PCNT_GPIO_A); + test_gpio_init_for_simulation(TEST_PCNT_GPIO_B); + + printf("install pcnt units\r\n"); + pcnt_unit_config_t unit_config = { + .low_limit = -100, + .high_limit = 100, + }; + pcnt_unit_handle_t units[2]; + for (int i = 0; i < 2; i++) { + TEST_ESP_OK(pcnt_new_unit(&unit_config, &units[i])); + } + + printf("install pcnt channels\r\n"); + const int channel_gpios[] = {TEST_PCNT_GPIO_A, TEST_PCNT_GPIO_B}; + pcnt_chan_config_t chan_config = { + .level_gpio_num = -1, + }; + pcnt_channel_handle_t chans[2]; + for (int i = 0; i < 2; i++) { + chan_config.edge_gpio_num = channel_gpios[i]; + TEST_ESP_OK(pcnt_new_channel(units[i], &chan_config, &chans[i])); + TEST_ESP_OK(pcnt_channel_set_edge_action(chans[i], PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD)); + TEST_ESP_OK(pcnt_channel_set_level_action(chans[i], PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP)); + } + + printf("enable and start unit\r\n"); + for (int i = 0; i < 2; i++) { + TEST_ESP_OK(pcnt_unit_enable(units[i])); + TEST_ESP_OK(pcnt_unit_start(units[i])); + } + + // trigger 10 rising edge on GPIO + test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_A, 10); + test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_B, 10); + + int count_value = 0; + for (int i = 0; i < 2; i++) { + TEST_ESP_OK(pcnt_unit_get_count(units[i], &count_value)); + TEST_ASSERT_EQUAL(10, count_value); + } + + // hold GPIO to avoid trigger PCNT counter during GPIO power down + gpio_hold_en(TEST_PCNT_GPIO_A); + gpio_hold_en(TEST_PCNT_GPIO_B); + + printf("stop and disable unit before sleep\r\n"); + for (int i = 0; i < 2; i++) { + TEST_ESP_OK(pcnt_unit_stop(units[i])); + TEST_ESP_OK(pcnt_unit_disable(units[i])); + } + + // 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 PCNT 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_PCNT_SUPPORT_SLEEP_RETENTION + // check if the power domain also is powered down + TEST_ASSERT_EQUAL(0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP); +#endif + esp_sleep_set_sleep_context(NULL); + + gpio_hold_dis(TEST_PCNT_GPIO_A); + gpio_hold_dis(TEST_PCNT_GPIO_B); + + printf("enable and start unit after sleep\r\n"); + for (int i = 0; i < 2; i++) { + TEST_ESP_OK(pcnt_unit_enable(units[i])); + TEST_ESP_OK(pcnt_unit_start(units[i])); + } + + // Verify the counter still holds the value + test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_A, 10); + test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_B, 10); + + int reg_value = 0; + for (int i = 0; i < 2; i++) { + // check the counter value (include accum value and register value) + TEST_ESP_OK(pcnt_unit_get_count(units[i], &count_value)); + TEST_ASSERT_EQUAL(20, count_value); + // check the register value + reg_value = pcnt_ll_get_count(&PCNT, i); + TEST_ASSERT_EQUAL(20, reg_value); + } + + for (int i = 0; i < 2; i++) { + TEST_ESP_OK(pcnt_unit_stop(units[i])); + TEST_ESP_OK(pcnt_unit_disable(units[i])); + TEST_ESP_OK(pcnt_del_channel(chans[i])); + TEST_ESP_OK(pcnt_del_unit(units[i])); + } +} + +TEST_CASE("pcnt light sleep", "[pcnt]") +{ + test_pcnt_sleep_retention(); +} diff --git a/components/esp_driver_pcnt/test_apps/pulse_cnt/sdkconfig.ci.release b/components/esp_driver_pcnt/test_apps/pulse_cnt/sdkconfig.ci.release index 91d93f163e..17aaee1e8e 100644 --- a/components/esp_driver_pcnt/test_apps/pulse_cnt/sdkconfig.ci.release +++ b/components/esp_driver_pcnt/test_apps/pulse_cnt/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_pcnt/test_apps/pulse_cnt/sdkconfig.defaults b/components/esp_driver_pcnt/test_apps/pulse_cnt/sdkconfig.defaults index fa8ac618b9..488ad46859 100644 --- a/components/esp_driver_pcnt/test_apps/pulse_cnt/sdkconfig.defaults +++ b/components/esp_driver_pcnt/test_apps/pulse_cnt/sdkconfig.defaults @@ -1,2 +1,5 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_ESP_TASK_WDT_EN=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 70fab9c675..e1ceec5368 100644 --- a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in @@ -767,6 +767,10 @@ config SOC_PCNT_SUPPORT_STEP_NOTIFY bool default y +config SOC_PCNT_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_RMT_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 6d7b045e0b..2396ca2c13 100644 --- a/components/soc/esp32c5/include/soc/retention_periph_defs.h +++ b/components/soc/esp32c5/include/soc/retention_periph_defs.h @@ -43,6 +43,7 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_PARLIO0 = 19, SLEEP_RETENTION_MODULE_GPSPI2 = 20, SLEEP_RETENTION_MODULE_LEDC = 21, + SLEEP_RETENTION_MODULE_PCNT0 = 22, /* 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_PARLIO0 = BIT(SLEEP_RETENTION_MODULE_PARLIO0), 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_GDMA_CH0 = BIT(SLEEP_RETENTION_MODULE_GDMA_CH0), SLEEP_RETENTION_MODULE_BM_GDMA_CH1 = BIT(SLEEP_RETENTION_MODULE_GDMA_CH1), @@ -115,6 +117,7 @@ typedef enum periph_retention_module_bitmap { | SLEEP_RETENTION_MODULE_BM_PARLIO0 \ | SLEEP_RETENTION_MODULE_BM_GPSPI2 \ | SLEEP_RETENTION_MODULE_BM_LEDC \ + | SLEEP_RETENTION_MODULE_BM_PCNT0 \ | 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 edafca6279..928e92e844 100644 --- a/components/soc/esp32c5/include/soc/soc_caps.h +++ b/components/soc/esp32c5/include/soc/soc_caps.h @@ -323,6 +323,7 @@ #define SOC_PCNT_SUPPORT_RUNTIME_THRES_UPDATE 1 #define SOC_PCNT_SUPPORT_CLEAR_SIGNAL 1 #define SOC_PCNT_SUPPORT_STEP_NOTIFY 1 +#define SOC_PCNT_SUPPORT_SLEEP_RETENTION 1 /*!< The sleep retention feature can help back up PCNT registers before sleep */ /*--------------------------- RMT CAPS ---------------------------------------*/ #define SOC_RMT_GROUPS 1U /*!< One RMT group */ diff --git a/components/soc/esp32c5/pcnt_periph.c b/components/soc/esp32c5/pcnt_periph.c index 162db5aa27..c96644dae1 100644 --- a/components/soc/esp32c5/pcnt_periph.c +++ b/components/soc/esp32c5/pcnt_periph.c @@ -6,6 +6,7 @@ #include "soc/pcnt_periph.h" #include "soc/gpio_sig_map.h" +#include "soc/pcnt_reg.h" const pcnt_signal_conn_t pcnt_periph_signals = { .groups = { @@ -68,3 +69,31 @@ const pcnt_signal_conn_t pcnt_periph_signals = { } } }; + +/** + * PCNT Registers to be saved during sleep retention + * - Configuration registers, e.g.: PCNT_CTRL_REG, PCNT_U0_CONF0_REG, PCNT_U0_CONF1_REG, PCNT_U0_CONF2_REG, PCNT_U1_CONF0_REG... + * - Step Configuration registers, e.g.: PCNT_U0_CHANGE_CONF_REG, PCNT_U1_CHANGE_CONF_REG, PCNT_U2_CHANGE_CONF_REG, PCNT_U3_CHANGE_CONF_REG + * - Interrupt enable registers, e.g.: PCNT_INT_ENA_REG +*/ +#define PCNT_RETENTION_REGS_CNT 18 +#define PCNT_RETENTION_REGS_BASE (DR_REG_PCNT_BASE + 0x0) +static const uint32_t pcnt_regs_map[4] = {0x1f040fff, 0x0, 0x0, 0x0}; +static const regdma_entries_config_t pcnt_regs_retention[] = { + // backup stage: save configuration registers + // restore stage: restore the configuration registers + [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_PCNT_LINK(0x00), \ + PCNT_RETENTION_REGS_BASE, PCNT_RETENTION_REGS_BASE, \ + PCNT_RETENTION_REGS_CNT, 0, 0, \ + pcnt_regs_map[0], pcnt_regs_map[1], \ + pcnt_regs_map[2], pcnt_regs_map[3]), \ + .owner = ENTRY(0) | ENTRY(2) }, \ +}; + +const pcnt_reg_retention_info_t pcnt_reg_retention_info[SOC_PCNT_GROUPS] = { + [0] = { + .regdma_entry_array = pcnt_regs_retention, + .array_size = ARRAY_SIZE(pcnt_regs_retention), + .retention_module = SLEEP_RETENTION_MODULE_PCNT0 + }, +}; diff --git a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in index 84c3cd72b3..a7626d57a7 100644 --- a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in @@ -779,6 +779,10 @@ config SOC_PCNT_SUPPORT_RUNTIME_THRES_UPDATE bool default y +config SOC_PCNT_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_RMT_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 f0f629f8f5..13140b7a4b 100644 --- a/components/soc/esp32c6/include/soc/retention_periph_defs.h +++ b/components/soc/esp32c6/include/soc/retention_periph_defs.h @@ -45,6 +45,7 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_PARLIO0 = 21, SLEEP_RETENTION_MODULE_GPSPI2 = 22, SLEEP_RETENTION_MODULE_LEDC = 23, + SLEEP_RETENTION_MODULE_PCNT0 = 24, /* Modem module, which includes WiFi, BLE and 802.15.4 */ SLEEP_RETENTION_MODULE_WIFI_MAC = 26, @@ -87,6 +88,7 @@ typedef enum periph_retention_module_bitmap { SLEEP_RETENTION_MODULE_BM_PARLIO0 = BIT(SLEEP_RETENTION_MODULE_PARLIO0), 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), /* 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), @@ -117,6 +119,7 @@ typedef enum periph_retention_module_bitmap { | SLEEP_RETENTION_MODULE_BM_PARLIO0 \ | SLEEP_RETENTION_MODULE_BM_GPSPI2 \ | SLEEP_RETENTION_MODULE_BM_LEDC \ + | SLEEP_RETENTION_MODULE_BM_PCNT0 \ | 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 948c1cd0f6..0750f12c3f 100644 --- a/components/soc/esp32c6/include/soc/soc_caps.h +++ b/components/soc/esp32c6/include/soc/soc_caps.h @@ -308,6 +308,7 @@ #define SOC_PCNT_CHANNELS_PER_UNIT 2 #define SOC_PCNT_THRES_POINT_PER_UNIT 2 #define SOC_PCNT_SUPPORT_RUNTIME_THRES_UPDATE 1 +#define SOC_PCNT_SUPPORT_SLEEP_RETENTION 1 /*!< The sleep retention feature can help back up PCNT registers before sleep */ /*--------------------------- RMT CAPS ---------------------------------------*/ #define SOC_RMT_GROUPS 1U /*!< One RMT group */ diff --git a/components/soc/esp32c6/pcnt_periph.c b/components/soc/esp32c6/pcnt_periph.c index b2b440245b..14c52fc0d6 100644 --- a/components/soc/esp32c6/pcnt_periph.c +++ b/components/soc/esp32c6/pcnt_periph.c @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "soc/pcnt_periph.h" #include "soc/gpio_sig_map.h" +#include "soc/pcnt_reg.h" const pcnt_signal_conn_t pcnt_periph_signals = { .groups = { @@ -64,3 +65,30 @@ const pcnt_signal_conn_t pcnt_periph_signals = { } } }; + +/** + * PCNT Registers to be saved during sleep retention + * - Configuration registers, e.g.: PCNT_CTRL_REG, PCNT_U0_CONF0_REG, PCNT_U0_CONF1_REG, PCNT_U0_CONF2_REG, PCNT_U1_CONF0_REG... + * - Interrupt enable registers, e.g.: PCNT_INT_ENA_REG +*/ +#define PCNT_RETENTION_REGS_CNT 14 +#define PCNT_RETENTION_REGS_BASE (DR_REG_PCNT_BASE + 0x0) +static const uint32_t pcnt_regs_map[4] = {0x1040fff, 0x0, 0x0, 0x0}; +static const regdma_entries_config_t pcnt_regs_retention[] = { + // backup stage: save configuration registers + // restore stage: restore the configuration registers + [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_PCNT_LINK(0x00), \ + PCNT_RETENTION_REGS_BASE, PCNT_RETENTION_REGS_BASE, \ + PCNT_RETENTION_REGS_CNT, 0, 0, \ + pcnt_regs_map[0], pcnt_regs_map[1], \ + pcnt_regs_map[2], pcnt_regs_map[3]), \ + .owner = ENTRY(0) | ENTRY(2) }, \ +}; + +const pcnt_reg_retention_info_t pcnt_reg_retention_info[SOC_PCNT_GROUPS] = { + [0] = { + .regdma_entry_array = pcnt_regs_retention, + .array_size = ARRAY_SIZE(pcnt_regs_retention), + .retention_module = SLEEP_RETENTION_MODULE_PCNT0 + }, +}; diff --git a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in index 72bfb1fa09..f655023a99 100644 --- a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in @@ -763,6 +763,10 @@ config SOC_PCNT_SUPPORT_RUNTIME_THRES_UPDATE bool default y +config SOC_PCNT_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_RMT_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 0b41e23c8d..a6c0bb37ee 100644 --- a/components/soc/esp32h2/include/soc/retention_periph_defs.h +++ b/components/soc/esp32h2/include/soc/retention_periph_defs.h @@ -45,6 +45,7 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_PARLIO0 = 21, SLEEP_RETENTION_MODULE_GPSPI2 = 22, SLEEP_RETENTION_MODULE_LEDC = 23, + SLEEP_RETENTION_MODULE_PCNT0 = 24, /* Modem module, which includes BLE and 802.15.4 */ SLEEP_RETENTION_MODULE_BLE_MAC = 28, @@ -85,6 +86,7 @@ typedef enum periph_retention_module_bitmap { SLEEP_RETENTION_MODULE_BM_PARLIO0 = BIT(SLEEP_RETENTION_MODULE_PARLIO0), 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), /* 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), @@ -113,6 +115,7 @@ typedef enum periph_retention_module_bitmap { | SLEEP_RETENTION_MODULE_BM_PARLIO0 \ | SLEEP_RETENTION_MODULE_BM_GPSPI2 \ | SLEEP_RETENTION_MODULE_BM_LEDC \ + | SLEEP_RETENTION_MODULE_BM_PCNT0 \ | 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 416f110682..26252ef134 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -302,6 +302,7 @@ #define SOC_PCNT_CHANNELS_PER_UNIT 2 #define SOC_PCNT_THRES_POINT_PER_UNIT 2 #define SOC_PCNT_SUPPORT_RUNTIME_THRES_UPDATE 1 +#define SOC_PCNT_SUPPORT_SLEEP_RETENTION 1 /*!< The sleep retention feature can help back up PCNT registers before sleep */ /*--------------------------- RMT CAPS ---------------------------------------*/ #define SOC_RMT_GROUPS 1U /*!< One RMT group */ diff --git a/components/soc/esp32h2/pcnt_periph.c b/components/soc/esp32h2/pcnt_periph.c index b2b440245b..14c52fc0d6 100644 --- a/components/soc/esp32h2/pcnt_periph.c +++ b/components/soc/esp32h2/pcnt_periph.c @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "soc/pcnt_periph.h" #include "soc/gpio_sig_map.h" +#include "soc/pcnt_reg.h" const pcnt_signal_conn_t pcnt_periph_signals = { .groups = { @@ -64,3 +65,30 @@ const pcnt_signal_conn_t pcnt_periph_signals = { } } }; + +/** + * PCNT Registers to be saved during sleep retention + * - Configuration registers, e.g.: PCNT_CTRL_REG, PCNT_U0_CONF0_REG, PCNT_U0_CONF1_REG, PCNT_U0_CONF2_REG, PCNT_U1_CONF0_REG... + * - Interrupt enable registers, e.g.: PCNT_INT_ENA_REG +*/ +#define PCNT_RETENTION_REGS_CNT 14 +#define PCNT_RETENTION_REGS_BASE (DR_REG_PCNT_BASE + 0x0) +static const uint32_t pcnt_regs_map[4] = {0x1040fff, 0x0, 0x0, 0x0}; +static const regdma_entries_config_t pcnt_regs_retention[] = { + // backup stage: save configuration registers + // restore stage: restore the configuration registers + [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_PCNT_LINK(0x00), \ + PCNT_RETENTION_REGS_BASE, PCNT_RETENTION_REGS_BASE, \ + PCNT_RETENTION_REGS_CNT, 0, 0, \ + pcnt_regs_map[0], pcnt_regs_map[1], \ + pcnt_regs_map[2], pcnt_regs_map[3]), \ + .owner = ENTRY(0) | ENTRY(2) }, \ +}; + +const pcnt_reg_retention_info_t pcnt_reg_retention_info[SOC_PCNT_GROUPS] = { + [0] = { + .regdma_entry_array = pcnt_regs_retention, + .array_size = ARRAY_SIZE(pcnt_regs_retention), + .retention_module = SLEEP_RETENTION_MODULE_PCNT0 + }, +}; diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 754ba90846..ee00d3083d 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -408,6 +408,7 @@ #define SOC_PCNT_THRES_POINT_PER_UNIT 2 #define SOC_PCNT_SUPPORT_RUNTIME_THRES_UPDATE 1 #define SOC_PCNT_SUPPORT_CLEAR_SIGNAL 1 /*!< Support clear signal input */ +// #define SOC_PCNT_SUPPORT_SLEEP_RETENTION 1 // TODO: IDF-9907 Waiting for expansion of module ID /*!< The sleep retention feature can help back up PCNT registers before sleep */ /*--------------------------- RMT CAPS ---------------------------------------*/ #define SOC_RMT_GROUPS 1U /*!< One RMT group */ diff --git a/components/soc/esp32p4/pcnt_periph.c b/components/soc/esp32p4/pcnt_periph.c index ae7d32b2fe..4b1cbcac5c 100644 --- a/components/soc/esp32p4/pcnt_periph.c +++ b/components/soc/esp32p4/pcnt_periph.c @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "soc/pcnt_periph.h" #include "soc/gpio_sig_map.h" +#include "soc/pcnt_reg.h" const pcnt_signal_conn_t pcnt_periph_signals = { .groups = { @@ -68,3 +69,33 @@ const pcnt_signal_conn_t pcnt_periph_signals = { } } }; + +#if SOC_PCNT_SUPPORT_SLEEP_RETENTION +/** + * PCNT Registers to be saved during sleep retention + * - Configuration registers, e.g.: PCNT_CTRL_REG, PCNT_U0_CONF0_REG, PCNT_U0_CONF1_REG, PCNT_U0_CONF2_REG, PCNT_U1_CONF0_REG... + * - Step Configuration registers, e.g.: PCNT_U0_CHANGE_CONF_REG, PCNT_U1_CHANGE_CONF_REG, PCNT_U2_CHANGE_CONF_REG, PCNT_U3_CHANGE_CONF_REG + * - Interrupt enable registers, e.g.: PCNT_INT_ENA_REG +*/ +#define PCNT_RETENTION_REGS_CNT 18 +#define PCNT_RETENTION_REGS_BASE (DR_REG_PCNT_BASE + 0x0) +static const uint32_t pcnt_regs_map[4] = {0x1f040fff, 0x0, 0x0, 0x0}; +static const regdma_entries_config_t pcnt_regs_retention[] = { + // backup stage: save configuration registers + // restore stage: restore the configuration registers + [0] = { .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_PCNT_LINK(0x00), \ + PCNT_RETENTION_REGS_BASE, PCNT_RETENTION_REGS_BASE, \ + PCNT_RETENTION_REGS_CNT, 0, 0, \ + pcnt_regs_map[0], pcnt_regs_map[1], \ + pcnt_regs_map[2], pcnt_regs_map[3]), \ + .owner = ENTRY(0)}, \ +}; + +const pcnt_reg_retention_info_t pcnt_reg_retention_info[SOC_PCNT_GROUPS] = { + [0] = { + .regdma_entry_array = pcnt_regs_retention, + .array_size = ARRAY_SIZE(pcnt_regs_retention), + .retention_module = SLEEP_RETENTION_MODULE_PCNT0 + }, +}; +#endif // SOC_PCNT_SUPPORT_SLEEP_RETENTION diff --git a/components/soc/include/soc/pcnt_periph.h b/components/soc/include/soc/pcnt_periph.h index e7d8818b09..a060af597f 100644 --- a/components/soc/include/soc/pcnt_periph.h +++ b/components/soc/include/soc/pcnt_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_PCNT_SUPPORT_SLEEP_RETENTION +#include "soc/retention_periph_defs.h" +#endif #ifdef __cplusplus extern "C" { @@ -31,6 +35,16 @@ typedef struct { extern const pcnt_signal_conn_t pcnt_periph_signals; +#if SOC_PCNT_SUPPORT_SLEEP_RETENTION +typedef struct { + const periph_retention_module_t retention_module; + const regdma_entries_config_t *regdma_entry_array; + uint32_t array_size; +} pcnt_reg_retention_info_t; + +extern const pcnt_reg_retention_info_t pcnt_reg_retention_info[SOC_PCNT_GROUPS]; +#endif // SOC_PCNT_SUPPORT_SLEEP_RETENTION + #endif // SOC_PCNT_SUPPORTED #ifdef __cplusplus diff --git a/components/soc/include/soc/regdma.h b/components/soc/include/soc/regdma.h index 44a116119f..7ee06f0095 100644 --- a/components/soc/include/soc/regdma.h +++ b/components/soc/include/soc/regdma.h @@ -60,6 +60,7 @@ extern "C" { #define REGDMA_PARLIO_LINK(_pri) ((0x22 << 8) | _pri) #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_MODEM_FE_LINK(_pri) ((0xFF << 8) | _pri) #define REGDMA_LINK_PRI_SYS_CLK REGDMA_LINK_PRI_0 @@ -79,6 +80,7 @@ extern "C" { #define REGDMA_LINK_PRI_I2C REGDMA_LINK_PRI_GENERAL_PERIPH #define REGDMA_LINK_PRI_I2S REGDMA_LINK_PRI_GENERAL_PERIPH #define REGDMA_LINK_PRI_PARLIO REGDMA_LINK_PRI_GENERAL_PERIPH +#define REGDMA_LINK_PRI_PCNT REGDMA_LINK_PRI_GENERAL_PERIPH #define REGDMA_LINK_PRI_UART REGDMA_LINK_PRI_GENERAL_PERIPH #define REGDMA_LINK_PRI_TEMPERATURE_SENSOR REGDMA_LINK_PRI_GENERAL_PERIPH #define REGDMA_LINK_PRI_TWAI REGDMA_LINK_PRI_GENERAL_PERIPH