diff --git a/components/esp_pm/linker.lf b/components/esp_pm/linker.lf index 67c8051b96b..f3325809788 100644 --- a/components/esp_pm/linker.lf +++ b/components/esp_pm/linker.lf @@ -72,3 +72,5 @@ entries: gpio_hal_workaround:gpio_hal_sleep_pupd_config_unapply (noflash) gpio_hal_workaround:gpio_hal_sleep_mode_setup_wrapper (noflash) gpio_hal_workaround:gpio_hal_fun_pupd_restore (noflash) + if PM_SLP_IRAM_OPT = y && ESP_SYSTEM_PM_POWER_DOWN_CPU = y: + rtc_cntl_hal:rtc_cntl_hal_enable_cpu_retention (noflash) diff --git a/components/esp_pm/pm_impl.c b/components/esp_pm/pm_impl.c index 6ad87035447..3b02cc64b07 100644 --- a/components/esp_pm/pm_impl.c +++ b/components/esp_pm/pm_impl.c @@ -60,6 +60,7 @@ #include "esp32c3/clk.h" #include "esp32c3/pm.h" #include "driver/gpio.h" +#include "esp_private/sleep_modes.h" #endif #define MHZ (1000000) @@ -303,6 +304,13 @@ esp_err_t esp_pm_configure(const void* vconfig) esp_sleep_gpio_status_switch_configure(config->light_sleep_enable); #endif +#if CONFIG_ESP_SYSTEM_PM_POWER_DOWN_CPU && SOC_PM_SUPPORT_CPU_PD + esp_err_t ret = esp_sleep_cpu_pd_low_init(config->light_sleep_enable); + if (config->light_sleep_enable && ret != ESP_OK) { + ESP_LOGW(TAG, "Failed to enable CPU power down during light sleep."); + } +#endif + return ESP_OK; } diff --git a/components/esp_system/Kconfig b/components/esp_system/Kconfig index 342b2c24ca5..c31ab7c3131 100644 --- a/components/esp_system/Kconfig +++ b/components/esp_system/Kconfig @@ -87,4 +87,12 @@ menu "ESP System Settings" If enabled, chip will try to power down flash at light sleep, which costs more time when chip wakes up. Only can be enabled if there is no SPIRAM configured. + config ESP_SYSTEM_PM_POWER_DOWN_CPU + bool "Power down CPU in light sleep" + depends on IDF_TARGET_ESP32C3 + default y + help + If enabled, the CPU will be powered down in light sleep. Enabling this option will consume + 1.68 KB of internal RAM and will reduce sleep current consumption by about 100 uA. + endmenu # ESP System Settings diff --git a/components/esp_system/include/esp_private/sleep_modes.h b/components/esp_system/include/esp_private/sleep_modes.h new file mode 100644 index 00000000000..e1584a8b573 --- /dev/null +++ b/components/esp_system/include/esp_private/sleep_modes.h @@ -0,0 +1,40 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef CONFIG_ESP_SYSTEM_PM_POWER_DOWN_CPU +/** + * @brief CPU Power down low-level initialize + * + * @param enable enable or disable CPU power down during light sleep + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM not enough retention memory + */ +esp_err_t esp_sleep_cpu_pd_low_init(bool enable); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_system/include/esp_sleep.h b/components/esp_system/include/esp_sleep.h index 423bee51a12..6bc65b980aa 100644 --- a/components/esp_system/include/esp_sleep.h +++ b/components/esp_system/include/esp_sleep.h @@ -42,6 +42,7 @@ typedef enum { ESP_PD_DOMAIN_RTC_SLOW_MEM, //!< RTC slow memory ESP_PD_DOMAIN_RTC_FAST_MEM, //!< RTC fast memory ESP_PD_DOMAIN_XTAL, //!< XTAL oscillator + ESP_PD_DOMAIN_CPU, //!< CPU core ESP_PD_DOMAIN_MAX //!< Number of domains } esp_sleep_pd_domain_t; diff --git a/components/esp_system/sleep_modes.c b/components/esp_system/sleep_modes.c index 4ecf1da2d74..1c846df1f32 100644 --- a/components/esp_system/sleep_modes.c +++ b/components/esp_system/sleep_modes.c @@ -70,6 +70,9 @@ #include "esp32s3/rom/cache.h" #include "esp32c3/rom/rtc.h" #include "soc/extmem_reg.h" +#include "esp_heap_caps.h" +#include "hal/rtc_hal.h" +#include "soc/rtc_caps.h" #endif // If light sleep time is less than that, don't power down flash @@ -141,10 +144,13 @@ typedef struct { uint32_t sleep_time_overhead_out; uint32_t rtc_clk_cal_period; uint64_t rtc_ticks_at_sleep_start; +#if SOC_PM_SUPPORT_CPU_PD + void *cpu_pd_mem; +#endif } sleep_config_t; static sleep_config_t s_config = { - .pd_options = { ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO }, + .pd_options = { ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO }, .ccount_ticks_record = 0, .sleep_time_overhead_out = DEFAULT_SLEEP_OUT_OVERHEAD_US, .wakeup_triggers = 0 @@ -259,6 +265,32 @@ static void IRAM_ATTR resume_uarts(void) inline static uint32_t IRAM_ATTR call_rtc_sleep_start(uint32_t reject_triggers); +#if SOC_PM_SUPPORT_CPU_PD +esp_err_t esp_sleep_cpu_pd_low_init(bool enable) +{ + if (enable) { + if (s_config.cpu_pd_mem == NULL) { + void *buf = heap_caps_aligned_alloc(RTC_CNTL_CPU_PD_DMA_ADDR_ALIGN, + RTC_CNTL_CPU_PD_RETENTION_MEM_SIZE + RTC_HAL_DMA_LINK_NODE_SIZE, + MALLOC_CAP_RETENTION|MALLOC_CAP_DEFAULT); + if (buf) { + memset(buf, 0, RTC_CNTL_CPU_PD_RETENTION_MEM_SIZE + RTC_HAL_DMA_LINK_NODE_SIZE); + s_config.cpu_pd_mem = rtc_cntl_hal_dma_link_init(buf, + buf+RTC_HAL_DMA_LINK_NODE_SIZE, RTC_CNTL_CPU_PD_RETENTION_MEM_SIZE, NULL); + } else { + return ESP_ERR_NO_MEM; + } + } + } else { + if (s_config.cpu_pd_mem) { + heap_caps_free(s_config.cpu_pd_mem); + s_config.cpu_pd_mem = NULL; + } + } + return ESP_OK; +} +#endif // SOC_PM_SUPPORT_CPU_PD + #if SOC_GPIO_SUPPORT_SLP_SWITCH #if CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL static inline void gpio_sleep_mode_config_apply(void) @@ -434,6 +466,10 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags) s_config.ccount_ticks_record = cpu_ll_get_cycle_count(); } +#if SOC_PM_SUPPORT_CPU_PD + rtc_cntl_hal_disable_cpu_retention(); +#endif + #if CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL gpio_sleep_mode_config_unapply(); #endif @@ -591,6 +627,10 @@ esp_err_t esp_light_sleep_start(void) periph_inform_out_light_sleep_overhead(s_config.sleep_time_adjustment - sleep_time_overhead_in); +#if SOC_PM_SUPPORT_CPU_PD + rtc_cntl_hal_enable_cpu_retention(s_config.cpu_pd_mem); +#endif + rtc_vddsdio_config_t vddsdio_config = rtc_vddsdio_get_config(); // Safety net: enable WDT in case exit from light sleep fails @@ -1046,6 +1086,12 @@ static uint32_t get_power_down_flags(void) #endif // SOC_TOUCH_PAD_WAKE_SUPPORTED } +#if !SOC_PM_SUPPORT_CPU_PD + if (s_config.pd_options[ESP_PD_DOMAIN_CPU] == ESP_PD_OPTION_AUTO) { + s_config.pd_options[ESP_PD_DOMAIN_CPU] = ESP_PD_OPTION_ON; + } +#endif + if (s_config.pd_options[ESP_PD_DOMAIN_XTAL] == ESP_PD_OPTION_AUTO) { s_config.pd_options[ESP_PD_DOMAIN_XTAL] = ESP_PD_OPTION_OFF; } @@ -1071,6 +1117,12 @@ static uint32_t get_power_down_flags(void) pd_flags |= RTC_SLEEP_PD_RTC_PERIPH; } +#if SOC_PM_SUPPORT_CPU_PD + if (s_config.pd_options[ESP_PD_DOMAIN_CPU] != ESP_PD_OPTION_ON) { + pd_flags |= RTC_SLEEP_PD_CPU; + } +#endif + #ifdef CONFIG_IDF_TARGET_ESP32 pd_flags |= RTC_SLEEP_PD_XTAL; #endif diff --git a/components/hal/CMakeLists.txt b/components/hal/CMakeLists.txt index 28e0bc32139..68e3cbfac62 100644 --- a/components/hal/CMakeLists.txt +++ b/components/hal/CMakeLists.txt @@ -92,7 +92,8 @@ if(NOT BOOTLOADER_BUILD) "esp32c3/systimer_hal.c" "esp32c3/hmac_hal.c" "spi_flash_hal_gpspi.c" - "spi_slave_hd_hal.c") + "spi_slave_hd_hal.c" + "esp32c3/rtc_cntl_hal.c") endif() endif() diff --git a/components/hal/esp32c3/include/hal/rtc_cntl_ll.h b/components/hal/esp32c3/include/hal/rtc_cntl_ll.h index 0e472eb80b0..bcc8d0fcdb3 100644 --- a/components/hal/esp32c3/include/hal/rtc_cntl_ll.h +++ b/components/hal/esp32c3/include/hal/rtc_cntl_ll.h @@ -17,6 +17,7 @@ #include "soc/soc.h" #include "soc/rtc.h" #include "soc/rtc_cntl_reg.h" +#include "soc/apb_ctrl_reg.h" #ifdef __cplusplus extern "C" { @@ -47,6 +48,21 @@ static inline void rtc_cntl_ll_ext1_clear_wakeup_pins(void) REG_SET_BIT(RTC_CNTL_GPIO_WAKEUP_REG, RTC_CNTL_GPIO_WAKEUP_STATUS_CLR); } +static inline void rtc_cntl_ll_enable_cpu_retention(uint32_t addr) +{ + /* write memory address to register */ + REG_SET_FIELD(APB_CTRL_RETENTION_CTRL_REG, APB_CTRL_RETENTION_LINK_ADDR, (uint32_t)addr); + /* Enable clock */ + REG_SET_BIT(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_EN); + /* Enable retention when cpu sleep enable */ + REG_SET_BIT(RTC_CNTL_RETENTION_CTRL_REG, RTC_CNTL_RETENTION_EN); +} + +static inline void rtc_cntl_ll_disable_cpu_retention(void) +{ + REG_CLR_BIT(RTC_CNTL_RETENTION_CTRL_REG, RTC_CNTL_RETENTION_EN); +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32c3/rtc_cntl_hal.c b/components/hal/esp32c3/rtc_cntl_hal.c new file mode 100644 index 00000000000..0c8e1eb9a38 --- /dev/null +++ b/components/hal/esp32c3/rtc_cntl_hal.c @@ -0,0 +1,61 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// The HAL layer for RTC CNTL (common part) + +#include "hal/rtc_hal.h" +#include "soc/soc_caps.h" +#include "esp32c3/rom/lldesc.h" +#include "esp_attr.h" + +#define RTC_CNTL_HAL_LINK_BUF_SIZE_MIN (RTC_CNTL_CPU_PD_DMA_BLOCK_SIZE) /* The minimum size of dma link buffer */ + +typedef struct rtc_cntl_link_buf_conf { + uint32_t cfg[4]; /* 4 word for dma link buffer configuration */ +} rtc_cntl_link_buf_conf_t; + +void * rtc_cntl_hal_dma_link_init(void *elem, void *buff, int size, void *next) +{ + assert(elem != NULL); + assert(buff != NULL); + assert(size >= RTC_CNTL_HAL_LINK_BUF_SIZE_MIN); + + lldesc_t *plink = (lldesc_t *)elem; + + plink->eof = next ? 0 : 1; + plink->owner = 1; + plink->size = size >> 4; /* in unit of 16 bytes */ + plink->length = size >> 4; + plink->buf = buff; + plink->offset = 0; + plink->sosf = 0; + STAILQ_NEXT(plink, qe) = next; + return (void *)plink; +} + +void rtc_cntl_hal_enable_cpu_retention(void *addr) +{ + if (addr) { + lldesc_t *plink = (lldesc_t *)addr; + + /* dma link buffer configure */ + rtc_cntl_link_buf_conf_t *pbuf = (rtc_cntl_link_buf_conf_t *)plink->buf; + pbuf->cfg[0] = 0; + pbuf->cfg[1] = 0; + pbuf->cfg[2] = 0; + pbuf->cfg[3] = (uint32_t)-1; + + rtc_cntl_ll_enable_cpu_retention((uint32_t)addr); + } +} diff --git a/components/hal/include/hal/rtc_hal.h b/components/hal/include/hal/rtc_hal.h index 94c016c5682..e4874ad1686 100644 --- a/components/hal/include/hal/rtc_hal.h +++ b/components/hal/include/hal/rtc_hal.h @@ -18,6 +18,8 @@ #include "hal/rtc_io_ll.h" #include "hal/rtc_cntl_ll.h" +#define RTC_HAL_DMA_LINK_NODE_SIZE (16) + #define rtc_hal_ext1_get_wakeup_pins() rtc_cntl_ll_ext1_get_wakeup_pins() #define rtc_hal_ext1_set_wakeup_pins(mask, mode) rtc_cntl_ll_ext1_set_wakeup_pins(mask, mode) @@ -26,6 +28,12 @@ #define rtc_hal_set_wakeup_timer(ticks) rtc_cntl_ll_set_wakeup_timer(ticks) +void * rtc_cntl_hal_dma_link_init(void *elem, void *buff, int size, void *next); + +void rtc_cntl_hal_enable_cpu_retention(void *addr); + +#define rtc_cntl_hal_disable_cpu_retention() rtc_cntl_ll_disable_cpu_retention() + /* * Enable wakeup from ULP coprocessor. */ diff --git a/components/soc/esp32c3/include/soc/rtc_caps.h b/components/soc/esp32c3/include/soc/rtc_caps.h new file mode 100644 index 00000000000..aa71a3ec2be --- /dev/null +++ b/components/soc/esp32c3/include/soc/rtc_caps.h @@ -0,0 +1,22 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#define RTC_CNTL_CPU_PD_DMA_BUS_WIDTH (128) +#define RTC_CNTL_CPU_PD_REG_FILE_NUM (108) +#define RTC_CNTL_CPU_PD_DMA_ADDR_ALIGN (RTC_CNTL_CPU_PD_DMA_BUS_WIDTH >> 3) +#define RTC_CNTL_CPU_PD_DMA_BLOCK_SIZE (RTC_CNTL_CPU_PD_DMA_BUS_WIDTH >> 3) + +#define RTC_CNTL_CPU_PD_RETENTION_MEM_SIZE (RTC_CNTL_CPU_PD_REG_FILE_NUM * (RTC_CNTL_CPU_PD_DMA_BUS_WIDTH >> 3)) diff --git a/components/soc/esp32c3/include/soc/soc_caps.h b/components/soc/esp32c3/include/soc/soc_caps.h index 0c063980b62..021f9b14387 100644 --- a/components/soc/esp32c3/include/soc/soc_caps.h +++ b/components/soc/esp32c3/include/soc/soc_caps.h @@ -44,6 +44,7 @@ #include "rmt_caps.h" #include "spi_caps.h" #include "uart_caps.h" +#include "rtc_caps.h" /*-------------------------- TOUCH SENSOR CAPS -------------------------------*/ #define SOC_TOUCH_SENSOR_NUM (0) /*! No touch sensors on ESP32-C3 */ @@ -126,3 +127,5 @@ #define SOC_PM_SUPPORT_WIFI_WAKEUP (1) #define SOC_PM_SUPPORT_BT_WAKEUP (1) + +#define SOC_PM_SUPPORT_CPU_PD (1)