Skip to content

Commit

Permalink
Merge branch 'feature/esp32c61_light_sleep_support_stage_3' into 'mas…
Browse files Browse the repository at this point in the history
…ter'

feat(esp_hw_support): esp32c61 sleep support (Stage 3:  support system peripheral sleep retention)

Closes IDF-10384, IDF-10382, and IDF-11004

See merge request espressif/esp-idf!33298
  • Loading branch information
jack0c committed Sep 21, 2024
2 parents 5d79291 + bc0404a commit 945af68
Show file tree
Hide file tree
Showing 33 changed files with 982 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def test_app_update(dut: Dut) -> None:


@pytest.mark.supported_targets
# TODO: [ESP32C61] IDF-9245, IDF-9247, IDF-10983
# TODO: [ESP32C61] IDF-9245, IDF-10983
@pytest.mark.temp_skip_ci(targets=['esp32c61'], reason='C61 has not supported deep sleep')
@pytest.mark.generic
@pytest.mark.parametrize(
Expand Down
9 changes: 9 additions & 0 deletions components/esp_hw_support/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -273,4 +273,13 @@ menu "Hardware Settings"
config ESP_SPI_BUS_LOCK_FUNCS_IN_IRAM
bool
default n

config ESP_CLK_RC32K_NOT_TO_USE
bool
default y if IDF_TARGET_ESP32C5 || IDF_TARGET_ESP32C61
default n
help
Due to the poor low-temperature characteristics of
RC32K (it cannot operate below -40 degrees Celsius),
please avoid using it whenever possible
endmenu
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ esp_err_t sleep_clock_system_retention_init(void *arg)
[0] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_PCR_LINK(0), PCR_AHB_FREQ_CONF_REG, 0, PCR_AHB_DIV_NUM, 1, 0), .owner = ENTRY(0) | ENTRY(1) }, /* Set AHB bus frequency to XTAL frequency */
[1] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_PCR_LINK(1), PCR_BUS_CLK_UPDATE_REG, 1, PCR_BUS_CLOCK_UPDATE, 1, 0), .owner = ENTRY(0) | ENTRY(1) },
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
[2] = { .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_PCR_LINK(2), DR_REG_PCR_BASE, DR_REG_PCR_BASE, 74, 0, 0, 0xffffffff, 0xffffffff, 0x7f7, 0x0), .owner = ENTRY(0) | ENTRY(1) },
[2] = { .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_PCR_LINK(2), DR_REG_PCR_BASE, DR_REG_PCR_BASE, 75, 0, 0, 0xffffffff, 0xffffffff, 0x200007f7, 0x0), .owner = ENTRY(0) | ENTRY(1) },
#endif
};
esp_err_t err = sleep_retention_entries_create(pcr_regs_retention, ARRAY_SIZE(pcr_regs_retention), REGDMA_LINK_PRI_SYS_CLK, SLEEP_RETENTION_MODULE_CLOCK_SYSTEM);
Expand Down
91 changes: 91 additions & 0 deletions components/esp_hw_support/lowpower/port/esp32c61/sleep_clock.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "esp_private/sleep_clock.h"
#include "soc/pcr_reg.h"
#include "modem/modem_syscon_reg.h"

static const char *TAG = "sleep_clock";

esp_err_t sleep_clock_system_retention_init(void *arg)
{
const static sleep_retention_entries_config_t pcr_regs_retention[] = {
[0] = { .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_PCR_LINK(0), DR_REG_PCR_BASE, DR_REG_PCR_BASE, 63, 0, 0, 0xfd73ffff, 0xfdffffff, 0xe001, 0x0), .owner = ENTRY(0) | ENTRY(1) },
};

esp_err_t err = sleep_retention_entries_create(pcr_regs_retention, ARRAY_SIZE(pcr_regs_retention), REGDMA_LINK_PRI_SYS_CLK, SLEEP_RETENTION_MODULE_CLOCK_SYSTEM);
ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for system (PCR) retention");
ESP_LOGI(TAG, "System Power, Clock and Reset sleep retention initialization");
return ESP_OK;
}

