Skip to main content

esp_hal/rtc_cntl/sleep/
esp32c6.rs

1use core::ops::Not;
2
3use crate::{
4    peripherals::{LP_AON, PMU},
5    private::DropGuard,
6    rtc_cntl::{
7        Rtc,
8        rtc::{HpAnalog, HpSysCntlReg, HpSysPower, LpAnalog, LpSysPower},
9        sleep::{SleepKind, pmu_common::SleepTimeConfig},
10    },
11    soc::{
12        clocks::{self, ClockTree, LpSlowClkConfig, SocRootClkConfig},
13        xtal32k,
14    },
15};
16
17/// Configuration for controlling the behavior during sleep modes.
18#[derive(Clone, Copy)]
19// pmu_sleep_analog_config_t
20pub struct AnalogSleepConfig {
21    /// High-power system configuration.
22    pub hp_sys: HpAnalog,
23    // pub lp_sys_active: LpAnalog, // unused
24    /// Low-power system analog configuration.
25    pub lp_sys_sleep: LpAnalog,
26}
27
28impl AnalogSleepConfig {
29    fn defaults_deep_sleep() -> Self {
30        Self {
31            // PMU_SLEEP_ANALOG_DSLP_CONFIG_DEFAULT
32            hp_sys: {
33                let mut cfg = HpAnalog::default();
34
35                cfg.bias.set_pd_cur(false);
36                cfg.bias.set_bias_sleep(false);
37                cfg.regulator0.set_xpd(false);
38                cfg.bias.set_dbg_atten(0);
39
40                cfg
41            },
42            // lp_sys_active: LpAnalog::default(),
43            lp_sys_sleep: {
44                let mut cfg = LpAnalog::default();
45
46                cfg.regulator1.set_drv_b(0);
47                cfg.bias.set_pd_cur(true);
48                cfg.bias.set_bias_sleep(true);
49                cfg.regulator0.set_slp_xpd(false);
50                cfg.regulator0.set_slp_dbias(0);
51                cfg.regulator0.set_xpd(true);
52                cfg.bias.set_dbg_atten(12);
53                cfg.regulator0.set_dbias(23); // 0.7V
54
55                cfg
56            },
57        }
58    }
59
60    fn defaults_light_sleep(pd_flags: PowerDownFlags) -> Self {
61        let mut this = Self {
62            // PMU_SLEEP_ANALOG_LSLP_CONFIG_DEFAULT
63            hp_sys: {
64                let mut cfg = HpAnalog::default();
65
66                cfg.regulator1.set_drv_b(0);
67                cfg.bias.set_pd_cur(true);
68                cfg.bias.set_bias_sleep(true);
69                cfg.regulator0.set_xpd(true);
70                cfg.bias.set_dbg_atten(0);
71                cfg.regulator0.set_dbias(1); // 0.6V
72
73                cfg
74            },
75            // lp_sys_active: LpAnalog::default(),
76            lp_sys_sleep: {
77                let mut cfg = LpAnalog::default();
78
79                cfg.regulator1.set_drv_b(0);
80                cfg.bias.set_pd_cur(true);
81                cfg.bias.set_bias_sleep(true);
82                cfg.regulator0.set_slp_xpd(false);
83                cfg.regulator0.set_slp_dbias(0);
84                cfg.regulator0.set_xpd(true);
85                cfg.bias.set_dbg_atten(0);
86                cfg.regulator0.set_dbias(12); // 0.7V
87
88                cfg
89            },
90        };
91
92        if !pd_flags.pd_xtal() {
93            this.hp_sys.bias.set_pd_cur(false);
94            this.hp_sys.bias.set_bias_sleep(false);
95            this.hp_sys.regulator0.set_dbias(25);
96
97            this.lp_sys_sleep.bias.set_pd_cur(false);
98            this.lp_sys_sleep.bias.set_bias_sleep(false);
99            this.lp_sys_sleep.regulator0.set_dbias(26);
100        }
101
102        this
103    }
104
105    fn apply(&self) {
106        // pmu_sleep_analog_init
107
108        unsafe {
109            // HP SLEEP (hp_sleep_*)
110            PMU::regs().hp_sleep_bias().modify(|_, w| {
111                // pmu_ll_hp_set_dbg_atten
112                w.hp_sleep_dbg_atten().bits(self.hp_sys.bias.dbg_atten());
113                // pmu_ll_hp_set_current_power_off
114                w.hp_sleep_pd_cur().bit(self.hp_sys.bias.pd_cur());
115                // pmu_ll_hp_set_bias_sleep_enable
116                w.sleep().bit(self.hp_sys.bias.bias_sleep())
117            });
118            PMU::regs().hp_sleep_hp_regulator0().modify(|_, w| {
119                // pmu_ll_hp_set_regulator_xpd
120                w.hp_sleep_hp_regulator_xpd()
121                    .bit(self.hp_sys.regulator0.xpd());
122                // pmu_ll_hp_set_regulator_dbias
123                w.hp_sleep_hp_regulator_dbias()
124                    .bits(self.hp_sys.regulator0.dbias())
125            });
126            PMU::regs().hp_sleep_hp_regulator1().modify(|_, w| {
127                // pmu_ll_hp_set_regulator_driver_bar
128                w.hp_sleep_hp_regulator_drv_b()
129                    .bits(self.hp_sys.regulator1.drv_b())
130            });
131
132            // LP SLEEP (lp_sleep_*)
133            PMU::regs().lp_sleep_bias().modify(|_, w| {
134                // pmu_ll_lp_set_dbg_atten
135                w.lp_sleep_dbg_atten()
136                    .bits(self.lp_sys_sleep.bias.dbg_atten());
137                // pmu_ll_lp_set_current_power_off
138                w.lp_sleep_pd_cur().bit(self.lp_sys_sleep.bias.pd_cur());
139                // pmu_ll_lp_set_bias_sleep_enable
140                w.sleep().bit(self.lp_sys_sleep.bias.bias_sleep())
141            });
142
143            PMU::regs().lp_sleep_lp_regulator0().modify(|_, w| {
144                // pmu_ll_lp_set_regulator_slp_xpd
145                w.lp_sleep_lp_regulator_slp_xpd()
146                    .bit(self.lp_sys_sleep.regulator0.slp_xpd());
147                // pmu_ll_lp_set_regulator_xpd
148                w.lp_sleep_lp_regulator_xpd()
149                    .bit(self.lp_sys_sleep.regulator0.xpd());
150                // pmu_ll_lp_set_regulator_sleep_dbias
151                w.lp_sleep_lp_regulator_slp_dbias()
152                    .bits(self.lp_sys_sleep.regulator0.slp_dbias());
153                // pmu_ll_lp_set_regulator_dbias
154                w.lp_sleep_lp_regulator_dbias()
155                    .bits(self.lp_sys_sleep.regulator0.dbias())
156            });
157
158            PMU::regs().lp_sleep_lp_regulator1().modify(|_, w| {
159                // pmu_ll_lp_set_regulator_driver_bar
160                w.lp_sleep_lp_regulator_drv_b()
161                    .bits(self.lp_sys_sleep.regulator1.drv_b())
162            });
163        }
164    }
165}
166
167/// Configuration for controlling the behavior of digital peripherals during
168/// sleep modes.
169#[derive(Clone, Copy)]
170// pmu_sleep_digital_config_t
171pub struct DigitalSleepConfig {
172    /// High-power system control register configuration.
173    pub syscntl: HpSysCntlReg,
174}
175
176impl DigitalSleepConfig {
177    fn defaults_light_sleep(pd_flags: PowerDownFlags) -> Self {
178        Self {
179            // PMU_SLEEP_DIGITAL_LSLP_CONFIG_DEFAULT
180            syscntl: {
181                let mut cfg = HpSysCntlReg::default();
182
183                cfg.set_dig_pad_slp_sel(pd_flags.pd_top().not());
184
185                cfg
186            },
187        }
188    }
189
190    fn apply(&self) {
191        // pmu_sleep_digital_init
192        PMU::regs().hp_sleep_hp_sys_cntl().modify(|_, w| {
193            w.hp_sleep_dig_pad_slp_sel()
194                .bit(self.syscntl.dig_pad_slp_sel())
195        });
196    }
197}
198
199/// Configuration for controlling the power settings of high-power and low-power
200/// systems during sleep modes.
201#[derive(Clone, Copy)]
202// pmu_sleep_power_config_t
203pub struct PowerSleepConfig {
204    /// Power configuration for the high-power system during sleep.
205    pub hp_sys: HpSysPower,
206    /// Power configuration for the low-power system when it is active.
207    pub lp_sys_active: LpSysPower,
208    /// Power configuration for the low-power system when it is in sleep mode.
209    pub lp_sys_sleep: LpSysPower,
210}
211
212impl PowerSleepConfig {
213    fn defaults(pd_flags: PowerDownFlags) -> Self {
214        let mut this = Self {
215            hp_sys: HpSysPower::default(),
216            lp_sys_active: LpSysPower::default(),
217            lp_sys_sleep: LpSysPower::default(),
218        };
219        this.apply_flags(pd_flags);
220        this
221    }
222
223    fn apply_flags(&mut self, pd_flags: PowerDownFlags) {
224        // PMU_SLEEP_POWER_CONFIG_DEFAULT
225        self.hp_sys
226            .dig_power
227            .set_vdd_spi_pd_en(pd_flags.pd_vddsdio());
228        self.hp_sys.dig_power.set_wifi_pd_en(pd_flags.pd_modem());
229        self.hp_sys.dig_power.set_cpu_pd_en(pd_flags.pd_cpu());
230        self.hp_sys.dig_power.set_aon_pd_en(pd_flags.pd_hp_aon());
231        self.hp_sys.dig_power.set_top_pd_en(pd_flags.pd_top());
232
233        if pd_flags.pd_modem() {
234            // The modem (Wi-Fi/BLE) power domain is powered down during sleep, so
235            // isolate and retain its analog I2C buses as before.
236            self.hp_sys.clk.set_i2c_iso_en(true);
237            self.hp_sys.clk.set_i2c_retention(true);
238        } else {
239            // The modem power domain is kept on across (light-)sleep
240            // (`pd_modem == false`, the default). In that case its analog blocks -
241            // the BBPLL and the analog I2C buses used to (re)configure it - must be
242            // kept powered too, matching the `HP_MODEM` power state. Otherwise the
243            // radio comes back without a usable PLL and can no longer transmit
244            // (e.g. BLE advertising silently stops working) after wakeup.
245            self.hp_sys.clk.set_i2c_iso_en(false);
246            self.hp_sys.clk.set_i2c_retention(false);
247            self.hp_sys.clk.set_xpd_bb_i2c(true);
248            self.hp_sys.clk.set_xpd_bbpll_i2c(true);
249            self.hp_sys.clk.set_xpd_bbpll(true);
250        }
251
252        self.hp_sys.xtal.set_xpd_xtal(pd_flags.pd_xtal().not());
253
254        self.lp_sys_active
255            .clk_power
256            .set_xpd_xtal32k(xtal32k::use_xtal32k());
257        self.lp_sys_active.clk_power.set_xpd_rc32k(true);
258        self.lp_sys_active.clk_power.set_xpd_fosc(true);
259
260        self.lp_sys_sleep
261            .dig_power
262            .set_peri_pd_en(pd_flags.pd_lp_periph());
263        self.lp_sys_sleep.dig_power.set_mem_dslp(true);
264
265        self.lp_sys_sleep
266            .clk_power
267            .set_xpd_xtal32k(pd_flags.pd_xtal32k().not());
268        self.lp_sys_sleep
269            .clk_power
270            .set_xpd_rc32k(pd_flags.pd_rc32k().not());
271        self.lp_sys_sleep
272            .clk_power
273            .set_xpd_fosc(pd_flags.pd_rc_fast().not());
274
275        self.lp_sys_sleep
276            .xtal
277            .set_xpd_xtal(pd_flags.pd_xtal().not());
278    }
279
280    fn apply(&self) {
281        // pmu_sleep_power_init
282
283        // HP SLEEP (hp_sleep_*)
284        PMU::regs()
285            .hp_sleep_dig_power()
286            .modify(|_, w| unsafe { w.bits(self.hp_sys.dig_power.0) });
287        PMU::regs()
288            .hp_sleep_hp_ck_power()
289            .modify(|_, w| unsafe { w.bits(self.hp_sys.clk.0) });
290        PMU::regs()
291            .hp_sleep_xtal()
292            .modify(|_, w| w.hp_sleep_xpd_xtal().bit(self.hp_sys.xtal.xpd_xtal()));
293
294        // LP ACTIVE (hp_sleep_lp_*)
295        PMU::regs()
296            .hp_sleep_lp_dig_power()
297            .modify(|_, w| unsafe { w.bits(self.lp_sys_active.dig_power.0) });
298        PMU::regs()
299            .hp_sleep_lp_ck_power()
300            .modify(|_, w| unsafe { w.bits(self.lp_sys_active.clk_power.0) });
301
302        // LP SLEEP (lp_sleep_*)
303        PMU::regs()
304            .lp_sleep_lp_dig_power()
305            .modify(|_, w| unsafe { w.bits(self.lp_sys_sleep.dig_power.0) });
306        PMU::regs()
307            .lp_sleep_lp_ck_power()
308            .modify(|_, w| unsafe { w.bits(self.lp_sys_sleep.clk_power.0) });
309        PMU::regs()
310            .lp_sleep_xtal()
311            .modify(|_, w| w.lp_sleep_xpd_xtal().bit(self.lp_sys_sleep.xtal.xpd_xtal()));
312    }
313}
314
315/// Parameters for high-power system configurations during sleep modes.
316#[derive(Clone, Copy)]
317// pmu_hp_param_t
318pub struct HpParam {
319    /// Number of cycles to wait for the modem to wake up.
320    pub modem_wakeup_wait_cycle: u32,
321    /// Number of cycles to wait for the analog component stabilization.
322    pub analog_wait_target_cycle: u16,
323    /// Number of cycles to wait for the digital power-down sequence.
324    pub digital_power_down_wait_cycle: u16,
325    /// Number of cycles to wait for the digital power supply to stabilize.
326    pub digital_power_supply_wait_cycle: u16,
327    /// Number of cycles to wait for the digital power-up sequence.
328    pub digital_power_up_wait_cycle: u16,
329    /// Number of cycles to wait for the PLL to stabilize.
330    pub pll_stable_wait_cycle: u16,
331    /// Number of cycles to wait for modifying the ICG control.
332    pub modify_icg_cntl_wait_cycle: u8,
333    /// Number of cycles to wait for switching the ICG control.
334    pub switch_icg_cntl_wait_cycle: u8,
335    /// Minimum sleep time measured in slow clock cycles.
336    pub min_slp_slow_clk_cycle: u8,
337}
338
339/// Parameters for low-power system configurations during sleep modes.
340#[derive(Clone, Copy)]
341// pmu_lp_param_t
342pub struct LpParam {
343    /// Number of cycles to wait for the digital power supply to stabilize.
344    pub digital_power_supply_wait_cycle: u16,
345    /// Minimum sleep time measured in slow clock cycles.
346    pub min_slp_slow_clk_cycle: u8,
347    /// Number of cycles to wait for the analog component stabilization.
348    pub analog_wait_target_cycle: u8,
349    /// Number of cycles to wait for the digital power-down sequence.
350    pub digital_power_down_wait_cycle: u8,
351    /// Number of cycles to wait for the digital power-up sequence.
352    pub digital_power_up_wait_cycle: u8,
353}
354
355/// Parameters for high-power and low-power system configurations during sleep
356/// modes.
357#[derive(Clone, Copy)]
358// pmu_hp_lp_param_t
359pub struct HpLpParam {
360    /// Union of two u16 variants
361    pub xtal_stable_wait_cycle: u16,
362}
363
364/// Configuration of parameters for sleep modes
365#[derive(Clone, Copy)]
366// pmu_sleep_param_config_t
367pub struct ParamSleepConfig {
368    /// Configuration of high-power system parameters.
369    pub hp_sys: HpParam,
370    /// Configuration of low-power system parameters.
371    pub lp_sys: LpParam,
372    /// Shared configuration parameters for high-power and low-power systems.
373    pub hp_lp: HpLpParam,
374}
375impl ParamSleepConfig {
376    const PMU_SLEEP_PARAM_CONFIG_DEFAULT: Self = Self {
377        hp_sys: HpParam {
378            min_slp_slow_clk_cycle: 10,
379            analog_wait_target_cycle: 2419,
380            digital_power_supply_wait_cycle: 32,
381            digital_power_up_wait_cycle: 32,
382            modem_wakeup_wait_cycle: 20700,
383            pll_stable_wait_cycle: 2,
384
385            digital_power_down_wait_cycle: 0,
386            modify_icg_cntl_wait_cycle: 0,
387            switch_icg_cntl_wait_cycle: 0,
388        },
389        lp_sys: LpParam {
390            min_slp_slow_clk_cycle: 10,
391            analog_wait_target_cycle: 23,
392            digital_power_supply_wait_cycle: 32,
393            digital_power_up_wait_cycle: 32,
394
395            digital_power_down_wait_cycle: 0,
396        },
397        hp_lp: HpLpParam {
398            xtal_stable_wait_cycle: 30,
399        },
400    };
401
402    fn apply(&self) {
403        // pmu_sleep_param_init
404
405        unsafe {
406            PMU::regs().slp_wakeup_cntl3().modify(|_, w| {
407                // pmu_ll_hp_set_min_sleep_cycle
408                w.hp_min_slp_val().bits(self.hp_sys.min_slp_slow_clk_cycle);
409                // pmu_ll_lp_set_min_sleep_cycle
410                w.lp_min_slp_val().bits(self.lp_sys.min_slp_slow_clk_cycle)
411            });
412
413            PMU::regs().slp_wakeup_cntl7().modify(|_, w| {
414                w.ana_wait_target() // pmu_ll_hp_set_analog_wait_target_cycle
415                    .bits(self.hp_sys.analog_wait_target_cycle)
416            });
417
418            PMU::regs().power_wait_timer0().modify(|_, w| {
419                // pmu_ll_hp_set_digital_power_supply_wait_cycle
420                w.dg_hp_wait_timer()
421                    .bits(self.hp_sys.digital_power_supply_wait_cycle);
422                // pmu_ll_hp_set_digital_power_up_wait_cycle
423                w.dg_hp_powerup_timer()
424                    .bits(self.hp_sys.digital_power_up_wait_cycle)
425            });
426
427            PMU::regs().power_wait_timer1().modify(|_, w| {
428                // pmu_ll_lp_set_digital_power_supply_wait_cycle
429                w.dg_lp_wait_timer()
430                    .bits(self.lp_sys.digital_power_supply_wait_cycle);
431                // pmu_ll_lp_set_digital_power_up_wait_cycle
432                w.dg_lp_powerup_timer()
433                    .bits(self.lp_sys.digital_power_up_wait_cycle)
434            });
435
436            PMU::regs().slp_wakeup_cntl5().modify(|_, w| {
437                // pmu_ll_lp_set_analog_wait_target_cycle
438                w.lp_ana_wait_target()
439                    .bits(self.lp_sys.analog_wait_target_cycle);
440                // pmu_ll_hp_set_modem_wakeup_wait_cycle
441                w.modem_wait_target()
442                    .bits(self.hp_sys.modem_wakeup_wait_cycle)
443            });
444            PMU::regs().power_ck_wait_cntl().modify(|_, w| {
445                // pmu_ll_hp_set_xtal_stable_wait_cycle
446                w.wait_xtl_stable().bits(self.hp_lp.xtal_stable_wait_cycle);
447                // pmu_ll_hp_set_pll_stable_wait_cycle
448                w.wait_pll_stable().bits(self.hp_sys.pll_stable_wait_cycle)
449            });
450        }
451    }
452
453    fn defaults(config: SleepTimeConfig, pd_flags: PowerDownFlags, pd_xtal: bool) -> Self {
454        let mut param = Self::PMU_SLEEP_PARAM_CONFIG_DEFAULT;
455
456        // pmu_sleep_param_config_default
457        param.hp_sys.min_slp_slow_clk_cycle =
458            config.us_to_slowclk(MachineConstants::HP_MIN_SLP_TIME_US) as u8;
459        param.hp_sys.analog_wait_target_cycle =
460            config.us_to_fastclk(MachineConstants::HP_ANALOG_WAIT_TIME_US) as u16;
461        param.hp_sys.digital_power_supply_wait_cycle =
462            config.us_to_fastclk(MachineConstants::HP_POWER_SUPPLY_WAIT_TIME_US) as u16;
463        param.hp_sys.digital_power_up_wait_cycle =
464            config.us_to_fastclk(MachineConstants::HP_POWER_UP_WAIT_TIME_US) as u16;
465        param.hp_sys.pll_stable_wait_cycle =
466            config.us_to_fastclk(MachineConstants::HP_PLL_WAIT_STABLE_TIME_US) as u16;
467
468        let hw_wait_time_us = config.pmu_sleep_calculate_hw_wait_time(pd_flags);
469
470        let modem_wakeup_wait_time_us = (config.sleep_time_adjustment
471            + MachineConstants::MODEM_STATE_SKIP_TIME_US
472            + MachineConstants::HP_REGDMA_RF_ON_WORK_TIME_US)
473            .saturating_sub(hw_wait_time_us);
474        param.hp_sys.modem_wakeup_wait_cycle = config.us_to_fastclk(modem_wakeup_wait_time_us);
475
476        param.lp_sys.min_slp_slow_clk_cycle =
477            config.us_to_slowclk(MachineConstants::LP_MIN_SLP_TIME_US) as u8;
478        param.lp_sys.analog_wait_target_cycle =
479            config.us_to_slowclk(MachineConstants::LP_ANALOG_WAIT_TIME_US) as u8;
480        param.lp_sys.digital_power_supply_wait_cycle =
481            config.us_to_fastclk(MachineConstants::LP_POWER_SUPPLY_WAIT_TIME_US) as u16;
482        param.lp_sys.digital_power_up_wait_cycle =
483            config.us_to_fastclk(MachineConstants::LP_POWER_UP_WAIT_TIME_US) as u8;
484
485        // This looks different from esp-idf but it is the same:
486        // Both `xtal_stable_wait_cycle` and `xtal_stable_wait_slow_clk_cycle` are
487        // u16 variants of the same union
488        param.hp_lp.xtal_stable_wait_cycle = if pd_xtal {
489            config.us_to_slowclk(MachineConstants::LP_XTAL_WAIT_STABLE_TIME_US) as u16
490        } else {
491            config.us_to_fastclk(MachineConstants::HP_XTAL_WAIT_STABLE_TIME_US) as u16
492        };
493
494        param
495    }
496}
497
498impl SleepTimeConfig {
499    pub(crate) const CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ: u32 = 160;
500    pub(crate) const LIGHT_SLEEP_TIME_OVERHEAD_US: u32 = 56;
501
502    pub(crate) fn pmu_sleep_calculate_hw_wait_time(&self, pd_flags: PowerDownFlags) -> u32 {
503        // LP core hardware wait time, microsecond
504        let lp_wakeup_wait_time_us = self.slowclk_to_us(MachineConstants::LP_WAKEUP_WAIT_CYCLE);
505        let lp_clk_switch_time_us = self.slowclk_to_us(MachineConstants::LP_CLK_SWITCH_CYCLE);
506        let lp_clk_power_on_wait_time_us = if pd_flags.pd_xtal() {
507            MachineConstants::LP_XTAL_WAIT_STABLE_TIME_US
508        } else {
509            self.slowclk_to_us(MachineConstants::LP_CLK_POWER_ON_WAIT_CYCLE)
510        };
511
512        let lp_hw_wait_time_us = MachineConstants::LP_MIN_SLP_TIME_US
513            + MachineConstants::LP_ANALOG_WAIT_TIME_US
514            + lp_clk_power_on_wait_time_us
515            + lp_wakeup_wait_time_us
516            + lp_clk_switch_time_us
517            + MachineConstants::LP_POWER_SUPPLY_WAIT_TIME_US
518            + MachineConstants::LP_POWER_UP_WAIT_TIME_US;
519
520        // HP core hardware wait time, microsecond
521        let hp_digital_power_up_wait_time_us = MachineConstants::HP_POWER_SUPPLY_WAIT_TIME_US
522            + MachineConstants::HP_POWER_UP_WAIT_TIME_US;
523        let hp_regdma_wait_time_us = u32::max(
524            MachineConstants::HP_REGDMA_S2M_WORK_TIME_US
525                + MachineConstants::HP_REGDMA_M2A_WORK_TIME_US,
526            MachineConstants::HP_REGDMA_S2A_WORK_TIME_US,
527        );
528        let hp_clock_wait_time_us = MachineConstants::HP_XTAL_WAIT_STABLE_TIME_US
529            + MachineConstants::HP_PLL_WAIT_STABLE_TIME_US;
530
531        let hp_hw_wait_time_us = MachineConstants::HP_ANALOG_WAIT_TIME_US
532            + u32::max(
533                hp_digital_power_up_wait_time_us + hp_regdma_wait_time_us,
534                hp_clock_wait_time_us,
535            );
536
537        #[rustfmt::skip] // ASCII art
538        //  When the SOC wakeup (lp timer or GPIO wakeup) and Modem wakeup (Beacon wakeup) complete,
539        // the soc wakeup will be delayed until the RF is turned on in Modem state.
540        //
541        //              modem wakeup                      TBTT, RF on by HW
542        //                   |                                    |
543        //                  \|/                                  \|/
544        // PMU_HP_ACTIVE                                                                         /------
545        // PMU_HP_MODEM                                           /------------//////////////////
546        // PMU_HP_SLEEP   ----------------------//////////////////
547        //                  /|\                /|\ /|\          /|\           /|\              /|\
548        //                   |<- some hw wait ->|   |            |             |<- M2A switch ->|
549        //                   |  slow cycles &   | soc wakeup     |                              |
550        //                   |   FOSC cycles    |<- S2M switch ->|                              |
551        //                   |                                                                  |
552        //                   |<--      PMU guard time, also the maximum time for the SOC     -->|
553        //                   |                           wake-up delay                          |
554        //
555        const CONFIG_ESP_RADIO_ENHANCED_LIGHT_SLEEP: bool = true;
556
557        let (rf_on_protect_time_us, sync_time_us) = if CONFIG_ESP_RADIO_ENHANCED_LIGHT_SLEEP {
558            (
559                MachineConstants::HP_REGDMA_RF_ON_WORK_TIME_US,
560                MachineConstants::HP_CLOCK_DOMAIN_SYNC_TIME_US,
561            )
562        } else {
563            (0, 0)
564        };
565
566        lp_hw_wait_time_us + hp_hw_wait_time_us + sync_time_us + rf_on_protect_time_us
567    }
568}
569
570/// Configuration for the RTC sleep behavior.
571#[derive(Clone, Copy)]
572// pmu_sleep_config_t + deep sleep flag + pd flags
573pub struct RtcSleepConfig {
574    /// Deep Sleep flag
575    pub deep: bool,
576    /// Power Down flags
577    pub pd_flags: PowerDownFlags,
578}
579
580impl Default for RtcSleepConfig {
581    fn default() -> Self {
582        // from pmu_sleep_param_config_default
583        // sleep flags will be applied by wakeup methods and apply
584
585        Self {
586            deep: false,
587            pd_flags: PowerDownFlags(0),
588        }
589    }
590}
591
592bitfield::bitfield! {
593    #[derive(Clone, Copy)]
594    /// Power domains to be powered down during sleep
595    pub struct PowerDownFlags(u32);
596
597    /// Controls the power-down status of the top power domain.
598    pub u32, pd_top      , set_pd_top      : 0;
599    /// Controls the power-down status of the VDD_SDIO power domain.
600    pub u32, pd_vddsdio  , set_pd_vddsdio  : 1;
601    /// Controls the power-down status of the modem power domain.
602    pub u32, pd_modem    , set_pd_modem    : 2;
603    /// Controls the power-down status of the high-performance peripheral power domain.
604    pub u32, pd_hp_periph, set_pd_hp_periph: 3;
605    /// Controls the power-down status of the CPU power domain.
606    pub u32, pd_cpu      , set_pd_cpu      : 4;
607    /// Controls the power-down status of the high-performance always-on domain.
608    pub u32, pd_hp_aon   , set_pd_hp_aon   : 5;
609    /// Controls the power-down status of memory group 0.
610    pub u32, pd_mem_g0   , set_pd_mem_g0   : 6;
611    /// Controls the power-down status of memory group 1.
612    pub u32, pd_mem_g1   , set_pd_mem_g1   : 7;
613    /// Controls the power-down status of memory group 2.
614    pub u32, pd_mem_g2   , set_pd_mem_g2   : 8;
615    /// Controls the power-down status of memory group 3.
616    pub u32, pd_mem_g3   , set_pd_mem_g3   : 9;
617    /// Controls the power-down status of the crystal oscillator.
618    pub u32, pd_xtal     , set_pd_xtal     : 10;
619    /// Controls the power-down status of the fast RC oscillator.
620    pub u32, pd_rc_fast  , set_pd_rc_fast  : 11;
621    /// Controls the power-down status of the 32kHz crystal oscillator.
622    pub u32, pd_xtal32k  , set_pd_xtal32k  : 12;
623    /// Controls the power-down status of the 32kHz RC oscillator.
624    pub u32, pd_rc32k    , set_pd_rc32k    : 13;
625    /// Controls the power-down status of the low-power peripheral domain.
626    pub u32, pd_lp_periph, set_pd_lp_periph: 14;
627}
628
629impl PowerDownFlags {
630    /// Checks whether all memory groups (G0, G1, G2, G3) are powered down.
631    pub fn pd_mem(self) -> bool {
632        self.pd_mem_g0() && self.pd_mem_g1() && self.pd_mem_g2() && self.pd_mem_g3()
633    }
634
635    /// Sets the power-down status for all memory groups (G0, G1, G2, G3) at
636    /// once.
637    pub fn set_pd_mem(&mut self, value: bool) {
638        self.set_pd_mem_g0(value);
639        self.set_pd_mem_g1(value);
640        self.set_pd_mem_g2(value);
641        self.set_pd_mem_g3(value);
642    }
643}
644
645// Constants defined in `PMU_SLEEP_MC_DEFAULT()`
646struct MachineConstants;
647impl MachineConstants {
648    const LP_MIN_SLP_TIME_US: u32 = 450;
649    const LP_WAKEUP_WAIT_CYCLE: u32 = 4;
650    const LP_ANALOG_WAIT_TIME_US: u32 = 154;
651    const LP_XTAL_WAIT_STABLE_TIME_US: u32 = 250;
652    const LP_CLK_SWITCH_CYCLE: u32 = 1;
653    const LP_CLK_POWER_ON_WAIT_CYCLE: u32 = 1;
654    const LP_POWER_SUPPLY_WAIT_TIME_US: u32 = 2;
655    const LP_POWER_UP_WAIT_TIME_US: u32 = 2;
656
657    const HP_MIN_SLP_TIME_US: u32 = 450;
658    const HP_CLOCK_DOMAIN_SYNC_TIME_US: u32 = 150;
659    const HP_SYSTEM_DFS_UP_WORK_TIME_US: u32 = 124;
660    const HP_ANALOG_WAIT_TIME_US: u32 = 154;
661    const HP_POWER_SUPPLY_WAIT_TIME_US: u32 = 2;
662    const HP_POWER_UP_WAIT_TIME_US: u32 = 2;
663    const HP_REGDMA_S2M_WORK_TIME_US: u32 = 172;
664    const HP_REGDMA_S2A_WORK_TIME_US: u32 = 480;
665    const HP_REGDMA_M2A_WORK_TIME_US: u32 = 278;
666    // Unused, but defined in esp-idf. May be needed later.
667    // const HP_REGDMA_A2S_WORK_TIME_US: u32 = 382;
668    const HP_REGDMA_RF_ON_WORK_TIME_US: u32 = 70;
669    // Unused, but defined in esp-idf. May be needed later.
670    // const HP_REGDMA_RF_OFF_WORK_TIME_US: u32 = 23;
671    const HP_XTAL_WAIT_STABLE_TIME_US: u32 = 250;
672    const HP_PLL_WAIT_STABLE_TIME_US: u32 = 1;
673
674    const MODEM_STATE_SKIP_TIME_US: u32 = Self::HP_REGDMA_M2A_WORK_TIME_US
675        + Self::HP_SYSTEM_DFS_UP_WORK_TIME_US
676        + Self::LP_MIN_SLP_TIME_US;
677}
678
679impl RtcSleepConfig {
680    /// Returns whether the device is in deep sleep mode.
681    pub fn deep_slp(&self) -> bool {
682        self.deep
683    }
684
685    /// Configures the device for deep sleep mode with ultra-low power settings.
686    pub fn deep() -> Self {
687        // Set up for ultra-low power sleep. Wakeup sources may modify these settings.
688        Self {
689            deep: true,
690            ..Self::default()
691        }
692    }
693
694    pub(crate) fn is_deep_sleep(&self) -> bool {
695        self.deep
696    }
697
698    pub(crate) fn set_sleep_kind(&mut self, kind: SleepKind) {
699        self.deep = kind == SleepKind::Deep;
700    }
701
702    pub(crate) fn base_settings(_rtc: &Rtc<'_>) {}
703
704    /// Finalize power-down flags, apply configuration based on the flags.
705    pub(crate) fn apply(&mut self) {
706        let lp_slow_uses_xtal32k = ClockTree::with(|clocks| {
707            matches!(
708                clocks::lp_slow_clk_config(clocks),
709                Some(LpSlowClkConfig::Xtal32k)
710            )
711        });
712
713        if self.deep {
714            // force-disable certain power domains
715            self.pd_flags.set_pd_top(true);
716            self.pd_flags.set_pd_vddsdio(true);
717            self.pd_flags.set_pd_modem(true);
718            self.pd_flags.set_pd_hp_periph(true);
719            self.pd_flags.set_pd_cpu(true);
720            self.pd_flags.set_pd_mem(true);
721            self.pd_flags.set_pd_xtal(true);
722            self.pd_flags.set_pd_hp_aon(true);
723            self.pd_flags.set_pd_lp_periph(true);
724            self.pd_flags.set_pd_xtal32k(!lp_slow_uses_xtal32k);
725            self.pd_flags.set_pd_rc32k(true);
726            self.pd_flags.set_pd_rc_fast(true);
727        } else {
728            // Light sleep: the digital domain (CPU, RAM, peripherals) stays powered
729            // and only clock-gated, so execution resumes in place. To cut power we
730            // turn off the analog clock sources that nothing needs while the core is
731            // gated. Powering down XTAL also makes the analog config drop the HP
732            // regulator to the 0.6 V light-sleep voltage instead of holding it at the
733            // active calibration voltage (see `AnalogSleepConfig::defaults_light_sleep`),
734            // which is the dominant saving.
735            self.pd_flags.set_pd_xtal(true);
736            self.pd_flags.set_pd_rc_fast(true);
737            self.pd_flags.set_pd_xtal32k(!lp_slow_uses_xtal32k);
738        }
739    }
740
741    /// Configures the wakeup options and requests the sleep.
742    ///
743    /// The caller waits for the result of the request. The return value is a guard that restores
744    /// what sleep entry changed for the sleep only, so the caller keeps it until the sleep ends.
745    pub(crate) fn start_sleep(&self, wakeup_mask: u32, reject_mask: u32) -> impl Sized {
746        let restore_clock_config = ClockTree::with(|clocks| {
747            let old_root = clocks.soc_root_clk();
748
749            clocks::configure_soc_root_clk(clocks, SocRootClkConfig::Xtal);
750
751            // Restore the old clock settings when we return
752            DropGuard::new((), move |_| {
753                ClockTree::with(|clocks| {
754                    if let Some(old_root) = old_root {
755                        clocks::configure_soc_root_clk(clocks, old_root);
756                    }
757                });
758            })
759        });
760
761        // pmu_sleep_config_default + pmu_sleep_init.
762
763        let power = PowerSleepConfig::defaults(self.pd_flags);
764        power.apply();
765
766        // Needs to happen after rtc_clk_cpu_freq_set_xtal
767        let config = if self.deep {
768            SleepTimeConfig::deep_sleep()
769        } else {
770            SleepTimeConfig::light_sleep(self.pd_flags)
771        };
772
773        let mut param =
774            ParamSleepConfig::defaults(config, self.pd_flags, power.hp_sys.xtal.xpd_xtal());
775
776        if self.deep {
777            const PMU_LP_ANALOG_WAIT_TARGET_TIME_DSLP_US: u32 = 500;
778            param.lp_sys.analog_wait_target_cycle =
779                config.us_to_slowclk(PMU_LP_ANALOG_WAIT_TARGET_TIME_DSLP_US) as u8;
780
781            AnalogSleepConfig::defaults_deep_sleep().apply();
782        } else {
783            AnalogSleepConfig::defaults_light_sleep(self.pd_flags).apply();
784            DigitalSleepConfig::defaults_light_sleep(self.pd_flags).apply();
785        }
786
787        param.apply();
788
789        // like esp-idf pmu_sleep_start()
790
791        // lp_aon_hal_inform_wakeup_type - tells ROM which wakeup stub to run
792        LP_AON::regs()
793            .store9()
794            .modify(|r, w| unsafe { w.bits(r.bits() & !0x01 | self.deep as u32) });
795
796        // pmu_ll_hp_set_wakeup_enable
797        PMU::regs()
798            .slp_wakeup_cntl2()
799            .write(|w| unsafe { w.bits(wakeup_mask) });
800
801        // pmu_ll_hp_set_reject_enable
802        PMU::regs().slp_wakeup_cntl1().modify(|_, w| unsafe {
803            w.slp_reject_en().bit(reject_mask != 0);
804            w.sleep_reject_ena().bits(reject_mask)
805        });
806
807        // pmu_ll_hp_clear_reject_cause
808        PMU::regs()
809            .slp_wakeup_cntl4()
810            .write(|w| w.slp_reject_cause_clr().bit(true));
811
812        PMU::regs().int_clr().write(|w| {
813            // pmu_ll_hp_clear_sw_intr_status
814            w.sw().clear_bit_by_one();
815            // pmu_ll_hp_clear_reject_intr_status
816            w.soc_sleep_reject().clear_bit_by_one();
817            // pmu_ll_hp_clear_wakeup_intr_status
818            w.soc_wakeup().clear_bit_by_one()
819        });
820
821        // misc_modules_sleep_prepare
822
823        // Start entry into sleep mode
824
825        // pmu_ll_hp_set_sleep_enable
826        PMU::regs()
827            .slp_wakeup_cntl0()
828            .write(|w| w.sleep_req().bit(true));
829
830        restore_clock_config
831    }
832
833    /// Cleans up after sleep
834    pub(crate) fn finish_sleep(&self) {
835        // like esp-idf pmu_sleep_finish()
836        // In "pd_cpu lightsleep" and "deepsleep" modes we never get here
837
838        // The post-wake hook of the GPIO driver releases the pads that the sleep armed. Only that
839        // driver knows which pads it prepared.
840    }
841}