diff --git a/components/esp_driver_sdm/include/driver/sdm.h b/components/esp_driver_sdm/include/driver/sdm.h index b4f66b578f..55849e2635 100644 --- a/components/esp_driver_sdm/include/driver/sdm.h +++ b/components/esp_driver_sdm/include/driver/sdm.h @@ -29,6 +29,8 @@ typedef struct { uint32_t sample_rate_hz; /*!< Over sample rate in Hz, it determines the frequency of the carrier pulses */ struct { uint32_t invert_out: 1; /*!< Whether to invert the output signal */ + uint32_t allow_pd: 1; /*!< If set, driver allows the power domain to be powered off when system enters sleep mode. + This can save power, but at the expense of more RAM being consumed to save register context. */ } flags; /*!< Extra flags */ } sdm_config_t; diff --git a/components/esp_driver_sdm/src/sdm.c b/components/esp_driver_sdm/src/sdm.c index e4ce80a0ef..4ea47994aa 100644 --- a/components/esp_driver_sdm/src/sdm.c +++ b/components/esp_driver_sdm/src/sdm.c @@ -39,6 +39,8 @@ #define SDM_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT #endif +#define SDM_USE_RETENTION_LINK (SOC_SDM_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP) + ///!< Logging settings #define TAG "sdm" @@ -81,6 +83,31 @@ struct sdm_channel_t { // sdm driver platform, it's always a singleton static sdm_platform_t s_platform; +#if SDM_USE_RETENTION_LINK +static esp_err_t sdm_create_sleep_retention_link_cb(void *group) +{ + int group_id = ((sdm_group_t *)group)->group_id; + esp_err_t err = sleep_retention_entries_create(soc_sdm_retention_infos[group_id].regdma_entry_array, + soc_sdm_retention_infos[group_id].array_size, + REGDMA_LINK_PRI_SDM, soc_sdm_retention_infos[group_id].module); + return err; +} + +static void sdm_create_retention_module(sdm_group_t *group) +{ + int group_id = group->group_id; + sleep_retention_module_t module = soc_sdm_retention_infos[group_id].module; + _lock_acquire(&s_platform.mutex); + if (sleep_retention_is_module_inited(module) && !sleep_retention_is_module_created(module)) { + if (sleep_retention_module_allocate(module) != ESP_OK) { + // even though the sleep retention module create failed, SDM driver should still work, so just warning here + ESP_LOGW(TAG, "create retention link failed on SDM Group%d, power domain won't be turned off during sleep", group_id); + } + } + _lock_release(&s_platform.mutex); +} +#endif // SDM_USE_RETENTION_LINK + static sdm_group_t *sdm_acquire_group_handle(int group_id, sdm_clock_source_t clk_src) { bool new_group = false; @@ -97,6 +124,25 @@ static sdm_group_t *sdm_acquire_group_handle(int group_id, sdm_clock_source_t cl group->group_id = group_id; group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; group->clk_src = clk_src; + +#if SDM_USE_RETENTION_LINK + sleep_retention_module_t module = soc_sdm_retention_infos[group->group_id].module; + sleep_retention_module_init_param_t init_param = { + .cbs = { + .create = { + .handle = sdm_create_sleep_retention_link_cb, + .arg = group, + }, + }, + .depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM) + }; + // retention module init must be called BEFORE the hal init + if (sleep_retention_module_init(module, &init_param) != ESP_OK) { + ESP_LOGW(TAG, "init sleep retention failed on SDM Group%d, power domain may be turned off during sleep", group->group_id); + } +#endif // SDM_USE_RETENTION_LINK + + // [IDF-12975]: enable APB register clock explicitly // initialize HAL context sdm_hal_init_config_t hal_config = { .group_id = group_id, @@ -142,6 +188,16 @@ static void sdm_release_group_handle(sdm_group_t *group) do_deinitialize = true; s_platform.groups[group_id] = NULL; // deregister from platform sdm_hal_deinit(&group->hal); + +#if SDM_USE_RETENTION_LINK + sleep_retention_module_t module = soc_sdm_retention_infos[group_id].module; + if (sleep_retention_is_module_created(module)) { + sleep_retention_module_free(module); + } + if (sleep_retention_is_module_inited(module)) { + sleep_retention_module_deinit(module); + } +#endif // SDM_USE_RETENTION_LINK } _lock_release(&s_platform.mutex); @@ -216,6 +272,10 @@ esp_err_t sdm_new_channel(const sdm_config_t *config, sdm_channel_handle_t *ret_ ESP_RETURN_ON_FALSE(config && ret_chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, TAG, "invalid GPIO number"); + [[maybe_unused]] bool allow_pd = config->flags.allow_pd == 1; +#if !SOC_SDM_SUPPORT_SLEEP_RETENTION + ESP_RETURN_ON_FALSE(allow_pd == false, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep"); +#endif // SOC_SDM_SUPPORT_SLEEP_RETENTION // allocate channel memory from internal memory because it contains atomic variable chan = heap_caps_calloc(1, sizeof(sdm_channel_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); @@ -271,6 +331,12 @@ esp_err_t sdm_new_channel(const sdm_config_t *config, sdm_channel_handle_t *ret_ chan->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; atomic_init(&chan->fsm, SDM_FSM_INIT); // set the initial state to INIT +#if SDM_USE_RETENTION_LINK + if (allow_pd) { + sdm_create_retention_module(group); + } +#endif // SDM_USE_RETENTION_LINK + ESP_LOGD(TAG, "new sdm channel (%d,%d) at %p, gpio=%d, sample rate=%"PRIu32"Hz", group_id, chan_id, chan, chan->gpio_num, chan->sample_rate_hz); *ret_chan = chan; return ESP_OK; diff --git a/components/esp_driver_sdm/test_apps/sigma_delta/main/CMakeLists.txt b/components/esp_driver_sdm/test_apps/sigma_delta/main/CMakeLists.txt index b416b209f1..4c644fce2d 100644 --- a/components/esp_driver_sdm/test_apps/sigma_delta/main/CMakeLists.txt +++ b/components/esp_driver_sdm/test_apps/sigma_delta/main/CMakeLists.txt @@ -1,7 +1,7 @@ -set(srcs "test_app_main.c") +set(srcs "test_app_main.c" "test_sdm.cpp") -if(CONFIG_SOC_SDM_SUPPORTED) - list(APPEND srcs "test_sdm.cpp") +if(CONFIG_SOC_LIGHT_SLEEP_SUPPORTED AND CONFIG_PM_ENABLE) + list(APPEND srcs "test_sdm_sleep.cpp") endif() # In order for the cases defined by `TEST_CASE` to be linked into the final elf, diff --git a/components/esp_driver_sdm/test_apps/sigma_delta/main/test_sdm.cpp b/components/esp_driver_sdm/test_apps/sigma_delta/main/test_sdm.cpp index 75f4626e9d..c10468a61e 100644 --- a/components/esp_driver_sdm/test_apps/sigma_delta/main/test_sdm.cpp +++ b/components/esp_driver_sdm/test_apps/sigma_delta/main/test_sdm.cpp @@ -22,6 +22,7 @@ TEST_CASE("sdm_channel_install_uninstall", "[sdm]") .sample_rate_hz = 1000000, .flags = { .invert_out = false, + .allow_pd = false, }, }; sdm_channel_handle_t chans[SOC_SDM_ATTR(INST_NUM)][SOC_SDM_ATTR(CHANS_PER_INST)] = {}; @@ -49,6 +50,7 @@ TEST_CASE("sdm_channel_set_pulse_density", "[sdm]") .sample_rate_hz = 1000000, .flags = { .invert_out = false, + .allow_pd = false, }, }; sdm_channel_handle_t chans[2] = {}; diff --git a/components/esp_driver_sdm/test_apps/sigma_delta/main/test_sdm_sleep.cpp b/components/esp_driver_sdm/test_apps/sigma_delta/main/test_sdm_sleep.cpp new file mode 100644 index 0000000000..e08b6e5549 --- /dev/null +++ b/components/esp_driver_sdm/test_apps/sigma_delta/main/test_sdm_sleep.cpp @@ -0,0 +1,92 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "unity.h" +#include "driver/sdm.h" +#include "soc/soc_caps.h" +#include "esp_sleep.h" +#include "esp_private/sleep_cpu.h" +#include "esp_private/esp_sleep_internal.h" +#include "esp_private/esp_pmu.h" + +/** + * @brief Test the SDM driver can still work after light sleep + * + * @param allow_pd Whether to allow power down the peripheral in light sleep + */ +static void test_sdm_sleep_retention(bool allow_pd) +{ + const gpio_num_t sdm_chan_gpios[2] = {GPIO_NUM_0, GPIO_NUM_2}; + sdm_config_t sdm_config = { + .gpio_num = GPIO_NUM_NC, // will be set later + .clk_src = SDM_CLK_SRC_DEFAULT, + .sample_rate_hz = 1000000, + .flags = { + .invert_out = false, + .allow_pd = allow_pd, + }, + }; + sdm_channel_handle_t chans[2] = {0}; + for (int i = 0; i < 2; i++) { + sdm_config.gpio_num = sdm_chan_gpios[i]; + TEST_ESP_OK(sdm_new_channel(&sdm_config, &chans[i])); + // should see a ~250KHz (sample_rate/4) square wave + TEST_ESP_OK(sdm_channel_set_pulse_density(chans[i], 0)); + TEST_ESP_OK(sdm_channel_enable(chans[i])); + } + vTaskDelay(pdMS_TO_TICKS(500)); + + // Note: disable the sdm before going to sleep, ensure no power management lock is acquired by it + for (int i = 0; i < 2; i++) { + TEST_ESP_OK(sdm_channel_disable(chans[i])); + } + + 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 SDM 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_SDM_SUPPORT_SLEEP_RETENTION && !SOC_PM_TOP_PD_NOT_ALLOWED + // 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); + + // re-enable the SDM channel + for (int i = 0; i < 2; i++) { + TEST_ESP_OK(sdm_channel_enable(chans[i])); + } + // should see a ~250KHz (sample_rate/4) square wave again + vTaskDelay(pdMS_TO_TICKS(500)); + + for (int i = 0; i < 2; i++) { + TEST_ESP_OK(sdm_channel_disable(chans[i])); + TEST_ESP_OK(sdm_del_channel(chans[i])); + } +} + +TEST_CASE("sdm can work after light sleep", "[sdm]") +{ + test_sdm_sleep_retention(false); +#if SOC_SDM_SUPPORT_SLEEP_RETENTION + test_sdm_sleep_retention(true); +#endif +} diff --git a/components/esp_driver_sdm/test_apps/sigma_delta/sdkconfig.ci.release b/components/esp_driver_sdm/test_apps/sigma_delta/sdkconfig.ci.release index 91d93f163e..1a87ebbb4a 100644 --- a/components/esp_driver_sdm/test_apps/sigma_delta/sdkconfig.ci.release +++ b/components/esp_driver_sdm/test_apps/sigma_delta/sdkconfig.ci.release @@ -1,5 +1,7 @@ CONFIG_PM_ENABLE=y CONFIG_FREERTOS_USE_TICKLESS_IDLE=y +CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y +CONFIG_PM_DFS_INIT_AUTO=y CONFIG_COMPILER_OPTIMIZATION_SIZE=y CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y diff --git a/components/esp_driver_sdm/test_apps/sigma_delta/sdkconfig.defaults b/components/esp_driver_sdm/test_apps/sigma_delta/sdkconfig.defaults index fa8ac618b9..488ad46859 100644 --- a/components/esp_driver_sdm/test_apps/sigma_delta/sdkconfig.defaults +++ b/components/esp_driver_sdm/test_apps/sigma_delta/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/esp_hw_support/sleep_system_peripheral.c b/components/esp_hw_support/sleep_system_peripheral.c index 9c1a62a48b..91020a5e98 100644 --- a/components/esp_hw_support/sleep_system_peripheral.c +++ b/components/esp_hw_support/sleep_system_peripheral.c @@ -291,6 +291,10 @@ bool peripheral_domain_pd_allowed(void) mask.bitmap[SLEEP_RETENTION_MODULE_MCPWM0 >> 5] |= BIT(SLEEP_RETENTION_MODULE_MCPWM0 % 32); #endif +#if SOC_SDM_SUPPORT_SLEEP_RETENTION + mask.bitmap[SLEEP_RETENTION_MODULE_SDM0 >> 5] |= BIT(SLEEP_RETENTION_MODULE_SDM0 % 32); +#endif + const sleep_retention_module_bitmap_t peripheral_domain_inited_modules = sleep_retention_module_bitmap_and(inited_modules, mask); const sleep_retention_module_bitmap_t peripheral_domain_created_modules = sleep_retention_module_bitmap_and(created_modules, mask); return sleep_retention_module_bitmap_eq(peripheral_domain_inited_modules, peripheral_domain_created_modules); diff --git a/components/hal/esp32p4/include/hal/pmu_ll.h b/components/hal/esp32p4/include/hal/pmu_ll.h index 599dcbb79a..eefa07ea57 100644 --- a/components/hal/esp32p4/include/hal/pmu_ll.h +++ b/components/hal/esp32p4/include/hal/pmu_ll.h @@ -638,9 +638,9 @@ FORCE_INLINE_ATTR uint32_t pmu_ll_hp_get_analog_wait_target_cycle(pmu_dev_t *hw) return HAL_FORCE_READ_U32_REG_FIELD(hw->wakeup.cntl7, ana_wait_target); } -FORCE_INLINE_ATTR uint32_t pmu_ll_hp_set_lite_wakeup_enable(pmu_dev_t *hw, bool wakeup_en) +FORCE_INLINE_ATTR void pmu_ll_hp_set_lite_wakeup_enable(pmu_dev_t *hw, bool wakeup_en) { - return hw->wakeup.cntl8.lp_lite_wakeup_ena = wakeup_en; + hw->wakeup.cntl8.lp_lite_wakeup_ena = wakeup_en; } FORCE_INLINE_ATTR void pmu_ll_hp_set_digital_power_supply_wait_cycle(pmu_dev_t *hw, uint32_t cycle) diff --git a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in index 7440849224..ad32bd89e7 100644 --- a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in @@ -647,6 +647,10 @@ config SOC_DEDIC_PERIPH_ALWAYS_ENABLE bool default y +config SOC_SDM_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_ANA_CMPR_NUM 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 a9cda6140e..9ceee5a2b9 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_MCPWM0 = 22, + SLEEP_RETENTION_MODULE_SDM0 = 23, /* modem module, which includes WiFi, BLE and 802.15.4 */ SLEEP_RETENTION_MODULE_WIFI_MAC = 26, @@ -80,6 +81,7 @@ typedef enum periph_retention_module { : ((m) == SLEEP_RETENTION_MODULE_GPSPI2) ? true \ : ((m) == SLEEP_RETENTION_MODULE_LEDC) ? true \ : ((m) == SLEEP_RETENTION_MODULE_MCPWM0) ? true \ + : ((m) == SLEEP_RETENTION_MODULE_SDM0) ? true \ : false) #ifdef __cplusplus diff --git a/components/soc/esp32c5/include/soc/soc_caps.h b/components/soc/esp32c5/include/soc/soc_caps.h index 34b3944b87..1cacb6a7bf 100644 --- a/components/soc/esp32c5/include/soc/soc_caps.h +++ b/components/soc/esp32c5/include/soc/soc_caps.h @@ -267,6 +267,8 @@ #define SOC_DEDIC_GPIO_IN_CHANNELS_NUM (8) /*!< 8 inward channels on each CPU core */ #define SOC_DEDIC_PERIPH_ALWAYS_ENABLE (1) /*!< The dedicated GPIO (a.k.a. fast GPIO) is featured by some customized CPU instructions, which is always enabled */ +#define SOC_SDM_SUPPORT_SLEEP_RETENTION 1 + /*------------------------- Analog Comparator CAPS ---------------------------*/ #define SOC_ANA_CMPR_NUM (1U) #define SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE (1) // Support positive/negative/any cross interrupt diff --git a/components/soc/esp32c5/sdm_periph.c b/components/soc/esp32c5/sdm_periph.c index b92e215fb5..ef1b0c1ded 100644 --- a/components/soc/esp32c5/sdm_periph.c +++ b/components/soc/esp32c5/sdm_periph.c @@ -6,6 +6,7 @@ #include "soc/sdm_periph.h" #include "soc/gpio_sig_map.h" +#include "soc/gpio_ext_reg.h" const soc_sdm_signal_desc_t soc_sdm_signals[1] = { [0] = { @@ -26,3 +27,32 @@ const soc_sdm_signal_desc_t soc_sdm_signals[1] = { } } }; + +/** + * @brief Registers in retention context: + * GPIO_EXT_SIGMADELTA[x]_REG + * GPIO_EXT_SIGMADELTA_MISC_REG + */ +#define GPIO_EXT_RETENTION_REGS_CNT 5 +#define GPIO_EXT_RETENTION_REGS_BASE (DR_REG_GPIO_EXT_BASE + 0x4) +static const uint32_t gpio_ext_regs_map[4] = {0x1f, 0x0, 0x0, 0x0}; +static const regdma_entries_config_t gpio_ext_regdma_entries[] = { + // backup stage: save configuration and status registers + // restore stage: restore the configuration and status registers + [0] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_SDM_LINK(0x00), + GPIO_EXT_RETENTION_REGS_BASE, GPIO_EXT_RETENTION_REGS_BASE, + GPIO_EXT_RETENTION_REGS_CNT, 0, 0, + gpio_ext_regs_map[0], gpio_ext_regs_map[1], + gpio_ext_regs_map[2], gpio_ext_regs_map[3]), + .owner = ENTRY(0) | ENTRY(2), + }, +}; + +const soc_sdm_retention_desc_t soc_sdm_retention_infos[1] = { + [0] = { + .module = SLEEP_RETENTION_MODULE_SDM0, + .regdma_entry_array = gpio_ext_regdma_entries, + .array_size = ARRAY_SIZE(gpio_ext_regdma_entries) + } +}; diff --git a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in index eb3ba8d31a..d263d930de 100644 --- a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in @@ -579,6 +579,10 @@ config SOC_DEDIC_PERIPH_ALWAYS_ENABLE bool default y +config SOC_SDM_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_I2C_NUM int default 2 diff --git a/components/soc/esp32c6/include/soc/retention_periph_defs.h b/components/soc/esp32c6/include/soc/retention_periph_defs.h index d944f62397..ee48a60b13 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_MCPWM0 = 24, + SLEEP_RETENTION_MODULE_SDM0 = 25, /* Modem module, which includes WiFi, BLE and 802.15.4 */ SLEEP_RETENTION_MODULE_WIFI_MAC = 26, @@ -82,6 +83,7 @@ typedef enum periph_retention_module { : ((m) == SLEEP_RETENTION_MODULE_GPSPI2) ? true \ : ((m) == SLEEP_RETENTION_MODULE_LEDC) ? true \ : ((m) == SLEEP_RETENTION_MODULE_MCPWM0) ? true \ + : ((m) == SLEEP_RETENTION_MODULE_SDM0) ? true \ : false) #ifdef __cplusplus diff --git a/components/soc/esp32c6/include/soc/soc_caps.h b/components/soc/esp32c6/include/soc/soc_caps.h index 46580ed16e..7e6d5ac37d 100644 --- a/components/soc/esp32c6/include/soc/soc_caps.h +++ b/components/soc/esp32c6/include/soc/soc_caps.h @@ -245,6 +245,8 @@ #define SOC_DEDIC_GPIO_IN_CHANNELS_NUM (8) /*!< 8 inward channels on each CPU core */ #define SOC_DEDIC_PERIPH_ALWAYS_ENABLE (1) /*!< The dedicated GPIO (a.k.a. fast GPIO) is featured by some customized CPU instructions, which is always enabled */ +#define SOC_SDM_SUPPORT_SLEEP_RETENTION 1 + /*-------------------------- I2C CAPS ----------------------------------------*/ // ESP32-C6 has 1 I2C #define SOC_I2C_NUM (2U) // I2C_NUM = HP_I2C + LP_I2C diff --git a/components/soc/esp32c6/sdm_periph.c b/components/soc/esp32c6/sdm_periph.c index b92e215fb5..c4c4319411 100644 --- a/components/soc/esp32c6/sdm_periph.c +++ b/components/soc/esp32c6/sdm_periph.c @@ -6,6 +6,7 @@ #include "soc/sdm_periph.h" #include "soc/gpio_sig_map.h" +#include "soc/gpio_ext_reg.h" const soc_sdm_signal_desc_t soc_sdm_signals[1] = { [0] = { @@ -26,3 +27,32 @@ const soc_sdm_signal_desc_t soc_sdm_signals[1] = { } } }; + +/** + * @brief Registers in retention context: + * GPIO_EXT_SIGMADELTA[x]_REG + * GPIO_EXT_SIGMADELTA_MISC_REG + */ +#define GPIO_EXT_RETENTION_REGS_CNT 5 +#define GPIO_EXT_RETENTION_REGS_BASE (DR_REG_GPIO_EXT_BASE + 0x0) +static const uint32_t gpio_ext_regs_map[4] = {0x20f, 0x0, 0x0, 0x0}; +static const regdma_entries_config_t gpio_ext_regdma_entries[] = { + // backup stage: save configuration and status registers + // restore stage: restore the configuration and status registers + [0] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_SDM_LINK(0x00), + GPIO_EXT_RETENTION_REGS_BASE, GPIO_EXT_RETENTION_REGS_BASE, + GPIO_EXT_RETENTION_REGS_CNT, 0, 0, + gpio_ext_regs_map[0], gpio_ext_regs_map[1], + gpio_ext_regs_map[2], gpio_ext_regs_map[3]), + .owner = ENTRY(0) | ENTRY(2), + }, +}; + +const soc_sdm_retention_desc_t soc_sdm_retention_infos[1] = { + [0] = { + .module = SLEEP_RETENTION_MODULE_SDM0, + .regdma_entry_array = gpio_ext_regdma_entries, + .array_size = ARRAY_SIZE(gpio_ext_regdma_entries) + } +}; diff --git a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in index dc57925b10..2824306da9 100644 --- a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in @@ -583,6 +583,10 @@ config SOC_DEDIC_PERIPH_ALWAYS_ENABLE bool default y +config SOC_SDM_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_ANA_CMPR_NUM 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 e4ef9608dd..b4585071ca 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_MCPWM0 = 24, + SLEEP_RETENTION_MODULE_SDM0 = 25, /* Modem module, which includes BLE and 802.15.4 */ SLEEP_RETENTION_MODULE_BLE_MAC = 28, @@ -80,6 +81,7 @@ typedef enum periph_retention_module { : ((m) == SLEEP_RETENTION_MODULE_GPSPI2) ? true \ : ((m) == SLEEP_RETENTION_MODULE_LEDC) ? true \ : ((m) == SLEEP_RETENTION_MODULE_MCPWM0) ? true \ + : ((m) == SLEEP_RETENTION_MODULE_SDM0) ? true \ : false) #ifdef __cplusplus diff --git a/components/soc/esp32h2/include/soc/soc_caps.h b/components/soc/esp32h2/include/soc/soc_caps.h index b64fb789d4..d4e7a19267 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -265,6 +265,8 @@ #define SOC_DEDIC_GPIO_IN_CHANNELS_NUM (8) /*!< 8 inward channels on each CPU core */ #define SOC_DEDIC_PERIPH_ALWAYS_ENABLE (1) /*!< The dedicated GPIO (a.k.a. fast GPIO) is featured by some customized CPU instructions, which is always enabled */ +#define SOC_SDM_SUPPORT_SLEEP_RETENTION 1 + /*------------------------- Analog Comparator CAPS ---------------------------*/ #define SOC_ANA_CMPR_NUM (1U) #define SOC_ANA_CMPR_INTR_SHARE_WITH_GPIO (1) diff --git a/components/soc/esp32h2/sdm_periph.c b/components/soc/esp32h2/sdm_periph.c index b92e215fb5..c4c4319411 100644 --- a/components/soc/esp32h2/sdm_periph.c +++ b/components/soc/esp32h2/sdm_periph.c @@ -6,6 +6,7 @@ #include "soc/sdm_periph.h" #include "soc/gpio_sig_map.h" +#include "soc/gpio_ext_reg.h" const soc_sdm_signal_desc_t soc_sdm_signals[1] = { [0] = { @@ -26,3 +27,32 @@ const soc_sdm_signal_desc_t soc_sdm_signals[1] = { } } }; + +/** + * @brief Registers in retention context: + * GPIO_EXT_SIGMADELTA[x]_REG + * GPIO_EXT_SIGMADELTA_MISC_REG + */ +#define GPIO_EXT_RETENTION_REGS_CNT 5 +#define GPIO_EXT_RETENTION_REGS_BASE (DR_REG_GPIO_EXT_BASE + 0x0) +static const uint32_t gpio_ext_regs_map[4] = {0x20f, 0x0, 0x0, 0x0}; +static const regdma_entries_config_t gpio_ext_regdma_entries[] = { + // backup stage: save configuration and status registers + // restore stage: restore the configuration and status registers + [0] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_SDM_LINK(0x00), + GPIO_EXT_RETENTION_REGS_BASE, GPIO_EXT_RETENTION_REGS_BASE, + GPIO_EXT_RETENTION_REGS_CNT, 0, 0, + gpio_ext_regs_map[0], gpio_ext_regs_map[1], + gpio_ext_regs_map[2], gpio_ext_regs_map[3]), + .owner = ENTRY(0) | ENTRY(2), + }, +}; + +const soc_sdm_retention_desc_t soc_sdm_retention_infos[1] = { + [0] = { + .module = SLEEP_RETENTION_MODULE_SDM0, + .regdma_entry_array = gpio_ext_regdma_entries, + .array_size = ARRAY_SIZE(gpio_ext_regdma_entries) + } +}; diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 812dfafa3f..2bcd5eac7e 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -767,6 +767,10 @@ config SOC_DEDIC_PERIPH_ALWAYS_ENABLE bool default y +config SOC_SDM_SUPPORT_SLEEP_RETENTION + bool + default y + config SOC_ANA_CMPR_NUM int default 2 diff --git a/components/soc/esp32p4/include/soc/retention_periph_defs.h b/components/soc/esp32p4/include/soc/retention_periph_defs.h index d68770ef3a..09640ba728 100644 --- a/components/soc/esp32p4/include/soc/retention_periph_defs.h +++ b/components/soc/esp32p4/include/soc/retention_periph_defs.h @@ -59,6 +59,7 @@ typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_LEDC = 33, SLEEP_RETENTION_MODULE_MCPWM0 = 34, SLEEP_RETENTION_MODULE_MCPWM1 = 35, + SLEEP_RETENTION_MODULE_SDM0 = 36, SLEEP_RETENTION_MODULE_MAX = SOC_PM_RETENTION_MODULE_NUM - 1 } periph_retention_module_t; @@ -100,6 +101,7 @@ typedef enum periph_retention_module { : ((m) == SLEEP_RETENTION_MODULE_LEDC) ? true \ : ((m) == SLEEP_RETENTION_MODULE_MCPWM0) ? true \ : ((m) == SLEEP_RETENTION_MODULE_MCPWM1) ? true \ + : ((m) == SLEEP_RETENTION_MODULE_SDM0) ? true \ : false) #ifdef __cplusplus diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index e6491e1463..b392b7d3ef 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -298,6 +298,8 @@ #define SOC_DEDIC_GPIO_IN_CHANNELS_NUM (8) /*!< 8 inward channels on each CPU core */ #define SOC_DEDIC_PERIPH_ALWAYS_ENABLE (1) /*!< The dedicated GPIO (a.k.a. fast GPIO) is featured by some customized CPU instructions, which is always enabled */ +#define SOC_SDM_SUPPORT_SLEEP_RETENTION 1 + /*------------------------- Analog Comparator CAPS ---------------------------*/ #define SOC_ANA_CMPR_NUM (2) #define SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE (1) // Support positive/negative/any cross interrupt diff --git a/components/soc/esp32p4/sdm_periph.c b/components/soc/esp32p4/sdm_periph.c index d013c5a45c..4b89dce5a3 100644 --- a/components/soc/esp32p4/sdm_periph.c +++ b/components/soc/esp32p4/sdm_periph.c @@ -6,6 +6,7 @@ #include "soc/sdm_periph.h" #include "soc/gpio_sig_map.h" +#include "soc/gpio_ext_reg.h" const soc_sdm_signal_desc_t soc_sdm_signals[1] = { [0] = { @@ -38,3 +39,32 @@ const soc_sdm_signal_desc_t soc_sdm_signals[1] = { } } }; + +/** + * @brief Registers in retention context: + * GPIO_EXT_SIGMADELTA[x]_REG + * GPIO_EXT_SIGMADELTA_MISC_REG + */ +#define GPIO_EXT_RETENTION_REGS_CNT 9 +#define GPIO_EXT_RETENTION_REGS_BASE (DR_REG_GPIO_EXT_BASE + 0x0) +static const uint32_t gpio_ext_regs_map[4] = {0x2ff, 0x0, 0x0, 0x0}; +static const regdma_entries_config_t gpio_ext_regdma_entries[] = { + // backup stage: save configuration and status registers + // restore stage: restore the configuration and status registers + [0] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_SDM_LINK(0x00), + GPIO_EXT_RETENTION_REGS_BASE, GPIO_EXT_RETENTION_REGS_BASE, + GPIO_EXT_RETENTION_REGS_CNT, 0, 0, + gpio_ext_regs_map[0], gpio_ext_regs_map[1], + gpio_ext_regs_map[2], gpio_ext_regs_map[3]), + .owner = ENTRY(0), + }, +}; + +const soc_sdm_retention_desc_t soc_sdm_retention_infos[1] = { + [0] = { + .module = SLEEP_RETENTION_MODULE_SDM0, + .regdma_entry_array = gpio_ext_regdma_entries, + .array_size = ARRAY_SIZE(gpio_ext_regdma_entries) + } +}; diff --git a/components/soc/include/soc/regdma.h b/components/soc/include/soc/regdma.h index 67b5c1d7fb..e3015fcbb7 100644 --- a/components/soc/include/soc/regdma.h +++ b/components/soc/include/soc/regdma.h @@ -64,6 +64,7 @@ extern "C" { #define REGDMA_GPSPI_LINK(_pri) ((0x23 << 8) | _pri) #define REGDMA_LEDC_LINK(_pri) ((0x24 << 8) | _pri) #define REGDMA_MCPWM_LINK(_pri) ((0x25 << 8) | _pri) +#define REGDMA_SDM_LINK(_pri) ((0x26 << 8) | _pri) #define REGDMA_MODEM_FE_LINK(_pri) ((0xFF << 8) | _pri) @@ -90,6 +91,7 @@ extern "C" { #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 +#define REGDMA_LINK_PRI_SDM REGDMA_LINK_PRI_GENERAL_PERIPH typedef enum { REGDMA_LINK_PRI_0 = 0, diff --git a/components/soc/include/soc/sdm_periph.h b/components/soc/include/soc/sdm_periph.h index c304196fd1..65f37a3dd9 100644 --- a/components/soc/include/soc/sdm_periph.h +++ b/components/soc/include/soc/sdm_periph.h @@ -23,6 +23,8 @@ extern "C" { #endif +#if SOC_HAS(SDM) + typedef struct { const char *module_name; // Module name struct { @@ -32,6 +34,20 @@ typedef struct { extern const soc_sdm_signal_desc_t soc_sdm_signals[SOC_SDM_ATTR(INST_NUM)]; +#if SOC_HAS(PAU) + +typedef struct { + const periph_retention_module_t module; // ID of the SDM as a retention module + const regdma_entries_config_t *regdma_entry_array; // Array of regdma entries for retention + const size_t array_size; // Size of the regdma_entry_array +} soc_sdm_retention_desc_t; + +extern const soc_sdm_retention_desc_t soc_sdm_retention_infos[SOC_SDM_ATTR(INST_NUM)]; + +#endif // SOC_HAS(PAU) + +#endif // SOC_HAS(SDM) + #ifdef __cplusplus } #endif