#if CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE || CONFIG_IEEE802154_SLEEP_ENABLE
esp_err_t sleep_clock_modem_retention_init(void *arg)
{
#define N_REGS_SYSCON() (((MODEM_SYSCON_MEM_CONF_REG - MODEM_SYSCON_TEST_CONF_REG) / 4) + 1)

const static sleep_retention_entries_config_t modem_regs_retention[] = {
[0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_MODEMSYSCON_LINK(0), MODEM_SYSCON_TEST_CONF_REG, MODEM_SYSCON_TEST_CONF_REG, N_REGS_SYSCON(), 0, 0), .owner = ENTRY(0) | ENTRY(1) }, /* MODEM SYSCON */
};

esp_err_t err = sleep_retention_entries_create(modem_regs_retention, ARRAY_SIZE(modem_regs_retention), REGDMA_LINK_PRI_MODEM_CLK, SLEEP_RETENTION_MODULE_CLOCK_MODEM);
ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for modem (SYSCON) retention, 1 level priority");
ESP_LOGI(TAG, "Modem Power, Clock and Reset sleep retention initialization");
return ESP_OK;
}
#endif

bool clock_domain_pd_allowed(void)
{
const uint32_t inited_modules = sleep_retention_get_inited_modules();
const uint32_t created_modules = sleep_retention_get_created_modules();
const uint32_t sys_clk_dep_modules = (const uint32_t) (BIT(SLEEP_RETENTION_MODULE_SYS_PERIPH));

/* The clock and reset of MODEM (WiFi, BLE and 15.4) modules are managed
* through MODEM_SYSCON, when one or more MODEMs are initialized, it is
* necessary to check the state of CLOCK_MODEM to determine MODEM domain on
* or off. The clock and reset of digital peripherals are managed through
* PCR, with TOP domain similar to MODEM domain. */
uint32_t modem_clk_dep_modules = 0;
#if SOC_WIFI_SUPPORTED
modem_clk_dep_modules |= BIT(SLEEP_RETENTION_MODULE_WIFI_MAC) | BIT(SLEEP_RETENTION_MODULE_WIFI_BB);
#endif
#if SOC_BT_SUPPORTED
modem_clk_dep_modules |= BIT(SLEEP_RETENTION_MODULE_BLE_MAC) | BIT(SLEEP_RETENTION_MODULE_BT_BB);
#endif
#if SOC_IEEE802154_SUPPORTED
modem_clk_dep_modules |= BIT(SLEEP_RETENTION_MODULE_802154_MAC) | BIT(SLEEP_RETENTION_MODULE_BT_BB);
#endif

uint32_t mask = 0;
if (inited_modules & sys_clk_dep_modules) {
mask |= BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM);
}
if (inited_modules & modem_clk_dep_modules) {
#if SOC_WIFI_SUPPORTED || SOC_BT_SUPPORTED || SOC_IEEE802154_SUPPORTED
mask |= BIT(SLEEP_RETENTION_MODULE_CLOCK_MODEM);
#endif
}
return ((inited_modules & mask) == (created_modules & mask));
}

ESP_SYSTEM_INIT_FN(sleep_clock_startup_init, SECONDARY, BIT(0), 106)
{
sleep_retention_module_init_param_t init_param = {
.cbs = { .create = { .handle = sleep_clock_system_retention_init, .arg = NULL } },
.attribute = SLEEP_RETENTION_MODULE_ATTR_PASSIVE
};
sleep_retention_module_init(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM, &init_param);

#if CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE || CONFIG_IEEE802154_SLEEP_ENABLE
init_param = (sleep_retention_module_init_param_t) {
.cbs = { .create = { .handle = sleep_clock_modem_retention_init, .arg = NULL } },
.attribute = SLEEP_RETENTION_MODULE_ATTR_PASSIVE
};
sleep_retention_module_init(SLEEP_RETENTION_MODULE_CLOCK_MODEM, &init_param);
#endif
return ESP_OK;
}
162 changes: 162 additions & 0 deletions components/esp_hw_support/lowpower/port/esp32c61/sleep_mmu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stddef.h>
#include <string.h>
#include <inttypes.h>

#include "esp_attr.h"
#include "esp_check.h"
#include "esp_sleep.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "soc/soc_caps.h"
#include "sdkconfig.h"
#include "soc/spi_mem_reg.h"
#include "esp_private/startup_internal.h"

static const char *TAG = "sleep_mmu";

