|
|
@@ -0,0 +1,256 @@
|
|
|
+/*
|
|
|
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
|
|
+ *
|
|
|
+ * SPDX-License-Identifier: Apache-2.0
|
|
|
+ */
|
|
|
+
|
|
|
+#include <stdint.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <sys/param.h>
|
|
|
+#include <esp_types.h>
|
|
|
+#include "sdkconfig.h"
|
|
|
+#include "esp_err.h"
|
|
|
+#include "esp_attr.h"
|
|
|
+#include "soc/soc.h"
|
|
|
+#include "soc/rtc.h"
|
|
|
+#include "soc/pmu_struct.h"
|
|
|
+#include "esp_private/esp_pmu.h"
|
|
|
+
|
|
|
+#define HP(state) (PMU_MODE_HP_ ## state)
|
|
|
+#define LP(state) (PMU_MODE_LP_ ## state)
|
|
|
+
|
|
|
+void pmu_sleep_enable_regdma_backup(void)
|
|
|
+{
|
|
|
+ assert(PMU_instance()->hal);
|
|
|
+ /* entry 0, 1, 2 is used by pmu HP_SLEEP and HP_ACTIVE, HP_SLEEP
|
|
|
+ * and HP_MODEM or HP_MODEM and HP_ACTIVE states switching,
|
|
|
+ * respectively. entry 3 is reserved, not used yet! */
|
|
|
+ pmu_hal_hp_set_sleep_active_backup_enable(PMU_instance()->hal);
|
|
|
+ pmu_hal_hp_set_sleep_modem_backup_enable(PMU_instance()->hal);
|
|
|
+ pmu_hal_hp_set_modem_active_backup_enable(PMU_instance()->hal);
|
|
|
+}
|
|
|
+
|
|
|
+void pmu_sleep_disable_regdma_backup(void)
|
|
|
+{
|
|
|
+ assert(PMU_instance()->hal);
|
|
|
+ pmu_hal_hp_set_sleep_active_backup_disable(PMU_instance()->hal);
|
|
|
+ pmu_hal_hp_set_sleep_modem_backup_disable(PMU_instance()->hal);
|
|
|
+ pmu_hal_hp_set_modem_active_backup_disable(PMU_instance()->hal);
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t pmu_sleep_calculate_hw_wait_time(uint32_t pd_flags, uint32_t slowclk_period, uint32_t fastclk_period)
|
|
|
+{
|
|
|
+ const pmu_sleep_machine_constant_t *mc = (pmu_sleep_machine_constant_t *)PMU_instance()->mc;
|
|
|
+
|
|
|
+ /* LP core hardware wait time, microsecond */
|
|
|
+ const int lp_wakeup_wait_time = rtc_time_slowclk_to_us(mc->lp.wakeup_wait_cycle, slowclk_period);
|
|
|
+ const int lp_clk_switch_time = rtc_time_slowclk_to_us(mc->lp.clk_switch_cycle, slowclk_period);
|
|
|
+ const int lp_clk_power_on_wait_time = (pd_flags & PMU_SLEEP_PD_XTAL) ? mc->lp.xtal_wait_stable_time_ms \
|
|
|
+ : rtc_time_slowclk_to_us(mc->lp.clk_power_on_wait_cycle, slowclk_period);
|
|
|
+
|
|
|
+ const int lp_hw_wait_time = mc->lp.min_slp_time_ms + mc->lp.analog_wait_time_ms + lp_clk_power_on_wait_time \
|
|
|
+ + lp_wakeup_wait_time + lp_clk_switch_time + mc->lp.power_supply_wait_time_ms \
|
|
|
+ + mc->lp.power_up_wait_time_ms;
|
|
|
+
|
|
|
+ /* HP core hardware wait time, microsecond */
|
|
|
+ const int hp_digital_power_up_wait_time_ms = mc->hp.power_supply_wait_time_ms + mc->hp.power_up_wait_time_ms;
|
|
|
+ const int hp_regdma_wait_time = MAX(mc->hp.regdma_s2m_work_time_ms + mc->hp.regdma_m2a_work_time_ms, mc->hp.regdma_s2a_work_time_ms);
|
|
|
+ const int hp_clock_wait_time = mc->hp.xtal_wait_stable_time_ms + mc->hp.pll_wait_stable_time_ms;
|
|
|
+
|
|
|
+ const int hp_hw_wait_time = mc->hp.analog_wait_time_ms + MAX(hp_digital_power_up_wait_time_ms + hp_regdma_wait_time, hp_clock_wait_time);
|
|
|
+
|
|
|
+ /* When the SOC wakeup (lp timer or GPIO wakeup) and Modem wakeup (Beacon wakeup) complete, the soc
|
|
|
+ * wakeup will be delayed until the RF is turned on in Modem state.
|
|
|
+ *
|
|
|
+ * modem wakeup TBTT, RF on by HW
|
|
|
+ * | |
|
|
|
+ * \|/ \|/
|
|
|
+ * PMU_HP_ACTIVE /------
|
|
|
+ * PMU_HP_MODEM /------------//////////////////
|
|
|
+ * PMU_HP_SLEEP ----------------------//////////////////
|
|
|
+ * /|\ /|\ /|\ /|\ /|\ /|\
|
|
|
+ * |<- some hw wait ->| | | |<- M2A switch ->|
|
|
|
+ * | slow cycles & | soc wakeup | |
|
|
|
+ * | FOSC cycles |<- S2M switch ->| |
|
|
|
+ * | |
|
|
|
+ * |<-- PMU guard time, also the maximum time for the SOC -->|
|
|
|
+ * | wake-up delay |
|
|
|
+ */
|
|
|
+#if SOC_PM_SUPPORT_PMU_MODEM_STATE && CONFIG_ESP_WIFI_AUTO_BEACON_ENABLE
|
|
|
+ const int rf_on_protect_time = mc->hp.regdma_rf_on_work_time_ms;
|
|
|
+ const int total_hw_wait_time = lp_hw_wait_time + hp_hw_wait_time + mc->hp.clock_domain_sync_time_ms;
|
|
|
+#else
|
|
|
+ const int rf_on_protect_time = 0;
|
|
|
+ const int total_hw_wait_time = lp_hw_wait_time + hp_hw_wait_time;
|
|
|
+#endif
|
|
|
+ return total_hw_wait_time + rf_on_protect_time;
|
|
|
+}
|
|
|
+
|
|
|
+#define rtc_time_us_to_fastclk(time_us, period) rtc_time_us_to_slowclk((time_us), (period))
|
|
|
+
|
|
|
+static inline pmu_sleep_param_config_t * pmu_sleep_param_config_default(
|
|
|
+ pmu_sleep_param_config_t *param,
|
|
|
+ pmu_sleep_power_config_t *power, /* We'll use the runtime power parameter to determine some hardware parameters */
|
|
|
+ const uint32_t pd_flags,
|
|
|
+ const uint32_t adjustment,
|
|
|
+ const uint32_t slowclk_period,
|
|
|
+ const uint32_t fastclk_period
|
|
|
+ )
|
|
|
+{
|
|
|
+ const pmu_sleep_machine_constant_t *mc = (pmu_sleep_machine_constant_t *)PMU_instance()->mc;
|
|
|
+
|
|
|
+ param->hp_sys.min_slp_slow_clk_cycle = rtc_time_us_to_slowclk(mc->hp.min_slp_time_ms, slowclk_period);
|
|
|
+ param->hp_sys.analog_wait_target_cycle = rtc_time_us_to_fastclk(mc->hp.analog_wait_time_ms, fastclk_period);
|
|
|
+ param->hp_sys.digital_power_supply_wait_cycle = rtc_time_us_to_fastclk(mc->hp.power_supply_wait_time_ms, fastclk_period);
|
|
|
+ param->hp_sys.digital_power_up_wait_cycle = rtc_time_us_to_fastclk(mc->hp.power_up_wait_time_ms, fastclk_period);
|
|
|
+ param->hp_sys.pll_stable_wait_cycle = rtc_time_us_to_fastclk(mc->hp.pll_wait_stable_time_ms, fastclk_period);
|
|
|
+
|
|
|
+ const int hw_wait_time = pmu_sleep_calculate_hw_wait_time(pd_flags, slowclk_period, fastclk_period);
|
|
|
+ const int modem_state_skip_time = mc->hp.regdma_m2a_work_time_ms + mc->hp.system_dfs_up_work_time_ms + mc->lp.min_slp_time_ms;
|
|
|
+ const int modem_wakeup_wait_time = adjustment - hw_wait_time + modem_state_skip_time + mc->hp.regdma_rf_on_work_time_ms;
|
|
|
+ param->hp_sys.modem_wakeup_wait_cycle = rtc_time_us_to_fastclk(modem_wakeup_wait_time, fastclk_period);
|
|
|
+
|
|
|
+ param->lp_sys.min_slp_slow_clk_cycle = rtc_time_us_to_slowclk(mc->lp.min_slp_time_ms, slowclk_period);
|
|
|
+ param->lp_sys.analog_wait_target_cycle = rtc_time_us_to_slowclk(mc->lp.analog_wait_time_ms, slowclk_period);
|
|
|
+ param->lp_sys.digital_power_supply_wait_cycle = rtc_time_us_to_fastclk(mc->lp.power_supply_wait_time_ms, fastclk_period);
|
|
|
+ param->lp_sys.digital_power_up_wait_cycle = rtc_time_us_to_fastclk(mc->lp.power_up_wait_time_ms, fastclk_period);
|
|
|
+
|
|
|
+ if (power->hp_sys.xtal.xpd_xtal) {
|
|
|
+ param->hp_lp.xtal_stable_wait_cycle = rtc_time_us_to_fastclk(mc->hp.xtal_wait_stable_time_ms, fastclk_period);
|
|
|
+ } else {
|
|
|
+ param->hp_lp.xtal_stable_wait_slow_clk_cycle = rtc_time_us_to_slowclk(mc->lp.xtal_wait_stable_time_ms, slowclk_period);
|
|
|
+ }
|
|
|
+ return param;
|
|
|
+}
|
|
|
+
|
|
|
+const pmu_sleep_config_t* pmu_sleep_config_default(
|
|
|
+ pmu_sleep_config_t *config,
|
|
|
+ uint32_t pd_flags,
|
|
|
+ uint32_t adjustment,
|
|
|
+ uint32_t slowclk_period,
|
|
|
+ uint32_t fastclk_period,
|
|
|
+ bool dslp
|
|
|
+ )
|
|
|
+{
|
|
|
+ pmu_sleep_power_config_t power_default = PMU_SLEEP_POWER_CONFIG_DEFAULT(pd_flags);
|
|
|
+
|
|
|
+ uint32_t iram_pd_flags = 0;
|
|
|
+ iram_pd_flags |= (pd_flags & PMU_SLEEP_PD_MEM_G0) ? BIT(0) : 0;
|
|
|
+ iram_pd_flags |= (pd_flags & PMU_SLEEP_PD_MEM_G1) ? BIT(1) : 0;
|
|
|
+ iram_pd_flags |= (pd_flags & PMU_SLEEP_PD_MEM_G2) ? BIT(2) : 0;
|
|
|
+ iram_pd_flags |= (pd_flags & PMU_SLEEP_PD_MEM_G3) ? BIT(3) : 0;
|
|
|
+ config->power = power_default;
|
|
|
+
|
|
|
+ pmu_sleep_param_config_t param_default = PMU_SLEEP_PARAM_CONFIG_DEFAULT(pd_flags);
|
|
|
+ config->param = *pmu_sleep_param_config_default(¶m_default, &power_default, pd_flags, adjustment, slowclk_period, fastclk_period);
|
|
|
+
|
|
|
+ if (dslp) {
|
|
|
+ pmu_sleep_analog_config_t analog_default = PMU_SLEEP_ANALOG_DSLP_CONFIG_DEFAULT(pd_flags);
|
|
|
+ config->analog = analog_default;
|
|
|
+ } else {
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ if (!(pd_flags & PMU_SLEEP_PD_TOP)){
|
|
|
+ analog_default.hp_sys.analog.slp_logic_dbias += 2;
|
|
|
+ }
|
|
|
+ config->analog = analog_default;
|
|
|
+ }
|
|
|
+ return config;
|
|
|
+}
|
|
|
+
|
|
|
+static void pmu_sleep_power_init(pmu_context_t *ctx, const pmu_sleep_power_config_t *power, bool dslp)
|
|
|
+{
|
|
|
+ pmu_ll_hp_set_dig_power(ctx->hal->dev, HP(SLEEP), power->hp_sys.dig_power.val);
|
|
|
+ pmu_ll_hp_set_clk_power(ctx->hal->dev, HP(SLEEP), power->hp_sys.clk_power.val);
|
|
|
+ pmu_ll_hp_set_xtal_xpd (ctx->hal->dev, HP(SLEEP), power->hp_sys.xtal.xpd_xtal);
|
|
|
+
|
|
|
+ pmu_ll_lp_set_dig_power(ctx->hal->dev, LP(ACTIVE), power->lp_sys[LP(ACTIVE)].dig_power.val);
|
|
|
+ pmu_ll_lp_set_clk_power(ctx->hal->dev, LP(ACTIVE), power->lp_sys[LP(ACTIVE)].clk_power.val);
|
|
|
+
|
|
|
+ pmu_ll_lp_set_dig_power(ctx->hal->dev, LP(SLEEP), power->lp_sys[LP(SLEEP)].dig_power.val);
|
|
|
+ pmu_ll_lp_set_clk_power(ctx->hal->dev, LP(SLEEP), power->lp_sys[LP(SLEEP)].clk_power.val);
|
|
|
+ pmu_ll_lp_set_xtal_xpd (ctx->hal->dev, LP(SLEEP), power->lp_sys[LP(SLEEP)].xtal.xpd_xtal);
|
|
|
+
|
|
|
+ if (dslp) {
|
|
|
+ // TODO: IDF-5349
|
|
|
+ } else {
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void pmu_sleep_analog_init(pmu_context_t *ctx, const pmu_sleep_analog_config_t *analog, bool dslp)
|
|
|
+{
|
|
|
+ assert(ctx->hal);
|
|
|
+ pmu_ll_hp_set_current_power_off (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.pd_cur);
|
|
|
+ pmu_ll_hp_set_bias_sleep_enable (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.bias_sleep);
|
|
|
+ pmu_ll_hp_set_regulator_sleep_memory_xpd (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.slp_mem_xpd);
|
|
|
+ pmu_ll_hp_set_regulator_sleep_logic_xpd (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.slp_logic_xpd);
|
|
|
+ pmu_ll_hp_set_regulator_xpd (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.xpd);
|
|
|
+ pmu_ll_hp_set_regulator_sleep_memory_dbias(ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.slp_mem_dbias);
|
|
|
+ pmu_ll_hp_set_regulator_sleep_logic_dbias (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.slp_logic_dbias);
|
|
|
+ pmu_ll_hp_set_regulator_dbias (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.dbias);
|
|
|
+ pmu_ll_hp_set_regulator_driver_bar (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.drv_b);
|
|
|
+
|
|
|
+ pmu_ll_lp_set_regulator_sleep_dbias(ctx->hal->dev, LP(ACTIVE), analog->lp_sys[LP(ACTIVE)].analog.slp_dbias);
|
|
|
+ pmu_ll_lp_set_regulator_dbias (ctx->hal->dev, LP(ACTIVE), analog->lp_sys[LP(ACTIVE)].analog.dbias);
|
|
|
+ pmu_ll_lp_set_regulator_driver_bar (ctx->hal->dev, LP(ACTIVE), analog->lp_sys[LP(ACTIVE)].analog.drv_b);
|
|
|
+
|
|
|
+ pmu_ll_lp_set_current_power_off (ctx->hal->dev, LP(SLEEP), analog->lp_sys[LP(SLEEP)].analog.pd_cur);
|
|
|
+ pmu_ll_lp_set_bias_sleep_enable (ctx->hal->dev, LP(SLEEP), analog->lp_sys[LP(SLEEP)].analog.bias_sleep);
|
|
|
+ pmu_ll_lp_set_regulator_xpd (ctx->hal->dev, LP(SLEEP), analog->lp_sys[LP(SLEEP)].analog.xpd);
|
|
|
+ pmu_ll_lp_set_regulator_sleep_dbias(ctx->hal->dev, LP(SLEEP), analog->lp_sys[LP(SLEEP)].analog.slp_dbias);
|
|
|
+ pmu_ll_lp_set_regulator_dbias (ctx->hal->dev, LP(SLEEP), analog->lp_sys[LP(SLEEP)].analog.dbias);
|
|
|
+ pmu_ll_lp_set_regulator_driver_bar (ctx->hal->dev, LP(SLEEP), analog->lp_sys[LP(SLEEP)].analog.drv_b);
|
|
|
+
|
|
|
+ if (dslp) {
|
|
|
+ // TODO: IDF-5349
|
|
|
+ } else {
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void pmu_sleep_param_init(pmu_context_t *ctx, const pmu_sleep_param_config_t *param, bool dslp)
|
|
|
+{
|
|
|
+ assert(ctx->hal);
|
|
|
+ pmu_ll_hp_set_min_sleep_cycle(ctx->hal->dev, param->hp_sys.min_slp_slow_clk_cycle);
|
|
|
+ pmu_ll_lp_set_min_sleep_cycle(ctx->hal->dev, param->lp_sys.min_slp_slow_clk_cycle);
|
|
|
+
|
|
|
+ pmu_ll_hp_set_analog_wait_target_cycle(ctx->hal->dev, param->hp_sys.analog_wait_target_cycle);
|
|
|
+ pmu_ll_lp_set_analog_wait_target_cycle(ctx->hal->dev, param->lp_sys.analog_wait_target_cycle);
|
|
|
+
|
|
|
+ pmu_hal_hp_set_digital_power_up_wait_cycle(ctx->hal, param->hp_sys.digital_power_supply_wait_cycle, param->hp_sys.digital_power_up_wait_cycle);
|
|
|
+ pmu_hal_lp_set_digital_power_up_wait_cycle(ctx->hal, param->lp_sys.digital_power_supply_wait_cycle, param->lp_sys.digital_power_up_wait_cycle);
|
|
|
+
|
|
|
+ pmu_ll_set_modem_wait_target_cycle(ctx->hal->dev, param->hp_sys.modem_wakeup_wait_cycle);
|
|
|
+ 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);
|
|
|
+}
|
|
|
+
|
|
|
+void pmu_sleep_init(const pmu_sleep_config_t *config, bool dslp)
|
|
|
+{
|
|
|
+ assert(PMU_instance());
|
|
|
+ pmu_sleep_power_init(PMU_instance(), &config->power, dslp);
|
|
|
+ pmu_sleep_analog_init(PMU_instance(), &config->analog, dslp);
|
|
|
+ pmu_sleep_param_init(PMU_instance(), &config->param, dslp);
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t pmu_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu, bool dslp)
|
|
|
+{
|
|
|
+ assert(PMU_instance()->hal);
|
|
|
+ pmu_ll_hp_set_wakeup_enable(PMU_instance()->hal->dev, wakeup_opt);
|
|
|
+ pmu_ll_hp_set_reject_enable(PMU_instance()->hal->dev, reject_opt);
|
|
|
+
|
|
|
+ pmu_ll_hp_clear_wakeup_intr_status(PMU_instance()->hal->dev);
|
|
|
+ pmu_ll_hp_clear_reject_intr_status(PMU_instance()->hal->dev);
|
|
|
+ pmu_ll_hp_clear_reject_cause(PMU_instance()->hal->dev);
|
|
|
+
|
|
|
+ /* Start entry into sleep mode */
|
|
|
+ pmu_ll_hp_set_sleep_enable(PMU_instance()->hal->dev);
|
|
|
+
|
|
|
+ while (!pmu_ll_hp_is_sleep_wakeup(PMU_instance()->hal->dev) &&
|
|
|
+ !pmu_ll_hp_is_sleep_reject(PMU_instance()->hal->dev)) {
|
|
|
+ ;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ESP_OK;
|
|
|
+}
|