diff --git a/components/esp_hal_pmu/esp32h4/include/hal/pmu_ll.h b/components/esp_hal_pmu/esp32h4/include/hal/pmu_ll.h index 4668db72eb3..a5fb3f36a11 100644 --- a/components/esp_hal_pmu/esp32h4/include/hal/pmu_ll.h +++ b/components/esp_hal_pmu/esp32h4/include/hal/pmu_ll.h @@ -94,6 +94,11 @@ FORCE_INLINE_ATTR void pmu_ll_hp_set_clk_power(pmu_dev_t *hw, pmu_hp_mode_t mode hw->hp_sys[mode].clk_power.val = xpd_flag; } +FORCE_INLINE_ATTR uint32_t pmu_ll_hp_get_clk_power(pmu_dev_t *hw, pmu_hp_mode_t mode) +{ + return hw->hp_sys[mode].clk_power.val; +} + /** * @brief Set the power and isolation of the analog i2c master shared by all the PLLs * @@ -122,6 +127,11 @@ FORCE_INLINE_ATTR void pmu_ll_hp_set_xtalx2_xpd(pmu_dev_t *hw, pmu_hp_mode_t mod hw->hp_sys[mode].xtal.xpd_xtalx2 = xpd_xtalx2; } +FORCE_INLINE_ATTR uint32_t pmu_ll_hp_get_xtalx2_xpd(pmu_dev_t *hw, pmu_hp_mode_t mode) +{ + return hw->hp_sys[mode].xtal.xpd_xtalx2; +} + FORCE_INLINE_ATTR void pmu_ll_hp_set_bias_xpd(pmu_dev_t *hw, pmu_hp_mode_t mode, bool xpd_bias) { hw->hp_sys[mode].bias.xpd_bias = xpd_bias; diff --git a/components/esp_hw_support/linker.lf b/components/esp_hw_support/linker.lf index abb6b0b2e93..2b97ef85ad7 100644 --- a/components/esp_hw_support/linker.lf +++ b/components/esp_hw_support/linker.lf @@ -52,6 +52,7 @@ entries: pmu_sleep_clock_icg:pmu_sleep_clock_icg_config (noflash) if IDF_TARGET_ESP32H4 = y && BT_LE_MODEM_STATE_ENABLED = y: pmu_sleep_power:pmu_sleep_power_analog_wait_config (noflash) + pmu_sleep_power:pmu_sleep_power_clock_config (noflash) sleep_mspi (noflash) if PM_SLP_IRAM_OPT = y && IDF_TARGET_ESP32S31 != y: pmu_param:get_act_lp_dbias (noflash) diff --git a/components/esp_hw_support/port/esp32h4/CMakeLists.txt b/components/esp_hw_support/port/esp32h4/CMakeLists.txt index 8943bdfc0c3..347712bf849 100644 --- a/components/esp_hw_support/port/esp32h4/CMakeLists.txt +++ b/components/esp_hw_support/port/esp32h4/CMakeLists.txt @@ -14,11 +14,9 @@ endif() if(NOT BOOTLOADER_BUILD) list(APPEND srcs "sar_periph_ctrl.c") - -if(CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT) - list(APPEND srcs "pmu_sleep_power.c") -endif() - + if(CONFIG_BT_LE_MODEM_STATE_ENABLED) + list(APPEND srcs "pmu_sleep_power.c") + endif() endif() add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/" "${srcs}") diff --git a/components/esp_hw_support/port/esp32h4/pmu_sleep.c b/components/esp_hw_support/port/esp32h4/pmu_sleep.c index 6b8127e247d..7ea5af4a2c8 100644 --- a/components/esp_hw_support/port/esp32h4/pmu_sleep.c +++ b/components/esp_hw_support/port/esp32h4/pmu_sleep.c @@ -250,7 +250,7 @@ static void pmu_sleep_param_init(pmu_context_t *ctx, const pmu_sleep_param_confi pmu_ll_set_xtal_stable_wait_cycle(ctx->hal->dev, param->hp_lp.xtal_stable_wait_slow_clk_cycle); pmu_ll_set_pll_stable_wait_cycle(ctx->hal->dev, param->hp_sys.pll_stable_wait_cycle); -#if CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT +#if CONFIG_BT_LE_MODEM_STATE_ENABLED && CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT uint16_t ana_wait[ANALOG_WAIT_CTRL_NUM] = { 0x20, // about 1.6us param->hp_sys.analog_wait_target_cycle, @@ -258,6 +258,18 @@ static void pmu_sleep_param_init(pmu_context_t *ctx, const pmu_sleep_param_confi }; pmu_sleep_power_analog_wait_config(ctx->priv, ana_wait); #endif +#if CONFIG_BT_LE_MODEM_STATE_ENABLED && SOC_PM_MODEM_LOCK_CLK_WORKAROUND + pmu_hp_clk_power_reg_t hp_ck = { .val = pmu_ll_hp_get_clk_power(ctx->hal->dev, HP(ACTIVE)) }; + uint32_t xtalx2_xpd = pmu_ll_hp_get_xtalx2_xpd(ctx->hal->dev, HP(ACTIVE)); + pmu_imm_hp_clk_power_reg_t imm = { + .tie_high_xpd_bbpll = hp_ck.xpd_bbpll, + .tie_high_xpd_bbpll_i2c = hp_ck.xpd_bbpll_i2c, + .tie_high_global_bbpll_icg = hp_ck.xpd_bbpll || hp_ck.xpd_bbpll_i2c, + .tie_high_xtalx2 = xtalx2_xpd, + .tie_high_global_xtalx2_icg = xtalx2_xpd, + }; + pmu_sleep_power_clock_config(ctx->priv, imm.val); +#endif } bool pmu_sleep_pll_already_enabled(void) diff --git a/components/esp_hw_support/port/esp32h4/pmu_sleep_power.c b/components/esp_hw_support/port/esp32h4/pmu_sleep_power.c index 123dd01b9ba..f648a4f2022 100644 --- a/components/esp_hw_support/port/esp32h4/pmu_sleep_power.c +++ b/components/esp_hw_support/port/esp32h4/pmu_sleep_power.c @@ -20,22 +20,44 @@ ESP_LOG_ATTR_TAG(TAG, "sleep_power"); #if SOC_PM_MODEM_LOCK_CLK_WORKAROUND +#define XTALX2_TIE_HIGH_MASK (PMU_TIE_HIGH_XTALX2_M | PMU_TIE_HIGH_GLOBAL_XTALX2_ICG_M) +#define XTALX2_TIE_HIGH_ON (PMU_TIE_HIGH_XTALX2 | PMU_TIE_HIGH_GLOBAL_XTALX2_ICG) +#define BBPLL_TIE_HIGH_MASK (PMU_TIE_HIGH_XPD_BBPLL_M | PMU_TIE_HIGH_XPD_BBPLL_I2C_M | PMU_TIE_HIGH_GLOBAL_BBPLL_ICG_M) +#define BBPLL_TIE_HIGH_ON (PMU_TIE_HIGH_XPD_BBPLL | PMU_TIE_HIGH_XPD_BBPLL_I2C | PMU_TIE_HIGH_GLOBAL_BBPLL_ICG) + +typedef struct { + void *regdma_desc[2]; +} pmu_sleep_power_clock_context_t; + static esp_err_t sleep_power_system_retention_init(void *arg) { const static sleep_retention_entries_config_t power_regs_retention[] = { /* During the modem-to-active transition, the MODEM lock keeps the xtalx2, bbpll xpd status unchanged. - * Therefore, if xtalx2, bbpll is disabled in modem state, we need to set xtalx2, bbpll tie-high to enable it in active state. */ - [0] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_POWER_LINK(0), PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_XTALX2 | PMU_TIE_HIGH_GLOBAL_XTALX2_ICG, PMU_TIE_HIGH_XTALX2_M | PMU_TIE_HIGH_GLOBAL_XTALX2_ICG_M, 1, 0), .owner = ENTRY(2) }, - [1] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_POWER_LINK(1), PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_XPD_BBPLL | PMU_TIE_HIGH_XPD_BBPLL_I2C, PMU_TIE_HIGH_XPD_BBPLL_M | PMU_TIE_HIGH_XPD_BBPLL_I2C_M, 1, 0), .owner = ENTRY(2) }, + * Therefore, if xtalx2, bbpll is disabled in modem state, we need to set xtalx2, bbpll tie-high to enable it in active state. + * + * XTALX2 and BBPLL write values are back-filled before sleep by pmu_sleep_power_clock_config(). */ + [0] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_POWER_LINK(0), PMU_IMM_HP_CK_POWER_REG, XTALX2_TIE_HIGH_ON, XTALX2_TIE_HIGH_MASK, 1, 0), .owner = ENTRY(2) }, + [1] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_POWER_LINK(1), PMU_IMM_HP_CK_POWER_REG, BBPLL_TIE_HIGH_ON, BBPLL_TIE_HIGH_MASK, 1, 0), .owner = ENTRY(2) }, }; esp_err_t err = sleep_retention_entries_create(power_regs_retention, ARRAY_SIZE(power_regs_retention), REGDMA_LINK_PRI_POWER, SLEEP_RETENTION_MODULE_POWER); + + pmu_sleep_power_clock_context_t *clk = (pmu_sleep_power_clock_context_t *)arg; + int id_array[ARRAY_SIZE(clk->regdma_desc)] = { REGDMA_POWER_LINK(0), REGDMA_POWER_LINK(1) }; + for (int i = 0; i < ARRAY_SIZE(id_array); i++) { + void *head = sleep_retention_find_link_by_id(id_array[i]); + if (head) { + clk->regdma_desc[i] = head; + } + } + ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for clock power retention"); - ESP_LOGI(TAG, "Clock power sleep retention initialization"); return ESP_OK; } #endif +#if CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT + #define SLEEP_POWER_ANA_WAIT_LINK_BASE 10 #define SLEEP_POWER_ANA_WAIT_LINK_ID(_idx) REGDMA_POWER_LINK((SLEEP_POWER_ANA_WAIT_LINK_BASE + (_idx))) @@ -67,32 +89,53 @@ static esp_err_t sleep_power_analog_wait_ctrl_init(void *arg) return ESP_OK; } +#endif + +typedef struct { +#if SOC_PM_MODEM_LOCK_CLK_WORKAROUND + pmu_sleep_power_clock_context_t clock; +#endif +#if CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT + pmu_sleep_power_ana_wait_context_t ana_wait; +#endif +} pmu_sleep_power_context_t; + static esp_err_t sleep_power_retention_init(void *arg) { + pmu_sleep_power_context_t *ctx = (pmu_sleep_power_context_t *)arg; #if SOC_PM_MODEM_LOCK_CLK_WORKAROUND - ESP_RETURN_ON_ERROR(sleep_power_system_retention_init(arg), TAG, "system retention init failed"); + ESP_RETURN_ON_ERROR(sleep_power_system_retention_init(&ctx->clock), TAG, "system retention init failed"); +#endif +#if CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT + ESP_RETURN_ON_ERROR(sleep_power_analog_wait_ctrl_init(&ctx->ana_wait), TAG, "analog wait ctrl retention init failed"); #endif - ESP_RETURN_ON_ERROR(sleep_power_analog_wait_ctrl_init(arg), TAG, "analog wait ctrl retention init failed"); return ESP_OK; } static esp_err_t sleep_power_retention_deinit(void *arg) { - pmu_sleep_power_ana_wait_context_t *ana_wait_ctx = (pmu_sleep_power_ana_wait_context_t *)arg; - for (int i = 0; i < ANALOG_WAIT_CTRL_NUM; i++) { - ana_wait_ctx->regdma_desc[i] = NULL; + pmu_sleep_power_context_t *ctx = (pmu_sleep_power_context_t *)arg; +#if SOC_PM_MODEM_LOCK_CLK_WORKAROUND + for (int i = 0; i < ARRAY_SIZE(ctx->clock.regdma_desc); i++) { + ctx->clock.regdma_desc[i] = NULL; } +#endif +#if CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT + for (int i = 0; i < ANALOG_WAIT_CTRL_NUM; i++) { + ctx->ana_wait.regdma_desc[i] = NULL; + } +#endif return ESP_OK; } ESP_SYSTEM_INIT_FN(sleep_power_startup_init, SECONDARY, BIT(0), 108) { - static DRAM_ATTR pmu_sleep_power_ana_wait_context_t ana_wait_ctx; + static DRAM_ATTR pmu_sleep_power_context_t power_context = { 0 }; sleep_retention_module_init_param_t init_param = { .cbs = { - .create = { .handle = sleep_power_retention_init, .arg = &ana_wait_ctx }, - .destroy = { .handle = sleep_power_retention_deinit, .arg = &ana_wait_ctx }, + .create = { .handle = sleep_power_retention_init, .arg = &power_context }, + .destroy = { .handle = sleep_power_retention_deinit, .arg = &power_context }, }, .attribute = SLEEP_RETENTION_MODULE_ATTR_PASSIVE | SLEEP_RETENTION_MODULE_ATTR_ATTACH }; @@ -101,13 +144,21 @@ ESP_SYSTEM_INIT_FN(sleep_power_startup_init, SECONDARY, BIT(0), 108) if (err != ESP_OK) { ESP_LOGW(TAG, "failed to init power retention module, err=%d", err); } else { +#if PMU_SLEEP_PRIV_ENABLED pmu_sleep_data_t *data = (pmu_sleep_data_t *)PMU_instance()->priv; - data->func[PMU_SLEEP_PRIV_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT] = &ana_wait_ctx; +#if CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT + data->func[PMU_SLEEP_PRIV_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT] = &power_context.ana_wait; +#endif +#if SOC_PM_MODEM_LOCK_CLK_WORKAROUND + data->func[PMU_SLEEP_PRIV_MODEM_LOCK_CLK_POWER] = &power_context.clock; +#endif +#endif } return ESP_OK; } +#if CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT void pmu_sleep_power_analog_wait_config(void *data, const uint16_t analog_wait[ANALOG_WAIT_CTRL_NUM]) { pmu_sleep_data_t *data_ctx = (pmu_sleep_data_t *)data; @@ -123,3 +174,22 @@ void pmu_sleep_power_analog_wait_config(void *data, const uint16_t analog_wait[A regdma_link_set_write_wait_content(ana_wait_ctx->regdma_desc[i], (uint32_t)analog_wait[i] << PMU_ANA_WAIT_TARGET_S, PMU_ANA_WAIT_TARGET_M); } } +#endif + +#if SOC_PM_MODEM_LOCK_CLK_WORKAROUND +void pmu_sleep_power_clock_config(void *data, const uint32_t config) +{ + pmu_sleep_data_t *datap = (pmu_sleep_data_t *)data; + if (!datap || !datap->func[PMU_SLEEP_PRIV_MODEM_LOCK_CLK_POWER]) { + return; + } + + pmu_sleep_power_clock_context_t *clk = (pmu_sleep_power_clock_context_t *)datap->func[PMU_SLEEP_PRIV_MODEM_LOCK_CLK_POWER]; + if (clk->regdma_desc[0]) { + regdma_link_set_write_wait_content(clk->regdma_desc[0], config & XTALX2_TIE_HIGH_MASK, XTALX2_TIE_HIGH_MASK); + } + if (clk->regdma_desc[1]) { + regdma_link_set_write_wait_content(clk->regdma_desc[1], config & BBPLL_TIE_HIGH_MASK, BBPLL_TIE_HIGH_MASK); + } +} +#endif diff --git a/components/esp_hw_support/port/esp32h4/private_include/pmu_param.h b/components/esp_hw_support/port/esp32h4/private_include/pmu_param.h index ed749b6c43e..5f6566f85aa 100644 --- a/components/esp_hw_support/port/esp32h4/private_include/pmu_param.h +++ b/components/esp_hw_support/port/esp32h4/private_include/pmu_param.h @@ -123,13 +123,26 @@ const pmu_lp_system_analog_param_t* pmu_lp_system_analog_param_default(pmu_lp_mo void pmu_sleep_power_analog_wait_config(void *data, const uint16_t analog_wait[ANALOG_WAIT_CTRL_NUM]); #endif +#if SOC_PM_MODEM_LOCK_CLK_WORKAROUND +/** + * @brief Back-fill M2A clk power REGDMA write values before sleep. + * + * @param data PMU sleep data context. + * @param config IMM TIE_HIGH bits to write. + */ +void pmu_sleep_power_clock_config(void *data, const uint32_t config); +#endif + /* Enabled when this chip needs any pmu_sleep_data_t priv slot; conditions differ per chip. */ -#define PMU_SLEEP_PRIV_ENABLED (CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT || SOC_PM_SUPPORT_PMU_RETENTION_CLK_ICG) +#define PMU_SLEEP_PRIV_ENABLED (CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT || SOC_PM_SUPPORT_PMU_RETENTION_CLK_ICG || SOC_PM_MODEM_LOCK_CLK_WORKAROUND) #if PMU_SLEEP_PRIV_ENABLED enum { #if CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT PMU_SLEEP_PRIV_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT, #endif +#if SOC_PM_MODEM_LOCK_CLK_WORKAROUND + PMU_SLEEP_PRIV_MODEM_LOCK_CLK_POWER, +#endif #if SOC_PM_SUPPORT_PMU_RETENTION_CLK_ICG PMU_SLEEP_PRIV_HW_RETENTION_ICG_CLK, #endif