typedef struct {
uint32_t start;
uint32_t end;
} mmu_domain_dev_regs_region_t;

typedef struct {
mmu_domain_dev_regs_region_t *region;
int region_num;
uint32_t *regs_frame;
} mmu_domain_dev_sleep_frame_t;

/**
* Internal structure which holds all requested light sleep mmu retention parameters
*/
typedef struct {
struct {
mmu_domain_dev_sleep_frame_t *mmu_table_frame;
} retent;
} sleep_mmu_retention_t;

static DRAM_ATTR __attribute__((unused)) sleep_mmu_retention_t s_mmu_retention;

static void * mmu_domain_dev_sleep_frame_alloc_and_init(const mmu_domain_dev_regs_region_t *regions, const int region_num)
{
const int region_sz = sizeof(mmu_domain_dev_regs_region_t) * region_num;
int regs_frame_sz = 0;
for (int num = 0; num < region_num; num++) {
regs_frame_sz += regions[num].end - regions[num].start;
}
void *frame = heap_caps_malloc(sizeof(mmu_domain_dev_sleep_frame_t) + region_sz + regs_frame_sz, MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
if (frame) {
mmu_domain_dev_regs_region_t *region = (mmu_domain_dev_regs_region_t *)(frame + sizeof(mmu_domain_dev_sleep_frame_t));
memcpy(region, regions, region_num * sizeof(mmu_domain_dev_regs_region_t));
void *regs_frame = frame + sizeof(mmu_domain_dev_sleep_frame_t) + region_sz;
memset(regs_frame, 0, regs_frame_sz);
*(mmu_domain_dev_sleep_frame_t *)frame = (mmu_domain_dev_sleep_frame_t) {
.region = region,
.region_num = region_num,
.regs_frame = (uint32_t *)regs_frame
};
}
return frame;
}

static inline void * mmu_domain_mmu_table_sleep_frame_alloc_and_init(void)
{
#define MMU_TABLE_SIZE (512 * 4)
const static mmu_domain_dev_regs_region_t regions[] = {
{ .start = SPI_MEM_MMU_ITEM_CONTENT_REG(0), .end = SPI_MEM_MMU_ITEM_CONTENT_REG(0) + MMU_TABLE_SIZE}
};
return mmu_domain_dev_sleep_frame_alloc_and_init(regions, sizeof(regions) / sizeof(regions[0]));
}

static IRAM_ATTR void mmu_domain_dev_regs_save(mmu_domain_dev_sleep_frame_t *frame)
{
assert(frame);
mmu_domain_dev_regs_region_t *region = frame->region;
uint32_t *regs_frame = frame->regs_frame;

int offset = 0;
for (int i = 0; i < frame->region_num; i++) {
for (uint32_t addr = region[i].start; addr < region[i].end; addr+=4) {
REG_WRITE(SPI_MEM_MMU_ITEM_INDEX_REG(0), offset);
regs_frame[offset++] = REG_READ(SPI_MEM_MMU_ITEM_CONTENT_REG(0));
}
}
}

static IRAM_ATTR void mmu_domain_dev_regs_restore(mmu_domain_dev_sleep_frame_t *frame)
{
assert(frame);
mmu_domain_dev_regs_region_t *region = frame->region;
uint32_t *regs_frame = frame->regs_frame;

int offset = 0;
for (int i = 0; i < frame->region_num; i++) {
for (uint32_t addr = region[i].start; addr < region[i].end; addr+=4) {
REG_WRITE(SPI_MEM_MMU_ITEM_INDEX_REG(0), offset);
REG_WRITE(SPI_MEM_MMU_ITEM_CONTENT_REG(0),regs_frame[offset++]);
}
}
}

IRAM_ATTR void esp_sleep_mmu_retention(bool backup_or_restore)
{
if (backup_or_restore) {
mmu_domain_dev_regs_save(s_mmu_retention.retent.mmu_table_frame);
} else {
mmu_domain_dev_regs_restore(s_mmu_retention.retent.mmu_table_frame);
}
}

static esp_err_t esp_sleep_mmu_retention_deinit_impl(void)
{
if (s_mmu_retention.retent.mmu_table_frame) {
heap_caps_free((void *)s_mmu_retention.retent.mmu_table_frame);
s_mmu_retention.retent.mmu_table_frame = NULL;
}
return ESP_OK;
}

static esp_err_t esp_sleep_mmu_retention_init_impl(void)
{
if (s_mmu_retention.retent.mmu_table_frame == NULL) {
void *frame = mmu_domain_mmu_table_sleep_frame_alloc_and_init();
if (frame == NULL) {
goto err;
}
s_mmu_retention.retent.mmu_table_frame = (mmu_domain_dev_sleep_frame_t *)frame;
}
return ESP_OK;
err:
esp_sleep_mmu_retention_deinit();
return ESP_ERR_NO_MEM;
}

esp_err_t esp_sleep_mmu_retention_init(void)
{
return esp_sleep_mmu_retention_init_impl();
}

esp_err_t esp_sleep_mmu_retention_deinit(void)
{
return esp_sleep_mmu_retention_deinit_impl();
}

bool mmu_domain_pd_allowed(void)
{
return (s_mmu_retention.retent.mmu_table_frame != NULL);
}

ESP_SYSTEM_INIT_FN(sleep_mmu_startup_init, SECONDARY, BIT(0), 108)
{
esp_err_t ret;
ret = esp_sleep_mmu_retention_init();
if (ret != ESP_OK) {
ESP_EARLY_LOGW(TAG, "Failed to enable TOP power down during light sleep.");
}
return ESP_OK;
}
6 changes: 3 additions & 3 deletions components/esp_hw_support/modem_clock.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,15 +423,15 @@ void modem_clock_select_lp_clock_source(periph_module_t module, modem_clock_lpcl
esp_sleep_pd_domain_t pd_domain = (esp_sleep_pd_domain_t) (
(last_src == MODEM_CLOCK_LPCLK_SRC_RC_FAST) ? ESP_PD_DOMAIN_RC_FAST :
(last_src == MODEM_CLOCK_LPCLK_SRC_MAIN_XTAL) ? ESP_PD_DOMAIN_XTAL :
#if !SOC_CLK_RC32K_NOT_TO_USE
#if !CONFIG_ESP_CLK_RC32K_NOT_TO_USE
(last_src == MODEM_CLOCK_LPCLK_SRC_RC32K) ? ESP_PD_DOMAIN_RC32K :
#endif
(last_src == MODEM_CLOCK_LPCLK_SRC_XTAL32K) ? ESP_PD_DOMAIN_XTAL32K :
ESP_PD_DOMAIN_MAX);
esp_sleep_pd_domain_t pu_domain = (esp_sleep_pd_domain_t) (
(src == MODEM_CLOCK_LPCLK_SRC_RC_FAST) ? ESP_PD_DOMAIN_RC_FAST :
(src == MODEM_CLOCK_LPCLK_SRC_MAIN_XTAL) ? ESP_PD_DOMAIN_XTAL :
#if !SOC_CLK_RC32K_NOT_TO_USE
#if !CONFIG_ESP_CLK_RC32K_NOT_TO_USE
(src == MODEM_CLOCK_LPCLK_SRC_RC32K) ? ESP_PD_DOMAIN_RC32K :
#endif
(src == MODEM_CLOCK_LPCLK_SRC_XTAL32K) ? ESP_PD_DOMAIN_XTAL32K :
Expand Down Expand Up @@ -486,7 +486,7 @@ void modem_clock_deselect_lp_clock_source(periph_module_t module)
esp_sleep_pd_domain_t pd_domain = (esp_sleep_pd_domain_t) (
(last_src == MODEM_CLOCK_LPCLK_SRC_RC_FAST) ? ESP_PD_DOMAIN_RC_FAST :
(last_src == MODEM_CLOCK_LPCLK_SRC_MAIN_XTAL) ? ESP_PD_DOMAIN_XTAL :
#if !SOC_CLK_RC32K_NOT_TO_USE
#if !CONFIG_ESP_CLK_RC32K_NOT_TO_USE
(last_src == MODEM_CLOCK_LPCLK_SRC_RC32K) ? ESP_PD_DOMAIN_RC32K :
#endif
(last_src == MODEM_CLOCK_LPCLK_SRC_XTAL32K) ? ESP_PD_DOMAIN_XTAL32K :
Expand Down
2 changes: 1 addition & 1 deletion components/esp_hw_support/port/esp32c61/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ if(NOT BOOTLOADER_BUILD)

endif()

# TODO: [ESP32C61] IDF-9250
# TODO: [ESP32C61] IDF-9304
if(CONFIG_IDF_TARGET_ESP32C61)
list(REMOVE_ITEM srcs
"sar_periph_ctrl.c"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
extern "C" {
#endif

// TODO: IDF-9247

#define PMU_EXT0_WAKEUP_EN BIT(0)
#define PMU_EXT1_WAKEUP_EN BIT(1)
#define PMU_GPIO_WAKEUP_EN BIT(2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,12 +476,12 @@ typedef struct pmu_sleep_machine_constant {
.reset_wait_time_us = 1, \
.power_supply_wait_time_us = 20, \
.power_up_wait_time_us = 2, \
.regdma_s2m_work_time_us = 172, \
.regdma_s2a_work_time_us = 480, \
.regdma_m2a_work_time_us = 278, \
.regdma_a2s_work_time_us = 382, \
.regdma_rf_on_work_time_us = 70, \
.regdma_rf_off_work_time_us = 23, \
.regdma_s2m_work_time_us = 270, \
.regdma_s2a_work_time_us = 666, \
.regdma_m2a_work_time_us = 296, \
.regdma_a2s_work_time_us = 586, \
.regdma_rf_on_work_time_us = 138, \
.regdma_rf_off_work_time_us = 28, \
.xtal_wait_stable_time_us = 250, \
.pll_wait_stable_time_us = 1 \
} \
Expand Down
12 changes: 8 additions & 4 deletions components/esp_hw_support/sleep_modes.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@
#define DEFAULT_SLEEP_OUT_OVERHEAD_US (318)
#define DEFAULT_HARDWARE_OUT_OVERHEAD_US (56)
#elif CONFIG_IDF_TARGET_ESP32C61
#define DEFAULT_SLEEP_OUT_OVERHEAD_US (318)
#define DEFAULT_HARDWARE_OUT_OVERHEAD_US (56)
#define DEFAULT_SLEEP_OUT_OVERHEAD_US (1148) //TODO: PM-231
#define DEFAULT_HARDWARE_OUT_OVERHEAD_US (107)
#elif CONFIG_IDF_TARGET_ESP32H2
#define DEFAULT_SLEEP_OUT_OVERHEAD_US (118)
#define DEFAULT_HARDWARE_OUT_OVERHEAD_US (9)
Expand Down Expand Up @@ -189,8 +189,12 @@
#endif

#if SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD
#if CONFIG_IDF_TARGET_ESP32C61
#define SLEEP_MMU_TABLE_RETENTION_OVERHEAD_US (1232)
#elif CONFIG_IDF_TARGET_ESP32C5
#define SLEEP_MMU_TABLE_RETENTION_OVERHEAD_US (1220)
#endif
#endif

// Minimal amount of time we can sleep for
#define LIGHT_SLEEP_MIN_TIME_US 200
Expand Down Expand Up @@ -705,7 +709,7 @@ FORCE_INLINE_ATTR void misc_modules_wake_prepare(uint32_t pd_flags)
#if SOC_USB_SERIAL_JTAG_SUPPORTED && !SOC_USB_SERIAL_JTAG_SUPPORT_LIGHT_SLEEP
sleep_console_usj_pad_restore();
#endif
#if !CONFIG_IDF_TARGET_ESP32C61
#if !CONFIG_IDF_TARGET_ESP32C61 // TODO: IDF-9304
sar_periph_ctrl_power_enable();
#endif
#if CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP && SOC_PM_CPU_RETENTION_BY_RTCCNTL
Expand Down Expand Up @@ -2359,7 +2363,7 @@ static uint32_t get_power_down_flags(void)
}
#endif
#if SOC_PM_SUPPORT_RC32K_PD
#if !SOC_CLK_RC32K_NOT_TO_USE
#if !CONFIG_ESP_CLK_RC32K_NOT_TO_USE
if (s_config.domain[ESP_PD_DOMAIN_RC32K].pd_option != ESP_PD_OPTION_ON) {
pd_flags |= PMU_SLEEP_PD_RC32K;
}
Expand Down
Loading

0 comments on commit 945af68

Please sign in to comment.