diff --git a/components/esp_common/include/esp_macros.h b/components/esp_common/include/esp_macros.h index 28aa5106c2..753f095ddb 100644 --- a/components/esp_common/include/esp_macros.h +++ b/components/esp_common/include/esp_macros.h @@ -46,6 +46,35 @@ extern "C" { #define CHOOSE_MACRO_VA_ARG(MACRO_WITH_ARGS, MACRO_WITH_NO_ARGS, ...) CHOOSE_MACRO_VA_ARG_INN(0, ##__VA_ARGS__, MACRO_WITH_ARGS, MACRO_WITH_NO_ARGS, 0) #endif +/* Count number of arguments of __VA_ARGS__ + * - reference https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s + * - __GET_NTH_ARG__() takes args >= N (64) but only expand to Nth one (64th) + * - __RSEQ_N__() is reverse sequential to N to add padding to have Nth + * position is the same as the number of arguments + * - ##__VA_ARGS__ is used to deal with 0 paramerter (swallows comma) + */ +#ifndef __VA_NARG__ +# define __VA_NARG__(...) __NARG__(_0, ##__VA_ARGS__, __RSEQ_N__()) + +# define __NARG__(...) __GET_NTH_ARG__(__VA_ARGS__) +# define __GET_NTH_ARG__( \ + _01,_02,_03,_04,_05,_06,_07,_08,_09,_10, \ + _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ + _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ + _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ + _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ + _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ + _61,_62,_63,N,...) N +# define __RSEQ_N__() \ + 62,61,60, \ + 59,58,57,56,55,54,53,52,51,50, \ + 49,48,47,46,45,44,43,42,41,40, \ + 39,38,37,36,35,34,33,32,31,30, \ + 29,28,27,26,25,24,23,22,21,20, \ + 19,18,17,16,15,14,13,12,11,10, \ + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +#endif + /* test macros */ #define foo_args(...) 1 #define foo_no_args() 2 diff --git a/components/esp_hw_support/CMakeLists.txt b/components/esp_hw_support/CMakeLists.txt index fb5e8056fc..56ad26c2f8 100644 --- a/components/esp_hw_support/CMakeLists.txt +++ b/components/esp_hw_support/CMakeLists.txt @@ -34,6 +34,10 @@ if(NOT BOOTLOADER_BUILD) list(APPEND srcs "sleep_cpu.c") endif() + if(CONFIG_SOC_PAU_SUPPORTED) + list(APPEND srcs "sleep_retention.c" "sleep_peripheral.c" "sleep_clock.c") + endif() + # [refactor-todo]: requires "driver" for GPIO and RTC (by sleep_gpio and sleep_modes) list(APPEND priv_requires driver esp_timer) @@ -73,6 +77,11 @@ if(NOT BOOTLOADER_BUILD) list(APPEND srcs "esp_ds.c") endif() + if(CONFIG_SOC_PAU_SUPPORTED) + list(APPEND srcs "port/pau_regdma.c" + "port/regdma_link.c") + endif() + if(CONFIG_SOC_PM_CPU_RETENTION_BY_SW) list(APPEND srcs "sleep_cpu_asm.S") set_property(TARGET ${COMPONENT_LIB} diff --git a/components/esp_hw_support/include/esp_private/esp_pau.h b/components/esp_hw_support/include/esp_private/esp_pau.h new file mode 100644 index 0000000000..66c5e6ab3a --- /dev/null +++ b/components/esp_hw_support/include/esp_private/esp_pau.h @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include "soc/soc.h" +#include "soc/soc_caps.h" + +#if SOC_PAU_SUPPORTED +#include "hal/pau_hal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set the addresses of all REGDMA Links + * @param link_entries all linked lists addresses + */ +void pau_regdma_set_entry_link_addr(pau_regdma_link_addr_t *link_entries); + +/** + * @brief Set the address of WiFi MAC REGDMA Link in modem state + * @param link_addr linked lists address + */ +void pau_regdma_set_modem_link_addr(void *link_addr); + +/** + * @brief Software trigger regdma to perform modem link backup + */ +void pau_regdma_trigger_modem_link_backup(void); + +/** + * @brief Software trigger regdma to perform modem link restore + */ +void pau_regdma_trigger_modem_link_restore(void); + +/** + * @brief Set the address of extra REGDMA Link in active state + * @param link_addr linked lists address + */ +void pau_regdma_set_extra_link_addr(void *link_addr); + +/** + * @brief Software trigger regdma to perform extra link backup + */ +void pau_regdma_trigger_extra_link_backup(void); + +/** + * @brief Software trigger regdma to perform extra link restore + */ +void pau_regdma_trigger_extra_link_restore(void); + +#ifdef __cplusplus +} +#endif + +#endif //SOC_PAU_SUPPORTED diff --git a/components/esp_hw_support/include/esp_private/esp_regdma.h b/components/esp_hw_support/include/esp_private/esp_regdma.h new file mode 100644 index 0000000000..2235d83b86 --- /dev/null +++ b/components/esp_hw_support/include/esp_private/esp_regdma.h @@ -0,0 +1,649 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "esp_assert.h" +#include "esp_macros.h" +#include "esp_err.h" +#include "esp_bit_defs.h" +#include "soc/soc_caps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if SOC_PAU_SUPPORTED +#include "hal/pau_types.h" + +#define REGDMA_LINK_DBG 0 /* Enable REGDMA link info dump apis*/ +#define REGDMA_LINK_ENTRY_NUM (PAU_REGDMA_LINK_NUM) /* Maximum number of REG DMA linked list entries */ + +#define ENTRY(n) (BIT(n)) + +#define REGDMA_PCR_LINK(_pri) ((0x01 << 8) | _pri) +#define REGDMA_MODEMSYSCON_LINK(_pri) ((0x02 << 8) | _pri) + +#define REGDMA_INTMTX_LINK(_pri) ((0x0d << 8) | _pri) +#define REGDMA_HPSYS_LINK(_pri) ((0x0e << 8) | _pri) +#define REGDMA_TEEAPM_LINK(_pri) ((0x0f << 8) | _pri) + +#define REGDMA_UART_LINK(_pri) ((0x10 << 8) | _pri) +#define REGDMA_TIMG_LINK(_pri) ((0x11 << 8) | _pri) +#define REGDMA_IOMUX_LINK(_pri) ((0x12 << 8) | _pri) +#define REGDMA_SPIMEM_LINK(_pri) ((0x13 << 8) | _pri) +#define REGDMA_SYSTIMER_LINK(_pri) ((0x14 << 8) | _pri) + +typedef enum { + REGDMA_LINK_PRI_0 = 0, + REGDMA_LINK_PRI_1, + REGDMA_LINK_PRI_2, + REGDMA_LINK_PRI_3, + REGDMA_LINK_PRI_4, + REGDMA_LINK_PRI_5, + REGDMA_LINK_PRI_6, + REGDMA_LINK_PRI_7, +} regdma_link_priority_t; + +typedef pau_regdma_link_addr_t regdma_entry_buf_t; + +typedef enum regdma_link_mode { + REGDMA_LINK_MODE_CONTINUOUS = 0, /*!< Link used to backup registers with consecutive addresses */ + REGDMA_LINK_MODE_ADDR_MAP, /*!< Link used to backup selected registers according to bitmap */ + REGDMA_LINK_MODE_WRITE, /*!< Link used to direct write to registers*/ + REGDMA_LINK_MODE_WAIT /*!< Link used to wait for register value to meet condition*/ +} regdma_link_mode_t; + + +typedef struct regdma_link_head { + volatile uint32_t length: 10, /* total count of registers that need to be backup or restore, unit: 1 word = 4 bytes */ + reserve0: 6, + mode : 4, /* mode of current link */ + reserve1: 8, + branch : 1, /* branch link flag */ + skip_r : 1, /* skip the current linked node when restore the register */ + skip_b : 1, /* skip the current linked node when backup the register */ + eof : 1; /* end of link */ +} regdma_link_head_t; + +/* Continuous type linked list node body type definition */ +typedef struct regdma_link_continuous_body { + volatile void *next; + volatile void *backup; + volatile void *restore; + volatile void *mem; +} regdma_link_continuous_body_t; + +/* Address Map type linked list node body type definition */ +typedef struct regdma_link_addr_map_body { + volatile void *next; + volatile void *backup; + volatile void *restore; + volatile void *mem; + volatile uint32_t map[4]; +} regdma_link_addr_map_body_t; + +/* Write/Wait type linked list node body type definition */ +typedef struct regdma_link_write_wait_body { + volatile void *next; + volatile void *backup; + volatile uint32_t value; + volatile uint32_t mask; +} regdma_link_write_wait_body_t; + +/* Branch Continuous type linked list node body type definition */ +typedef struct regdma_link_branch_continuous_body { + regdma_entry_buf_t next; + volatile void *backup; + volatile void *restore; + volatile void *mem; +} regdma_link_branch_continuous_body_t; + +/* Branch Address Map type linked list node body type definition */ +typedef struct regdma_link_branch_addr_map_body { + regdma_entry_buf_t next; + volatile void *backup; + volatile void *restore; + volatile void *mem; + volatile uint32_t map[4]; +} regdma_link_branch_addr_map_body_t; + +/* Branch Write/Wait type linked list node body type definition */ +typedef struct regdma_link_branch_write_wait_body { + regdma_entry_buf_t next; + volatile void *backup; + volatile uint32_t value; + volatile uint32_t mask; +} regdma_link_branch_write_wait_body_t; + +ESP_STATIC_ASSERT(REGDMA_LINK_ENTRY_NUM < 16, "regdma link entry number should less 16"); +typedef struct regdma_link_stats { + volatile uint32_t ref: REGDMA_LINK_ENTRY_NUM, /* a bitmap, identifies which entry has referenced the current link */ + reserve: 16-REGDMA_LINK_ENTRY_NUM, + id: 16; /* REGDMA linked list node unique identifier */ + volatile uint32_t module; /* a bitmap used to identify the module to which the current node belongs */ +} regdma_link_stats_t; + +typedef struct regdma_link_continuous { + regdma_link_stats_t stat; + regdma_link_head_t head; + regdma_link_continuous_body_t body; + volatile uint32_t buff[0]; +} regdma_link_continuous_t; + +typedef struct regdma_link_addr_map { + regdma_link_stats_t stat; + regdma_link_head_t head; + regdma_link_addr_map_body_t body; + volatile uint32_t buff[0]; +} regdma_link_addr_map_t; + +typedef struct regdma_link_write_wait { + regdma_link_stats_t stat; + regdma_link_head_t head; + regdma_link_write_wait_body_t body; +} regdma_link_write_wait_t; + +typedef struct regdma_link_branch_continuous { + regdma_link_stats_t stat; + regdma_link_head_t head; + regdma_link_branch_continuous_body_t body; + volatile uint32_t buff[0]; +} regdma_link_branch_continuous_t; + +typedef struct regdma_link_branch_addr_map { + regdma_link_stats_t stat; + regdma_link_head_t head; + regdma_link_branch_addr_map_body_t body; + volatile uint32_t buff[0]; +} regdma_link_branch_addr_map_t; + +typedef struct regdma_link_branch_write_wait { + regdma_link_stats_t stat; + regdma_link_head_t head; + regdma_link_branch_write_wait_body_t body; +} regdma_link_branch_write_wait_t; + +typedef struct regdma_link_config { + regdma_link_head_t head; + union { + regdma_link_continuous_body_t continuous; + regdma_link_addr_map_body_t addr_map; + regdma_link_write_wait_body_t write_wait; + }; + int id; /* REGDMA linked list node unique identifier */ +} regdma_link_config_t; + + +#define REGDMA_LINK_HEAD(plink) (((regdma_link_config_t *)plink)->head) + + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +#define REGDMA_LINK_HEAD_INIT(_l, _m, _b, _sr, _sb) \ + { \ + .length = (_l), \ + .mode = (_m), \ + .branch = (_b), \ + .skip_r = (_sr), \ + .skip_b = (_sb), \ + .eof = 0 \ + } + +#define REGDMA_LINK_CONTINUOUS_INIT(_id, _backup, _restore, _len, _skip_b, _skip_r) \ + { \ + .head = REGDMA_LINK_HEAD_INIT( \ + _len, \ + REGDMA_LINK_MODE_CONTINUOUS,\ + 0, \ + _skip_r, \ + _skip_b \ + ), \ + .continuous = { \ + .next = NULL, \ + .backup = (void *)_backup, \ + .restore = (void *)_restore, \ + .mem = NULL \ + }, \ + .id = (_id) \ + } + +#define REGDMA_LINK_ADDR_MAP_INIT(_id, _backup, _restore, _len, _skip_b, _skip_r, ...) \ + { \ + .head = REGDMA_LINK_HEAD_INIT( \ + _len, \ + REGDMA_LINK_MODE_ADDR_MAP, \ + 0, \ + _skip_r, \ + _skip_b \ + ), \ + .addr_map = { \ + .next = NULL, \ + .backup = (void *)_backup, \ + .restore = (void *)_restore, \ + .mem = NULL, \ + .map = {__VA_ARGS__} \ + }, \ + .id = (_id) \ + } + +#define REGDMA_LINK_WRITE_INIT(_id, _backup, _val, _mask, _skip_b, _skip_r) \ + { \ + .head = REGDMA_LINK_HEAD_INIT( \ + 0, \ + REGDMA_LINK_MODE_WRITE, \ + 0, \ + _skip_r, \ + _skip_b \ + ), \ + .write_wait = { \ + .next = NULL, \ + .backup = (void *)_backup, \ + .value = (_val), \ + .mask = (_mask) \ + }, \ + .id = (_id) \ + } + +#define REGDMA_LINK_WAIT_INIT(_id, _backup, _val, _mask, _skip_b, _skip_r) \ + { \ + .head = REGDMA_LINK_HEAD_INIT( \ + 0, \ + REGDMA_LINK_MODE_WAIT, \ + 0, \ + _skip_r, \ + _skip_b \ + ), \ + .write_wait = { \ + .next = NULL, \ + .backup = (void *)_backup, \ + .value = (_val), \ + .mask = (_mask) \ + }, \ + .id = (_id) \ + } + + +/** + * @brief Create a REGDMA continuous type linked list node without retention buffer and the retention buffer is passed in by the caller + * @param backup Register address to be backed up by REGDMA + * @param buff Retention buffer, it needs to be allocated by the caller and passed in by this argument + * @param len Number of registers to be backed up + * @param restore Register address to be restored by REGDMA + * @param next The next REGDMA linked list node + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_continuous(void *backup, void *buff, int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a REGDMA addr_map type linked list node without retention buffer and the retention buffer is passed in by the caller + * @param backup Register address to be backed up by REGDMA + * @param buff Retention buffer, it needs to be allocated by the caller and passed in by this argument + * @param bitmap The register bitmap that needs to be backed up and restored. when the bitmap corresponding to the + * register is 1, it needs to be backed up or restored, otherwise the corresponding register is skipped. + * @param len Number of registers to be backed up + * @param restore Register address to be restored by REGDMA + * @param next The next REGDMA linked list node + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_addr_map(void *backup, void *buff, uint32_t bitmap[4], int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a REGDMA write type linked list node without retention buffer and the retention buffer is passed in by the caller + * @param backup Register address to be backed up by REGDMA + * @param value The value to be written to the register + * @param mask The mask of value + * @param next The next REGDMA linked list node + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_write(void *backup, uint32_t value, uint32_t mask, void *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a REGDMA write type linked list node without retention buffer and the retention buffer is passed in by the caller + * @param backup Register address to be backed up by REGDMA + * @param value The register value that needs to be waited for + * @param mask The mask of value + * @param next The next REGDMA linked list node + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_wait(void *backup, uint32_t value, uint32_t mask, void *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a REGDMA continuouos branch list node without retention buffer and the retention buffer is passed in by the caller + * @param backup Register address to be backed up by REGDMA + * @param buff Retention buffer, it needs to be allocated by the caller and passed in by this argument + * @param len Number of registers to be backed up + * @param restore Register address to be restored by REGDMA + * @param next The next REGDMA linked list node (supports up to 4 next pointers) + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_branch_continuous(void *backup, void *buff, int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a REGDMA addr_map branch list node without retention buffer and the retention buffer is passed in by the caller + * @param backup Register address to be backed up by REGDMA + * @param buff Retention buffer, it needs to be allocated by the caller and passed in by this argument + * @param bitmap The register bitmap that needs to be backed up and restored. when the bitmap corresponding to the + * register is 1, it needs to be backed up or restored, otherwise the corresponding register is skipped. + * @param len Number of registers to be backed up + * @param restore Register address to be restored by REGDMA + * @param next The next REGDMA linked list node (supports up to 4 next pointers) + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_branch_addr_map(void *backup, void *buff, uint32_t bitmap[4], int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a REGDMA write branch list node without retention buffer and the retention buffer is passed in by the caller + * @param backup Register address to be backed up by REGDMA + * @param value The value to be written to the register + * @param mask The mask of value + * @param next The next REGDMA linked list node (supports up to 4 next pointers) + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_branch_write(void *backup, uint32_t value, uint32_t mask, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a REGDMA wait branch list node without retention buffer and the retention buffer is passed in by the caller + * @param backup Register address to be backed up by REGDMA + * @param value The register value that needs to be waited for + * @param mask The mask of value + * @param next The next REGDMA linked list node (supports up to 4 next pointers) + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_branch_wait(void *backup, uint32_t value, uint32_t mask, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a default REGDMA continuous type linked list node with retention buffer + * @param backup Register address to be backed up by REGDMA + * @param len Number of registers to be backed up + * @param restore Register address to be restored by REGDMA + * @param next The next REGDMA linked list node + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_continuous_default(void *backup, int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a default REGDMA addr_map type linked list node with retention buffer + * @param backup Register address to be backed up by REGDMA + * @param bitmap The register bitmap that needs to be backed up and restored. when the bitmap corresponding to the + * register is 1, it needs to be backed up or restored, otherwise the corresponding register is skipped. + * @param len Number of registers to be backed up + * @param restore Register address to be restored by REGDMA + * @param next The next REGDMA linked list node + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_addr_map_default(void *backup, uint32_t bitmap[4], int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a default REGDMA write type linked list node with retention buffer + * @param backup Register address to be backed up by REGDMA + * @param value The register value that needs to be waited for + * @param mask The mask of value + * @param next The next REGDMA linked list node + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_write_default(void *backup, uint32_t value, uint32_t mask, void *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a default REGDMA wait type linked list node with retention buffer + * @param backup Register address to be backed up by REGDMA + * @param value The register value that needs to be waited for + * @param mask The mask of value + * @param next The next REGDMA linked list node + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_wait_default(void *backup, uint32_t value, uint32_t mask, void *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a default REGDMA continuous branch list node with retention buffer + * @param backup Register address to be backed up by REGDMA + * @param len Number of registers to be backed up + * @param restore Register address to be restored by REGDMA + * @param next The next REGDMA linked list node (supports up to 4 next pointers) + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_branch_continuous_default(void *backup, int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a default REGDMA addr_map branch list node with retention buffer + * @param backup Register address to be backed up by REGDMA + * @param bitmap The register bitmap that needs to be backed up and restored. when the bitmap corresponding to the + * register is 1, it needs to be backed up or restored, otherwise the corresponding register is skipped. + * @param len Number of registers to be backed up + * @param restore Register address to be restored by REGDMA + * @param next The next REGDMA linked list node (supports up to 4 next pointers) + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_branch_addr_map_default(void *backup, uint32_t bitmap[4], int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a default REGDMA write branch list node with retention buffer + * @param backup Register address to be backed up by REGDMA + * @param value The value to be written to the register + * @param mask The mask of value + * @param next The next REGDMA linked list node (supports up to 4 next pointers) + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_branch_write_default(void *backup, uint32_t value, uint32_t mask, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create a default REGDMA wait branch list node with retention buffer + * @param backup Register address to be backed up by REGDMA + * @param value The register value that needs to be waited for + * @param mask The mask of value + * @param next The next REGDMA linked list node (supports up to 4 next pointers) + * @param skip_b Skip backup, True means that REGDMA skips the current node when executing the backup task + * @param skip_r Skip restore, True means that REGDMA skips the current node when executing the restore task + * @param id REGDMA linked list node unique identifier, the caller needs to ensure that the id of each node is unique + * @param module The module identifier of the current linked list node + * @return Created REGDMA linked list node pointer + */ +void *regdma_link_new_branch_wait_default(void *backup, uint32_t value, uint32_t mask, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module); + +/** + * @brief Create and initialize a REGDMA linked list node through configuration parameters + * @param config REGDMA linked node configuration parameters + * @param branch Is it a branch node + * @param module The module identifier of the current linked list node + * @param nentry The number of next pointers + * @param args next pointer, Since REGDMA supports 4 entries, it supports up to 4 variable parameter next pointers, and more will be ignored + * @return Initialized REGDMA linked list head node pointer + */ +void *regdma_link_init(const regdma_link_config_t *config, bool branch, uint32_t module, int nentry, ...); + +/** + * @brief Recurse the REGDMA linked list and call the hook subroutine for each node + * @param link The REGDMA linkded list head pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + * @param hook Subroutines called during recursion, argument 1 is the pointer to the + * recursive node object, argument 2 is the entry to which the node belongs + * and the argument 3 is the position of the node in the current linked + * list (from head to tail, the position of the head node is 0) + * @return The REGDMA linked list node pointer indicated by the link argument + */ +void *regdma_link_recursive(void *link, int entry, void (*hook)(void *, int, int)); + +/** + * @brief Find the linked list node object by node position + * @param link The REGDMA linkded list head pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + * @param pos Node position + * @return The linked list node object pointer or NULL + */ +void *regdma_find_link_by_pos(void *link, int entry, int pos); + +/** + * @brief Find the linked list node object by node unique identifier + * @param link The REGDMA linkded list head pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + * @param id REGDMA linked list node unique identifier + * @return The linked list node object pointer or NULL + */ +void *regdma_find_link_by_id(void *link, int entry, int id); + +/** + * @brief Destroy the REGDMA linked list indicated by the entry argument + * @param link The REGDMA linkded list head pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + */ +void regdma_link_destroy(void *link, int entry); + +/** + * @brief Generate the statistics information of the REGDMA linked list indicated by the entry argument + * @param link The REGDMA linkded list head pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + */ +void regdma_link_stats(void *link, int entry); + +/** + * @brief Set the value and mask of write or wait type REGDMA linked list node + * @param link Write or wait type REGDMA linked list node pointer + * @param value The value to be written to the register + * @param mask The mask of value + */ +void regdma_link_set_write_wait_content(void *link, uint32_t value, uint32_t mask); + +/** + * @brief Print all node information of the REGDMA linked list indicated by the entry argument + * @param link The REGDMA linkded list head pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + */ +void regdma_link_show_memories(void *link, int entry); + +/** + * @brief Update REGDMA linked list node next pointers + * @param link The pointer of the REGDMA linked list node whose next field will be modified + * @param nentry The number of next pointers + */ +void regdma_link_update_next(void *link, int nentry, ...); + +/** + * @brief Get all node entry reference bitmaps from the start of the link argument to the + * end of the tail argument in the REGDMA linked list indicated by the entry argument + * @param link The REGDMA linkded list head pointer + * @param tail The REGDMA linkded list tail pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + * @return The entry reference bitmap of all nodes starting from the link argument to the end of the tail argument + */ +uint32_t regdma_link_get_owner_bitmap(void *link, void *tail, int entry); + +/** + * @brief Find the head node of the specified module in the REGDMA linked list indicated by the + * entry argument starting from the link argument to the end of the tail argument + * @param link The REGDMA linkded list head pointer + * @param tail The REGDMA linkded list tail pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + * @param module Module bitmap Identification + * @return The found head node pointer or NULL + */ +void *regdma_find_module_link_head(void *link, void *tail, int entry, uint32_t module); + +/** + * @brief Find the tail node of the specified module in the REGDMA linked list indicated by the + * entry argument starting from the link argument to the end of the tail argument + * @param link The REGDMA linkded list head pointer + * @param tail The REGDMA linkded list tail pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + * @param module Module bitmap Identification + * @return The found tail node pointer or NULL + */ +void *regdma_find_module_link_tail(void *link, void *tail, int entry, uint32_t module); + +/** + * @brief Find the tail node of the previous module of the specified module in the REGDMA linked list + * indicated by the entry argument starting from the link argment to the end of the tail argument + * @param link The REGDMA linkded list head pointer + * @param tail The REGDMA linkded list tail pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + * @param module Module bitmap Identification + * @return The found tail node pointer or NULL + */ +void *regdma_find_prev_module_link_tail(void *link, void *tail, int entry, uint32_t module); + +/** + * @brief Find the head node of the next module of the specified module in the REGDMA linked list + * indicated by the entry argument starting from the link argment to the end of the tail argument + * @param link The REGDMA linkded list head pointer + * @param tail The REGDMA linkded list tail pointer + * @param entry For nodes that support branching, use the branch specified by entry argument recursively + * @param module Module bitmap Identification + * @return The found head node pointer or NULL + */ +void *regdma_find_next_module_link_head(void *link, void *tail, int entry, uint32_t module); + +#define regdma_link_init_safe(pcfg, branch, module, ...) regdma_link_init((pcfg), (branch), (module), __VA_NARG__(__VA_ARGS__), ##__VA_ARGS__) + +#define regdma_link_update_next_safe(link, ...) regdma_link_update_next((link), __VA_NARG__(__VA_ARGS__), ##__VA_ARGS__) + +#endif // SOC_PAU_SUPPORTED + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hw_support/include/esp_private/regdma_link.h b/components/esp_hw_support/include/esp_private/regdma_link.h new file mode 100644 index 0000000000..2585e13233 --- /dev/null +++ b/components/esp_hw_support/include/esp_private/regdma_link.h @@ -0,0 +1,199 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __REGDMA_LINK_H__ +#define __REGDMA_LINK_H__ + +#include +#include +#include "esp_bit_defs.h" +#include "soc/soc_caps.h" + +#if SOC_PAU_SUPPORTED +#include "esp_regdma.h" + +#define FILL_PLINK_HEAD(_pl, _len, _mode, _branch, _sr, _sb, _eof) { \ + _pl->head.length = _len; \ + _pl->head.mode = _mode; \ + _pl->head.branch = _branch; \ + _pl->head.skip_r = _sr; \ + _pl->head.skip_b = _sb; \ + _pl->head.eof = _eof; \ +} + +#define FILL_PLINK_STAT(_pl, _ref, _id, _module) { \ + _pl->stat.ref = _ref; \ + _pl->stat.id = _id; \ + _pl->stat.module = _module; \ +} + +static inline void * regdma_link_init_continuous( + regdma_link_continuous_t *plink, void *buff, void *backup, int len, + void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + assert(plink != NULL); + assert(buff !=NULL); + + FILL_PLINK_HEAD(plink, len, REGDMA_LINK_MODE_CONTINUOUS, 0, skip_r, skip_b, !next); + plink->body.next = next; + plink->body.backup = backup; + plink->body.restore = restore; + plink->body.mem = buff; + FILL_PLINK_STAT(plink, 0, (uint16_t)id, module); + + return (void *)plink; +} + +static inline int regdma_link_addr_map_count(uint32_t bitmap[4]) +{ + return __builtin_popcount(bitmap[0]) + \ + __builtin_popcount(bitmap[1]) + \ + __builtin_popcount(bitmap[2]) + \ + __builtin_popcount(bitmap[3]); +} + +static inline void * regdma_link_init_addr_map( + regdma_link_addr_map_t *plink, void *buff, void *backup, uint32_t bitmap[4], + int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + assert(plink != NULL); + assert(buff != NULL); + assert(len == regdma_link_addr_map_count(bitmap)); + + FILL_PLINK_HEAD(plink, len, REGDMA_LINK_MODE_ADDR_MAP, 0, skip_r, skip_b, !next); + plink->body.next = next; + plink->body.backup = backup; + plink->body.restore = restore; + plink->body.mem = buff; + plink->body.map[0] = bitmap[0]; + plink->body.map[1] = bitmap[1]; + plink->body.map[2] = bitmap[2]; + plink->body.map[3] = bitmap[3]; + FILL_PLINK_STAT(plink, 0, (uint16_t)id, module); + + return (void *)plink; +} + +static inline void * regdma_link_init_write( + regdma_link_write_wait_t *plink, void *backup, uint32_t value, + uint32_t mask, void *next, bool skip_b, bool skip_r, int id, + uint32_t module) +{ + assert(plink != NULL); + + FILL_PLINK_HEAD(plink, 0, REGDMA_LINK_MODE_WRITE, 0, skip_r, skip_b, !next); + plink->body.next = next; + plink->body.backup = backup; + plink->body.value = value; + plink->body.mask = mask; + FILL_PLINK_STAT(plink, 0, (uint16_t)id, module); + + return (void *)plink; +} + +static inline void * regdma_link_init_wait( + regdma_link_write_wait_t *plink, void *backup, uint32_t value, + uint32_t mask, void *next, bool skip_b, bool skip_r, int id, + uint32_t module) +{ + assert(plink != NULL); + + FILL_PLINK_HEAD(plink, 0, REGDMA_LINK_MODE_WAIT, 0, skip_r, skip_b, !next); + plink->body.next = next; + plink->body.backup = backup; + plink->body.value = value; + plink->body.mask = mask; + FILL_PLINK_STAT(plink, 0, (uint16_t)id, module); + + return (void *)plink; +} + +static inline void * regdma_link_init_branch_continuous( + regdma_link_branch_continuous_t *plink, void *buff, void *backup, int len, void *restore, + regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + assert(plink != NULL); + assert(buff !=NULL); + + FILL_PLINK_HEAD(plink, len, REGDMA_LINK_MODE_CONTINUOUS, 1, skip_r, skip_b, 0); + plink->body.backup = backup; + plink->body.restore = restore; + plink->body.mem = buff; + for (int i = 0; i < REGDMA_LINK_ENTRY_NUM; i++) { + plink->body.next[i] = (*next)[i]; + } + FILL_PLINK_STAT(plink, 0, (uint16_t)id, module); + + return (void *)plink; +} + +static inline void * regdma_link_init_branch_addr_map( + regdma_link_branch_addr_map_t *plink, void *buff, void *backup, uint32_t bitmap[4], + int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, + uint32_t module) +{ + assert(plink != NULL); + assert(buff != NULL); + + FILL_PLINK_HEAD(plink, len, REGDMA_LINK_MODE_ADDR_MAP, 1, skip_r, skip_b, 0); + plink->body.backup = backup; + plink->body.restore = restore; + plink->body.mem = buff; + memcpy(plink->body.next, *next, REGDMA_LINK_ENTRY_NUM * sizeof((*next)[0])); + plink->body.map[0] = bitmap[0]; + plink->body.map[1] = bitmap[1]; + plink->body.map[2] = bitmap[2]; + plink->body.map[3] = bitmap[3]; + FILL_PLINK_STAT(plink, 0, (uint16_t)id, module); + + return (void *)plink; +} + +static inline void * regdma_link_init_branch_write( + regdma_link_branch_write_wait_t *plink, void *backup, uint32_t value, uint32_t mask, + regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + assert(plink != NULL); + + FILL_PLINK_HEAD(plink, 0, REGDMA_LINK_MODE_WRITE, 1, skip_r, skip_b, 0); + plink->body.backup = backup; + plink->body.value = value; + plink->body.mask = mask; + for (int i = 0; i < REGDMA_LINK_ENTRY_NUM; i++) { + plink->body.next[i] = (*next)[i]; + } + FILL_PLINK_STAT(plink, 0, (uint16_t)id, module); + + return (void *)plink; +} + +static inline void * regdma_link_init_branch_wait( + regdma_link_branch_write_wait_t *plink, void *backup, uint32_t value, uint32_t mask, + regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + assert(plink != NULL); + + FILL_PLINK_HEAD(plink, 0, REGDMA_LINK_MODE_WAIT, 1, skip_r, skip_b, 0); + plink->body.backup = backup; + plink->body.value = value; + plink->body.mask = mask; + for (int i = 0; i < REGDMA_LINK_ENTRY_NUM; i++) { + plink->body.next[i] = (*next)[i]; + } + FILL_PLINK_STAT(plink, 0, (uint16_t)id, module); + + return (void *)plink; +} + +static inline void regdma_link_update_stats(regdma_link_stats_t *stats, int entry, int depth) +{ + assert(stats != NULL); + + stats->ref |= BIT(entry); +} + +#endif // SOC_PAU_SUPPORTED + +#endif /* __REGDMA_LINK_H__ */ diff --git a/components/esp_hw_support/include/esp_private/sleep_clock.h b/components/esp_hw_support/include/esp_private/sleep_clock.h new file mode 100644 index 0000000000..5b204c1856 --- /dev/null +++ b/components/esp_hw_support/include/esp_private/sleep_clock.h @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once +#include +#include +#include "sdkconfig.h" +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file sleep_clock.h + * + * This file contains declarations of digital peripheral clock retention related functions in light sleep mode. + */ + +/** + * @brief Whether to allow the TOP power domain to be powered off. + * + * In light sleep mode, only when the system can provide enough memory + * for digital peripheral clock retention, the TOP power domain can be + * powered off. + * + * @return True to allow power off + */ +bool clock_domain_pd_allowed(void); + +/** + * @brief PCR module power down initialize + * + * @return ESP_OK on success + * ESP_ERR_INVALID_ARG on invalid sleep_retention_entries_create args + * No memory for the retention link + */ +esp_err_t sleep_clock_system_retention_init(void); + +/** + * @brief PCR module power down deinitialize + */ +void sleep_clock_system_retention_deinit(void); + +/** + * @brief Modem syscon module power down initialize + * + * @return ESP_OK on success + * ESP_ERR_INVALID_ARG on invalid sleep_retention_entries_create args + * No memory for the retention link + */ +esp_err_t sleep_clock_modem_retention_init(void); + +/** + * @brief Modem syscon module power down deinitialize + */ +void sleep_clock_modem_retention_deinit(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hw_support/include/esp_private/sleep_peripheral.h b/components/esp_hw_support/include/esp_private/sleep_peripheral.h new file mode 100644 index 0000000000..5c16a5110a --- /dev/null +++ b/components/esp_hw_support/include/esp_private/sleep_peripheral.h @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once +#include +#include +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file sleep_peripheral.h + * + * This file contains declarations of digital peripheral retention related functions in light sleep mode. + */ + +/** + * @brief Whether to allow the TOP power domain to be powered off. + * + * In light sleep mode, only when the system can provide enough memory + * for digital peripheral retention, the TOP power domain can be powered off. + * + * @return True to allow power off + */ +bool peripheral_domain_pd_allowed(void); + +/** + * @brief Digital peripheral power down initialize\ + * + * @return ESP_OK on success + * ESP_ERR_INVALID_ARG on invalid sleep_retention_entries_create args + * No memory for the retention link + */ +void sleep_peripheral_retention_init(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hw_support/include/esp_private/sleep_retention.h b/components/esp_hw_support/include/esp_private/sleep_retention.h new file mode 100644 index 0000000000..10a0d1b80b --- /dev/null +++ b/components/esp_hw_support/include/esp_private/sleep_retention.h @@ -0,0 +1,120 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once +#include +#include "sdkconfig.h" +#include "soc/soc_caps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if SOC_PAU_SUPPORTED +#include "esp_regdma.h" + +/** + * @file sleep_retention.h + * + * This file contains declarations of sleep retention related functions, it + * includes sleep retention list creation, destruction and debugging interfaces. + */ + +typedef enum sleep_retention_module_bitmap { + /* clock module, which includes system and modem */ + SLEEP_RETENTION_MODULE_CLOCK_SYSTEM = BIT(1), + SLEEP_RETENTION_MODULE_CLOCK_MODEM = BIT(2), + + /* modem module, which includes WiFi, BLE and 802.15.4 */ + SLEEP_RETENTION_MODULE_WIFI_MAC = BIT(10), + SLEEP_RETENTION_MODULE_WIFI_BB = BIT(11), + SLEEP_RETENTION_MODULE_BLE_MAC = BIT(12), + SLEEP_RETENTION_MODULE_BLE_BB = BIT(13), + SLEEP_RETENTION_MODULE_802154_MAC = BIT(14), + SLEEP_RETENTION_MODULE_802154_BB = BIT(15), + + /* digital peripheral module, which includes Interrupt Matrix, HP_SYSTEM, + * TEE, APM, UART, Timer Group, IOMUX, SPIMEM, SysTimer, etc.. */ + SLEEP_RETENTION_MODULE_INTR_MATRIX = BIT(16), + SLEEP_RETENTION_MODULE_HP_SYSTEM = BIT(17), + SLEEP_RETENTION_MODULE_TEE_APM = BIT(18), + SLEEP_RETENTION_MODULE_UART0 = BIT(19), + SLEEP_RETENTION_MODULE_TG0 = BIT(20), + SLEEP_RETENTION_MODULE_IOMUX = BIT(21), + SLEEP_RETENTION_MODULE_SPIMEM = BIT(22), + SLEEP_RETENTION_MODULE_SYSTIMER = BIT(23), + + SLEEP_RETENTION_MODULE_ALL = (uint32_t)-1 +} sleep_retention_module_bitmap_t; + +typedef regdma_entry_buf_t sleep_retention_entries_t; + +typedef struct { + regdma_link_config_t config; + uint32_t owner; /**< Indicates which regdma entries the current node will insert into */ +} sleep_retention_entries_config_t; + +/** + * @brief Create a runtime sleep retention linked list + * + * @param retent sleep retention linked list node configuration table + * @param num the total number of sleep retention linked list configuration + * items + * @param priority the priority of the created sleep retention linked list + * @param module the bitmap of the module to which the created sleep retention + * linked list belongs + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM not enough memory for sleep retention + * - ESP_ERR_INVALID_ARG if either of the arguments is out of range + */ +esp_err_t sleep_retention_entries_create(const sleep_retention_entries_config_t retent[], int num, regdma_link_priority_t priority, int module); + +/** + * @brief Destroy a runtime sleep retention linked list + * + * @param module the bitmap of the module to be destroyed + */ +void sleep_retention_entries_destroy(int module); + +/** + * @brief Print all runtime sleep retention linked lists + */ +void sleep_retention_entries_show_memories(void); + +/** + * @brief Find the linked list node with the unique id + * + * @param id the unique identifier of specified linked list node + * + * @return NULL or the address of the linked list node found + */ +void * sleep_retention_find_link_by_id(int id); + +/** + * @brief Get the head pointer of all entry linked list of REGDMA + * + * @param entries buffer for getting results + */ +void sleep_retention_entries_get(sleep_retention_entries_t *entries); + +/** + * @brief Get all registered modules that require sleep retention + * + * This is an unprotected interface for getting a bitmap of all modules that + * require sleep retention. + * + * It can only be called by the sleep procedure. + * + * @return the bitmap of all modules requiring sleep retention + */ +uint32_t sleep_retention_get_modules(void); + +#endif // SOC_PAU_SUPPORTED + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hw_support/include/esp_sleep.h b/components/esp_hw_support/include/esp_sleep.h index 3088942e95..b9b861d2cc 100644 --- a/components/esp_hw_support/include/esp_sleep.h +++ b/components/esp_hw_support/include/esp_sleep.h @@ -61,6 +61,9 @@ typedef enum { #endif #if SOC_PM_SUPPORT_VDDSDIO_PD ESP_PD_DOMAIN_VDDSDIO, //!< VDD_SDIO +#endif +#if SOC_PM_SUPPORT_TOP_PD + ESP_PD_DOMAIN_TOP, //!< SoC TOP #endif ESP_PD_DOMAIN_MAX //!< Number of domains } esp_sleep_pd_domain_t; @@ -95,6 +98,14 @@ typedef enum { ESP_SLEEP_WAKEUP_BT, //!< Wakeup caused by BT (light sleep only) } esp_sleep_source_t; +/** + * @brief Sleep mode + */ +typedef enum { + ESP_SLEEP_MODE_LIGHT_SLEEP, //!< light sleep mode + ESP_SLEEP_MODE_DEEP_SLEEP //!< deep sleep mode +} esp_sleep_mode_t; + /* Leave this type define for compatibility */ typedef esp_sleep_source_t esp_sleep_wakeup_cause_t; diff --git a/components/esp_hw_support/port/esp32c6/pmu_sleep.c b/components/esp_hw_support/port/esp32c6/pmu_sleep.c index 4e320b7aef..37c9dedd85 100644 --- a/components/esp_hw_support/port/esp32c6/pmu_sleep.c +++ b/components/esp_hw_support/port/esp32c6/pmu_sleep.c @@ -148,6 +148,9 @@ const pmu_sleep_config_t* pmu_sleep_config_default( pmu_sleep_analog_config_t analog_default = PMU_SLEEP_ANALOG_DSLP_CONFIG_DEFAULT(pd_flags); config->analog = analog_default; } else { + pmu_sleep_digital_config_t digital_default = PMU_SLEEP_DIGITAL_LSLP_CONFIG_DEFAULT(pd_flags); + config->digital = digital_default; + pmu_sleep_analog_config_t analog_default = PMU_SLEEP_ANALOG_LSLP_CONFIG_DEFAULT(pd_flags); if (!(pd_flags & PMU_SLEEP_PD_MODEM)){ analog_default.hp_sys.analog.slp_logic_dbias += 2; @@ -179,6 +182,11 @@ static void pmu_sleep_power_init(pmu_context_t *ctx, const pmu_sleep_power_confi } } +static void pmu_sleep_digital_init(pmu_context_t *ctx, const pmu_sleep_digital_config_t *dig) +{ + pmu_ll_hp_set_dig_pad_slp_sel (ctx->hal->dev, HP(SLEEP), dig->syscntl.dig_pad_slp_sel); +} + static void pmu_sleep_analog_init(pmu_context_t *ctx, const pmu_sleep_analog_config_t *analog, bool dslp) { assert(ctx->hal); @@ -230,6 +238,9 @@ void pmu_sleep_init(const pmu_sleep_config_t *config, bool dslp) { assert(PMU_instance()); pmu_sleep_power_init(PMU_instance(), &config->power, dslp); + if(!dslp){ + pmu_sleep_digital_init(PMU_instance(), &config->digital); + } pmu_sleep_analog_init(PMU_instance(), &config->analog, dslp); pmu_sleep_param_init(PMU_instance(), &config->param, dslp); } diff --git a/components/esp_hw_support/port/esp32c6/private_include/pmu_param.h b/components/esp_hw_support/port/esp32c6/private_include/pmu_param.h index 31f49a0a87..24b15a2958 100644 --- a/components/esp_hw_support/port/esp32c6/private_include/pmu_param.h +++ b/components/esp_hw_support/port/esp32c6/private_include/pmu_param.h @@ -150,6 +150,16 @@ typedef struct { } \ } +typedef struct { + pmu_hp_sys_cntl_reg_t syscntl; +} pmu_sleep_digital_config_t; + +#define PMU_SLEEP_DIGITAL_LSLP_CONFIG_DEFAULT(pd_flags) { \ + .syscntl = { \ + .dig_pad_slp_sel = ((pd_flags) & PMU_SLEEP_PD_TOP) ? 0 : 1, \ + } \ +} + typedef struct { struct { pmu_hp_analog_t analog; @@ -266,9 +276,10 @@ typedef struct { } typedef struct { - pmu_sleep_power_config_t power; - pmu_sleep_analog_config_t analog; - pmu_sleep_param_config_t param; + pmu_sleep_power_config_t power; + pmu_sleep_digital_config_t digital; + pmu_sleep_analog_config_t analog; + pmu_sleep_param_config_t param; } pmu_sleep_config_t; typedef struct pmu_sleep_machine_constant { diff --git a/components/esp_hw_support/port/pau_regdma.c b/components/esp_hw_support/port/pau_regdma.c new file mode 100644 index 0000000000..5d340c1b0a --- /dev/null +++ b/components/esp_hw_support/port/pau_regdma.c @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "sdkconfig.h" +#include "esp_attr.h" +#include "esp_log.h" +#include "soc/soc.h" +#include "soc/pcr_reg.h" +#include "esp_private/esp_pau.h" +#include "esp_private/periph_ctrl.h" + +static __attribute__((unused)) const char *TAG = "pau_regdma"; + +typedef struct { + pau_hal_context_t *hal; +} pau_context_t; + +pau_context_t * __attribute__((weak)) PAU_instance(void) +{ + static pau_hal_context_t pau_hal = { .dev = NULL }; + static pau_context_t pau_context = { .hal = &pau_hal }; + + if (pau_hal.dev == NULL) { + pau_hal.dev = &PAU; + periph_module_enable(PERIPH_REGDMA_MODULE); + } + + return &pau_context; +} + +void pau_regdma_set_entry_link_addr(pau_regdma_link_addr_t *link_entries) +{ + ESP_LOGD(TAG, "All link addresses %p,%p,%p,%p", (*link_entries)[0], (*link_entries)[1], (*link_entries)[2], (*link_entries)[3]); + pau_hal_set_regdma_entry_link_addr(PAU_instance()->hal, link_entries); +} + +void pau_regdma_set_modem_link_addr(void *link_addr) +{ + pau_hal_set_regdma_modem_link_addr(PAU_instance()->hal, link_addr); +} + +void pau_regdma_trigger_modem_link_backup(void) +{ + pau_hal_start_regdma_modem_link(PAU_instance()->hal, true); + pau_hal_stop_regdma_modem_link(PAU_instance()->hal); +} + +void pau_regdma_trigger_modem_link_restore(void) +{ + pau_hal_start_regdma_modem_link(PAU_instance()->hal, false); + pau_hal_stop_regdma_modem_link(PAU_instance()->hal); +} + +void pau_regdma_set_extra_link_addr(void *link_addr) +{ + pau_hal_set_regdma_extra_link_addr(PAU_instance()->hal, link_addr); +} + +void pau_regdma_trigger_extra_link_backup(void) +{ + pau_hal_start_regdma_extra_link(PAU_instance()->hal, true); + pau_hal_stop_regdma_extra_link(PAU_instance()->hal); +} + +void pau_regdma_trigger_extra_link_restore(void) +{ + pau_hal_start_regdma_extra_link(PAU_instance()->hal, false); + pau_hal_stop_regdma_extra_link(PAU_instance()->hal); +} diff --git a/components/esp_hw_support/port/regdma_link.c b/components/esp_hw_support/port/regdma_link.c new file mode 100644 index 0000000000..d941b87540 --- /dev/null +++ b/components/esp_hw_support/port/regdma_link.c @@ -0,0 +1,798 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include + +#include "esp_private/regdma_link.h" + +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_regdma.h" + + + +#define REGDMA_LINK_ADDR_ALIGN (4) +#define REGDMA_LINK_MEM_TYPE_CAPS (MALLOC_CAP_DMA | MALLOC_CAP_DEFAULT) + +void * regdma_link_new_continuous(void *backup, void *buff, int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + regdma_link_continuous_t *link = (regdma_link_continuous_t *)heap_caps_aligned_alloc( + REGDMA_LINK_ADDR_ALIGN, + buff ? sizeof(regdma_link_continuous_t) : (sizeof(regdma_link_continuous_t) + (len<<2)), + REGDMA_LINK_MEM_TYPE_CAPS + ); + if (link) { + memset(link, 0, buff ? sizeof(regdma_link_continuous_t) : (sizeof(regdma_link_continuous_t) + (len<<2))); + void *buf = buff ? buff : (void *)(link->buff); + link = regdma_link_init_continuous(link, buf, backup, len, restore, next, skip_b, skip_r, id, module); + return (void *)((void *)link + offsetof(regdma_link_continuous_t, head)); + } + return NULL; +} + +void * regdma_link_new_addr_map(void *backup, void *buff, uint32_t bitmap[4], int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + regdma_link_addr_map_t *link = (regdma_link_addr_map_t *)heap_caps_aligned_alloc( + REGDMA_LINK_ADDR_ALIGN, + buff ? sizeof(regdma_link_addr_map_t) : (sizeof(regdma_link_addr_map_t) + (len<<2)), + REGDMA_LINK_MEM_TYPE_CAPS + ); + if (link) { + memset(link, 0, buff ? sizeof(regdma_link_addr_map_t) : (sizeof(regdma_link_addr_map_t) + (len<<2))); + void *buf = buff ? buff : (void *)(link->buff); + link = regdma_link_init_addr_map(link, buf, backup, bitmap, len, restore, next, skip_b, skip_r, id, module); + return (void *)((void *)link + offsetof(regdma_link_addr_map_t, head)); + } + return NULL; +} + +void * regdma_link_new_write(void *backup, uint32_t value, uint32_t mask, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + regdma_link_write_wait_t *link = (regdma_link_write_wait_t *)heap_caps_aligned_alloc( + REGDMA_LINK_ADDR_ALIGN, sizeof(regdma_link_write_wait_t), REGDMA_LINK_MEM_TYPE_CAPS); + if (link) { + memset(link, 0, sizeof(regdma_link_write_wait_t)); + link = regdma_link_init_write(link, backup, value, mask, next, skip_b, skip_r, id, module); + return (void *)((void *)link + offsetof(regdma_link_write_wait_t, head)); + } + return NULL; +} + +void * regdma_link_new_wait(void *backup, uint32_t value, uint32_t mask, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + regdma_link_write_wait_t *link = (regdma_link_write_wait_t *)heap_caps_aligned_alloc( + REGDMA_LINK_ADDR_ALIGN, sizeof(regdma_link_write_wait_t), REGDMA_LINK_MEM_TYPE_CAPS); + if (link) { + memset(link, 0, sizeof(regdma_link_write_wait_t)); + link = regdma_link_init_wait(link, backup, value, mask, next, skip_b, skip_r, id, module); + return (void *)((void *)link + offsetof(regdma_link_write_wait_t, head)); + } + return NULL; +} + +void * regdma_link_new_branch_continuous(void *backup, void *buff, int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + regdma_link_branch_continuous_t *link = (regdma_link_branch_continuous_t *)heap_caps_aligned_alloc( + REGDMA_LINK_ADDR_ALIGN, + buff ? sizeof(regdma_link_branch_continuous_t) : (sizeof(regdma_link_branch_continuous_t) + (len<<2)), + REGDMA_LINK_MEM_TYPE_CAPS + ); + if (link) { + memset(link, 0, buff ? sizeof(regdma_link_branch_continuous_t) : (sizeof(regdma_link_branch_continuous_t) + (len<<2))); + void *buf = buff ? buff : (void *)(link->buff); + link = regdma_link_init_branch_continuous(link, buf, backup, len, restore, next, skip_b, skip_r, id, module); + return (void *)((void *)link + offsetof(regdma_link_branch_continuous_t, head)); + } + return NULL; +} + +void * regdma_link_new_branch_addr_map(void *backup, void *buff, uint32_t bitmap[4], int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + regdma_link_branch_addr_map_t *link = (regdma_link_branch_addr_map_t *)heap_caps_aligned_alloc( + REGDMA_LINK_ADDR_ALIGN, + buff ? sizeof(regdma_link_branch_addr_map_t) : (sizeof(regdma_link_branch_addr_map_t) + (len<<2)), + REGDMA_LINK_MEM_TYPE_CAPS + ); + if (link) { + memset(link, 0, buff ? sizeof(regdma_link_branch_addr_map_t) : (sizeof(regdma_link_branch_addr_map_t) + (len<<2))); + void *buf = buff ? buff : (void *)(link->buff); + link = regdma_link_init_branch_addr_map(link, buf, backup, bitmap, len, restore, next, skip_b, skip_r, id, module); + return (void *)((void *)link + offsetof(regdma_link_branch_addr_map_t, head)); + } + return NULL; +} + +void * regdma_link_new_branch_write(void *backup, uint32_t value, uint32_t mask, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + regdma_link_branch_write_wait_t *link = (regdma_link_branch_write_wait_t *)heap_caps_aligned_alloc( + REGDMA_LINK_ADDR_ALIGN, sizeof(regdma_link_branch_write_wait_t), REGDMA_LINK_MEM_TYPE_CAPS); + if (link) { + memset(link, 0, sizeof(regdma_link_branch_write_wait_t)); + link = regdma_link_init_branch_write(link, backup, value, mask, next, skip_b, skip_r, id, module); + return (void *)((void *)link + offsetof(regdma_link_branch_write_wait_t, head)); + } + return NULL; +} + +void * regdma_link_new_branch_wait(void *backup, uint32_t value, uint32_t mask, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + regdma_link_branch_write_wait_t *link = (regdma_link_branch_write_wait_t *)heap_caps_aligned_alloc( + REGDMA_LINK_ADDR_ALIGN, sizeof(regdma_link_branch_write_wait_t), REGDMA_LINK_MEM_TYPE_CAPS); + if (link) { + memset(link, 0, sizeof(regdma_link_branch_write_wait_t)); + link = regdma_link_init_branch_wait(link, backup, value, mask, next, skip_b, skip_r, id, module); + return (void *)((void *)link + offsetof(regdma_link_branch_write_wait_t, head)); + } + return NULL; +} + +void * regdma_link_new_continuous_default(void *backup, int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + return regdma_link_new_continuous(backup, NULL, len, restore, next, skip_b, skip_r, id, module); +} + +void * regdma_link_new_addr_map_default(void *backup, uint32_t bitmap[4], int len, void *restore, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + return regdma_link_new_addr_map(backup, NULL, bitmap, len, restore, next, skip_b, skip_r, id, module); +} + +void * regdma_link_new_write_default(void *backup, uint32_t value, uint32_t mask, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + return regdma_link_new_write(backup, value, mask, next, skip_b, skip_r, id, module); +} + +void * regdma_link_new_wait_default(void *backup, uint32_t value, uint32_t mask, void *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + return regdma_link_new_wait(backup, value, mask, next, skip_b, skip_r, id, module); +} + +void * regdma_link_new_branch_continuous_default(void *backup, int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + return regdma_link_new_branch_continuous(backup, NULL, len, restore, next, skip_b, skip_r, id, module); +} + +void * regdma_link_new_branch_addr_map_default(void *backup, uint32_t bitmap[4], int len, void *restore, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + return regdma_link_new_branch_addr_map(backup, NULL, bitmap, len, restore, next, skip_b, skip_r, id, module); +} + +void * regdma_link_new_branch_write_default(void *backup, uint32_t value, uint32_t mask, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + return regdma_link_new_branch_write(backup, value, mask, next, skip_b, skip_r, id, module); +} + +void * regdma_link_new_branch_wait_default(void *backup, uint32_t value, uint32_t mask, regdma_entry_buf_t *next, bool skip_b, bool skip_r, int id, uint32_t module) +{ + return regdma_link_new_branch_wait(backup, value, mask, next, skip_b, skip_r, id, module); +} + + +static void * regdma_link_init_continuous_wrapper(const regdma_link_config_t *config, uint32_t module, int n, va_list args) +{ + regdma_entry_buf_t next; + + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < n && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + return regdma_link_new_continuous_default((void *)(config->continuous.backup), config->head.length, + (void *)(config->continuous.restore), next[0], config->head.skip_b, + config->head.skip_r, config->id, module); +} + +static void * regdma_link_init_addr_map_wrapper(const regdma_link_config_t *config, uint32_t module, int n, va_list args) +{ + regdma_entry_buf_t next; + + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < n && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + return regdma_link_new_addr_map_default((void *)(config->addr_map.backup), (void *)(config->addr_map.map), + config->head.length, (void *)(config->addr_map.restore), next[0], config->head.skip_b, + config->head.skip_r, config->id, module); +} + +static void * regdma_link_init_write_wrapper(const regdma_link_config_t *config, uint32_t module, int n, va_list args) +{ + regdma_entry_buf_t next; + + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < n && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + return regdma_link_new_write_default((void *)(config->write_wait.backup), config->write_wait.value, + config->write_wait.mask, next[0], config->head.skip_b, config->head.skip_r, + config->id, module); +} + +static void * regdma_link_init_wait_wrapper(const regdma_link_config_t *config, uint32_t module, int n, va_list args) +{ + regdma_entry_buf_t next; + + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < n && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + return regdma_link_new_wait_default((void *)(config->write_wait.backup), config->write_wait.value, + config->write_wait.mask, next[0], config->head.skip_b, config->head.skip_r, + config->id, module); +} + +static void * regdma_link_init_branch_continuous_wrapper(const regdma_link_config_t *config, uint32_t module, int n, va_list args) +{ + regdma_entry_buf_t next; + + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < n && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + return regdma_link_new_branch_continuous_default((void *)(config->continuous.backup), + config->head.length, (void *)(config->continuous.restore), &next, + config->head.skip_b, config->head.skip_r, config->id, module); +} + +static void * regdma_link_init_branch_addr_map_wrapper(const regdma_link_config_t *config, uint32_t module, int n, va_list args) +{ + regdma_entry_buf_t next; + + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < n && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + return regdma_link_new_branch_addr_map_default((void *)(config->addr_map.backup), + (void *)(config->addr_map.map), config->head.length, (void *)(config->addr_map.restore), + &next, config->head.skip_b, config->head.skip_r, config->id, module); +} + +static void * regdma_link_init_branch_write_wrapper(const regdma_link_config_t *config, uint32_t module, int n, va_list args) +{ + regdma_entry_buf_t next; + + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < n && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + return regdma_link_new_branch_write_default((void *)(config->write_wait.backup), + config->write_wait.value, config->write_wait.mask, &next, config->head.skip_b, + config->head.skip_r, config->id, module); +} + +static void * regdma_link_init_branch_wait_wrapper(const regdma_link_config_t *config, uint32_t module, int n, va_list args) +{ + regdma_entry_buf_t next; + + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < n && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + return regdma_link_new_branch_wait_default((void *)(config->write_wait.backup), + config->write_wait.value, config->write_wait.mask, &next, config->head.skip_b, + config->head.skip_r, config->id, module); +} + +static void * regdma_link_init_wrapper(const regdma_link_config_t *config, bool branch, uint32_t module, int nentry, va_list args) +{ + typedef void * (*init_fn_t)(const void *, uint32_t, int, va_list); + + const static init_fn_t initfn[] = { + [0] = (init_fn_t)regdma_link_init_continuous_wrapper, /* REGDMA_LINK_MODE_CONTINUOUS */ + [1] = (init_fn_t)regdma_link_init_addr_map_wrapper, /* REGDMA_LINK_MODE_ADDR_MAP */ + [2] = (init_fn_t)regdma_link_init_write_wrapper, /* REGDMA_LINK_MODE_WRITE */ + [3] = (init_fn_t)regdma_link_init_wait_wrapper /* REGDMA_LINK_MODE_WAIT */ + }; + const static init_fn_t initfn_b[] = { + [0] = (init_fn_t)regdma_link_init_branch_continuous_wrapper, + [1] = (init_fn_t)regdma_link_init_branch_addr_map_wrapper, + [2] = (init_fn_t)regdma_link_init_branch_write_wrapper, + [3] = (init_fn_t)regdma_link_init_branch_wait_wrapper + }; + + assert((config->head.mode < ARRAY_SIZE(initfn)) && (config->head.mode < ARRAY_SIZE(initfn_b))); + + init_fn_t pfn = branch ? initfn_b[config->head.mode] : initfn[config->head.mode]; + return (*pfn)(config, module, nentry, args); +} + +void * regdma_link_init(const regdma_link_config_t *config, bool branch, uint32_t module, int nentry, ...) +{ + assert(config != NULL); + + va_list args; + va_start(args, nentry); + void * link = regdma_link_init_wrapper(config, branch, module, nentry, args); + va_end(args); + return link; +} + +static void * regdma_link_get_next_continuous_wrapper(void *link) +{ + regdma_link_continuous_t *continuous = __containerof(link, regdma_link_continuous_t, head); + return (void *)(continuous->body.next); +} + +static void * regdma_link_get_next_addr_map_wrapper(void *link) +{ + regdma_link_addr_map_t *addr_map = __containerof(link, regdma_link_addr_map_t, head); + return (void *)(addr_map->body.next); +} + +static void * regdma_link_get_next_write_wait_wrapper(void *link) +{ + regdma_link_write_wait_t *write_wait = __containerof(link, regdma_link_write_wait_t, head); + return (void *)(write_wait->body.next); +} + +static regdma_entry_buf_t * regdma_link_get_next_branch_continuous_wrapper(void *link) +{ + regdma_link_branch_continuous_t *branch_continuous = __containerof(link, regdma_link_branch_continuous_t, head); + return &branch_continuous->body.next; +} + +static regdma_entry_buf_t * regdma_link_get_next_branch_addr_map_wrapper(void *link) +{ + regdma_link_branch_addr_map_t *branch_addr_map = __containerof(link, regdma_link_branch_addr_map_t, head); + return &branch_addr_map->body.next; +} + +static regdma_entry_buf_t * regdma_link_get_next_branch_write_wait_wrapper(void *link) +{ + regdma_link_branch_write_wait_t *branch_write_wait = __containerof(link, regdma_link_branch_write_wait_t, head); + return &branch_write_wait->body.next; +} + +static void * regdma_link_get_next(void *link, int entry) +{ + if (link) { + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + if (head.branch) { + typedef regdma_entry_buf_t * (*get_nextfn1_t)(void *); + const static get_nextfn1_t nextfn1[] = { + [0] = (get_nextfn1_t)regdma_link_get_next_branch_continuous_wrapper, + [1] = (get_nextfn1_t)regdma_link_get_next_branch_addr_map_wrapper, + [2] = (get_nextfn1_t)regdma_link_get_next_branch_write_wait_wrapper, + [3] = (get_nextfn1_t)regdma_link_get_next_branch_write_wait_wrapper + }; + assert(head.mode < ARRAY_SIZE(nextfn1)); + regdma_entry_buf_t *next = (*nextfn1[head.mode])(link); + if ((entry < REGDMA_LINK_ENTRY_NUM) && (*next)[entry] && (head.eof == 0)) { + return (*next)[entry]; + } + } else { + typedef void * (*get_nextfn0_t)(void *); + const static get_nextfn0_t nextfn0[] = { + [0] = (get_nextfn0_t)regdma_link_get_next_continuous_wrapper, + [1] = (get_nextfn0_t)regdma_link_get_next_addr_map_wrapper, + [2] = (get_nextfn0_t)regdma_link_get_next_write_wait_wrapper, + [3] = (get_nextfn0_t)regdma_link_get_next_write_wait_wrapper + }; + assert(head.mode < ARRAY_SIZE(nextfn0)); + void *next = (*nextfn0[head.mode])(link); + if (next && (head.eof == 0)) { + return next; + } + } + } + return NULL; +} + +static void * regdma_link_recursive_impl(void *link, int entry, int depth, void (*hook)(void *, int, int)) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + + if (link) { + regdma_link_recursive_impl(regdma_link_get_next(link, entry), entry, depth+1, hook); + if (hook) { + (*hook)(link, entry, depth); + } + } + return link; +} + +void * regdma_link_recursive(void *link, int entry, void (*hook)(void *, int, int)) +{ + return regdma_link_recursive_impl(link, entry, 0, hook); +} + +static void * regdma_link_get_instance(void *link) +{ + void * container_memaddr[] = { + (void *)__containerof(link, regdma_link_continuous_t, head), + (void *)__containerof(link, regdma_link_addr_map_t, head), + (void *)__containerof(link, regdma_link_write_wait_t, head), + (void *)__containerof(link, regdma_link_write_wait_t, head), + (void *)__containerof(link, regdma_link_branch_continuous_t, head), + (void *)__containerof(link, regdma_link_branch_addr_map_t, head), + (void *)__containerof(link, regdma_link_branch_write_wait_t, head), + (void *)__containerof(link, regdma_link_branch_write_wait_t, head) + }; + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + int it = (head.branch << 2) | head.mode; + assert(it < ARRAY_SIZE(container_memaddr)); + + return container_memaddr[it]; +} +static regdma_link_stats_t * regdma_link_get_stats(void *link) +{ + const static size_t stats_offset[] = { + offsetof(regdma_link_continuous_t, stat), + offsetof(regdma_link_addr_map_t, stat), + offsetof(regdma_link_write_wait_t, stat), + offsetof(regdma_link_write_wait_t, stat), + offsetof(regdma_link_branch_continuous_t, stat), + offsetof(regdma_link_branch_addr_map_t, stat), + offsetof(regdma_link_branch_write_wait_t, stat), + offsetof(regdma_link_branch_write_wait_t, stat) + }; + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + int it = (head.branch << 2) | head.mode; + assert(it < ARRAY_SIZE(stats_offset)); + + return (regdma_link_stats_t *)(regdma_link_get_instance(link) + stats_offset[it]); +} + +static void regdma_link_update_stats_wrapper(void *link, int entry, int depth) +{ + if (link == NULL) { + return; + } + regdma_link_update_stats(regdma_link_get_stats(link), entry, depth); +} + +void regdma_link_stats(void *link, int entry) +{ + regdma_link_recursive_impl(link, entry, 0, regdma_link_update_stats_wrapper); +} + +static void regdma_link_destroy_wrapper(void *link, int entry, int depth) +{ + if (link == NULL) { + return; + } + regdma_link_stats_t *stat = regdma_link_get_stats(link); + stat->ref &= ~BIT(entry); + if (stat->ref == 0) { + free(regdma_link_get_instance(link)); + } +} + +void regdma_link_destroy(void *link, int entry) +{ + regdma_link_recursive_impl(link, entry, 0, regdma_link_destroy_wrapper); +} + +void * regdma_find_link_by_pos(void *link, int entry, int pos) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + + void *next = link; + if (link) { + int iter = 0; + do { + if (pos == iter++) { + break; + } + } while ((next = regdma_link_get_next(next, entry)) != NULL); + } + return next; +} + +void * regdma_find_link_by_id(void *link, int entry, int id) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + + void *find_addr = NULL; + void *next = link; + if (link) { + int linkid = 0; + do { + regdma_link_head_t head = REGDMA_LINK_HEAD(next); + if (head.branch) { + regdma_link_branch_continuous_t *continuous = (regdma_link_branch_continuous_t *)regdma_link_get_instance(next); + linkid = continuous->stat.id; + } else { + regdma_link_continuous_t *continuous = (regdma_link_continuous_t *)regdma_link_get_instance(next); + linkid = continuous->stat.id; + } + if (linkid == id) { + find_addr = next; + break; + } + } while ((next = regdma_link_get_next(next, entry)) != NULL); + } + return find_addr; +} + +void regdma_link_set_write_wait_content(void *link, uint32_t value, uint32_t mask) +{ + if (link) { + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + if (head.mode == REGDMA_LINK_MODE_WRITE || head.mode == REGDMA_LINK_MODE_WAIT) { + if (head.branch) { + regdma_link_branch_write_wait_t *write_wait = (regdma_link_branch_write_wait_t *)regdma_link_get_instance(link); + write_wait->body.value = value; + write_wait->body.mask = mask; + } else { + regdma_link_write_wait_t *write_wait = (regdma_link_write_wait_t *)regdma_link_get_instance(link); + write_wait->body.value = value; + write_wait->body.mask = mask; + } + } + } +} + +static void regdma_link_update_continuous_next_wrapper(void *link, void *next) +{ + regdma_link_continuous_t *continuous = __containerof(link, regdma_link_continuous_t, head); + continuous->body.next = next; +} + +static void regdma_link_update_addr_map_next_wrapper(void *link, void *next) +{ + regdma_link_addr_map_t *addr_map = __containerof(link, regdma_link_addr_map_t, head); + addr_map->body.next = next; +} + +static void regdma_link_update_write_wait_next_wrapper(void *link, void *next) +{ + regdma_link_write_wait_t *write_wait = __containerof(link, regdma_link_write_wait_t, head); + write_wait->body.next = next; +} + +static void regdma_link_update_branch_continuous_next_wrapper(void *link, regdma_entry_buf_t *next) +{ + regdma_link_branch_continuous_t *branch_continuous = __containerof(link, regdma_link_branch_continuous_t, head); + for (int i = 0; i < REGDMA_LINK_ENTRY_NUM; i++) { + branch_continuous->body.next[i] = (*next)[i]; + } +} + +static void regdma_link_update_branch_addr_map_next_wrapper(void *link, regdma_entry_buf_t *next) +{ + regdma_link_branch_addr_map_t *branch_addr_map = __containerof(link, regdma_link_branch_addr_map_t, head); + for (int i = 0; i < REGDMA_LINK_ENTRY_NUM; i++) { + branch_addr_map->body.next[i] = (*next)[i]; + } +} + +static void regdma_link_update_branch_write_wait_next_wrapper(void *link, regdma_entry_buf_t *next) +{ + regdma_link_branch_write_wait_t *branch_write_wait = __containerof(link, regdma_link_branch_write_wait_t, head); + for (int i = 0; i < REGDMA_LINK_ENTRY_NUM; i++) { + branch_write_wait->body.next[i] = (*next)[i]; + } +} + +void regdma_link_update_next(void *link, int nentry, ...) +{ + va_list args; + va_start(args, nentry); + if (link) { + regdma_entry_buf_t next; + memset(next, 0, sizeof(regdma_entry_buf_t)); + for (int i = 0; i < nentry && i < REGDMA_LINK_ENTRY_NUM; i++) { // Ignore more arguments + next[i] = va_arg(args, void *); + } + + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + if (head.branch) { + typedef void (*update_branch_fn_t)(void *, regdma_entry_buf_t *); + static const update_branch_fn_t updatefn_b[] = { + [0] = regdma_link_update_branch_continuous_next_wrapper, + [1] = regdma_link_update_branch_addr_map_next_wrapper, + [2] = regdma_link_update_branch_write_wait_next_wrapper, + [3] = regdma_link_update_branch_write_wait_next_wrapper + }; + assert((head.mode < ARRAY_SIZE(updatefn_b))); + (*updatefn_b[head.mode])(link, &next); + } else { + typedef void (*update_fn_t)(void *, void *); + static const update_fn_t updatefn[] = { + [0] = regdma_link_update_continuous_next_wrapper, + [1] = regdma_link_update_addr_map_next_wrapper, + [2] = regdma_link_update_write_wait_next_wrapper, + [3] = regdma_link_update_write_wait_next_wrapper + }; + assert((head.mode < ARRAY_SIZE(updatefn))); + (*updatefn[head.mode])(link, next[0]); + } + } + va_end(args); +} + +uint32_t regdma_link_get_owner_bitmap(void *link, void *tail, int entry) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + + uint32_t owner = 0; + void *next = link; + if (link) { + do { + owner |= regdma_link_get_stats(next)->ref; + if (next == tail) { + break; + } + } while ((next = regdma_link_get_next(next, entry)) != NULL); + } + return owner; +} + +void * regdma_find_module_link_head(void *link, void *tail, int entry, uint32_t module) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + + void *find_link = NULL; + void *next = link; + if (link) { + do { + if (next == tail) { + break; + } + if (regdma_link_get_stats(next)->module & module) { + find_link = next; + break; + } + } while ((next = regdma_link_get_next(next, entry)) != NULL); + } + return find_link; +} + +void * regdma_find_module_link_tail(void *link, void *tail, int entry, uint32_t module) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + + void *find_tail = NULL; + void *next = link; + if (link) { + do { + if (next != tail) { + void *temp = regdma_link_get_next(next, entry); + if ((regdma_link_get_stats(next)->module & module) && + !(regdma_link_get_stats(temp)->module & module)) { + find_tail = next; + break; + } + } else { + if (regdma_link_get_stats(next)->module & module) { + find_tail = next; + break; + } + } + } while ((next = regdma_link_get_next(next, entry)) != NULL); + } + return find_tail; +} + +void * regdma_find_next_module_link_head(void *link, void *tail, int entry, uint32_t module) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + void *find_tail = regdma_find_module_link_tail(link, tail, entry, module); + if (find_tail && find_tail != tail) { + return regdma_link_get_next(find_tail, entry); + } + return NULL; +} + +void * regdma_find_prev_module_link_tail(void *link, void *tail, int entry, uint32_t module) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + void *find_head = regdma_find_module_link_head(link, tail, entry, module); + void *next = link; + if (find_head && find_head != link) { + do { + if (next == tail) { + break; + } + if (regdma_link_get_next(next, entry) == find_head) { + return next; + } + } while ((next = regdma_link_get_next(next, entry)) != NULL); + } + return NULL; +} + +#if REGDMA_LINK_DBG +static const char *TAG = "regdma_link"; + +static void print_info_continuous_wrapper(void *link) +{ + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + regdma_link_continuous_t *cons = __containerof(link, regdma_link_continuous_t, head); + ESP_EARLY_LOGI(TAG, "[%08x/%04x] link:%x, head:%x, next:%x, backup:%x, restore:%x, buff:%x", + cons->stat.module, cons->stat.id, link, cons->head, cons->body.next, + cons->body.backup, cons->body.restore, cons->body.mem); + ESP_LOG_BUFFER_HEX(TAG, (const void *)cons->body.mem, head.length); +} + +static void print_info_addr_map_wrapper(void *link) +{ + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + regdma_link_addr_map_t *map = __containerof(link, regdma_link_addr_map_t, head); + ESP_EARLY_LOGI(TAG, "[%08x/%04x] link:%x, head:%x, next:%x, backup:%x, restore:%x, buff:%x, map:{%x,%x,%x,%x}", + map->stat.module, map->stat.id, link, map->head, map->body.next, map->body.backup, + map->body.restore, map->body.mem, map->body.map[0], map->body.map[1], + map->body.map[2], map->body.map[3]); + ESP_LOG_BUFFER_HEX(TAG, (const void *)map->body.mem, head.length); +} + +static void print_info_write_wait_wrapper(void *link) +{ + regdma_link_write_wait_t *ww = __containerof(link, regdma_link_write_wait_t, head); + ESP_EARLY_LOGI(TAG, "[%08x/%04x] link:%x, head:%x, next:%x, backup:%x, value:%x, mask:%x", + ww->stat.module, ww->stat.id, link, ww->head, ww->body.next, + ww->body.backup, ww->body.value, ww->body.mask); +} + +static void print_info_branch_continuous_wrapper(void *link) +{ + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + regdma_link_branch_continuous_t *cons = __containerof(link, regdma_link_branch_continuous_t, head); + ESP_EARLY_LOGI(TAG, "[%08x/%04x] link:%x, head:%x, next:{%x,%x,%x,%x}, backup:%x, restore:%x, buff:%x", + cons->stat.module, cons->stat.id, link, cons->head, cons->body.next[0], cons->body.next[1], + cons->body.next[2], cons->body.next[3], cons->body.backup, cons->body.restore, + cons->body.mem); + ESP_LOG_BUFFER_HEX(TAG, (const void *)cons->body.mem, head.length); +} + +static void print_info_branch_addr_map_wrapper(void *link) +{ + regdma_link_head_t head = REGDMA_LINK_HEAD(link); + regdma_link_branch_addr_map_t *map = __containerof(link, regdma_link_branch_addr_map_t, head); + ESP_EARLY_LOGI(TAG, "[%08x/%04x] link:%x, head:%x, next:{%x,%x,%x,%x}, backup:%x, restore:%x, buff:%x, map:{%x,%x,%x,%x}", + map->stat.module, map->stat.id, link, map->head, map->body.next[0], map->body.next[1], map->body.next[2], + map->body.next[3], map->body.backup, map->body.restore, map->body.mem, map->body.map[0], + map->body.map[1], map->body.map[2], map->body.map[3]); + ESP_LOG_BUFFER_HEX(TAG, (const void *)map->body.mem, head.length); +} + +static void print_info_branch_write_wait_wrapper(void *link) +{ + regdma_link_branch_write_wait_t *ww = __containerof(link, regdma_link_branch_write_wait_t, head); + ESP_EARLY_LOGI(TAG, "[%08x/%04x] link:%x, head:%x, next:{%x,%x,%x,%x}, backup:%x, value:%x, mask:%x", + ww->stat.module, ww->stat.id, link, ww->head, ww->body.next[0], ww->body.next[1], + ww->body.next[2], ww->body.next[3], ww->body.backup, ww->body.value, + ww->body.mask); +} + +static void print_link_info(void *args, int entry, int depth) +{ + typedef void (*prinf_fn_t)(void *); + + const static prinf_fn_t prinf_fn[] = { + [0] = (prinf_fn_t)print_info_continuous_wrapper, + [1] = (prinf_fn_t)print_info_addr_map_wrapper, + [2] = (prinf_fn_t)print_info_write_wait_wrapper, + [3] = (prinf_fn_t)print_info_write_wait_wrapper, + [4] = (prinf_fn_t)print_info_branch_continuous_wrapper, + [5] = (prinf_fn_t)print_info_branch_addr_map_wrapper, + [6] = (prinf_fn_t)print_info_branch_write_wait_wrapper, + [7] = (prinf_fn_t)print_info_branch_write_wait_wrapper + }; + + regdma_link_head_t head = REGDMA_LINK_HEAD(args); + int it = (head.branch << 2) | head.mode; + assert(it < ARRAY_SIZE(prinf_fn)); + + (*prinf_fn[it])(args); +} + +void regdma_link_show_memories(void *link, int entry) +{ + assert(entry < REGDMA_LINK_ENTRY_NUM); + + void *next = link; + if (link) { + do { + print_link_info(next, entry, 0); + } while ((next = regdma_link_get_next(next, entry)) != NULL); + } else { + ESP_EARLY_LOGW(TAG, "This REGDMA linked list is empty!\n"); + } +} +#endif diff --git a/components/esp_hw_support/sleep_clock.c b/components/esp_hw_support/sleep_clock.c new file mode 100644 index 0000000000..cc48d28b61 --- /dev/null +++ b/components/esp_hw_support/sleep_clock.c @@ -0,0 +1,87 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "sdkconfig.h" +#include "soc/soc_caps.h" + +#include "esp_err.h" +#include "esp_log.h" +#include "esp_attr.h" +#include "esp_check.h" +#include "esp_regdma.h" +#include "esp_private/startup_internal.h" +#include "esp_private/sleep_retention.h" +#include "esp_private/sleep_clock.h" + +#include "soc/pcr_reg.h" +#include "modem/modem_syscon_reg.h" + +static __attribute__((unused)) const char *TAG = "sleep_clock"; + +esp_err_t sleep_clock_system_retention_init(void) +{ + #define N_REGS_PCR() (((PCR_SRAM_POWER_CONF_REG - DR_REG_PCR_BASE) / 4) + 1) + + const static sleep_retention_entries_config_t pcr_regs_retention[] = { + [0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_PCR_LINK(0), DR_REG_PCR_BASE, DR_REG_PCR_BASE, N_REGS_PCR(), 0, 0), .owner = ENTRY(0) | ENTRY(2) } /* pcr */ + }; + + esp_err_t err = sleep_retention_entries_create(pcr_regs_retention, ARRAY_SIZE(pcr_regs_retention), REGDMA_LINK_PRI_1, 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; +} + +void sleep_clock_system_retention_deinit(void) +{ + sleep_retention_entries_destroy(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM); +} + +esp_err_t sleep_clock_modem_retention_init(void) +{ + #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_2, SLEEP_RETENTION_MODULE_CLOCK_MODEM); + ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for modem (SYSCON) retention"); + ESP_LOGI(TAG, "Modem Power, Clock and Reset sleep retention initialization"); + return ESP_OK; +} + +void sleep_clock_modem_retention_deinit(void) +{ + sleep_retention_entries_destroy(SLEEP_RETENTION_MODULE_CLOCK_MODEM); +} + +bool IRAM_ATTR clock_domain_pd_allowed(void) +{ + const uint32_t modules = sleep_retention_get_modules(); + const uint32_t mask = (const uint32_t) ( + SLEEP_RETENTION_MODULE_CLOCK_SYSTEM +#if CONFIG_MAC_BB_PD + | SLEEP_RETENTION_MODULE_CLOCK_MODEM +#endif + ); + return ((modules & mask) == mask); +} + +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP || CONFIG_MAC_BB_PD +ESP_SYSTEM_INIT_FN(sleep_clock_startup_init, BIT(0), 106) +{ + sleep_clock_system_retention_init(); +#if CONFIG_MAC_BB_PD + sleep_clock_modem_retention_init(); +#endif + return ESP_OK; +} +#endif diff --git a/components/esp_hw_support/sleep_gpio.c b/components/esp_hw_support/sleep_gpio.c index 8deadc2e80..0b30f0b10d 100644 --- a/components/esp_hw_support/sleep_gpio.c +++ b/components/esp_hw_support/sleep_gpio.c @@ -185,9 +185,13 @@ void esp_deep_sleep_wakeup_io_reset(void) #if CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND || CONFIG_PM_SLP_DISABLE_GPIO ESP_SYSTEM_INIT_FN(esp_sleep_startup_init, BIT(0), 105) { +/* If the TOP domain is powered off, the GPIO will also be powered off during sleep, + and all configurations in the sleep state of GPIO will not take effect.*/ +#if !CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP // Configure to isolate (disable the Input/Output/Pullup/Pulldown // function of the pin) all GPIO pins in sleep state esp_sleep_config_gpio_isolate(); +#endif // Enable automatic switching of GPIO configuration esp_sleep_enable_gpio_switch(true); return ESP_OK; diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index ad5de434f5..0006c7e3e0 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -76,6 +76,8 @@ #include "esp32c6/rom/rtc.h" #include "hal/lp_timer_hal.h" #include "esp_private/esp_pmu.h" +#include "esp_private/sleep_peripheral.h" +#include "esp_private/sleep_clock.h" #elif CONFIG_IDF_TARGET_ESP32H2 #include "esp32h2/rom/rtc.h" #include "esp32h2/rom/cache.h" @@ -392,22 +394,24 @@ inline static void IRAM_ATTR misc_modules_wake_prepare(void) inline static uint32_t call_rtc_sleep_start(uint32_t reject_triggers, uint32_t lslp_mem_inf_fpu, bool dslp); -inline static bool is_light_sleep(uint32_t pd_flags) -{ - return (pd_flags & RTC_SLEEP_PD_DIG) == 0; -} - -static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags) +static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags, esp_sleep_mode_t mode) { // Stop UART output so that output is not lost due to APB frequency change. // For light sleep, suspend UART output — it will resume after wakeup. // For deep sleep, wait for the contents of UART FIFO to be sent. - bool deep_sleep = pd_flags & RTC_SLEEP_PD_DIG; + bool deep_sleep = (mode == ESP_SLEEP_MODE_DEEP_SLEEP); if (deep_sleep) { flush_uarts(); } else { - suspend_uarts(); +#if SOC_PM_SUPPORT_TOP_PD + if (pd_flags & PMU_SLEEP_PD_TOP) { + flush_uarts(); + } else +#endif + { + suspend_uarts(); + } } #if SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256 @@ -482,7 +486,7 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags) #endif uint32_t reject_triggers = 0; - if (is_light_sleep(pd_flags)) { + if (!deep_sleep) { /* Light sleep, enable sleep reject for faster return from this function, * in case the wakeup is already triggerred. */ @@ -542,14 +546,14 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags) size_t rtc_fast_length = (size_t)_rtc_force_fast_end - (size_t)_rtc_text_start; #endif esp_rom_set_rtc_wake_addr((esp_rom_wake_func_t)esp_wake_stub_entry, rtc_fast_length); - result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, 0); + result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, deep_sleep); #else #if !CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP /* If not possible stack is in RTC FAST memory, use the ROM function to calculate the CRC and save ~140 bytes IRAM */ #if SOC_RTC_FAST_MEM_SUPPORTED set_rtc_memory_crc(); #endif - result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, 0); + result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, deep_sleep); #else /* Otherwise, need to call the dedicated soc function for this */ result = rtc_deep_sleep_start(s_config.wakeup_triggers, reject_triggers); @@ -560,6 +564,13 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags) result = ESP_OK; #endif } else { + +/* On esp32c6, only the lp_aon pad hold function can only hold the GPIO state in the active mode. + In order to avoid the leakage of the SPI cs pin, hold it here */ +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND + rtcio_ll_force_hold_enable(SPI_CS0_GPIO_NUM); +#endif + #if SOC_PM_CPU_RETENTION_BY_SW if (pd_flags & PMU_SLEEP_PD_CPU) { result = esp_sleep_cpu_retention(pmu_sleep_start, s_config.wakeup_triggers, reject_triggers, config.power.hp_sys.dig_power.mem_dslp, deep_sleep); @@ -567,7 +578,12 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags) result = call_rtc_sleep_start(reject_triggers, config.power.hp_sys.dig_power.mem_dslp, deep_sleep); } #else - result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, 0); + result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, deep_sleep); +#endif + +/* Unhold the SPI CS pin */ +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND + rtcio_ll_force_hold_disable(SPI_CS0_GPIO_NUM); #endif } @@ -641,7 +657,7 @@ void IRAM_ATTR esp_deep_sleep_start(void) #endif // Enter sleep - esp_sleep_start(force_pd_flags | pd_flags); + esp_sleep_start(force_pd_flags | pd_flags, ESP_SLEEP_MODE_DEEP_SLEEP); // Because RTC is in a slower clock domain than the CPU, it // can take several CPU cycles for the sleep mode to start. @@ -664,7 +680,7 @@ static esp_err_t esp_light_sleep_inner(uint32_t pd_flags, uint32_t flash_enable_time_us) { // Enter sleep - uint32_t reject = esp_sleep_start(pd_flags); + uint32_t reject = esp_sleep_start(pd_flags, ESP_SLEEP_MODE_LIGHT_SLEEP); #if SOC_CONFIGURABLE_VDDSDIO_SUPPORTED rtc_vddsdio_config_t vddsdio_config = rtc_vddsdio_get_config(); @@ -1455,6 +1471,12 @@ static uint32_t get_power_down_flags(void) } #endif +#if SOC_PM_SUPPORT_TOP_PD + if (!cpu_domain_pd_allowed() || !clock_domain_pd_allowed() || !peripheral_domain_pd_allowed()) { + s_config.domain[ESP_PD_DOMAIN_TOP].pd_option = ESP_PD_OPTION_ON; + } +#endif + #ifdef CONFIG_IDF_TARGET_ESP32 s_config.domain[ESP_PD_DOMAIN_XTAL].pd_option = ESP_PD_OPTION_OFF; #endif @@ -1512,6 +1534,11 @@ static uint32_t get_power_down_flags(void) if (s_config.domain[ESP_PD_DOMAIN_XTAL].pd_option != ESP_PD_OPTION_ON) { pd_flags |= RTC_SLEEP_PD_XTAL; } +#if SOC_PM_SUPPORT_TOP_PD + if (s_config.domain[ESP_PD_DOMAIN_TOP].pd_option != ESP_PD_OPTION_ON) { + pd_flags |= PMU_SLEEP_PD_TOP; + } +#endif #if SOC_PM_SUPPORT_VDDSDIO_PD if (s_config.domain[ESP_PD_DOMAIN_VDDSDIO].pd_option != ESP_PD_OPTION_ON) { pd_flags |= RTC_SLEEP_PD_VDDSDIO; diff --git a/components/esp_hw_support/sleep_peripheral.c b/components/esp_hw_support/sleep_peripheral.c new file mode 100644 index 0000000000..6448f2dae7 --- /dev/null +++ b/components/esp_hw_support/sleep_peripheral.c @@ -0,0 +1,254 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "sdkconfig.h" +#include "soc/soc_caps.h" + +#include "esp_sleep.h" +#include "esp_log.h" +#include "esp_check.h" + +#include "esp_private/startup_internal.h" +#include "esp_private/sleep_retention.h" +#include "esp_regdma.h" + +#include "soc/uart_reg.h" +#include "soc/systimer_reg.h" +#include "soc/timer_group_reg.h" +#include "soc/spi_mem_reg.h" +#include "soc/hp_system_reg.h" +#include "soc/tee_reg.h" +#include "soc/hp_apm_reg.h" +#include "soc/gpio_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/interrupt_matrix_reg.h" + +static __attribute__((unused)) const char *TAG = "sleep_peripheral"; + +#define SLEEP_RETENTION_PERIPHERALS_PRIORITY_DEFAULT (REGDMA_LINK_PRI_6) + +esp_err_t sleep_peripheral_intr_matrix_retention_init(void) +{ + #define N_REGS_INTR_MATRIX() (((INTMTX_CORE0_CLOCK_GATE_REG - DR_REG_INTERRUPT_MATRIX_BASE) / 4) + 1) + + const static sleep_retention_entries_config_t intr_matrix_regs_retention[] = { + [0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_INTMTX_LINK(0), DR_REG_INTERRUPT_MATRIX_BASE, DR_REG_INTERRUPT_MATRIX_BASE, N_REGS_INTR_MATRIX(), 0, 0), .owner = ENTRY(0) | ENTRY(2) } /* intr matrix */ + }; + + esp_err_t err = sleep_retention_entries_create(intr_matrix_regs_retention, ARRAY_SIZE(intr_matrix_regs_retention), REGDMA_LINK_PRI_5, SLEEP_RETENTION_MODULE_INTR_MATRIX); + ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for digital peripherals (Interrupt matrix) retention"); + ESP_LOGI(TAG, "Interrupt Matrix sleep retention initialization"); + return ESP_OK; +} + +esp_err_t sleep_peripheral_hp_system_retention_init(void) +{ + #define N_REGS_HP_SYSTEM() (((HP_SYSTEM_MEM_TEST_CONF_REG - DR_REG_HP_SYSTEM_BASE) / 4) + 1) + + const static sleep_retention_entries_config_t hp_system_regs_retention[] = { + [0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_HPSYS_LINK(0), DR_REG_HP_SYSTEM_BASE, DR_REG_HP_SYSTEM_BASE, N_REGS_HP_SYSTEM(), 0, 0), .owner = ENTRY(0) | ENTRY(2) } /* hp system */ + }; + + esp_err_t err = sleep_retention_entries_create(hp_system_regs_retention, ARRAY_SIZE(hp_system_regs_retention), REGDMA_LINK_PRI_5, SLEEP_RETENTION_MODULE_HP_SYSTEM); + ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for digital peripherals (HP system) retention"); + ESP_LOGI(TAG, "HP System sleep retention initialization"); + return ESP_OK; +} + +esp_err_t sleep_peripheral_tee_apm_retention_init(void) +{ + #define N_REGS_TEE() (((TEE_CLOCK_GATE_REG - DR_REG_TEE_BASE) / 4) + 1) + #define N_REGS_APM() (((HP_APM_CLOCK_GATE_REG - DR_REG_HP_APM_BASE) / 4) + 1) + + const static sleep_retention_entries_config_t tee_apm_regs_retention[] = { + [0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_TEEAPM_LINK(0), DR_REG_TEE_BASE, DR_REG_TEE_BASE, N_REGS_TEE(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, /* tee */ + [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_TEEAPM_LINK(0), DR_REG_HP_APM_BASE, DR_REG_HP_APM_BASE, N_REGS_APM(), 0, 0), .owner = ENTRY(0) | ENTRY(2) } /* apm */ + }; + + esp_err_t err = sleep_retention_entries_create(tee_apm_regs_retention, ARRAY_SIZE(tee_apm_regs_retention), REGDMA_LINK_PRI_4, SLEEP_RETENTION_MODULE_TEE_APM); + ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for digital peripherals (TEE/APM) retention"); + ESP_LOGI(TAG, "TEE/APM sleep retention initialization"); + return ESP_OK; +} + +esp_err_t sleep_peripheral_uart0_retention_init(void) +{ + #define N_REGS_UART() (((UART_ID_REG(0) - REG_UART_BASE(0)) / 4) + 1) + + const static sleep_retention_entries_config_t uart_regs_retention[] = { + [0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_UART_LINK(0x00), REG_UART_BASE(0), REG_UART_BASE(0), N_REGS_UART(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, /* uart */ + /* Note: uart register should set update reg to make the configuration take effect */ + [1] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_UART_LINK(0x01), UART_REG_UPDATE_REG(0), UART_REG_UPDATE, UART_REG_UPDATE_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + [2] = { .config = REGDMA_LINK_WAIT_INIT (REGDMA_UART_LINK(0x02), UART_REG_UPDATE_REG(0), 0x0, UART_REG_UPDATE_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) } + }; + + esp_err_t err = sleep_retention_entries_create(uart_regs_retention, ARRAY_SIZE(uart_regs_retention), REGDMA_LINK_PRI_5, SLEEP_RETENTION_MODULE_UART0); + ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for digital peripherals (UART) retention"); + ESP_LOGI(TAG, "UART sleep retention initialization"); + return ESP_OK; +} + +esp_err_t sleep_peripheral_tg0_retention_init(void) +{ + #define N_REGS_TG() (((TIMG_REGCLK_REG(0) - REG_TIMG_BASE(0)) / 4) + 1) + + const static sleep_retention_entries_config_t tg_regs_retention[] = { + /*Timer group0 backup. T0_wdt should get of write project firstly. wdt used by RTOS.*/ + [0] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_TIMG_LINK(0x00), TIMG_WDTWPROTECT_REG(0), TIMG_WDT_WKEY_VALUE, TIMG_WDT_WKEY_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, /* TG0 */ + [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_TIMG_LINK(0x01), REG_TIMG_BASE(0), REG_TIMG_BASE(0), N_REGS_TG(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [2] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_TIMG_LINK(0x02), TIMG_WDTWPROTECT_REG(0), TIMG_WDT_WKEY_VALUE, TIMG_WDT_WKEY_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + [3] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_TIMG_LINK(0x03), TIMG_WDTCONFIG0_REG(0), TIMG_WDT_CONF_UPDATE_EN, TIMG_WDT_CONF_UPDATE_EN_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + [4] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_TIMG_LINK(0x04), TIMG_T0UPDATE_REG(0), TIMG_T0_UPDATE, TIMG_T0_UPDATE_M, 0, 1), .owner = ENTRY(0) | ENTRY(2) }, + [5] = { .config = REGDMA_LINK_WAIT_INIT (REGDMA_TIMG_LINK(0x05), TIMG_T0UPDATE_REG(0), 0x0, TIMG_T0_UPDATE_M, 0, 1), .owner = ENTRY(0) | ENTRY(2) }, + [6] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_TIMG_LINK(0x06), TIMG_T0LO_REG(0), TIMG_T0LOADLO_REG(0), 2, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [7] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_TIMG_LINK(0x07), TIMG_T0LOAD_REG(0), 0x1, TIMG_T0_LOAD_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) } + }; + + esp_err_t err = sleep_retention_entries_create(tg_regs_retention, ARRAY_SIZE(tg_regs_retention), SLEEP_RETENTION_PERIPHERALS_PRIORITY_DEFAULT, SLEEP_RETENTION_MODULE_TG0); + ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for digital peripherals (Timer Group) retention"); + ESP_LOGI(TAG, "Timer Group sleep retention initialization"); + return ESP_OK; +} + +esp_err_t sleep_peripheral_iomux_retention_init(void) +{ + #define N_REGS_IOMUX_0() (((PERIPHS_IO_MUX_SPID_U - REG_IO_MUX_BASE) / 4) + 1) + #define N_REGS_IOMUX_1() (((GPIO_FUNC34_OUT_SEL_CFG_REG - GPIO_FUNC0_OUT_SEL_CFG_REG) / 4) + 1) + #define N_REGS_IOMUX_2() (((GPIO_FUNC124_IN_SEL_CFG_REG - GPIO_STATUS_NEXT_REG) / 4) + 1) + #define N_REGS_IOMUX_3() (((GPIO_PIN34_REG - DR_REG_GPIO_BASE) / 4) + 1) + + const static sleep_retention_entries_config_t iomux_regs_retention[] = { + [0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x00), REG_IO_MUX_BASE, REG_IO_MUX_BASE, N_REGS_IOMUX_0(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, /* io_mux */ + [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x01), GPIO_FUNC0_OUT_SEL_CFG_REG, GPIO_FUNC0_OUT_SEL_CFG_REG, N_REGS_IOMUX_1(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x02), GPIO_STATUS_NEXT_REG, GPIO_STATUS_NEXT_REG, N_REGS_IOMUX_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), DR_REG_GPIO_BASE, DR_REG_GPIO_BASE, N_REGS_IOMUX_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) } + }; + + esp_err_t err = sleep_retention_entries_create(iomux_regs_retention, ARRAY_SIZE(iomux_regs_retention), SLEEP_RETENTION_PERIPHERALS_PRIORITY_DEFAULT, SLEEP_RETENTION_MODULE_IOMUX); + ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for digital peripherals (IO Matrix) retention"); + ESP_LOGI(TAG, "IO Matrix sleep retention initialization"); + return ESP_OK; +} + +esp_err_t sleep_peripheral_spimem_retention_init(void) +{ + #define N_REGS_SPI1_MEM_0() (((SPI_MEM_SPI_SMEM_DDR_REG(1) - REG_SPI_MEM_BASE(1)) / 4) + 1) + #define N_REGS_SPI1_MEM_1() (((SPI_MEM_SPI_SMEM_AC_REG(1) - SPI_MEM_SPI_FMEM_PMS0_ATTR_REG(1)) / 4) + 1) + #define N_REGS_SPI1_MEM_2() (1) + #define N_REGS_SPI1_MEM_3() (((SPI_MEM_DATE_REG(1) - SPI_MEM_DPA_CTRL_REG(1)) / 4) + 1) + + #define N_REGS_SPI0_MEM_0() (((SPI_MEM_SPI_SMEM_DDR_REG(0) - REG_SPI_MEM_BASE(0)) / 4) + 1) + #define N_REGS_SPI0_MEM_1() (((SPI_MEM_SPI_SMEM_AC_REG(0) - SPI_MEM_SPI_FMEM_PMS0_ATTR_REG(0)) / 4) + 1) + #define N_REGS_SPI0_MEM_2() (1) + #define N_REGS_SPI0_MEM_3() (((SPI_MEM_DATE_REG(0) - SPI_MEM_DPA_CTRL_REG(0)) / 4) + 1) + + const static sleep_retention_entries_config_t spimem_regs_retention[] = { + /* Note: SPI mem should not to write mmu SPI_MEM_MMU_ITEM_CONTENT_REG and SPI_MEM_MMU_ITEM_INDEX_REG */ + [0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x00), REG_SPI_MEM_BASE(1), REG_SPI_MEM_BASE(1), N_REGS_SPI1_MEM_0(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, /* spi1_mem */ + [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x01), SPI_MEM_SPI_FMEM_PMS0_ATTR_REG(1), SPI_MEM_SPI_FMEM_PMS0_ATTR_REG(1), N_REGS_SPI1_MEM_1(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x02), SPI_MEM_CLOCK_GATE_REG(1), SPI_MEM_CLOCK_GATE_REG(1), N_REGS_SPI1_MEM_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x03), SPI_MEM_DPA_CTRL_REG(1), SPI_MEM_DPA_CTRL_REG(1), N_REGS_SPI1_MEM_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + + /* Note: SPI mem should not to write mmu SPI_MEM_MMU_ITEM_CONTENT_REG and SPI_MEM_MMU_ITEM_INDEX_REG */ + [4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x04), REG_SPI_MEM_BASE(0), REG_SPI_MEM_BASE(0), N_REGS_SPI0_MEM_0(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, /* spi0_mem */ + [5] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x05), SPI_MEM_SPI_FMEM_PMS0_ATTR_REG(0), SPI_MEM_SPI_FMEM_PMS0_ATTR_REG(0), N_REGS_SPI0_MEM_1(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [6] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x06), SPI_MEM_CLOCK_GATE_REG(0), SPI_MEM_CLOCK_GATE_REG(0), N_REGS_SPI0_MEM_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [7] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x07), SPI_MEM_DPA_CTRL_REG(0), SPI_MEM_DPA_CTRL_REG(0), N_REGS_SPI0_MEM_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) } + }; + + esp_err_t err = sleep_retention_entries_create(spimem_regs_retention, ARRAY_SIZE(spimem_regs_retention), SLEEP_RETENTION_PERIPHERALS_PRIORITY_DEFAULT, SLEEP_RETENTION_MODULE_SPIMEM); + ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for digital peripherals (SPI mem) retention"); + ESP_LOGI(TAG, "SPI Mem sleep retention initialization"); + return ESP_OK; +} + +esp_err_t sleep_peripheral_systimer_retention_init(void) +{ + #define N_REGS_SYSTIMER_0() (((SYSTIMER_TARGET2_CONF_REG - SYSTIMER_TARGET0_HI_REG) / 4) + 1) + + const static sleep_retention_entries_config_t systimer_regs_retention[] = { + [0] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_SYSTIMER_LINK(0x00), SYSTIMER_UNIT0_OP_REG, SYSTIMER_TIMER_UNIT0_UPDATE_M, SYSTIMER_TIMER_UNIT0_UPDATE_M, 0, 1), .owner = ENTRY(0) | ENTRY(2) }, /* Systimer */ + [1] = { .config = REGDMA_LINK_WAIT_INIT (REGDMA_SYSTIMER_LINK(0x01), SYSTIMER_UNIT0_OP_REG, SYSTIMER_TIMER_UNIT0_VALUE_VALID, SYSTIMER_TIMER_UNIT0_VALUE_VALID, 0, 1), .owner = ENTRY(0) | ENTRY(2) }, + [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SYSTIMER_LINK(0x02), SYSTIMER_UNIT0_VALUE_HI_REG, SYSTIMER_UNIT0_LOAD_HI_REG, 2, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [3] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_SYSTIMER_LINK(0x03), SYSTIMER_UNIT0_LOAD_REG, SYSTIMER_TIMER_UNIT0_LOAD_M, SYSTIMER_TIMER_UNIT0_LOAD_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + + [4] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_SYSTIMER_LINK(0x04), SYSTIMER_UNIT1_OP_REG, SYSTIMER_TIMER_UNIT1_UPDATE_M, SYSTIMER_TIMER_UNIT1_UPDATE_M, 0, 1), .owner = ENTRY(0) | ENTRY(2) }, + [5] = { .config = REGDMA_LINK_WAIT_INIT (REGDMA_SYSTIMER_LINK(0x05), SYSTIMER_UNIT1_OP_REG, SYSTIMER_TIMER_UNIT1_VALUE_VALID, SYSTIMER_TIMER_UNIT1_VALUE_VALID, 0, 1), .owner = ENTRY(0) | ENTRY(2) }, + [6] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SYSTIMER_LINK(0x06), SYSTIMER_UNIT1_VALUE_HI_REG, SYSTIMER_UNIT1_LOAD_HI_REG, 2, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [7] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_SYSTIMER_LINK(0x07), SYSTIMER_UNIT1_LOAD_REG, SYSTIMER_TIMER_UNIT1_LOAD_M, SYSTIMER_TIMER_UNIT1_LOAD_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + + [8] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SYSTIMER_LINK(0x08), SYSTIMER_TARGET0_HI_REG, SYSTIMER_TARGET0_HI_REG, N_REGS_SYSTIMER_0(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, /* Systimer target value & period */ + + [9] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_SYSTIMER_LINK(0x09), SYSTIMER_COMP0_LOAD_REG, SYSTIMER_TIMER_COMP0_LOAD, SYSTIMER_TIMER_COMP0_LOAD, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + [10] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_SYSTIMER_LINK(0x0a), SYSTIMER_COMP1_LOAD_REG, SYSTIMER_TIMER_COMP1_LOAD, SYSTIMER_TIMER_COMP1_LOAD, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + [11] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_SYSTIMER_LINK(0x0b), SYSTIMER_COMP2_LOAD_REG, SYSTIMER_TIMER_COMP2_LOAD, SYSTIMER_TIMER_COMP2_LOAD, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + + [12] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_SYSTIMER_LINK(0x0c), SYSTIMER_TARGET0_CONF_REG, 0, SYSTIMER_TARGET0_PERIOD_MODE_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + [13] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_SYSTIMER_LINK(0x0d), SYSTIMER_TARGET0_CONF_REG, SYSTIMER_TARGET0_PERIOD_MODE_M, SYSTIMER_TARGET0_PERIOD_MODE_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + + [14] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_SYSTIMER_LINK(0x0e), SYSTIMER_TARGET1_CONF_REG, 0, SYSTIMER_TARGET1_PERIOD_MODE_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + [15] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_SYSTIMER_LINK(0x0f), SYSTIMER_TARGET1_CONF_REG, SYSTIMER_TARGET1_PERIOD_MODE_M, SYSTIMER_TARGET1_PERIOD_MODE_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + + [16] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_SYSTIMER_LINK(0x10), SYSTIMER_TARGET2_CONF_REG, 0, SYSTIMER_TARGET2_PERIOD_MODE_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + + [17] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SYSTIMER_LINK(0x11), SYSTIMER_CONF_REG, SYSTIMER_CONF_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, /* Systimer work enable */ + [18] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SYSTIMER_LINK(0x12), SYSTIMER_INT_ENA_REG, SYSTIMER_INT_ENA_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) } /* Systimer intr enable */ + }; + + esp_err_t err = sleep_retention_entries_create(systimer_regs_retention, ARRAY_SIZE(systimer_regs_retention), SLEEP_RETENTION_PERIPHERALS_PRIORITY_DEFAULT, SLEEP_RETENTION_MODULE_SYSTIMER); + ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for digital peripherals (SysTimer) retention"); + ESP_LOGI(TAG, "SysTimer sleep retention initialization"); + return ESP_OK; +} + +esp_err_t sleep_peripheral_retention_init(void) +{ + esp_err_t err; + err = sleep_peripheral_intr_matrix_retention_init(); + if(err) goto error; + err = sleep_peripheral_hp_system_retention_init(); + if(err) goto error; + err = sleep_peripheral_tee_apm_retention_init(); + if(err) goto error; + err = sleep_peripheral_uart0_retention_init(); + if(err) goto error; + err = sleep_peripheral_tg0_retention_init(); + if(err) goto error; + err = sleep_peripheral_iomux_retention_init(); + if(err) goto error; + err = sleep_peripheral_spimem_retention_init(); + if(err) goto error; + err = sleep_peripheral_systimer_retention_init(); + +error: + return err; +} + +bool IRAM_ATTR peripheral_domain_pd_allowed(void) +{ + const uint32_t modules = sleep_retention_get_modules(); + const uint32_t mask = (const uint32_t) ( + SLEEP_RETENTION_MODULE_INTR_MATRIX | \ + SLEEP_RETENTION_MODULE_HP_SYSTEM | \ + SLEEP_RETENTION_MODULE_TEE_APM | \ + SLEEP_RETENTION_MODULE_UART0 | \ + SLEEP_RETENTION_MODULE_TG0 | \ + SLEEP_RETENTION_MODULE_IOMUX | \ + SLEEP_RETENTION_MODULE_SPIMEM | \ + SLEEP_RETENTION_MODULE_SYSTIMER); + return ((modules & mask) == mask); +} + +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP +ESP_SYSTEM_INIT_FN(sleep_peripheral_startup_init, BIT(0), 107) +{ + sleep_peripheral_retention_init(); + return ESP_OK; +} +#endif diff --git a/components/esp_hw_support/sleep_retention.c b/components/esp_hw_support/sleep_retention.c new file mode 100644 index 0000000000..7a79204770 --- /dev/null +++ b/components/esp_hw_support/sleep_retention.c @@ -0,0 +1,463 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include "esp_attr.h" +#include "esp_log.h" +#include "esp_heap_caps.h" +#include "soc/soc_caps.h" +#include "esp_private/esp_regdma.h" +#include "esp_private/esp_pau.h" +#include "esp_private/sleep_retention.h" +#include "sdkconfig.h" +#include "esp_pmu.h" + + +static __attribute__((unused)) const char *TAG = "sleep"; + +/** + * Internal structure which holds all requested sleep retention parameters + */ +typedef struct { + /* The hardware retention module (REGDMA and PMU) uses 4 linked lists to + * record the hardware context information that needs to be backed up and + * restored when switching between different power states. The 4 linked + * lists are linked by 8 types of nodes. The 4 linked lists can reuse some + * nodes with each other, or separate their own unique nodes after branch + * type nodes. + * The REGDMA module iterates the entire linked list from the head of a + * linked list and backs up and restores the corresponding register context + * information according to the configuration information of the linked list + * nodes. + * The PMU module triggers REGDMA to use the corresponding linked list when + * swtiching between different power states. For example: + * + * Current power state Next power state This entry will be used by REGDMA + * PMU_HP_ACTIVE PMU_HP_SLEEP entry0 + * PMU_HP_SLEEP PMU_HP_ACTIVE entry0 + * PMU_HP_MODEM PMU_HP_SLEEP entry1 + * PMU_HP_SLEEP PMU_HP_MODEM entry1 + * PMU_HP_MODEM PMU_HP_ACTIVE entry2 + * + * +--------+ +-------------------------+ +-------------+ +-----------+ +--------+ +-----+ + * entry2 -> | | -> | WiFi MAC Minimum System | -> | | -------------------------> | | -> | | -> | End | + * | SOC | +-------------------------+ | Digital | | Bluetooth | | Zigbee | +-----+ + * | System | +--------+ | Peripherals | +------+ +------+ | / BLE | | | +-----+ + * entry0 -> | | ----------> | | ---------> | | -> | | -> | | -> | | -> | | -> | End | + * +--------+ | Modem | +-------------+ | WiFi | | WiFi | +-----------+ +--------+ +-----+ + * | System | | MAC | | BB | +-----+ + * entry1 ------------------------> | |-----------------------------> | | -> | | -> | End | + * +--------+ +------+ +------+ +-----+ + */ +#define SLEEP_RETENTION_REGDMA_LINK_NR_PRIORITIES (8u) +#define SLEEP_RETENTION_REGDMA_LINK_HIGHEST_PRIORITY (0) +#define SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY (SLEEP_RETENTION_REGDMA_LINK_NR_PRIORITIES - 1) + struct { + sleep_retention_entries_t entries; + uint32_t entries_bitmap: REGDMA_LINK_ENTRY_NUM, + runtime_bitmap: REGDMA_LINK_ENTRY_NUM, + reserved: 32-(2*REGDMA_LINK_ENTRY_NUM); + void *entries_tail; + } lists[SLEEP_RETENTION_REGDMA_LINK_NR_PRIORITIES]; + _lock_t lock; + regdma_link_priority_t highpri; + uint32_t modules; +} sleep_retention_t; + +static DRAM_ATTR __attribute__((unused)) sleep_retention_t s_retention = { .highpri = (uint8_t)-1, .modules = 0 }; + +#define SLEEP_RETENTION_ENTRY_BITMAP_MASK (BIT(REGDMA_LINK_ENTRY_NUM) - 1) +#define SLEEP_RETENTION_ENTRY_BITMAP(bitmap) ((bitmap) & SLEEP_RETENTION_ENTRY_BITMAP_MASK) + +static esp_err_t sleep_retention_entries_create_impl(const sleep_retention_entries_config_t retent[], int num, regdma_link_priority_t priority, int module); +static void sleep_retention_entries_join(void); + +static inline bool sleep_retention_entries_require_branch(uint32_t owner, uint32_t runtime_bitmap) +{ + bool use_new_entry = SLEEP_RETENTION_ENTRY_BITMAP(owner & ~runtime_bitmap) ? true : false; + bool intersection_exist = SLEEP_RETENTION_ENTRY_BITMAP(owner & runtime_bitmap) ? true : false; + return use_new_entry && intersection_exist; +} + +static esp_err_t sleep_retention_entries_check_and_create_default(uint32_t owner, uint32_t runtime_bitmap, uint32_t entries_bitmap, regdma_link_priority_t priority, uint32_t module) +{ + assert(sleep_retention_entries_require_branch(owner, runtime_bitmap)); + + static sleep_retention_entries_config_t dummy = { REGDMA_LINK_WAIT_INIT(0xffff, 0, 0, 0, 1, 1), 0 }; + dummy.owner = SLEEP_RETENTION_ENTRY_BITMAP(owner & ~entries_bitmap); + if (dummy.owner) { + return sleep_retention_entries_create_impl(&dummy, 1, priority, module); + } + return ESP_OK; +} + +static esp_err_t sleep_retention_entries_check_and_create_final_default(void) +{ + static const sleep_retention_entries_config_t final_dummy = { REGDMA_LINK_WAIT_INIT(0xffff, 0, 0, 0, 1, 1), SLEEP_RETENTION_ENTRY_BITMAP_MASK }; + + esp_err_t err = ESP_OK; + _lock_acquire_recursive(&s_retention.lock); + if (s_retention.lists[SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY].entries_bitmap == 0) { + err = sleep_retention_entries_create_impl(&final_dummy, 1, SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY, 0); + } + _lock_release_recursive(&s_retention.lock); + return err; +} + +static void sleep_retention_entries_update(uint32_t owner, void *new_link, regdma_link_priority_t priority) +{ + _lock_acquire_recursive(&s_retention.lock); + sleep_retention_entries_t retention_entries = { + (owner & BIT(0)) ? new_link : s_retention.lists[priority].entries[0], + (owner & BIT(1)) ? new_link : s_retention.lists[priority].entries[1], + (owner & BIT(2)) ? new_link : s_retention.lists[priority].entries[2], + (owner & BIT(3)) ? new_link : s_retention.lists[priority].entries[3] + }; + if (s_retention.lists[priority].entries_bitmap == 0) { + s_retention.lists[priority].entries_tail = new_link; + } + memcpy(s_retention.lists[priority].entries, retention_entries, sizeof(sleep_retention_entries_t)); + s_retention.lists[priority].runtime_bitmap = owner; + s_retention.lists[priority].entries_bitmap |= owner; + _lock_release_recursive(&s_retention.lock); +} + +static void * sleep_retention_entries_try_create(const regdma_link_config_t *config, uint32_t owner, regdma_link_priority_t priority, uint32_t module) +{ + void *link = NULL; + assert(owner > 0 && owner < BIT(REGDMA_LINK_ENTRY_NUM)); + + _lock_acquire_recursive(&s_retention.lock); + if (sleep_retention_entries_require_branch(owner, s_retention.lists[priority].runtime_bitmap)) { + if (sleep_retention_entries_check_and_create_default(owner, s_retention.lists[priority].runtime_bitmap, + s_retention.lists[priority].entries_bitmap, priority, module) == ESP_OK) { /* branch node can't as tail node */ + link = regdma_link_init_safe( + config, true, module, + (owner & BIT(0)) ? s_retention.lists[priority].entries[0] : NULL, + (owner & BIT(1)) ? s_retention.lists[priority].entries[1] : NULL, + (owner & BIT(2)) ? s_retention.lists[priority].entries[2] : NULL, + (owner & BIT(3)) ? s_retention.lists[priority].entries[3] : NULL + ); + } + } else { + link = regdma_link_init_safe(config, false, module, s_retention.lists[priority].entries[__builtin_ffs(owner) - 1]); + } + _lock_release_recursive(&s_retention.lock); + return link; +} + +static void * sleep_retention_entries_try_create_bonding(const regdma_link_config_t *config, uint32_t owner, regdma_link_priority_t priority, uint32_t module) +{ + assert(owner > 0 && owner < BIT(REGDMA_LINK_ENTRY_NUM)); + _lock_acquire_recursive(&s_retention.lock); + void *link = regdma_link_init_safe( + config, true, module, + (owner & BIT(0)) ? s_retention.lists[priority].entries[0] : NULL, + (owner & BIT(1)) ? s_retention.lists[priority].entries[1] : NULL, + (owner & BIT(2)) ? s_retention.lists[priority].entries[2] : NULL, + (owner & BIT(3)) ? s_retention.lists[priority].entries[3] : NULL + ); + _lock_release_recursive(&s_retention.lock); + return link; +} + +static void sleep_retention_entries_stats(void) +{ + _lock_acquire_recursive(&s_retention.lock); + if (s_retention.highpri >= SLEEP_RETENTION_REGDMA_LINK_HIGHEST_PRIORITY && s_retention.highpri <= SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY) { + for (int entry = 0; entry < ARRAY_SIZE(s_retention.lists[s_retention.highpri].entries); entry++) { + regdma_link_stats(s_retention.lists[s_retention.highpri].entries[entry], entry); + } + } + _lock_release_recursive(&s_retention.lock); +} + +#if REGDMA_LINK_DBG +void sleep_retention_entries_show_memories(void) +{ + if (&s_retention.lock) { + _lock_acquire_recursive(&s_retention.lock); + if (s_retention.highpri >= SLEEP_RETENTION_REGDMA_LINK_HIGHEST_PRIORITY && s_retention.highpri <= SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY) { + for (int entry = 0; entry < ARRAY_SIZE(s_retention.lists[s_retention.highpri].entries); entry++) { + ESP_LOGW(TAG, "Print sleep retention entries[%d] memories:", entry); + regdma_link_show_memories(s_retention.lists[s_retention.highpri].entries[entry], entry); + } + } + _lock_release_recursive(&s_retention.lock); + } +} +#endif + +void * sleep_retention_find_link_by_id(int id) +{ + void *link = NULL; + if (&s_retention.lock) { + _lock_acquire_recursive(&s_retention.lock); + if (s_retention.highpri >= SLEEP_RETENTION_REGDMA_LINK_HIGHEST_PRIORITY && + s_retention.highpri <= SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY) { + for (int entry = 0; (link == NULL && entry < ARRAY_SIZE(s_retention.lists[s_retention.highpri].entries)); entry++) { + link = regdma_find_link_by_id(s_retention.lists[s_retention.highpri].entries[entry], entry, id); + } + } + _lock_release_recursive(&s_retention.lock); + } + return link; +} + +static uint32_t sleep_retention_entries_owner_bitmap(sleep_retention_entries_t *entries, sleep_retention_entries_t *tails) +{ + uint32_t owner = 0; + _lock_acquire_recursive(&s_retention.lock); + for (int entry = 0; entry < ARRAY_SIZE(*entries); entry++) { + owner |= regdma_link_get_owner_bitmap((*entries)[entry], (*tails)[entry], entry); + } + _lock_release_recursive(&s_retention.lock); + return owner; +} + +static bool sleep_retention_entries_get_destroy_context(regdma_link_priority_t priority, uint32_t module, sleep_retention_entries_t *destroy_entries, void **destroy_tail, sleep_retention_entries_t *next_entries, void **prev_tail) +{ + bool exist = false; + sleep_retention_entries_t destroy_tails, prev_tails; + + memset(&destroy_tails, 0, sizeof(sleep_retention_entries_t)); + memset(&prev_tails, 0, sizeof(sleep_retention_entries_t)); + + _lock_acquire_recursive(&s_retention.lock); + for (int entry = 0; entry < ARRAY_SIZE(s_retention.lists[priority].entries); entry++) { + (*destroy_entries)[entry] = regdma_find_module_link_head( + s_retention.lists[priority].entries[entry], s_retention.lists[priority].entries_tail, entry, module); + destroy_tails [entry] = regdma_find_module_link_tail( + s_retention.lists[priority].entries[entry], s_retention.lists[priority].entries_tail, entry, module); + (*next_entries) [entry] = regdma_find_next_module_link_head( + s_retention.lists[priority].entries[entry], s_retention.lists[priority].entries_tail, entry, module); + prev_tails [entry] = regdma_find_prev_module_link_tail( + s_retention.lists[priority].entries[entry], s_retention.lists[priority].entries_tail, entry, module); + if ((*destroy_entries)[entry] && destroy_tails[entry]) { + exist = true; + } + assert(destroy_tails[entry] == destroy_tails[0]); + assert(prev_tails[entry] == prev_tails[0]); + } + *destroy_tail = destroy_tails[0]; + *prev_tail = prev_tails[0]; + _lock_release_recursive(&s_retention.lock); + return exist; +} + +static void sleep_retention_entries_context_update(regdma_link_priority_t priority) +{ + _lock_acquire_recursive(&s_retention.lock); + sleep_retention_entries_t tails = { + s_retention.lists[priority].entries_tail, s_retention.lists[priority].entries_tail, + s_retention.lists[priority].entries_tail, s_retention.lists[priority].entries_tail + }; + s_retention.lists[priority].entries_bitmap = sleep_retention_entries_owner_bitmap(&s_retention.lists[priority].entries, &tails); + s_retention.lists[priority].runtime_bitmap = sleep_retention_entries_owner_bitmap(&s_retention.lists[priority].entries, &s_retention.lists[priority].entries); + _lock_release_recursive(&s_retention.lock); +} + +static bool sleep_retention_entries_dettach(regdma_link_priority_t priority, sleep_retention_entries_t *destroy_entries, void *destroy_tail, sleep_retention_entries_t *next_entries, void *prev_tail) +{ + _lock_acquire_recursive(&s_retention.lock); + bool is_head = (memcmp(destroy_entries, &s_retention.lists[priority].entries, sizeof(sleep_retention_entries_t)) == 0); + bool is_tail = (destroy_tail == s_retention.lists[priority].entries_tail); + + if (is_head && is_tail) { + memset(s_retention.lists[priority].entries, 0, sizeof(sleep_retention_entries_t)); + s_retention.lists[priority].entries_tail = NULL; + } else if (is_head) { + memcpy(&s_retention.lists[priority].entries, next_entries, sizeof(sleep_retention_entries_t)); + } else if (is_tail) { + s_retention.lists[priority].entries_tail = prev_tail; + } else { + regdma_link_update_next_safe(prev_tail, (*next_entries)[0], (*next_entries)[1], (*next_entries)[2], (*next_entries)[3]); + } + sleep_retention_entries_context_update(priority); + + regdma_link_update_next_safe(destroy_tail, NULL, NULL, NULL, NULL); + _lock_release_recursive(&s_retention.lock); + return (is_head || is_tail); +} + +static void sleep_retention_entries_destroy_wrapper(sleep_retention_entries_t *destroy_entries) +{ + for (int entry = 0; entry < ARRAY_SIZE(*destroy_entries); entry++) { + regdma_link_destroy((*destroy_entries)[entry], entry); + } +} + +static void sleep_retention_entries_check_and_distroy_final_default(void) +{ + _lock_acquire_recursive(&s_retention.lock); + assert(s_retention.highpri == SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY); + assert(s_retention.modules == 0); + sleep_retention_entries_destroy_wrapper(&s_retention.lists[SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY].entries); + _lock_release_recursive(&s_retention.lock); +} + +static void sleep_retention_entries_all_destroy_wrapper(uint32_t module) +{ + void *destroy_tail = NULL, *prev_tail = NULL; + sleep_retention_entries_t destroy_entries, next_entries; + + memset(&destroy_entries, 0, sizeof(sleep_retention_entries_t)); + memset(&next_entries, 0, sizeof(sleep_retention_entries_t)); + + _lock_acquire_recursive(&s_retention.lock); + regdma_link_priority_t priority = 0; + do { + bool exist = sleep_retention_entries_get_destroy_context(priority, module, &destroy_entries, &destroy_tail, &next_entries, &prev_tail); + if (s_retention.lists[priority].entries_bitmap && exist) { + if (sleep_retention_entries_dettach(priority, &destroy_entries, destroy_tail, &next_entries, prev_tail)) { + sleep_retention_entries_join(); + } + sleep_retention_entries_destroy_wrapper(&destroy_entries); + } else { + priority++; + } + } while (priority < SLEEP_RETENTION_REGDMA_LINK_NR_PRIORITIES); + s_retention.modules &= ~module; + _lock_release_recursive(&s_retention.lock); +} + +void sleep_retention_entries_destroy(int module) +{ + assert(module != 0); + if (&s_retention.lock) { + _lock_acquire_recursive(&s_retention.lock); + sleep_retention_entries_join(); + sleep_retention_entries_stats(); + sleep_retention_entries_all_destroy_wrapper(module); + if (s_retention.modules == 0) { + sleep_retention_entries_check_and_distroy_final_default(); + pmu_sleep_disable_regdma_backup(); + memset((void *)s_retention.lists, 0, sizeof(s_retention.lists)); + s_retention.highpri = (uint8_t)-1; + _lock_release_recursive(&s_retention.lock); + _lock_close_recursive(&s_retention.lock); + s_retention.lock = NULL; + return; + } + _lock_acquire_recursive(&s_retention.lock); + } +} + +static esp_err_t sleep_retention_entries_create_impl(const sleep_retention_entries_config_t retent[], int num, regdma_link_priority_t priority, int module) +{ + _lock_acquire_recursive(&s_retention.lock); + for (int i = num - 1; i >= 0; i--) { + void *link = sleep_retention_entries_try_create(&retent[i].config, retent[i].owner, priority, module); + if (link == NULL) { + _lock_release_recursive(&s_retention.lock); + sleep_retention_entries_destroy(module); + return ESP_ERR_NO_MEM; + } + sleep_retention_entries_update(retent[i].owner, link, priority); + } + _lock_release_recursive(&s_retention.lock); + return ESP_OK; +} + +static esp_err_t sleep_retention_entries_create_bonding(regdma_link_priority_t priority, uint32_t module) +{ + static const sleep_retention_entries_config_t bonding_dummy = { REGDMA_LINK_WAIT_INIT(0xffff, 0, 0, 0, 1, 1), SLEEP_RETENTION_ENTRY_BITMAP_MASK }; + + _lock_acquire_recursive(&s_retention.lock); + void *link = sleep_retention_entries_try_create_bonding(&bonding_dummy.config, bonding_dummy.owner, priority, module); + if (link == NULL) { + _lock_release_recursive(&s_retention.lock); + sleep_retention_entries_destroy(module); + return ESP_ERR_NO_MEM; + } + sleep_retention_entries_update(bonding_dummy.owner, link, priority); + _lock_release_recursive(&s_retention.lock); + return ESP_OK; +} + +static void sleep_retention_entries_join(void) +{ + void *entries_tail = NULL; + _lock_acquire_recursive(&s_retention.lock); + s_retention.highpri = SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY; + for (regdma_link_priority_t priority = 0; priority < SLEEP_RETENTION_REGDMA_LINK_NR_PRIORITIES; priority++) { + if (s_retention.lists[priority].entries_bitmap == 0) continue; + if (priority < s_retention.highpri) { s_retention.highpri = priority; } + if (entries_tail) { + regdma_link_update_next_safe( + entries_tail, + s_retention.lists[priority].entries[0], + s_retention.lists[priority].entries[1], + s_retention.lists[priority].entries[2], + s_retention.lists[priority].entries[3] + ); + } + entries_tail = s_retention.lists[priority].entries_tail; + } + pau_regdma_set_entry_link_addr(&(s_retention.lists[s_retention.highpri].entries)); + _lock_release_recursive(&s_retention.lock); +} + +static esp_err_t sleep_retention_entries_create_wrapper(const sleep_retention_entries_config_t retent[], int num, regdma_link_priority_t priority, uint32_t module) +{ + _lock_acquire_recursive(&s_retention.lock); + esp_err_t err = sleep_retention_entries_create_bonding(priority, module); + if(err) goto error; + err = sleep_retention_entries_create_impl(retent, num, priority, module); + if(err) goto error; + err = sleep_retention_entries_create_bonding(priority, module); + if(err) goto error; + s_retention.modules |= module; + sleep_retention_entries_join(); + +error: + _lock_release_recursive(&s_retention.lock); + return err; +} + +esp_err_t sleep_retention_entries_create(const sleep_retention_entries_config_t retent[], int num, regdma_link_priority_t priority, int module) +{ + if (!(retent && num > 0 && (priority < SLEEP_RETENTION_REGDMA_LINK_NR_PRIORITIES) && (module != 0))) { + return ESP_ERR_INVALID_ARG; + } + if (s_retention.lock == NULL) { + _lock_init_recursive(&s_retention.lock); + if (s_retention.lock == NULL) { + ESP_LOGE(TAG, "Create sleep retention lock failed"); + return ESP_ERR_NO_MEM; + } + } + esp_err_t err = sleep_retention_entries_check_and_create_final_default(); + if (err) goto error; + err = sleep_retention_entries_create_wrapper(retent, num, priority, module); + if (err) goto error; + pmu_sleep_enable_regdma_backup(); + +error: + return err; +} + +void sleep_retention_entries_get(sleep_retention_entries_t *entries) +{ + memset(entries, 0, sizeof(sleep_retention_entries_t)); + if (&s_retention.lock) { + _lock_acquire_recursive(&s_retention.lock); + if (s_retention.highpri >= SLEEP_RETENTION_REGDMA_LINK_HIGHEST_PRIORITY && + s_retention.highpri <= SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY) { + memcpy(entries, &s_retention.lists[s_retention.highpri].entries, sizeof(sleep_retention_entries_t)); + } + _lock_release_recursive(&s_retention.lock); + } +} + +uint32_t IRAM_ATTR sleep_retention_get_modules(void) +{ + return s_retention.modules; +} diff --git a/components/esp_pm/Kconfig b/components/esp_pm/Kconfig index dbb638c717..ee57c3d2dd 100644 --- a/components/esp_pm/Kconfig +++ b/components/esp_pm/Kconfig @@ -104,6 +104,15 @@ menu "Power Management" If enabled, the I/D-cache tag memory will be retained in light sleep. Depending on the the cache configuration, if this option is enabled, it will consume up to 9 KB of internal RAM. + config PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + bool "Power down Digital Peripheral in light sleep" + depends on SOC_PAU_SUPPORTED + default n #TODO: enable by default if periph init/deinit management supported (WIFI-5252) + help + If enabled, the minimum digital peripheral register context to keep the system running is saved + and will reduce sleep current consumption by about 100 uA. enabling this option will consume + at least 4.55 KB of internal RAM. + config PM_UPDATE_CCOMPARE_HLI_WORKAROUND bool default y if PM_ENABLE && BTDM_CTRL_HLI diff --git a/components/esp_system/system_init_fn.txt b/components/esp_system/system_init_fn.txt index cd0fa76947..e8454171af 100644 --- a/components/esp_system/system_init_fn.txt +++ b/components/esp_system/system_init_fn.txt @@ -18,6 +18,8 @@ # esp_sleep doesn't have init dependencies 105: esp_sleep_startup_init in components/esp_hw_support/sleep_gpio.c on BIT(0) +106: sleep_clock_startup_init in components/esp_hw_support/sleep_clock.c on BIT(0) +107: sleep_peripheral_startup_init in components/esp_hw_support/sleep_peripheral.c on BIT(0) # app_trace has to be initialized before systemview 115: esp_apptrace_init in components/app_trace/app_trace.c on ESP_SYSTEM_INIT_ALL_CORES diff --git a/components/hal/CMakeLists.txt b/components/hal/CMakeLists.txt index 8f52010fdc..12a46d786e 100644 --- a/components/hal/CMakeLists.txt +++ b/components/hal/CMakeLists.txt @@ -131,6 +131,10 @@ if(NOT BOOTLOADER_BUILD) list(APPEND srcs "${target}/lp_timer_hal.c") endif() + if(CONFIG_SOC_PAU_SUPPORTED) + list(APPEND srcs "${target}/pau_hal.c") + endif() + if(CONFIG_SOC_BOD_SUPPORTED) list(APPEND srcs "brownout_hal.c") endif() diff --git a/components/hal/esp32c6/include/hal/clk_gate_ll.h b/components/hal/esp32c6/include/hal/clk_gate_ll.h index df4004c48c..4e221908e3 100644 --- a/components/hal/esp32c6/include/hal/clk_gate_ll.h +++ b/components/hal/esp32c6/include/hal/clk_gate_ll.h @@ -74,6 +74,8 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph) return PCR_TSENS_CLK_EN; case PERIPH_SDIO_SLAVE_MODULE: return PCR_SDIO_SLAVE_CLK_EN; + case PERIPH_REGDMA_MODULE: + return PCR_REGDMA_CLK_EN; // case PERIPH_RNG_MODULE: // return PCR_WIFI_CLK_RNG_EN; // case PERIPH_WIFI_MODULE: @@ -163,6 +165,8 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en return PCR_DS_RST_EN; case PERIPH_SDIO_SLAVE_MODULE: return PCR_SDIO_SLAVE_RST_EN; + case PERIPH_REGDMA_MODULE: + return PCR_REGDMA_RST_EN; // case PERIPH_RNG_MODULE: // return PCR_WIFI_CLK_RNG_EN; // case PERIPH_WIFI_MODULE: @@ -245,6 +249,8 @@ static uint32_t periph_ll_get_clk_en_reg(periph_module_t periph) return PCR_TSENS_CLK_CONF_REG; case PERIPH_SDIO_SLAVE_MODULE: return PCR_SDIO_SLAVE_CONF_REG; + case PERIPH_REGDMA_MODULE: + return PCR_REGDMA_CONF_REG; default: return 0; } @@ -307,6 +313,8 @@ static uint32_t periph_ll_get_rst_en_reg(periph_module_t periph) return PCR_TSENS_CLK_CONF_REG; case PERIPH_SDIO_SLAVE_MODULE: return PCR_SDIO_SLAVE_CONF_REG; + case PERIPH_REGDMA_MODULE: + return PCR_REGDMA_CONF_REG; default: return 0; } diff --git a/components/hal/esp32c6/include/hal/pau_ll.h b/components/hal/esp32c6/include/hal/pau_ll.h new file mode 100644 index 0000000000..8fb18d7146 --- /dev/null +++ b/components/hal/esp32c6/include/hal/pau_ll.h @@ -0,0 +1,155 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// The LL layer for ESP32-C6 PAU(Power Assist Unit) register operations + +#pragma once + +#include +#include +#include "soc/soc.h" +#include "soc/pau_reg.h" +#include "soc/pau_struct.h" +#include "hal/pau_types.h" +#include "hal/assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static inline uint32_t pau_ll_get_regdma_backup_flow_error(pau_dev_t *dev) +{ + return dev->regdma_conf.flow_err; +} + +static inline void pau_ll_select_regdma_entry_link(pau_dev_t *dev, int link) +{ + dev->regdma_conf.link_sel = link; +} + +static inline void pau_ll_set_regdma_entry_link_backup_direction(pau_dev_t *dev, bool to_mem) +{ + dev->regdma_conf.to_mem = to_mem ? 1 : 0; +} + +static inline void pau_ll_set_regdma_entry_link_backup_start_enable(pau_dev_t *dev) +{ + dev->regdma_conf.start = 1; +} + +static inline void pau_ll_set_regdma_entry_link_backup_start_disable(pau_dev_t *dev) +{ + dev->regdma_conf.start = 0; +} + +static inline void pau_ll_set_regdma_select_wifimac_link(pau_dev_t *dev) +{ + dev->regdma_conf.sel_mac = 1; +} + +static inline void pau_ll_set_regdma_deselect_wifimac_link(pau_dev_t *dev) +{ + dev->regdma_conf.sel_mac = 0; +} + +static inline void pau_ll_set_regdma_wifimac_link_backup_direction(pau_dev_t *dev, bool to_mem) +{ + dev->regdma_conf.to_mem_mac = to_mem ? 1 : 0; +} + +static inline void pau_ll_set_regdma_wifimac_link_backup_start_enable(pau_dev_t *dev) +{ + dev->regdma_conf.start_mac = 1; +} + +static inline void pau_ll_set_regdma_wifimac_link_backup_start_disable(pau_dev_t *dev) +{ + dev->regdma_conf.start_mac = 0; +} + +static inline void pau_ll_set_regdma_link0_addr(pau_dev_t *dev, void *link_addr) +{ + dev->regdma_link_0_addr.val = (uint32_t)link_addr; +} + +static inline void pau_ll_set_regdma_link1_addr(pau_dev_t *dev, void *link_addr) +{ + dev->regdma_link_1_addr.val = (uint32_t)link_addr; +} + +static inline void pau_ll_set_regdma_link2_addr(pau_dev_t *dev, void *link_addr) +{ + dev->regdma_link_2_addr.val = (uint32_t)link_addr; +} + +static inline void pau_ll_set_regdma_link3_addr(pau_dev_t *dev, void *link_addr) +{ + dev->regdma_link_3_addr.val = (uint32_t)link_addr; +} + +static inline void pau_ll_set_regdma_wifimac_link_addr(pau_dev_t *dev, void *link_addr) +{ + dev->regdma_link_mac_addr.val = (uint32_t)link_addr; +} + +static inline uint32_t pau_ll_get_regdma_current_link_addr(pau_dev_t *dev) +{ + return dev->regdma_current_link_addr.val; +} + +static inline uint32_t pau_ll_get_regdma_backup_addr(pau_dev_t *dev) +{ + return dev->regdma_backup_addr.val; +} + +static inline uint32_t pau_ll_get_regdma_memory_addr(pau_dev_t *dev) +{ + return dev->regdma_mem_addr.val; +} + +static inline uint32_t pau_ll_get_regdma_intr_raw_signal(pau_dev_t *dev) +{ + return dev->int_raw.val; +} + +static inline uint32_t pau_ll_get_regdma_intr_status(pau_dev_t *dev) +{ + return dev->int_st.val; +} + +static inline void pau_ll_set_regdma_backup_done_intr_enable(pau_dev_t *dev) +{ + dev->int_ena.done_int_ena = 1; +} + +static inline void pau_ll_set_regdma_backup_done_intr_disable(pau_dev_t *dev) +{ + dev->int_ena.done_int_ena = 0; +} + +static inline void pau_ll_set_regdma_backup_error_intr_enable(pau_dev_t *dev) +{ + dev->int_ena.error_int_ena = 1; +} + +static inline void pau_ll_set_regdma_backup_error_intr_disable(pau_dev_t *dev) +{ + dev->int_ena.error_int_ena = 0; +} + +static inline void pau_ll_clear_regdma_backup_done_intr_state(pau_dev_t *dev) +{ + dev->int_clr.done_int_clr = 1; +} + +static inline void pau_ll_clear_regdma_backup_error_intr_state(pau_dev_t *dev) +{ + dev->int_clr.error_int_clr = 1; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32c6/pau_hal.c b/components/hal/esp32c6/pau_hal.c new file mode 100644 index 0000000000..68f6b3a6f2 --- /dev/null +++ b/components/hal/esp32c6/pau_hal.c @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// The HAL layer for PAU (ESP32-C6 specific part) + +#include "soc/soc.h" +#include "esp_attr.h" +#include "hal/pau_hal.h" +#include "hal/pau_types.h" + +void pau_hal_set_regdma_entry_link_addr(pau_hal_context_t *hal, pau_regdma_link_addr_t *link_addr) +{ + pau_ll_set_regdma_link0_addr(hal->dev, (*link_addr)[0]); + pau_ll_set_regdma_link1_addr(hal->dev, (*link_addr)[1]); + pau_ll_set_regdma_link2_addr(hal->dev, (*link_addr)[2]); + /* The link 3 of REGDMA is reserved, PMU state switching will not use + * REGDMA link 3 */ +} + +void pau_hal_start_regdma_modem_link(pau_hal_context_t *hal, bool backup_or_restore) +{ + pau_ll_clear_regdma_backup_done_intr_state(hal->dev); + pau_ll_set_regdma_select_wifimac_link(hal->dev); + pau_ll_set_regdma_wifimac_link_backup_direction(hal->dev, backup_or_restore); + pau_ll_set_regdma_wifimac_link_backup_start_enable(hal->dev); + + while (!(pau_ll_get_regdma_intr_raw_signal(hal->dev) & PAU_DONE_INT_RAW)); +} + +void pau_hal_stop_regdma_modem_link(pau_hal_context_t *hal) +{ + pau_ll_set_regdma_wifimac_link_backup_start_disable(hal->dev); + pau_ll_set_regdma_deselect_wifimac_link(hal->dev); + pau_ll_clear_regdma_backup_done_intr_state(hal->dev); +} + +void pau_hal_start_regdma_extra_link(pau_hal_context_t *hal, bool backup_or_restore) +{ + pau_ll_clear_regdma_backup_done_intr_state(hal->dev); + /* The link 3 of REGDMA is reserved, we use it as an extra linked list to + * provide backup and restore services for BLE, IEEE802.15.4 and possibly + * other modules */ + pau_ll_select_regdma_entry_link(hal->dev, 3); + pau_ll_set_regdma_entry_link_backup_direction(hal->dev, true); + pau_ll_set_regdma_entry_link_backup_start_enable(hal->dev); + + while (!(pau_ll_get_regdma_intr_raw_signal(hal->dev) & PAU_DONE_INT_RAW)); +} + +void pau_hal_stop_regdma_extra_link(pau_hal_context_t *hal) +{ + pau_ll_set_regdma_entry_link_backup_start_disable(hal->dev); + pau_ll_select_regdma_entry_link(hal->dev, 0); /* restore link select to default */ + pau_ll_clear_regdma_backup_done_intr_state(hal->dev); +} diff --git a/components/hal/include/hal/pau_hal.h b/components/hal/include/hal/pau_hal.h new file mode 100644 index 0000000000..403765eef6 --- /dev/null +++ b/components/hal/include/hal/pau_hal.h @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// The HAL layer for PAU (Power Assist Unit) + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "soc/soc_caps.h" +#include "hal/pau_ll.h" +#include "hal/pau_types.h" + +typedef struct { + pau_dev_t *dev; +} pau_hal_context_t; + +/** + * @brief Set regdma entry link address + * + * @param hal regdma hal context + * @param link_addr entry link address value + */ +void pau_hal_set_regdma_entry_link_addr(pau_hal_context_t *hal, pau_regdma_link_addr_t *link_addr); + +/** + * @brief Set regdma modem link address + * + * @param hal regdma hal context + * @param link_addr modem link address value + */ +#define pau_hal_set_regdma_modem_link_addr(hal, addr) pau_ll_set_regdma_wifimac_link_addr((hal)->dev, (addr)) + +/** + * @brief Start transmission on regdma modem link + * + * @param hal regdma hal context + * @param backup_or_restore false:restore true:backup + */ +void pau_hal_start_regdma_modem_link(pau_hal_context_t *hal, bool backup_or_restore); + +/** + * @brief Stop transmission on regdma modem link + * + * @param hal regdma hal context + */ +void pau_hal_stop_regdma_modem_link(pau_hal_context_t *hal); + +/** + * @brief Set regdma extra link address + * + * @param hal regdma hal context + * @param link_addr extra link address value + */ +#define pau_hal_set_regdma_extra_link_addr(hal, addr) pau_ll_set_regdma_link3_addr(hal->dev, (addr)) + +/** + * @brief Start transmission on regdma extra link + * + * @param hal regdma hal context + * @param backup_or_restore false:restore true:backup + */ +void pau_hal_start_regdma_extra_link(pau_hal_context_t *hal, bool backup_or_restore); + +/** + * @brief Stop transmission on regdma extra link + * + * @param hal regdma hal context + */ +void pau_hal_stop_regdma_extra_link(pau_hal_context_t *hal); + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/include/hal/pau_types.h b/components/hal/include/hal/pau_types.h new file mode 100644 index 0000000000..19b4028f5a --- /dev/null +++ b/components/hal/include/hal/pau_types.h @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "sdkconfig.h" +#include "soc/soc_caps.h" + +#define PAU_REGDMA_LINK_NUM (SOC_PM_PAU_LINK_NUM) + +/** + * @brief PAU REGDMA all link address buffer + */ +typedef void * pau_regdma_link_addr_t[PAU_REGDMA_LINK_NUM]; diff --git a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in index bb8ebef7d3..2b18601d65 100644 --- a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in @@ -159,6 +159,10 @@ config SOC_PMU_SUPPORTED bool default y +config SOC_PAU_SUPPORTED + bool + default y + config SOC_LP_TIMER_SUPPORTED bool default y @@ -1015,6 +1019,10 @@ config SOC_PM_SUPPORT_VDDSDIO_PD bool default y +config SOC_PM_SUPPORT_TOP_PD + bool + default y + config SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY bool default y @@ -1023,6 +1031,10 @@ config SOC_PM_CPU_RETENTION_BY_SW bool default y +config SOC_PM_PAU_LINK_NUM + int + default 4 + config SOC_CLK_RC_FAST_SUPPORT_CALIBRATION bool default y diff --git a/components/soc/esp32c6/include/soc/periph_defs.h b/components/soc/esp32c6/include/soc/periph_defs.h index 906d0ff07b..90e8d777fd 100644 --- a/components/soc/esp32c6/include/soc/periph_defs.h +++ b/components/soc/esp32c6/include/soc/periph_defs.h @@ -40,6 +40,7 @@ typedef enum { PERIPH_SYSTIMER_MODULE, PERIPH_SARADC_MODULE, PERIPH_TEMPSENSOR_MODULE, + PERIPH_REGDMA_MODULE, PERIPH_WIFI_MODULE, PERIPH_BT_MODULE, PERIPH_IEEE802154_MODULE, diff --git a/components/soc/esp32c6/include/soc/soc_caps.h b/components/soc/esp32c6/include/soc/soc_caps.h index c2e87b8e37..ba5f5881ca 100644 --- a/components/soc/esp32c6/include/soc/soc_caps.h +++ b/components/soc/esp32c6/include/soc/soc_caps.h @@ -65,6 +65,7 @@ #define SOC_BOD_SUPPORTED 1 #define SOC_APM_SUPPORTED 1 #define SOC_PMU_SUPPORTED 1 +#define SOC_PAU_SUPPORTED 1 #define SOC_LP_TIMER_SUPPORTED 1 /*-------------------------- XTAL CAPS ---------------------------------------*/ @@ -427,11 +428,14 @@ #define SOC_PM_SUPPORT_RC32K_PD (1) #define SOC_PM_SUPPORT_RC_FAST_PD (1) #define SOC_PM_SUPPORT_VDDSDIO_PD (1) +#define SOC_PM_SUPPORT_TOP_PD (1) #define SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY (1) /*!