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..78a5ea7d4e 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 !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..2df0e0b9c0 --- /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() +{ + 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 f65bfde521..28f99fdbc4 100644 --- a/components/soc/esp32c5/include/soc/retention_periph_defs.h +++ b/components/soc/esp32c5/include/soc/retention_periph_defs.h @@ -42,6 +42,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, @@ -86,6 +87,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), @@ -112,6 +114,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 \ ) #ifdef __cplusplus } diff --git a/components/soc/esp32c5/include/soc/soc_caps.h b/components/soc/esp32c5/include/soc/soc_caps.h index 7eb49628ea..584cac7d54 100644 --- a/components/soc/esp32c5/include/soc/soc_caps.h +++ b/components/soc/esp32c5/include/soc/soc_caps.h @@ -322,6 +322,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 2b1854eced..6b82875353 100644 --- a/components/soc/esp32c6/include/soc/retention_periph_defs.h +++ b/components/soc/esp32c6/include/soc/retention_periph_defs.h @@ -44,6 +44,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, @@ -84,6 +85,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), @@ -114,6 +116,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 \ ) #ifdef __cplusplus 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 766db48639..c22b47fb59 100644 --- a/components/soc/esp32h2/include/soc/retention_periph_defs.h +++ b/components/soc/esp32h2/include/soc/retention_periph_defs.h @@ -44,6 +44,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, @@ -82,6 +83,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), @@ -110,6 +112,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 \ ) #ifdef __cplusplus 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