From cd294435dd03dba92c32b72dad96046b3121cb01 Mon Sep 17 00:00:00 2001 From: hebinglin Date: Wed, 9 Jul 2025 15:36:47 +0800 Subject: [PATCH] change(esp_hw_support): support light sleep with modem domain power down for esp32h4beta5 --- .../lowpower/port/esp32h4/sleep_clock.c | 108 ++++++++++++++++++ .../port/esp32h4/rtc_clk_init.c | 35 ++++++ .../test_apps/sleep_retention/README.md | 4 +- .../hal/esp32h4/include/hal/modem_lpcon_ll.h | 48 ++++++++ .../hal/esp32h4/include/hal/regi2c_ctrl_ll.h | 3 +- components/hal/esp32h4/modem_clock_hal.c | 76 ++++++++++++ .../esp32h4/include/soc/Kconfig.soc_caps.in | 8 ++ components/soc/esp32h4/include/soc/soc_caps.h | 6 +- 8 files changed, 282 insertions(+), 6 deletions(-) create mode 100644 components/esp_hw_support/lowpower/port/esp32h4/sleep_clock.c diff --git a/components/esp_hw_support/lowpower/port/esp32h4/sleep_clock.c b/components/esp_hw_support/lowpower/port/esp32h4/sleep_clock.c new file mode 100644 index 0000000000..9f4b09edc8 --- /dev/null +++ b/components/esp_hw_support/lowpower/port/esp32h4/sleep_clock.c @@ -0,0 +1,108 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_private/sleep_clock.h" +#include "soc/pcr_reg.h" +#include "modem/modem_syscon_reg.h" +#include "modem/modem_lpcon_reg.h" + + +static const char *TAG = "sleep_clock"; + +esp_err_t sleep_clock_system_retention_init(void *arg) +{ + #define N_REGS_PCR() (((PCR_ZERO_DET_CLK_CONF_REG - DR_REG_PCR_BASE) / 4) + 1) + + const static sleep_retention_entries_config_t pcr_regs_retention[] = { + /* Clock configuration retention */ + [0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_PCR_LINK(0x0), DR_REG_PCR_BASE, DR_REG_PCR_BASE, N_REGS_PCR(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [1] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_PCR_LINK(0x2), PCR_BUS_CLK_UPDATE_REG, PCR_BUS_CLOCK_UPDATE, PCR_BUS_CLOCK_UPDATE_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, + [2] = { .config = REGDMA_LINK_WAIT_INIT (REGDMA_PCR_LINK(0x3), PCR_BUS_CLK_UPDATE_REG, 0x0, PCR_BUS_CLOCK_UPDATE_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) } + }; + + esp_err_t err = sleep_retention_entries_create(pcr_regs_retention, ARRAY_SIZE(pcr_regs_retention), REGDMA_LINK_PRI_SYS_CLK, SLEEP_RETENTION_MODULE_CLOCK_SYSTEM); + ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for system (PCR) retention"); + ESP_LOGI(TAG, "System Power, Clock and Reset sleep retention initialization"); + return ESP_OK; + + #undef N_REGS_PCR +} + +#if CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE || CONFIG_IEEE802154_SLEEP_ENABLE +esp_err_t sleep_clock_modem_retention_init(void *arg) +{ + #define N_REGS_SYSCON() (((MODEM_SYSCON_MEM_RF2_CONF_REG - MODEM_SYSCON_TEST_CONF_REG) / 4) + 1) + + const static sleep_retention_entries_config_t modem_regs_retention[] = { + [0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_MODEMSYSCON_LINK(0), MODEM_SYSCON_TEST_CONF_REG, MODEM_SYSCON_TEST_CONF_REG, N_REGS_SYSCON(), 0, 0), .owner = ENTRY(0) | ENTRY(1) }, /* MODEM SYSCON */ + }; + + esp_err_t err = sleep_retention_entries_create(modem_regs_retention, ARRAY_SIZE(modem_regs_retention), REGDMA_LINK_PRI_MODEM_CLK, SLEEP_RETENTION_MODULE_CLOCK_MODEM); + ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for modem (SYSCON) retention, 1 level priority"); + ESP_LOGI(TAG, "Modem Power, Clock and Reset sleep retention initialization"); + return ESP_OK; + + #undef N_REGS_SYSCON +} +#endif + +bool clock_domain_pd_allowed(void) +{ + const sleep_retention_module_bitmap_t inited_modules = sleep_retention_get_inited_modules(); + const sleep_retention_module_bitmap_t created_modules = sleep_retention_get_created_modules(); + const sleep_retention_module_bitmap_t sys_clk_dep_modules = (sleep_retention_module_bitmap_t){ .bitmap[SLEEP_RETENTION_MODULE_SYS_PERIPH >> 5] = BIT(SLEEP_RETENTION_MODULE_SYS_PERIPH % 32) }; + + /* The clock and reset of MODEM (WiFi, BLE and 15.4) modules are managed + * through MODEM_SYSCON, when one or more MODEMs are initialized, it is + * necessary to check the state of CLOCK_MODEM to determine MODEM domain on + * or off. The clock and reset of digital peripherals are managed through + * PCR, with TOP domain similar to MODEM domain. */ + sleep_retention_module_bitmap_t modem_clk_dep_modules = (sleep_retention_module_bitmap_t){ .bitmap = { 0 } }; +#if SOC_BT_SUPPORTED + modem_clk_dep_modules.bitmap[SLEEP_RETENTION_MODULE_BLE_MAC >> 5] |= BIT(SLEEP_RETENTION_MODULE_BLE_MAC % 32); + modem_clk_dep_modules.bitmap[SLEEP_RETENTION_MODULE_BT_BB >> 5] |= BIT(SLEEP_RETENTION_MODULE_BT_BB % 32); +#endif +#if SOC_IEEE802154_SUPPORTED + modem_clk_dep_modules.bitmap[SLEEP_RETENTION_MODULE_802154_MAC >> 5] |= BIT(SLEEP_RETENTION_MODULE_802154_MAC % 32); + modem_clk_dep_modules.bitmap[SLEEP_RETENTION_MODULE_BT_BB >> 5] |= BIT(SLEEP_RETENTION_MODULE_BT_BB % 32); +#endif + + const sleep_retention_module_bitmap_t null_module = (sleep_retention_module_bitmap_t){ .bitmap = { 0 } }; + + sleep_retention_module_bitmap_t mask = (sleep_retention_module_bitmap_t){ .bitmap = { 0 } }; + const sleep_retention_module_bitmap_t system_modules = sleep_retention_module_bitmap_and(inited_modules, sys_clk_dep_modules); + if (!sleep_retention_module_bitmap_eq(system_modules, null_module)) { + mask.bitmap[SLEEP_RETENTION_MODULE_CLOCK_SYSTEM >> 5] |= BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM % 32); + } +#if SOC_BT_SUPPORTED || SOC_IEEE802154_SUPPORTED + const sleep_retention_module_bitmap_t modem_modules = sleep_retention_module_bitmap_and(inited_modules, modem_clk_dep_modules); + if (!sleep_retention_module_bitmap_eq(modem_modules, null_module)) { + mask.bitmap[SLEEP_RETENTION_MODULE_CLOCK_MODEM >> 5] |= BIT(SLEEP_RETENTION_MODULE_CLOCK_MODEM % 32); + } +#endif + + const sleep_retention_module_bitmap_t clock_domain_inited_modules = sleep_retention_module_bitmap_and(inited_modules, mask); + const sleep_retention_module_bitmap_t clock_domain_created_modules = sleep_retention_module_bitmap_and(created_modules, mask); + return sleep_retention_module_bitmap_eq(clock_domain_inited_modules, clock_domain_created_modules); +} + +ESP_SYSTEM_INIT_FN(sleep_clock_startup_init, SECONDARY, BIT(0), 106) +{ + sleep_retention_module_init_param_t init_param = { + .cbs = { .create = { .handle = sleep_clock_system_retention_init, .arg = NULL } }, + .attribute = SLEEP_RETENTION_MODULE_ATTR_PASSIVE + }; + sleep_retention_module_init(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM, &init_param); + +#if CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE || CONFIG_IEEE802154_SLEEP_ENABLE + init_param = (sleep_retention_module_init_param_t) { + .cbs = { .create = { .handle = sleep_clock_modem_retention_init, .arg = NULL } }, + .attribute = SLEEP_RETENTION_MODULE_ATTR_PASSIVE + }; + sleep_retention_module_init(SLEEP_RETENTION_MODULE_CLOCK_MODEM, &init_param); +#endif + return ESP_OK; +} diff --git a/components/esp_hw_support/port/esp32h4/rtc_clk_init.c b/components/esp_hw_support/port/esp32h4/rtc_clk_init.c index c391e8593c..f8ffb8b300 100644 --- a/components/esp_hw_support/port/esp32h4/rtc_clk_init.c +++ b/components/esp_hw_support/port/esp32h4/rtc_clk_init.c @@ -19,14 +19,49 @@ #include "hal/clk_tree_ll.h" #include "hal/pmu_ll.h" #include "soc/pmu_reg.h" +#if SOC_MODEM_CLOCK_SUPPORTED +#include "hal/modem_syscon_ll.h" +#include "hal/modem_lpcon_ll.h" +#endif #include "pmu_param.h" static const char *TAG = "rtc_clk_init"; +/** + * Initialize the ICG map of some modem clock domains in the PMU_ACTIVE state + * + * A pre-initialization interface is used to initialize the ICG map of the + * MODEM_APB, I2C_MST and LP_APB clock domains in the PMU_ACTIVE state, and + * disable the clock gating of these clock domains in the PMU_ACTIVE state, + * because the system clock source (PLL) in the system boot up process needs + * to use the i2c master peripheral. + * + * ICG map of all modem clock domains under different power states (PMU_ACTIVE, + * PMU_MODEM and PMU_SLEEP) will be initialized in esp_perip_clk_init(). + */ +static void rtc_clk_modem_clock_domain_active_state_icg_map_preinit(void) +{ + /* Configure modem ICG code in PMU_ACTIVE state */ + pmu_ll_hp_set_icg_modem(&PMU, PMU_MODE_HP_ACTIVE, PMU_HP_ICG_MODEM_CODE_ACTIVE); + +#if SOC_MODEM_CLOCK_SUPPORTED + /* Disable clock gating for MODEM_APB, I2C_MST and LP_APB clock domains in PMU_ACTIVE state */ + modem_syscon_ll_set_modem_apb_icg_bitmap(&MODEM_SYSCON, BIT(PMU_HP_ICG_MODEM_CODE_ACTIVE)); + modem_lpcon_ll_set_i2c_master_icg_bitmap(&MODEM_LPCON, BIT(PMU_HP_ICG_MODEM_CODE_ACTIVE)); + modem_lpcon_ll_set_lp_apb_icg_bitmap(&MODEM_LPCON, BIT(PMU_HP_ICG_MODEM_CODE_ACTIVE)); +#endif + + /* Software trigger force update modem ICG code and ICG switch */ + pmu_ll_imm_update_dig_icg_modem_code(&PMU, true); + pmu_ll_imm_update_dig_icg_switch(&PMU, true); +} + void rtc_clk_init(rtc_clk_config_t cfg) { rtc_cpu_freq_config_t old_config, new_config; + rtc_clk_modem_clock_domain_active_state_icg_map_preinit(); + /* Set tuning parameters for RC_FAST and RC_SLOW clocks. * Note: this doesn't attempt to set the clocks to precise frequencies. * Instead, we calibrate these clocks against XTAL frequency later, when necessary. diff --git a/components/esp_hw_support/test_apps/sleep_retention/README.md b/components/esp_hw_support/test_apps/sleep_retention/README.md index 77eda1867c..a688c01bf6 100644 --- a/components/esp_hw_support/test_apps/sleep_retention/README.md +++ b/components/esp_hw_support/test_apps/sleep_retention/README.md @@ -1,2 +1,2 @@ -| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | -| ----------------- | -------- | -------- | --------- | -------- | -------- | +| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H4 | ESP32-P4 | +| ----------------- | -------- | -------- | --------- | -------- | -------- | -------- | diff --git a/components/hal/esp32h4/include/hal/modem_lpcon_ll.h b/components/hal/esp32h4/include/hal/modem_lpcon_ll.h index 20afd4a53e..0a09a3c241 100644 --- a/components/hal/esp32h4/include/hal/modem_lpcon_ll.h +++ b/components/hal/esp32h4/include/hal/modem_lpcon_ll.h @@ -92,6 +92,54 @@ static inline void modem_lpcon_ll_reset_all(modem_lpcon_dev_t *hw) hw->rst_conf.val = 0; } +__attribute__((always_inline)) +static inline uint32_t modem_lpcon_ll_get_wifipwr_icg_bitmap(modem_lpcon_dev_t *hw) +{ + return hw->clk_conf_power_st.clk_wifipwr_st_map; +} + +__attribute__((always_inline)) +static inline void modem_lpcon_ll_set_wifipwr_icg_bitmap(modem_lpcon_dev_t *hw, uint32_t bitmap) +{ + hw->clk_conf_power_st.clk_wifipwr_st_map = bitmap; +} + +__attribute__((always_inline)) +static inline uint32_t modem_lpcon_ll_get_coex_icg_bitmap(modem_lpcon_dev_t *hw) +{ + return hw->clk_conf_power_st.clk_coex_st_map; +} + +__attribute__((always_inline)) +static inline void modem_lpcon_ll_set_coex_icg_bitmap(modem_lpcon_dev_t *hw, uint32_t bitmap) +{ + hw->clk_conf_power_st.clk_coex_st_map = bitmap; +} + +__attribute__((always_inline)) +static inline uint32_t modem_lpcon_ll_get_i2c_master_icg_bitmap(modem_lpcon_dev_t *hw) +{ + return hw->clk_conf_power_st.clk_i2c_mst_st_map; +} + +__attribute__((always_inline)) +static inline void modem_lpcon_ll_set_i2c_master_icg_bitmap(modem_lpcon_dev_t *hw, uint32_t bitmap) +{ + hw->clk_conf_power_st.clk_i2c_mst_st_map = bitmap; +} + +__attribute__((always_inline)) +static inline uint32_t modem_lpcon_ll_get_lp_apb_icg_bitmap(modem_lpcon_dev_t *hw) +{ + return hw->clk_conf_power_st.clk_lp_apb_st_map; +} + +__attribute__((always_inline)) +static inline void modem_lpcon_ll_set_lp_apb_icg_bitmap(modem_lpcon_dev_t *hw, uint32_t bitmap) +{ + hw->clk_conf_power_st.clk_lp_apb_st_map = bitmap; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32h4/include/hal/regi2c_ctrl_ll.h b/components/hal/esp32h4/include/hal/regi2c_ctrl_ll.h index ee3c7c94bc..319c8bfde8 100644 --- a/components/hal/esp32h4/include/hal/regi2c_ctrl_ll.h +++ b/components/hal/esp32h4/include/hal/regi2c_ctrl_ll.h @@ -12,6 +12,7 @@ #include "soc/i2c_ana_mst_reg.h" #include "soc/pmu_reg.h" #include "modem/modem_lpcon_struct.h" +#include "modem/modem_syscon_struct.h" #ifdef __cplusplus extern "C" { @@ -65,7 +66,7 @@ static inline __attribute__((always_inline)) void regi2c_ctrl_ll_master_force_en */ static inline __attribute__((always_inline)) void regi2c_ctrl_ll_master_configure_clock(void) { - // Nothing to configure + MODEM_SYSCON.clk_conf.clk_i2c_mst_sel_160m = 1; } /** diff --git a/components/hal/esp32h4/modem_clock_hal.c b/components/hal/esp32h4/modem_clock_hal.c index 30d180fe03..bc1a70861c 100644 --- a/components/hal/esp32h4/modem_clock_hal.c +++ b/components/hal/esp32h4/modem_clock_hal.c @@ -13,6 +13,82 @@ #include "hal/modem_clock_types.h" #include "hal/assert.h" +typedef enum { + MODEM_CLOCK_XTAL32K_CODE = 0, + MODEM_CLOCK_RC32K_CODE = 1, + MODEM_CLOCK_EXT32K_CODE = 2 +} modem_clock_32k_clk_src_code_t; + +void IRAM_ATTR modem_clock_hal_set_clock_domain_icg_bitmap(modem_clock_hal_context_t *hal, modem_clock_domain_t domain, uint32_t bitmap) +{ + HAL_ASSERT(domain < MODEM_CLOCK_DOMAIN_MAX); + switch (domain) + { + case MODEM_CLOCK_DOMAIN_MODEM_APB: + modem_syscon_ll_set_modem_apb_icg_bitmap(hal->syscon_dev, bitmap); + break; + case MODEM_CLOCK_DOMAIN_MODEM_PERIPH: + modem_syscon_ll_set_modem_periph_icg_bitmap(hal->syscon_dev, bitmap); + break; + case MODEM_CLOCK_DOMAIN_BT: + modem_syscon_ll_set_bt_icg_bitmap(hal->syscon_dev, bitmap); + break; + case MODEM_CLOCK_DOMAIN_MODEM_FE: + modem_syscon_ll_set_fe_icg_bitmap(hal->syscon_dev, bitmap); + break; + case MODEM_CLOCK_DOMAIN_IEEE802154: + modem_syscon_ll_set_ieee802154_icg_bitmap(hal->syscon_dev, bitmap); + break; + case MODEM_CLOCK_DOMAIN_LP_APB: + modem_lpcon_ll_set_lp_apb_icg_bitmap(hal->lpcon_dev, bitmap); + break; + case MODEM_CLOCK_DOMAIN_I2C_MASTER: + modem_lpcon_ll_set_i2c_master_icg_bitmap(hal->lpcon_dev, bitmap); + break; + case MODEM_CLOCK_DOMAIN_COEX: + modem_lpcon_ll_set_coex_icg_bitmap(hal->lpcon_dev, bitmap); + break; + case MODEM_CLOCK_DOMAIN_WIFIPWR: + modem_lpcon_ll_set_wifipwr_icg_bitmap(hal->lpcon_dev, bitmap); + break; + default: + HAL_ASSERT(0); + } +} + +uint32_t IRAM_ATTR modem_clock_hal_get_clock_domain_icg_bitmap(modem_clock_hal_context_t *hal, modem_clock_domain_t domain) +{ + HAL_ASSERT(domain < MODEM_CLOCK_DOMAIN_MAX); + uint32_t bitmap = 0; + switch (domain) + { + case MODEM_CLOCK_DOMAIN_MODEM_APB: + bitmap = modem_syscon_ll_get_modem_apb_icg_bitmap(hal->syscon_dev); + break; + case MODEM_CLOCK_DOMAIN_MODEM_PERIPH: + bitmap = modem_syscon_ll_get_modem_periph_icg_bitmap(hal->syscon_dev); + break; + case MODEM_CLOCK_DOMAIN_BT: + bitmap = modem_syscon_ll_get_bt_icg_bitmap(hal->syscon_dev); + break; + case MODEM_CLOCK_DOMAIN_MODEM_FE: + bitmap = modem_syscon_ll_get_fe_icg_bitmap(hal->syscon_dev); + break; + case MODEM_CLOCK_DOMAIN_IEEE802154: + bitmap = modem_syscon_ll_get_ieee802154_icg_bitmap(hal->syscon_dev); + break; + case MODEM_CLOCK_DOMAIN_LP_APB: + bitmap = modem_lpcon_ll_get_lp_apb_icg_bitmap(hal->lpcon_dev); + break; + case MODEM_CLOCK_DOMAIN_I2C_MASTER: + bitmap = modem_lpcon_ll_get_i2c_master_icg_bitmap(hal->lpcon_dev); + break; + default: + HAL_ASSERT(0); + } + return bitmap; +} + void IRAM_ATTR modem_clock_hal_enable_modem_common_fe_clock(modem_clock_hal_context_t *hal, bool enable) { modem_syscon_ll_enable_fe_apb_clock(hal->syscon_dev, enable); diff --git a/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in index 3384598489..714455f3d5 100644 --- a/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in @@ -887,6 +887,10 @@ config SOC_PM_SUPPORT_CPU_PD bool default y +config SOC_PM_SUPPORT_MODEM_PD + bool + default y + config SOC_PM_SUPPORT_XTAL32K_PD bool default y @@ -915,6 +919,10 @@ config SOC_PM_CPU_RETENTION_BY_SW bool default y +config SOC_PM_MODEM_RETENTION_BY_REGDMA + bool + default y + config SOC_PM_PAU_LINK_NUM int default 4 diff --git a/components/soc/esp32h4/include/soc/soc_caps.h b/components/soc/esp32h4/include/soc/soc_caps.h index 8c6671edcc..b49d9f1bf9 100644 --- a/components/soc/esp32h4/include/soc/soc_caps.h +++ b/components/soc/esp32h4/include/soc/soc_caps.h @@ -517,7 +517,7 @@ // #define SOC_PM_SUPPORT_EXT1_WAKEUP_MODE_PER_PIN (1) /*!