Skip to main content

esp_hal/rtc_cntl/sleep/
esp32h2.rs

1use core::ops::Not;
2
3use crate::{
4    clock::RtcClock,
5    gpio::{AnyPin, Input, InputConfig, Pull, RtcPin},
6    peripherals::APB_SARADC,
7    rtc_cntl::{
8        Rtc,
9        rtc::{HpSysCntlReg, HpSysPower, LpSysPower},
10        sleep::{Ext1WakeupSource, TimerWakeupSource, WakeSource, WakeTriggers, WakeupLevel},
11    },
12    soc::clocks::{ClockTree, CpuClkConfig, HpRootClkConfig, TimgCalibrationClockConfig},
13};
14
15impl WakeSource for TimerWakeupSource {
16    fn apply(
17        &self,
18        rtc: &Rtc<'_>,
19        triggers: &mut WakeTriggers,
20        _sleep_config: &mut RtcSleepConfig,
21    ) {
22        triggers.set_timer(true);
23
24        let lp_timer = unsafe { &*esp32h2::LP_TIMER::ptr() };
25        // TODO: maybe add check to prevent overflow?
26        let ticks = crate::clock::us_to_rtc_ticks(self.duration.as_micros() as u64);
27        // "alarm" time in slow rtc ticks
28        let now = rtc.time_since_boot_raw();
29        let time_in_ticks = now + ticks;
30        unsafe {
31            lp_timer.tar0_high().write(|w| {
32                w.main_timer_tar_high0()
33                    .bits(((time_in_ticks >> 32) & 0xffff) as u16)
34            });
35            lp_timer.tar0_low().write(|w| {
36                w.main_timer_tar_low0()
37                    .bits((time_in_ticks & 0xffffffff) as u32)
38            });
39            lp_timer
40                .int_clr()
41                .write(|w| w.soc_wakeup_int_clr().set_bit());
42            lp_timer
43                .tar0_high()
44                .modify(|_, w| w.main_timer_tar_en0().set_bit());
45        }
46    }
47}
48
49impl Ext1WakeupSource<'_, '_> {
50    /// Returns the currently configured wakeup pins.
51    fn wakeup_pins() -> u8 {
52        unsafe { lp_aon().ext_wakeup_cntl().read().ext_wakeup_sel().bits() }
53    }
54
55    /// Resets the pins that had been configured as wakeup trigger to their default state.
56    fn wake_io_reset() {
57        fn uninit_pin(pin: impl RtcPin, wakeup_pins: u8) {
58            if wakeup_pins & (1 << pin.rtc_number()) != 0 {
59                pin.rtcio_pad_hold(false);
60                pin.degrade().init_gpio();
61            }
62        }
63
64        let wakeup_pins = Ext1WakeupSource::wakeup_pins();
65        for_each_lp_function! {
66            (($_rtc:ident, LP_GPIOn, $n:literal), $gpio:ident) => {
67                uninit_pin(unsafe { $crate::peripherals::$gpio::steal() }, wakeup_pins);
68            };
69        }
70    }
71}
72
73impl WakeSource for Ext1WakeupSource<'_, '_> {
74    fn apply(
75        &self,
76        _rtc: &Rtc<'_>,
77        triggers: &mut WakeTriggers,
78        sleep_config: &mut RtcSleepConfig,
79    ) {
80        triggers.set_ext1(true);
81        sleep_config.need_pd_top = true;
82
83        // ext1_wakeup_prepare
84        let mut pins = self.pins.borrow_mut();
85        let mut pin_mask = 0u8;
86        let mut level_mask = 0u8;
87        for (pin, level) in pins.iter_mut() {
88            pin_mask |= 1 << pin.rtc_number();
89            level_mask |= match level {
90                WakeupLevel::High => 1 << pin.rtc_number(),
91                WakeupLevel::Low => 0,
92            };
93
94            pin.rtcio_pad_hold(true);
95            Input::new(
96                unsafe { AnyPin::steal(pin.number()) },
97                InputConfig::default().with_pull(match level {
98                    WakeupLevel::High => Pull::Down,
99                    WakeupLevel::Low => Pull::Up,
100                }),
101            );
102        }
103
104        unsafe {
105            // clear previous wakeup status
106            lp_aon()
107                .ext_wakeup_cntl()
108                .modify(|_, w| w.ext_wakeup_status_clr().set_bit());
109
110            // set pin + level register fields
111            lp_aon().ext_wakeup_cntl().modify(|r, w| {
112                w.ext_wakeup_sel()
113                    .bits(r.ext_wakeup_sel().bits() | pin_mask)
114                    .ext_wakeup_lv()
115                    .bits(r.ext_wakeup_lv().bits() & !pin_mask | level_mask)
116            });
117        }
118    }
119}
120
121impl Drop for Ext1WakeupSource<'_, '_> {
122    fn drop(&mut self) {
123        // reset GPIOs to default state
124        let mut pins = self.pins.borrow_mut();
125        for (pin, _level) in pins.iter_mut() {
126            pin.rtcio_pad_hold(false);
127            unsafe { AnyPin::steal(pin.number()) }.init_gpio();
128        }
129    }
130}
131
132/// Configuration for controlling the behavior during sleep modes.
133#[derive(Clone, Copy)]
134// Note: from all the fields defined in ESP-IDF's `pmu_sleep_analog_config_t`
135// the only one documented in Hardware Technical Reference Manual is `hp_sys.analog.xpd`
136// (corresponds to `PMU_HP_ACTIVE_HP_REGULATOR_XPD` in the Manual). Therefore only this
137// field is stored as bool without any nested structures.
138pub struct AnalogSleepConfig {
139    /// High-power system configuration.
140    pub regulator_xpd: bool,
141}
142
143impl AnalogSleepConfig {
144    fn defaults_deep_sleep() -> Self {
145        Self {
146            // PMU_SLEEP_ANALOG_DSLP_CONFIG_DEFAULT
147            regulator_xpd: false,
148        }
149    }
150
151    fn defaults_light_sleep() -> Self {
152        Self {
153            // PMU_SLEEP_ANALOG_LSLP_CONFIG_DEFAULT
154            regulator_xpd: true,
155        }
156    }
157
158    fn apply(&self) {
159        // based on pmu_sleep_analog_init, with undocumented registers omitted
160        unsafe {
161            pmu().hp_sleep_hp_regulator0().modify(|_, w| {
162                w.hp_sleep_hp_regulator_xpd() // pmu_ll_hp_set_regulator_xpd
163                    .bit(self.regulator_xpd)
164            });
165        }
166    }
167}
168
169/// Configuration for controlling the behavior of digital peripherals during
170/// sleep modes.
171#[derive(Clone, Copy)]
172// pmu_sleep_digital_config_t
173pub struct DigitalSleepConfig {
174    /// High-power system control register configuration.
175    pub syscntl: HpSysCntlReg,
176    /// ICG function control flags.
177    pub icg_func: u32,
178    /// Indicates whether deep sleep mode is requested.
179    pub deep_sleep: bool,
180}
181
182impl DigitalSleepConfig {
183    fn defaults_light_sleep(pd_flags: PowerDownFlags) -> Self {
184        Self {
185            // PMU_SLEEP_DIGITAL_LSLP_CONFIG_DEFAULT
186            syscntl: {
187                let mut cfg = HpSysCntlReg::default();
188
189                cfg.set_dig_pad_slp_sel(pd_flags.pd_top().not());
190
191                cfg
192            },
193            icg_func: 0xffff_ffff, // TODO: ESP-IDF determines this using get_sleep_clock_icg_flags
194            deep_sleep: false,
195        }
196    }
197
198    fn defaults_deep_sleep() -> Self {
199        Self {
200            // PMU_SLEEP_DIGITAL_DSLP_CONFIG_DEFAULT
201            syscntl: HpSysCntlReg::default(),
202            icg_func: 0,
203            deep_sleep: true,
204        }
205    }
206
207    fn apply(&self) {
208        // pmu_sleep_digital_init
209        unsafe {
210            pmu().hp_sleep_sysclk().modify(|_, w| {
211                w.hp_sleep_icg_sys_clock_en().bit(self.icg_func != 0) // pmu_ll_hp_set_icg_sysclk_enable
212            });
213            pmu().hp_sleep_icg_hp_func().modify(|_, w| {
214                w.hp_sleep_dig_icg_func_en().bits(self.icg_func) // pmu_ll_hp_set_icg_func
215            });
216            if !self.deep_sleep {
217                pmu().hp_sleep_hp_sys_cntl().modify(|_, w| {
218                    w.hp_sleep_dig_pad_slp_sel()
219                        .bit(self.syscntl.dig_pad_slp_sel()) // pmu_ll_hp_set_dig_pad_slp_sel
220                });
221            }
222        };
223    }
224}
225
226/// Configuration for controlling the power settings of high-power and low-power
227/// systems during sleep modes.
228#[derive(Clone, Copy)]
229// pmu_sleep_power_config_t
230pub struct PowerSleepConfig {
231    /// Power configuration for the high-power system during sleep.
232    pub hp_sys: HpSysPower,
233    /// Power configuration for the low-power system when it is active.
234    pub lp_sys_active: LpSysPower,
235    /// Power configuration for the low-power system when it is in sleep mode.
236    pub lp_sys_sleep: LpSysPower,
237}
238
239impl PowerSleepConfig {
240    fn defaults(pd_flags: PowerDownFlags) -> Self {
241        let mut this = Self {
242            hp_sys: HpSysPower::default(),
243            lp_sys_active: LpSysPower::default(),
244            lp_sys_sleep: LpSysPower::default(),
245        };
246        this.apply_flags(pd_flags);
247        this
248    }
249
250    fn apply_flags(&mut self, pd_flags: PowerDownFlags) {
251        // PMU_SLEEP_POWER_CONFIG_DEFAULT
252        self.hp_sys
253            .dig_power
254            .set_vdd_spi_pd_en(pd_flags.pd_vddsdio());
255        self.hp_sys.dig_power.set_modem_pd_en(pd_flags.pd_modem());
256        self.hp_sys.dig_power.set_cpu_pd_en(pd_flags.pd_cpu());
257        self.hp_sys.dig_power.set_top_pd_en(pd_flags.pd_top());
258
259        self.hp_sys.clk.set_xpd_bbpll(false);
260
261        self.hp_sys.xtal.set_xpd_xtal(pd_flags.pd_xtal().not());
262
263        self.lp_sys_active.clk_power.set_xpd_xtal32k(true);
264        self.lp_sys_active.clk_power.set_xpd_fosc(true);
265
266        self.lp_sys_sleep.dig_power.set_mem_dslp(true);
267        self.lp_sys_sleep.dig_power.set_vddbat_mode(0);
268        self.lp_sys_sleep.dig_power.set_bod_source_sel(true);
269
270        self.lp_sys_sleep
271            .clk_power
272            .set_xpd_xtal32k(pd_flags.pd_xtal32k().not());
273        self.lp_sys_sleep
274            .clk_power
275            .set_xpd_fosc(pd_flags.pd_rc_fast().not());
276
277        self.lp_sys_sleep
278            .xtal
279            .set_xpd_xtal(pd_flags.pd_xtal().not());
280    }
281
282    fn apply(&self) {
283        // pmu_sleep_power_init
284        unsafe {
285            // HP SLEEP (hp_sleep_*)
286            pmu()
287                .hp_sleep_dig_power()
288                .modify(|_, w| w.bits(self.hp_sys.dig_power.0));
289            pmu()
290                .hp_sleep_hp_ck_power()
291                .modify(|_, w| w.bits(self.hp_sys.clk.0));
292            pmu()
293                .hp_sleep_xtal()
294                .modify(|_, w| w.hp_sleep_xpd_xtal().bit(self.hp_sys.xtal.xpd_xtal()));
295
296            // LP ACTIVE (hp_sleep_lp_*)
297            pmu()
298                .hp_sleep_lp_dig_power()
299                .modify(|_, w| w.bits(self.lp_sys_active.dig_power.0));
300            pmu()
301                .hp_sleep_lp_ck_power()
302                .modify(|_, w| w.bits(self.lp_sys_active.clk_power.0));
303
304            // LP SLEEP (lp_sleep_*)
305            pmu()
306                .lp_sleep_lp_dig_power()
307                .modify(|_, w| w.bits(self.lp_sys_sleep.dig_power.0));
308            pmu()
309                .lp_sleep_lp_ck_power()
310                .modify(|_, w| w.bits(self.lp_sys_sleep.clk_power.0));
311            pmu()
312                .lp_sleep_xtal()
313                .modify(|_, w| w.lp_sleep_xpd_xtal().bit(self.lp_sys_sleep.xtal.xpd_xtal()));
314        }
315    }
316}
317
318/// Parameters for high-power system configurations during sleep modes.
319#[derive(Clone, Copy)]
320// pmu_hp_param_t
321pub struct HpParam {
322    /// Number of cycles to wait for the modem to wake up.
323    pub modem_wakeup_wait_cycle: u32,
324    /// Number of cycles to wait for the analog component stabilization.
325    pub analog_wait_target_cycle: u16,
326    /// Number of cycles to wait for the digital power-down sequence.
327    pub digital_power_down_wait_cycle: u16,
328    /// Number of cycles to wait for the digital power supply to stabilize.
329    pub digital_power_supply_wait_cycle: u16,
330    /// Number of cycles to wait for the digital power-up sequence.
331    pub digital_power_up_wait_cycle: u16,
332    /// Number of cycles to wait for the PLL to stabilize.
333    pub pll_stable_wait_cycle: u16,
334    /// Number of cycles to wait for modifying the ICG control.
335    pub modify_icg_cntl_wait_cycle: u8,
336    /// Number of cycles to wait for switching the ICG control.
337    pub switch_icg_cntl_wait_cycle: u8,
338    /// Minimum sleep time measured in slow clock cycles.
339    pub min_slp_slow_clk_cycle: u8,
340}
341
342/// Parameters for low-power system configurations during sleep modes.
343#[derive(Clone, Copy)]
344// pmu_lp_param_t
345pub struct LpParam {
346    /// Number of cycles to wait for the digital power supply to stabilize.
347    pub digital_power_supply_wait_cycle: u16,
348    /// Minimum sleep time measured in slow clock cycles.
349    pub min_slp_slow_clk_cycle: u8,
350    /// Number of cycles to wait for the analog component stabilization.
351    pub analog_wait_target_cycle: u8,
352    /// Number of cycles to wait for the digital power-down sequence.
353    pub digital_power_down_wait_cycle: u8,
354    /// Number of cycles to wait for the digital power-up sequence.
355    pub digital_power_up_wait_cycle: u8,
356}
357
358/// Parameters for high-power and low-power system configurations during sleep
359/// modes.
360#[derive(Clone, Copy)]
361// pmu_hp_lp_param_t
362pub struct HpLpParam {
363    /// Union of two u16 variants
364    pub xtal_stable_wait_cycle: u16,
365}
366
367/// Configuration of parameters for sleep modes
368#[derive(Clone, Copy)]
369// pmu_sleep_param_config_t
370pub struct ParamSleepConfig {
371    /// Configuration of high-power system parameters.
372    pub hp_sys: HpParam,
373    /// Configuration of low-power system parameters.
374    pub lp_sys: LpParam,
375    /// Shared configuration parameters for high-power and low-power systems.
376    pub hp_lp: HpLpParam,
377}
378impl ParamSleepConfig {
379    const PMU_SLEEP_PARAM_CONFIG_DEFAULT: Self = Self {
380        hp_sys: HpParam {
381            min_slp_slow_clk_cycle: 10,
382            analog_wait_target_cycle: 1700,
383            digital_power_supply_wait_cycle: 32,
384            digital_power_up_wait_cycle: 32,
385            modem_wakeup_wait_cycle: 0,
386            pll_stable_wait_cycle: 2,
387
388            digital_power_down_wait_cycle: 0,
389            modify_icg_cntl_wait_cycle: 0,
390            switch_icg_cntl_wait_cycle: 0,
391        },
392        lp_sys: LpParam {
393            min_slp_slow_clk_cycle: 10,
394            analog_wait_target_cycle: 15,
395            digital_power_supply_wait_cycle: 32,
396            digital_power_up_wait_cycle: 32,
397
398            digital_power_down_wait_cycle: 0,
399        },
400        hp_lp: HpLpParam {
401            xtal_stable_wait_cycle: 30,
402        },
403    };
404
405    fn apply(&self) {
406        // pmu_sleep_param_init
407        unsafe {
408            pmu().slp_wakeup_cntl3().modify(|_, w| {
409                w.hp_min_slp_val() // pmu_ll_hp_set_min_sleep_cycle
410                    .bits(self.hp_sys.min_slp_slow_clk_cycle)
411                    .lp_min_slp_val() // pmu_ll_lp_set_min_sleep_cycle
412                    .bits(self.lp_sys.min_slp_slow_clk_cycle)
413            });
414
415            pmu().slp_wakeup_cntl7().modify(|_, w| {
416                w.ana_wait_target() // pmu_ll_hp_set_analog_wait_target_cycle
417                    .bits(self.hp_sys.analog_wait_target_cycle)
418            });
419
420            pmu().slp_wakeup_cntl5().modify(|_, w| {
421                w.lp_ana_wait_target() // pmu_ll_lp_set_analog_wait_target_cycle
422                    .bits(self.lp_sys.analog_wait_target_cycle)
423            });
424
425            pmu().power_wait_timer0().modify(|_, w| {
426                w.dg_hp_wait_timer() // pmu_ll_hp_set_digital_power_supply_wait_cycle
427                    .bits(self.hp_sys.digital_power_supply_wait_cycle)
428                    .dg_hp_powerup_timer() // pmu_ll_hp_set_digital_power_up_wait_cycle
429                    .bits(self.hp_sys.digital_power_up_wait_cycle)
430            });
431
432            pmu().power_wait_timer1().modify(|_, w| {
433                w.dg_lp_wait_timer() // pmu_ll_lp_set_digital_power_supply_wait_cycle
434                    .bits(self.lp_sys.digital_power_supply_wait_cycle)
435                    .dg_lp_powerup_timer() // pmu_ll_lp_set_digital_power_up_wait_cycle
436                    .bits(self.lp_sys.digital_power_up_wait_cycle)
437            });
438
439            pmu().power_ck_wait_cntl().modify(|_, w| {
440                w.wait_xtl_stable() // pmu_ll_set_xtal_stable_wait_cycle
441                    .bits(self.hp_lp.xtal_stable_wait_cycle)
442                    .wait_pll_stable() // pmu_ll_set_pll_stable_wait_cycle
443                    .bits(self.hp_sys.pll_stable_wait_cycle)
444            });
445        }
446    }
447
448    fn defaults(config: SleepTimeConfig, pd_xtal: bool) -> Self {
449        let mut param = Self::PMU_SLEEP_PARAM_CONFIG_DEFAULT;
450
451        // pmu_sleep_param_config_default
452        param.hp_sys.min_slp_slow_clk_cycle =
453            config.us_to_slowclk(machine_constants::HP_MIN_SLP_TIME_US) as u8;
454        param.hp_sys.analog_wait_target_cycle =
455            config.us_to_fastclk(machine_constants::HP_ANALOG_WAIT_TIME_US) as u16;
456        param.hp_sys.digital_power_supply_wait_cycle =
457            config.us_to_fastclk(machine_constants::HP_POWER_SUPPLY_WAIT_TIME_US) as u16;
458        param.hp_sys.digital_power_up_wait_cycle =
459            config.us_to_fastclk(machine_constants::HP_POWER_UP_WAIT_TIME_US) as u16;
460        param.hp_sys.pll_stable_wait_cycle =
461            config.us_to_fastclk(machine_constants::HP_PLL_WAIT_STABLE_TIME_US) as u16;
462
463        param.lp_sys.min_slp_slow_clk_cycle =
464            config.us_to_slowclk(machine_constants::LP_MIN_SLP_TIME_US) as u8;
465        param.lp_sys.analog_wait_target_cycle =
466            config.us_to_slowclk(machine_constants::LP_ANALOG_WAIT_TIME_US) as u8;
467        param.lp_sys.digital_power_supply_wait_cycle =
468            config.us_to_fastclk(machine_constants::LP_POWER_SUPPLY_WAIT_TIME_US) as u16;
469        param.lp_sys.digital_power_up_wait_cycle =
470            config.us_to_fastclk(machine_constants::LP_POWER_UP_WAIT_TIME_US) as u8;
471
472        // This looks different from esp-idf but it is the same:
473        // Both `xtal_stable_wait_cycle` and `xtal_stable_wait_slow_clk_cycle` are
474        // u16 variants of the same union
475        param.hp_lp.xtal_stable_wait_cycle = if pd_xtal {
476            config.us_to_slowclk(machine_constants::LP_XTAL_WAIT_STABLE_TIME_US) as u16
477        } else {
478            config.us_to_fastclk(machine_constants::HP_XTAL_WAIT_STABLE_TIME_US) as u16
479        };
480
481        param
482    }
483}
484
485#[derive(Clone, Copy)]
486struct SleepTimeConfig {
487    sleep_time_adjustment: u32, // TODO: use this adjustment for calculating wakeup time
488    slowclk_period: u32,
489    fastclk_period: u32,
490}
491
492const CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ: u32 = 96;
493
494impl SleepTimeConfig {
495    const RTC_CLK_CAL_FRACT: u32 = 19;
496
497    fn rtc_clk_cal_fast(slowclk_cycles: u32) -> u32 {
498        RtcClock::calibrate(TimgCalibrationClockConfig::RcFastDivClk, slowclk_cycles)
499    }
500
501    fn new() -> Self {
502        let slowclk_period = unsafe { lp_aon().store1().read().data().bits() };
503
504        // Calibrate rtc fast clock, only PMU supported chips sleep process is needed.
505        const FAST_CLK_SRC_CAL_CYCLES: u32 = 2048;
506        let fastclk_period = Self::rtc_clk_cal_fast(FAST_CLK_SRC_CAL_CYCLES);
507
508        Self {
509            sleep_time_adjustment: 0,
510            slowclk_period,
511            fastclk_period,
512        }
513    }
514
515    fn light_sleep(pd_flags: PowerDownFlags) -> Self {
516        const LIGHT_SLEEP_TIME_OVERHEAD_US: u32 = 9;
517
518        let mut this = Self::new();
519
520        let sw = LIGHT_SLEEP_TIME_OVERHEAD_US;
521        let hw = this.pmu_sleep_calculate_hw_wait_time(pd_flags);
522
523        this.sleep_time_adjustment = sw + hw;
524
525        this
526    }
527
528    fn deep_sleep() -> Self {
529        let mut this = Self::new();
530
531        this.sleep_time_adjustment = 250 + 100 * 240 / CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ;
532
533        this
534    }
535
536    fn us_to_slowclk(&self, us: u32) -> u32 {
537        (us << Self::RTC_CLK_CAL_FRACT) / self.slowclk_period
538    }
539
540    fn slowclk_to_us(&self, rtc_cycles: u32) -> u32 {
541        (rtc_cycles * self.slowclk_period) >> Self::RTC_CLK_CAL_FRACT
542    }
543
544    fn us_to_fastclk(&self, us: u32) -> u32 {
545        (us << Self::RTC_CLK_CAL_FRACT) / self.fastclk_period
546    }
547
548    fn pmu_sleep_calculate_hw_wait_time(&self, pd_flags: PowerDownFlags) -> u32 {
549        let lp_clk_switch_time_us = self.slowclk_to_us(machine_constants::LP_CLK_SWITCH_CYCLE);
550        let lp_clk_power_on_wait_time_us = if pd_flags.pd_xtal() {
551            machine_constants::LP_XTAL_WAIT_STABLE_TIME_US
552        } else {
553            self.slowclk_to_us(machine_constants::LP_CLK_POWER_ON_WAIT_CYCLE)
554        };
555
556        let lp_hw_wait_time_us = machine_constants::LP_MIN_SLP_TIME_US
557            + machine_constants::LP_ANALOG_WAIT_TIME_US
558            + lp_clk_power_on_wait_time_us
559            + lp_clk_switch_time_us
560            + machine_constants::LP_POWER_SUPPLY_WAIT_TIME_US
561            + machine_constants::LP_POWER_UP_WAIT_TIME_US;
562
563        let hp_digital_power_up_wait_time_us = machine_constants::HP_POWER_SUPPLY_WAIT_TIME_US
564            + machine_constants::HP_POWER_UP_WAIT_TIME_US;
565        let hp_regdma_wait_time_us = if pd_flags.pd_top() {
566            machine_constants::HP_REGDMA_S2A_WORK_TIME_PD_TOP_US
567        } else {
568            machine_constants::HP_REGDMA_S2A_WORK_TIME_PU_TOP_US
569        };
570        let hp_clock_wait_time_us = machine_constants::HP_XTAL_WAIT_STABLE_TIME_US
571            + machine_constants::HP_PLL_WAIT_STABLE_TIME_US;
572
573        let hp_hw_wait_time_us = machine_constants::HP_ANALOG_WAIT_TIME_US
574            + hp_digital_power_up_wait_time_us
575            + hp_regdma_wait_time_us
576            + hp_clock_wait_time_us;
577
578        lp_hw_wait_time_us + hp_hw_wait_time_us
579    }
580}
581
582/// Configuration for the RTC sleep behavior.
583#[derive(Clone, Copy)]
584pub struct RtcSleepConfig {
585    /// Deep Sleep flag
586    pub deep: bool,
587    /// Power Down flags
588    pub pd_flags: PowerDownFlags,
589    /// Indicates whether the top power domain should remain enabled during sleep.
590    need_pd_top: bool,
591}
592
593impl Default for RtcSleepConfig {
594    fn default() -> Self {
595        // from pmu_sleep_param_config_default
596        // sleep flags will be applied by wakeup methods and apply
597
598        Self {
599            deep: false,
600            pd_flags: PowerDownFlags(0),
601            need_pd_top: false,
602        }
603    }
604}
605
606unsafe fn pmu<'a>() -> &'a esp32h2::pmu::RegisterBlock {
607    unsafe { &*esp32h2::PMU::ptr() }
608}
609
610unsafe fn lp_aon<'a>() -> &'a esp32h2::lp_aon::RegisterBlock {
611    unsafe { &*esp32h2::LP_AON::ptr() }
612}
613
614bitfield::bitfield! {
615    #[derive(Clone, Copy)]
616    /// Power domains to be powered down during sleep
617    pub struct PowerDownFlags(u32);
618
619    /// Controls the power-down status of the top power domain.
620    pub u32, pd_top      , set_pd_top      : 0;
621    /// Controls the power-down status of the VDD_SDIO power domain.
622    pub u32, pd_vddsdio  , set_pd_vddsdio  : 1;
623    /// Controls the power-down status of the modem power domain.
624    pub u32, pd_modem    , set_pd_modem    : 2;
625    /// Controls the power-down status of the CPU power domain.
626    pub u32, pd_cpu      , set_pd_cpu      : 3;
627    /// Controls the power-down status of the crystal oscillator.
628    pub u32, pd_xtal     , set_pd_xtal     : 4;
629    /// Controls the power-down status of the fast RC oscillator.
630    pub u32, pd_rc_fast  , set_pd_rc_fast  : 5;
631    /// Controls the power-down status of the 32kHz crystal oscillator.
632    pub u32, pd_xtal32k  , set_pd_xtal32k  : 6;
633}
634
635/// Constants defined in `PMU_SLEEP_MC_DEFAULT()`
636mod machine_constants {
637    pub const LP_MIN_SLP_TIME_US: u32 = 450;
638    pub const LP_ANALOG_WAIT_TIME_US: u32 = 154;
639    pub const LP_XTAL_WAIT_STABLE_TIME_US: u32 = 250;
640    pub const LP_CLK_SWITCH_CYCLE: u32 = 1;
641    pub const LP_CLK_POWER_ON_WAIT_CYCLE: u32 = 1;
642    pub const LP_POWER_SUPPLY_WAIT_TIME_US: u32 = 2;
643    pub const LP_POWER_UP_WAIT_TIME_US: u32 = 2;
644
645    pub const HP_MIN_SLP_TIME_US: u32 = 450;
646    pub const HP_ANALOG_WAIT_TIME_US: u32 = 154;
647    pub const HP_POWER_SUPPLY_WAIT_TIME_US: u32 = 2;
648    pub const HP_POWER_UP_WAIT_TIME_US: u32 = 2;
649    pub const HP_REGDMA_S2A_WORK_TIME_PD_TOP_US: u32 = 480;
650    pub const HP_REGDMA_S2A_WORK_TIME_PU_TOP_US: u32 = 390;
651    pub const HP_XTAL_WAIT_STABLE_TIME_US: u32 = 250;
652    pub const HP_PLL_WAIT_STABLE_TIME_US: u32 = 50;
653}
654
655impl RtcSleepConfig {
656    /// Returns whether the device is in deep sleep mode.
657    pub fn deep_slp(&self) -> bool {
658        self.deep
659    }
660
661    /// Configures the device for deep sleep mode with ultra-low power settings.
662    pub fn deep() -> Self {
663        // Set up for ultra-low power sleep. Wakeup sources may modify these settings.
664        Self {
665            deep: true,
666            ..Self::default()
667        }
668    }
669
670    pub(crate) fn base_settings(_rtc: &Rtc<'_>) {
671        Self::wake_io_reset();
672    }
673
674    fn wake_io_reset() {
675        Ext1WakeupSource::wake_io_reset();
676    }
677
678    /// Finalize power-down flags, apply configuration based on the flags.
679    pub(crate) fn apply(&mut self) {
680        if self.deep {
681            // force-disable certain power domains
682            self.pd_flags.set_pd_top(self.need_pd_top.not());
683            self.pd_flags.set_pd_vddsdio(true);
684            self.pd_flags.set_pd_modem(true);
685            self.pd_flags.set_pd_cpu(true);
686            self.pd_flags.set_pd_xtal(true);
687            self.pd_flags.set_pd_rc_fast(true);
688            self.pd_flags.set_pd_xtal32k(true);
689        } else if self.need_pd_top {
690            self.pd_flags.set_pd_top(false);
691        }
692    }
693
694    /// Configures wakeup options and enters sleep.
695    ///
696    /// This function does not return if deep sleep is requested.
697    pub(crate) fn start_sleep(&self, wakeup_triggers: WakeTriggers) {
698        const PMU_EXT1_WAKEUP_EN: u32 = 1 << 1;
699        const PMU_GPIO_WAKEUP_EN: u32 = 1 << 2;
700        const PMU_LP_TIMER_WAKEUP_EN: u32 = 1 << 4;
701        const PMU_UART0_WAKEUP_EN: u32 = 1 << 6;
702        const PMU_UART1_WAKEUP_EN: u32 = 1 << 7;
703        const PMU_BLE_SOC_WAKEUP_EN: u32 = 1 << 10;
704
705        const RTC_SLEEP_REJECT_MASK: u32 = PMU_EXT1_WAKEUP_EN
706            | PMU_GPIO_WAKEUP_EN
707            | PMU_LP_TIMER_WAKEUP_EN
708            | PMU_UART0_WAKEUP_EN
709            | PMU_UART1_WAKEUP_EN
710            | PMU_BLE_SOC_WAKEUP_EN;
711
712        let wakeup_mask = wakeup_triggers.0 as u32;
713        let reject_mask = if self.deep {
714            0
715        } else {
716            let reject_mask = RTC_SLEEP_REJECT_MASK;
717            wakeup_mask & reject_mask
718        };
719
720        let cpu_freq_config = ClockTree::with(|clocks| {
721            let cpu_freq_config = SavedClockConfig::save(clocks);
722            crate::soc::clocks::configure_hp_root_clk(
723                clocks,
724                crate::soc::clocks::HpRootClkConfig::Xtal,
725            );
726            crate::soc::clocks::configure_cpu_clk(clocks, crate::soc::clocks::CpuClkConfig::new(0));
727            cpu_freq_config
728        });
729
730        // misc_modules_sleep_prepare
731        // TODO: IDF-7370
732
733        APB_SARADC::regs()
734            .ctrl()
735            .modify(|_, w| w.saradc2_pwdet_drv().bit(false));
736
737        // pmu_sleep_config_default + pmu_sleep_init
738
739        let power = PowerSleepConfig::defaults(self.pd_flags);
740        power.apply();
741
742        // Needs to happen after rtc_clk_cpu_freq_set_xtal
743        let config = if self.deep {
744            SleepTimeConfig::deep_sleep()
745        } else {
746            SleepTimeConfig::light_sleep(self.pd_flags)
747        };
748
749        let param = ParamSleepConfig::defaults(config, power.hp_sys.xtal.xpd_xtal());
750
751        if self.deep {
752            DigitalSleepConfig::defaults_deep_sleep().apply();
753            AnalogSleepConfig::defaults_deep_sleep().apply();
754        } else {
755            DigitalSleepConfig::defaults_light_sleep(self.pd_flags).apply();
756            AnalogSleepConfig::defaults_light_sleep().apply();
757        }
758
759        param.apply();
760
761        // pmu_sleep_start
762
763        unsafe {
764            // lp_aon_hal_inform_wakeup_type - tells ROM which wakeup stub to run
765            lp_aon()
766                .store9()
767                .modify(|r, w| w.bits(r.bits() & !0x01 | self.deep as u32));
768
769            // pmu_ll_hp_set_wakeup_enable
770            pmu().slp_wakeup_cntl2().write(|w| w.bits(wakeup_mask));
771
772            // pmu_ll_hp_set_reject_enable
773            pmu().slp_wakeup_cntl1().modify(|_, w| {
774                w.slp_reject_en()
775                    .bit(true)
776                    .sleep_reject_ena()
777                    .bits(reject_mask)
778            });
779
780            pmu().int_clr().write(|w| {
781                w.soc_wakeup() // pmu_ll_hp_clear_wakeup_intr_status
782                    .clear_bit_by_one()
783                    .soc_sleep_reject() // pmu_ll_hp_clear_reject_intr_status
784                    .clear_bit_by_one()
785            });
786
787            // pmu_ll_hp_clear_reject_cause
788            pmu()
789                .slp_wakeup_cntl4()
790                .write(|w| w.slp_reject_cause_clr().bit(true));
791
792            // Start entry into sleep mode
793
794            // pmu_ll_hp_set_sleep_enable
795            pmu().slp_wakeup_cntl0().write(|w| w.sleep_req().bit(true));
796
797            loop {
798                let int_raw = pmu().int_raw().read();
799                if int_raw.soc_wakeup().bit_is_set() || int_raw.soc_sleep_reject().bit_is_set() {
800                    break;
801                }
802            }
803        }
804
805        // esp-idf returns if the sleep was rejected, we don't return anything
806
807        ClockTree::with(|clocks| {
808            cpu_freq_config.restore(clocks);
809        });
810    }
811
812    /// Cleans up after sleep
813    pub(crate) fn finish_sleep(&self) {
814        Self::wake_io_reset();
815    }
816}
817
818#[derive(Clone, Copy)]
819pub(crate) struct SavedClockConfig {
820    /// The clock from which CPU clock is derived
821    old_hp_root_clk: Option<HpRootClkConfig>,
822
823    /// CPU divider
824    old_cpu_divider: Option<CpuClkConfig>,
825}
826
827impl SavedClockConfig {
828    pub(crate) fn save(clocks: &ClockTree) -> Self {
829        let old_hp_root_clk = clocks.hp_root_clk();
830        let old_cpu_divider = clocks.cpu_clk();
831
832        SavedClockConfig {
833            old_hp_root_clk,
834            old_cpu_divider,
835        }
836    }
837
838    // rtc_clk_cpu_freq_set_config
839    pub(crate) fn restore(self, clocks: &mut ClockTree) {
840        if let Some(old_hp_root_clk) = self.old_hp_root_clk {
841            crate::soc::clocks::configure_hp_root_clk(clocks, old_hp_root_clk);
842        }
843        if let Some(old_cpu_divider) = self.old_cpu_divider {
844            crate::soc::clocks::configure_cpu_clk(clocks, old_cpu_divider);
845        }
846    }
847}