Skip to main content

esp_hal/rtc_cntl/sleep/
esp32c6.rs

1use core::ops::Not;
2
3use crate::{
4    clock::RtcClock,
5    gpio::RtcFunction,
6    rtc_cntl::{
7        Rtc,
8        rtc::{HpAnalog, HpSysCntlReg, HpSysPower, LpAnalog, LpSysPower, SavedClockConfig},
9        sleep::{
10            Ext1WakeupSource,
11            TimerWakeupSource,
12            WakeFromLpCoreWakeupSource,
13            WakeSource,
14            WakeTriggers,
15            WakeupLevel,
16        },
17    },
18    soc::clocks::{ClockTree, TimgCalibrationClockConfig},
19};
20
21impl WakeSource for TimerWakeupSource {
22    fn apply(
23        &self,
24        rtc: &Rtc<'_>,
25        triggers: &mut WakeTriggers,
26        _sleep_config: &mut RtcSleepConfig,
27    ) {
28        triggers.set_timer(true);
29
30        let lp_timer = unsafe { &*esp32c6::LP_TIMER::ptr() };
31        // TODO: maybe add check to prevent overflow?
32        let ticks = crate::clock::us_to_rtc_ticks(self.duration.as_micros() as u64);
33        // "alarm" time in slow rtc ticks
34        let now = rtc.time_since_boot_raw();
35        let time_in_ticks = now + ticks;
36        unsafe {
37            lp_timer.tar0_high().write(|w| {
38                w.main_timer_tar_high0()
39                    .bits(((time_in_ticks >> 32) & 0xffff) as u16)
40            });
41            lp_timer.tar0_low().write(|w| {
42                w.main_timer_tar_low0()
43                    .bits((time_in_ticks & 0xffffffff) as u32)
44            });
45            lp_timer
46                .int_clr()
47                .write(|w| w.soc_wakeup().clear_bit_by_one());
48            lp_timer
49                .tar0_high()
50                .modify(|_, w| w.main_timer_tar_en0().set_bit());
51        }
52    }
53}
54
55impl Ext1WakeupSource<'_, '_> {
56    /// Returns the currently configured wakeup pins.
57    fn wakeup_pins() -> u8 {
58        unsafe { lp_aon().ext_wakeup_cntl().read().ext_wakeup_sel().bits() }
59    }
60
61    fn wake_io_reset() {
62        use crate::gpio::RtcPin;
63
64        fn uninit_pin(pin: impl RtcPin, wakeup_pins: u8) {
65            if wakeup_pins & (1 << pin.number()) != 0 {
66                pin.rtcio_pad_hold(false);
67                pin.rtc_set_config(false, false, RtcFunction::Rtc);
68            }
69        }
70
71        let wakeup_pins = Ext1WakeupSource::wakeup_pins();
72        uninit_pin(unsafe { crate::peripherals::GPIO0::steal() }, wakeup_pins);
73        uninit_pin(unsafe { crate::peripherals::GPIO1::steal() }, wakeup_pins);
74        uninit_pin(unsafe { crate::peripherals::GPIO2::steal() }, wakeup_pins);
75        uninit_pin(unsafe { crate::peripherals::GPIO3::steal() }, wakeup_pins);
76        uninit_pin(unsafe { crate::peripherals::GPIO4::steal() }, wakeup_pins);
77        uninit_pin(unsafe { crate::peripherals::GPIO5::steal() }, wakeup_pins);
78        uninit_pin(unsafe { crate::peripherals::GPIO6::steal() }, wakeup_pins);
79        uninit_pin(unsafe { crate::peripherals::GPIO7::steal() }, wakeup_pins);
80    }
81}
82
83impl WakeSource for Ext1WakeupSource<'_, '_> {
84    fn apply(
85        &self,
86        _rtc: &Rtc<'_>,
87        triggers: &mut WakeTriggers,
88        _sleep_config: &mut RtcSleepConfig,
89    ) {
90        // We don't have to keep the LP domain powered if we hold the wakeup pin states.
91        triggers.set_ext1(true);
92
93        // set pins to RTC function
94        let mut pins = self.pins.borrow_mut();
95        let mut pin_mask = 0u8;
96        let mut level_mask = 0u8;
97        for (pin, level) in pins.iter_mut() {
98            pin_mask |= 1 << pin.number();
99            level_mask |= match level {
100                WakeupLevel::High => 1 << pin.number(),
101                WakeupLevel::Low => 0,
102            };
103
104            pin.rtc_set_config(true, true, RtcFunction::Rtc);
105            pin.rtcio_pad_hold(true);
106        }
107
108        unsafe {
109            // clear previous wakeup status
110            lp_aon()
111                .ext_wakeup_cntl()
112                .modify(|_, w| w.ext_wakeup_status_clr().set_bit());
113
114            // set pin + level register fields
115            lp_aon().ext_wakeup_cntl().modify(|r, w| {
116                w.ext_wakeup_sel()
117                    .bits(r.ext_wakeup_sel().bits() | pin_mask)
118                    .ext_wakeup_lv()
119                    .bits(r.ext_wakeup_lv().bits() & !pin_mask | level_mask)
120            });
121        }
122    }
123}
124
125impl Drop for Ext1WakeupSource<'_, '_> {
126    fn drop(&mut self) {
127        // should we have saved the pin configuration first?
128        // set pin back to IO_MUX (input_enable and func have no effect when pin is sent
129        // to IO_MUX)
130        let mut pins = self.pins.borrow_mut();
131        for (pin, _level) in pins.iter_mut() {
132            pin.rtc_set_config(true, false, RtcFunction::Rtc);
133        }
134    }
135}
136
137impl WakeSource for WakeFromLpCoreWakeupSource {
138    fn apply(
139        &self,
140        _rtc: &Rtc<'_>,
141        triggers: &mut WakeTriggers,
142        _sleep_config: &mut RtcSleepConfig,
143    ) {
144        triggers.set_lp_core(true);
145    }
146}
147
148/// Configuration for controlling the behavior during sleep modes.
149#[derive(Clone, Copy)]
150// pmu_sleep_analog_config_t
151pub struct AnalogSleepConfig {
152    /// High-power system configuration.
153    pub hp_sys: HpAnalog,
154    // pub lp_sys_active: LpAnalog, // unused
155    /// Low-power system analog configuration.
156    pub lp_sys_sleep: LpAnalog,
157}
158
159impl AnalogSleepConfig {
160    fn defaults_deep_sleep() -> Self {
161        Self {
162            // PMU_SLEEP_ANALOG_DSLP_CONFIG_DEFAULT
163            hp_sys: {
164                let mut cfg = HpAnalog::default();
165
166                cfg.bias.set_pd_cur(false);
167                cfg.bias.set_bias_sleep(false);
168                cfg.regulator0.set_xpd(false);
169                cfg.bias.set_dbg_atten(0);
170
171                cfg
172            },
173            // lp_sys_active: LpAnalog::default(),
174            lp_sys_sleep: {
175                let mut cfg = LpAnalog::default();
176
177                cfg.regulator1.set_drv_b(0);
178                cfg.bias.set_pd_cur(true);
179                cfg.bias.set_bias_sleep(true);
180                cfg.regulator0.set_slp_xpd(false);
181                cfg.regulator0.set_slp_dbias(0);
182                cfg.regulator0.set_xpd(true);
183                cfg.bias.set_dbg_atten(12);
184                cfg.regulator0.set_dbias(23); // 0.7V
185
186                cfg
187            },
188        }
189    }
190
191    fn defaults_light_sleep(pd_flags: PowerDownFlags) -> Self {
192        let mut this = Self {
193            // PMU_SLEEP_ANALOG_LSLP_CONFIG_DEFAULT
194            hp_sys: {
195                let mut cfg = HpAnalog::default();
196
197                cfg.regulator1.set_drv_b(0);
198                cfg.bias.set_pd_cur(true);
199                cfg.bias.set_bias_sleep(true);
200                cfg.regulator0.set_xpd(true);
201                cfg.bias.set_dbg_atten(0);
202                cfg.regulator0.set_dbias(1); // 0.6V
203
204                cfg
205            },
206            // lp_sys_active: LpAnalog::default(),
207            lp_sys_sleep: {
208                let mut cfg = LpAnalog::default();
209
210                cfg.regulator1.set_drv_b(0);
211                cfg.bias.set_pd_cur(true);
212                cfg.bias.set_bias_sleep(true);
213                cfg.regulator0.set_slp_xpd(false);
214                cfg.regulator0.set_slp_dbias(0);
215                cfg.regulator0.set_xpd(true);
216                cfg.bias.set_dbg_atten(0);
217                cfg.regulator0.set_dbias(12); // 0.7V
218
219                cfg
220            },
221        };
222
223        if !pd_flags.pd_xtal() {
224            this.hp_sys.bias.set_pd_cur(false);
225            this.hp_sys.bias.set_bias_sleep(false);
226            this.hp_sys.regulator0.set_dbias(25);
227
228            this.lp_sys_sleep.bias.set_pd_cur(false);
229            this.lp_sys_sleep.bias.set_bias_sleep(false);
230            this.lp_sys_sleep.regulator0.set_dbias(26);
231        }
232
233        this
234    }
235
236    fn apply(&self) {
237        // pmu_sleep_analog_init
238
239        unsafe {
240            // HP SLEEP (hp_sleep_*)
241            pmu().hp_sleep_bias().modify(|_, w| {
242                w.hp_sleep_dbg_atten() // pmu_ll_hp_set_dbg_atten
243                    .bits(self.hp_sys.bias.dbg_atten())
244                    .hp_sleep_pd_cur() // pmu_ll_hp_set_current_power_off
245                    .bit(self.hp_sys.bias.pd_cur())
246                    .sleep() // pmu_ll_hp_set_bias_sleep_enable
247                    .bit(self.hp_sys.bias.bias_sleep())
248            });
249            pmu().hp_sleep_hp_regulator0().modify(|_, w| {
250                w.hp_sleep_hp_regulator_xpd() // pmu_ll_hp_set_regulator_xpd
251                    .bit(self.hp_sys.regulator0.xpd())
252                    .hp_sleep_hp_regulator_dbias() // pmu_ll_hp_set_regulator_dbias
253                    .bits(self.hp_sys.regulator0.dbias())
254            });
255            pmu().hp_sleep_hp_regulator1().modify(|_, w| {
256                w.hp_sleep_hp_regulator_drv_b() // pmu_ll_hp_set_regulator_driver_bar
257                    .bits(self.hp_sys.regulator1.drv_b())
258            });
259
260            // LP SLEEP (lp_sleep_*)
261            pmu().lp_sleep_bias().modify(|_, w| {
262                w.lp_sleep_dbg_atten() // pmu_ll_lp_set_dbg_atten
263                    .bits(self.lp_sys_sleep.bias.dbg_atten())
264                    .lp_sleep_pd_cur() // pmu_ll_lp_set_current_power_off
265                    .bit(self.lp_sys_sleep.bias.pd_cur())
266                    .sleep() // pmu_ll_lp_set_bias_sleep_enable
267                    .bit(self.lp_sys_sleep.bias.bias_sleep())
268            });
269
270            pmu().lp_sleep_lp_regulator0().modify(|_, w| {
271                w.lp_sleep_lp_regulator_slp_xpd() // pmu_ll_lp_set_regulator_slp_xpd
272                    .bit(self.lp_sys_sleep.regulator0.slp_xpd())
273                    .lp_sleep_lp_regulator_xpd() // pmu_ll_lp_set_regulator_xpd
274                    .bit(self.lp_sys_sleep.regulator0.xpd())
275                    .lp_sleep_lp_regulator_slp_dbias() // pmu_ll_lp_set_regulator_sleep_dbias
276                    .bits(self.lp_sys_sleep.regulator0.slp_dbias())
277                    .lp_sleep_lp_regulator_dbias() // pmu_ll_lp_set_regulator_dbias
278                    .bits(self.lp_sys_sleep.regulator0.dbias())
279            });
280
281            pmu().lp_sleep_lp_regulator1().modify(|_, w| {
282                w.lp_sleep_lp_regulator_drv_b() // pmu_ll_lp_set_regulator_driver_bar
283                    .bits(self.lp_sys_sleep.regulator1.drv_b())
284            });
285        }
286    }
287}
288
289/// Configuration for controlling the behavior of digital peripherals during
290/// sleep modes.
291#[derive(Clone, Copy)]
292// pmu_sleep_digital_config_t
293pub struct DigitalSleepConfig {
294    /// High-power system control register configuration.
295    pub syscntl: HpSysCntlReg,
296}
297
298impl DigitalSleepConfig {
299    fn defaults_light_sleep(pd_flags: PowerDownFlags) -> Self {
300        Self {
301            // PMU_SLEEP_DIGITAL_LSLP_CONFIG_DEFAULT
302            syscntl: {
303                let mut cfg = HpSysCntlReg::default();
304
305                cfg.set_dig_pad_slp_sel(pd_flags.pd_top().not());
306
307                cfg
308            },
309        }
310    }
311
312    fn apply(&self) {
313        // pmu_sleep_digital_init
314        unsafe {
315            pmu().hp_sleep_hp_sys_cntl().modify(|_, w| {
316                w.hp_sleep_dig_pad_slp_sel()
317                    .bit(self.syscntl.dig_pad_slp_sel())
318            })
319        };
320    }
321}
322
323/// Configuration for controlling the power settings of high-power and low-power
324/// systems during sleep modes.
325#[derive(Clone, Copy)]
326// pmu_sleep_power_config_t
327pub struct PowerSleepConfig {
328    /// Power configuration for the high-power system during sleep.
329    pub hp_sys: HpSysPower,
330    /// Power configuration for the low-power system when it is active.
331    pub lp_sys_active: LpSysPower,
332    /// Power configuration for the low-power system when it is in sleep mode.
333    pub lp_sys_sleep: LpSysPower,
334}
335
336impl PowerSleepConfig {
337    fn defaults(pd_flags: PowerDownFlags) -> Self {
338        let mut this = Self {
339            hp_sys: HpSysPower::default(),
340            lp_sys_active: LpSysPower::default(),
341            lp_sys_sleep: LpSysPower::default(),
342        };
343        this.apply_flags(pd_flags);
344        this
345    }
346
347    fn apply_flags(&mut self, pd_flags: PowerDownFlags) {
348        // PMU_SLEEP_POWER_CONFIG_DEFAULT
349        self.hp_sys
350            .dig_power
351            .set_vdd_spi_pd_en(pd_flags.pd_vddsdio());
352        self.hp_sys.dig_power.set_wifi_pd_en(pd_flags.pd_modem());
353        self.hp_sys.dig_power.set_cpu_pd_en(pd_flags.pd_cpu());
354        self.hp_sys.dig_power.set_aon_pd_en(pd_flags.pd_hp_aon());
355        self.hp_sys.dig_power.set_top_pd_en(pd_flags.pd_top());
356
357        self.hp_sys.clk.set_i2c_iso_en(true);
358        self.hp_sys.clk.set_i2c_retention(true);
359
360        self.hp_sys.xtal.set_xpd_xtal(pd_flags.pd_xtal().not());
361
362        self.lp_sys_active.clk_power.set_xpd_xtal32k(true);
363        self.lp_sys_active.clk_power.set_xpd_rc32k(true);
364        self.lp_sys_active.clk_power.set_xpd_fosc(true);
365
366        self.lp_sys_sleep
367            .dig_power
368            .set_peri_pd_en(pd_flags.pd_lp_periph());
369        self.lp_sys_sleep.dig_power.set_mem_dslp(true);
370
371        self.lp_sys_sleep
372            .clk_power
373            .set_xpd_xtal32k(pd_flags.pd_xtal32k().not());
374        self.lp_sys_sleep
375            .clk_power
376            .set_xpd_rc32k(pd_flags.pd_rc32k().not());
377        self.lp_sys_sleep
378            .clk_power
379            .set_xpd_fosc(pd_flags.pd_rc_fast().not());
380
381        self.lp_sys_sleep
382            .xtal
383            .set_xpd_xtal(pd_flags.pd_xtal().not());
384    }
385
386    fn apply(&self) {
387        // pmu_sleep_power_init
388
389        unsafe {
390            // HP SLEEP (hp_sleep_*)
391            pmu()
392                .hp_sleep_dig_power()
393                .modify(|_, w| w.bits(self.hp_sys.dig_power.0));
394            pmu()
395                .hp_sleep_hp_ck_power()
396                .modify(|_, w| w.bits(self.hp_sys.clk.0));
397            pmu()
398                .hp_sleep_xtal()
399                .modify(|_, w| w.hp_sleep_xpd_xtal().bit(self.hp_sys.xtal.xpd_xtal()));
400
401            // LP ACTIVE (hp_sleep_lp_*)
402            pmu()
403                .hp_sleep_lp_dig_power()
404                .modify(|_, w| w.bits(self.lp_sys_active.dig_power.0));
405            pmu()
406                .hp_sleep_lp_ck_power()
407                .modify(|_, w| w.bits(self.lp_sys_active.clk_power.0));
408
409            // LP SLEEP (lp_sleep_*)
410            pmu()
411                .lp_sleep_lp_dig_power()
412                .modify(|_, w| w.bits(self.lp_sys_sleep.dig_power.0));
413            pmu()
414                .lp_sleep_lp_ck_power()
415                .modify(|_, w| w.bits(self.lp_sys_sleep.clk_power.0));
416            pmu()
417                .lp_sleep_xtal()
418                .modify(|_, w| w.lp_sleep_xpd_xtal().bit(self.lp_sys_sleep.xtal.xpd_xtal()));
419        }
420    }
421}
422
423/// Parameters for high-power system configurations during sleep modes.
424#[derive(Clone, Copy)]
425// pmu_hp_param_t
426pub struct HpParam {
427    /// Number of cycles to wait for the modem to wake up.
428    pub modem_wakeup_wait_cycle: u32,
429    /// Number of cycles to wait for the analog component stabilization.
430    pub analog_wait_target_cycle: u16,
431    /// Number of cycles to wait for the digital power-down sequence.
432    pub digital_power_down_wait_cycle: u16,
433    /// Number of cycles to wait for the digital power supply to stabilize.
434    pub digital_power_supply_wait_cycle: u16,
435    /// Number of cycles to wait for the digital power-up sequence.
436    pub digital_power_up_wait_cycle: u16,
437    /// Number of cycles to wait for the PLL to stabilize.
438    pub pll_stable_wait_cycle: u16,
439    /// Number of cycles to wait for modifying the ICG control.
440    pub modify_icg_cntl_wait_cycle: u8,
441    /// Number of cycles to wait for switching the ICG coйntrol.
442    pub switch_icg_cntl_wait_cycle: u8,
443    /// Minimum sleep time measured in slow clock cycles.
444    pub min_slp_slow_clk_cycle: u8,
445}
446
447/// Parameters for low-power system configurations during sleep modes.
448#[derive(Clone, Copy)]
449// pmu_lp_param_t
450pub struct LpParam {
451    /// Number of cycles to wait for the digital power supply to stabilize.
452    pub digital_power_supply_wait_cycle: u16,
453    /// Minimum sleep time measured in slow clock cycles.
454    pub min_slp_slow_clk_cycle: u8,
455    /// Number of cycles to wait for the analog component stabilization.
456    pub analog_wait_target_cycle: u8,
457    /// Number of cycles to wait for the digital power-down sequence.
458    pub digital_power_down_wait_cycle: u8,
459    /// Number of cycles to wait for the digital power-up sequence.
460    pub digital_power_up_wait_cycle: u8,
461}
462
463/// Parameters for high-power and low-power system configurations during sleep
464/// modes.
465#[derive(Clone, Copy)]
466// pmu_hp_lp_param_t
467pub struct HpLpParam {
468    /// Union of two u16 variants
469    pub xtal_stable_wait_cycle: u16,
470}
471
472/// Configuration of parameters for sleep modes
473#[derive(Clone, Copy)]
474// pmu_sleep_param_config_t
475pub struct ParamSleepConfig {
476    /// Configuration of high-power system parameters.
477    pub hp_sys: HpParam,
478    /// Configuration of low-power system parameters.
479    pub lp_sys: LpParam,
480    /// Shared configuration parameters for high-power and low-power systems.
481    pub hp_lp: HpLpParam,
482}
483impl ParamSleepConfig {
484    const PMU_SLEEP_PARAM_CONFIG_DEFAULT: Self = Self {
485        hp_sys: HpParam {
486            min_slp_slow_clk_cycle: 10,
487            analog_wait_target_cycle: 2419,
488            digital_power_supply_wait_cycle: 32,
489            digital_power_up_wait_cycle: 32,
490            modem_wakeup_wait_cycle: 20700,
491            pll_stable_wait_cycle: 2,
492
493            digital_power_down_wait_cycle: 0,
494            modify_icg_cntl_wait_cycle: 0,
495            switch_icg_cntl_wait_cycle: 0,
496        },
497        lp_sys: LpParam {
498            min_slp_slow_clk_cycle: 10,
499            analog_wait_target_cycle: 23,
500            digital_power_supply_wait_cycle: 32,
501            digital_power_up_wait_cycle: 32,
502
503            digital_power_down_wait_cycle: 0,
504        },
505        hp_lp: HpLpParam {
506            xtal_stable_wait_cycle: 30,
507        },
508    };
509
510    fn apply(&self) {
511        // pmu_sleep_param_init
512
513        unsafe {
514            pmu().slp_wakeup_cntl3().modify(|_, w| {
515                w.hp_min_slp_val() // pmu_ll_hp_set_min_sleep_cycle
516                    .bits(self.hp_sys.min_slp_slow_clk_cycle)
517                    .lp_min_slp_val() // pmu_ll_lp_set_min_sleep_cycle
518                    .bits(self.lp_sys.min_slp_slow_clk_cycle)
519            });
520
521            pmu().slp_wakeup_cntl7().modify(|_, w| {
522                w.ana_wait_target() // pmu_ll_hp_set_analog_wait_target_cycle
523                    .bits(self.hp_sys.analog_wait_target_cycle)
524            });
525
526            pmu().power_wait_timer0().modify(|_, w| {
527                w.dg_hp_wait_timer() // pmu_ll_hp_set_digital_power_supply_wait_cycle
528                    .bits(self.hp_sys.digital_power_supply_wait_cycle)
529                    .dg_hp_powerup_timer() // pmu_ll_hp_set_digital_power_up_wait_cycle
530                    .bits(self.hp_sys.digital_power_up_wait_cycle)
531            });
532
533            pmu().power_wait_timer1().modify(|_, w| {
534                w.dg_lp_wait_timer() // pmu_ll_lp_set_digital_power_supply_wait_cycle
535                    .bits(self.lp_sys.digital_power_supply_wait_cycle)
536                    .dg_lp_powerup_timer() // pmu_ll_lp_set_digital_power_up_wait_cycle
537                    .bits(self.lp_sys.digital_power_up_wait_cycle)
538            });
539
540            pmu().slp_wakeup_cntl5().modify(|_, w| {
541                w.lp_ana_wait_target() // pmu_ll_lp_set_analog_wait_target_cycle
542                    .bits(self.lp_sys.analog_wait_target_cycle)
543                    .modem_wait_target() // pmu_ll_hp_set_modem_wakeup_wait_cycle
544                    .bits(self.hp_sys.modem_wakeup_wait_cycle)
545            });
546            pmu().power_ck_wait_cntl().modify(|_, w| {
547                w.wait_xtl_stable() // pmu_ll_hp_set_xtal_stable_wait_cycle
548                    .bits(self.hp_lp.xtal_stable_wait_cycle)
549                    .wait_pll_stable() // pmu_ll_hp_set_pll_stable_wait_cycle
550                    .bits(self.hp_sys.pll_stable_wait_cycle)
551            });
552        }
553    }
554
555    fn defaults(config: SleepTimeConfig, pd_flags: PowerDownFlags, pd_xtal: bool) -> Self {
556        let mut param = Self::PMU_SLEEP_PARAM_CONFIG_DEFAULT;
557
558        // pmu_sleep_param_config_default
559        param.hp_sys.min_slp_slow_clk_cycle =
560            config.us_to_slowclk(MachineConstants::HP_MIN_SLP_TIME_US) as u8;
561        param.hp_sys.analog_wait_target_cycle =
562            config.us_to_fastclk(MachineConstants::HP_ANALOG_WAIT_TIME_US) as u16;
563        param.hp_sys.digital_power_supply_wait_cycle =
564            config.us_to_fastclk(MachineConstants::HP_POWER_SUPPLY_WAIT_TIME_US) as u16;
565        param.hp_sys.digital_power_up_wait_cycle =
566            config.us_to_fastclk(MachineConstants::HP_POWER_UP_WAIT_TIME_US) as u16;
567        param.hp_sys.pll_stable_wait_cycle =
568            config.us_to_fastclk(MachineConstants::HP_PLL_WAIT_STABLE_TIME_US) as u16;
569
570        let hw_wait_time_us = config.pmu_sleep_calculate_hw_wait_time(pd_flags);
571
572        let modem_wakeup_wait_time_us = (config.sleep_time_adjustment
573            + MachineConstants::MODEM_STATE_SKIP_TIME_US
574            + MachineConstants::HP_REGDMA_RF_ON_WORK_TIME_US)
575            .saturating_sub(hw_wait_time_us);
576        param.hp_sys.modem_wakeup_wait_cycle = config.us_to_fastclk(modem_wakeup_wait_time_us);
577
578        param.lp_sys.min_slp_slow_clk_cycle =
579            config.us_to_slowclk(MachineConstants::LP_MIN_SLP_TIME_US) as u8;
580        param.lp_sys.analog_wait_target_cycle =
581            config.us_to_slowclk(MachineConstants::LP_ANALOG_WAIT_TIME_US) as u8;
582        param.lp_sys.digital_power_supply_wait_cycle =
583            config.us_to_fastclk(MachineConstants::LP_POWER_SUPPLY_WAIT_TIME_US) as u16;
584        param.lp_sys.digital_power_up_wait_cycle =
585            config.us_to_fastclk(MachineConstants::LP_POWER_UP_WAIT_TIME_US) as u8;
586
587        // This looks different from esp-idf but it is the same:
588        // Both `xtal_stable_wait_cycle` and `xtal_stable_wait_slow_clk_cycle` are
589        // u16 variants of the same union
590        param.hp_lp.xtal_stable_wait_cycle = if pd_xtal {
591            config.us_to_slowclk(MachineConstants::LP_XTAL_WAIT_STABLE_TIME_US) as u16
592        } else {
593            config.us_to_fastclk(MachineConstants::HP_XTAL_WAIT_STABLE_TIME_US) as u16
594        };
595
596        param
597    }
598}
599
600#[derive(Clone, Copy)]
601struct SleepTimeConfig {
602    sleep_time_adjustment: u32,
603    // TODO: esp-idf does some calibration here to determine slowclk_period
604    slowclk_period: u32,
605    fastclk_period: u32,
606}
607
608const CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ: u32 = 160;
609
610impl SleepTimeConfig {
611    const RTC_CLK_CAL_FRACT: u32 = 19;
612
613    fn rtc_clk_cal_fast(slowclk_cycles: u32) -> u32 {
614        RtcClock::calibrate(TimgCalibrationClockConfig::RcFastDivClk, slowclk_cycles)
615    }
616
617    fn new(_deep: bool) -> Self {
618        // https://github.com/espressif/esp-idf/commit/e1d24ebd7f43c7c7ded183bc8800b20af3bf014b
619
620        // Calibrate rtc slow clock
621        // TODO: do an actual calibration instead of a read
622        let slowclk_period = unsafe { lp_aon().store1().read().data().bits() };
623
624        // Calibrate rtc fast clock, only PMU supported chips sleep process is needed.
625        const FAST_CLK_SRC_CAL_CYCLES: u32 = 2048;
626        let fastclk_period = Self::rtc_clk_cal_fast(FAST_CLK_SRC_CAL_CYCLES);
627
628        Self {
629            sleep_time_adjustment: 0,
630            slowclk_period,
631            fastclk_period,
632        }
633    }
634
635    fn light_sleep(pd_flags: PowerDownFlags) -> Self {
636        const LIGHT_SLEEP_TIME_OVERHEAD_US: u32 = 56;
637
638        let mut this = Self::new(false);
639
640        let sw = LIGHT_SLEEP_TIME_OVERHEAD_US; // TODO
641        let hw = this.pmu_sleep_calculate_hw_wait_time(pd_flags);
642
643        this.sleep_time_adjustment = sw + hw;
644
645        this
646    }
647
648    fn deep_sleep() -> Self {
649        let mut this = Self::new(true);
650
651        this.sleep_time_adjustment = 250 + 100 * 240 / CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ;
652
653        this
654    }
655
656    fn us_to_slowclk(&self, us: u32) -> u32 {
657        (us << Self::RTC_CLK_CAL_FRACT) / self.slowclk_period
658    }
659
660    fn slowclk_to_us(&self, rtc_cycles: u32) -> u32 {
661        (rtc_cycles * self.slowclk_period) >> Self::RTC_CLK_CAL_FRACT
662    }
663
664    fn us_to_fastclk(&self, us: u32) -> u32 {
665        (us << Self::RTC_CLK_CAL_FRACT) / self.fastclk_period
666    }
667
668    fn pmu_sleep_calculate_hw_wait_time(&self, pd_flags: PowerDownFlags) -> u32 {
669        // LP core hardware wait time, microsecond
670        let lp_wakeup_wait_time_us = self.slowclk_to_us(MachineConstants::LP_WAKEUP_WAIT_CYCLE);
671        let lp_clk_switch_time_us = self.slowclk_to_us(MachineConstants::LP_CLK_SWITCH_CYCLE);
672        let lp_clk_power_on_wait_time_us = if pd_flags.pd_xtal() {
673            MachineConstants::LP_XTAL_WAIT_STABLE_TIME_US
674        } else {
675            self.slowclk_to_us(MachineConstants::LP_CLK_POWER_ON_WAIT_CYCLE)
676        };
677
678        let lp_hw_wait_time_us = MachineConstants::LP_MIN_SLP_TIME_US
679            + MachineConstants::LP_ANALOG_WAIT_TIME_US
680            + lp_clk_power_on_wait_time_us
681            + lp_wakeup_wait_time_us
682            + lp_clk_switch_time_us
683            + MachineConstants::LP_POWER_SUPPLY_WAIT_TIME_US
684            + MachineConstants::LP_POWER_UP_WAIT_TIME_US;
685
686        // HP core hardware wait time, microsecond
687        let hp_digital_power_up_wait_time_us = MachineConstants::HP_POWER_SUPPLY_WAIT_TIME_US
688            + MachineConstants::HP_POWER_UP_WAIT_TIME_US;
689        let hp_regdma_wait_time_us = u32::max(
690            MachineConstants::HP_REGDMA_S2M_WORK_TIME_US
691                + MachineConstants::HP_REGDMA_M2A_WORK_TIME_US,
692            MachineConstants::HP_REGDMA_S2A_WORK_TIME_US,
693        );
694        let hp_clock_wait_time_us = MachineConstants::HP_XTAL_WAIT_STABLE_TIME_US
695            + MachineConstants::HP_PLL_WAIT_STABLE_TIME_US;
696
697        let hp_hw_wait_time_us = MachineConstants::HP_ANALOG_WAIT_TIME_US
698            + u32::max(
699                hp_digital_power_up_wait_time_us + hp_regdma_wait_time_us,
700                hp_clock_wait_time_us,
701            );
702
703        #[rustfmt::skip] // ASCII art
704        //  When the SOC wakeup (lp timer or GPIO wakeup) and Modem wakeup (Beacon wakeup) complete,
705        // the soc wakeup will be delayed until the RF is turned on in Modem state.
706        //
707        //              modem wakeup                      TBTT, RF on by HW
708        //                   |                                    |
709        //                  \|/                                  \|/
710        // PMU_HP_ACTIVE                                                                         /------
711        // PMU_HP_MODEM                                           /------------//////////////////
712        // PMU_HP_SLEEP   ----------------------//////////////////
713        //                  /|\                /|\ /|\          /|\           /|\              /|\
714        //                   |<- some hw wait ->|   |            |             |<- M2A switch ->|
715        //                   |  slow cycles &   | soc wakeup     |                              |
716        //                   |   FOSC cycles    |<- S2M switch ->|                              |
717        //                   |                                                                  |
718        //                   |<--      PMU guard time, also the maximum time for the SOC     -->|
719        //                   |                           wake-up delay                          |
720        //
721        const CONFIG_ESP_RADIO_ENHANCED_LIGHT_SLEEP: bool = true;
722
723        let (rf_on_protect_time_us, sync_time_us) = if CONFIG_ESP_RADIO_ENHANCED_LIGHT_SLEEP {
724            (
725                MachineConstants::HP_REGDMA_RF_ON_WORK_TIME_US,
726                MachineConstants::HP_CLOCK_DOMAIN_SYNC_TIME_US,
727            )
728        } else {
729            (0, 0)
730        };
731
732        lp_hw_wait_time_us + hp_hw_wait_time_us + sync_time_us + rf_on_protect_time_us
733    }
734}
735
736/// Configuration for the RTC sleep behavior.
737#[derive(Clone, Copy)]
738// pmu_sleep_config_t + deep sleep flag + pd flags
739pub struct RtcSleepConfig {
740    /// Deep Sleep flag
741    pub deep: bool,
742    /// Power Down flags
743    pub pd_flags: PowerDownFlags,
744}
745
746impl Default for RtcSleepConfig {
747    fn default() -> Self {
748        // from pmu_sleep_param_config_default
749        // sleep flags will be applied by wakeup methods and apply
750
751        Self {
752            deep: false,
753            pd_flags: PowerDownFlags(0),
754        }
755    }
756}
757
758unsafe fn pmu<'a>() -> &'a esp32c6::pmu::RegisterBlock {
759    unsafe { &*esp32c6::PMU::ptr() }
760}
761
762unsafe fn lp_aon<'a>() -> &'a esp32c6::lp_aon::RegisterBlock {
763    unsafe { &*esp32c6::LP_AON::ptr() }
764}
765
766bitfield::bitfield! {
767    #[derive(Clone, Copy)]
768    /// Power domains to be powered down during sleep
769    pub struct PowerDownFlags(u32);
770
771    /// Controls the power-down status of the top power domain.
772    pub u32, pd_top      , set_pd_top      : 0;
773    /// Controls the power-down status of the VDD_SDIO power domain.
774    pub u32, pd_vddsdio  , set_pd_vddsdio  : 1;
775    /// Controls the power-down status of the modem power domain.
776    pub u32, pd_modem    , set_pd_modem    : 2;
777    /// Controls the power-down status of the high-performance peripheral power domain.
778    pub u32, pd_hp_periph, set_pd_hp_periph: 3;
779    /// Controls the power-down status of the CPU power domain.
780    pub u32, pd_cpu      , set_pd_cpu      : 4;
781    /// Controls the power-down status of the high-performance always-on domain.
782    pub u32, pd_hp_aon   , set_pd_hp_aon   : 5;
783    /// Controls the power-down status of memory group 0.
784    pub u32, pd_mem_g0   , set_pd_mem_g0   : 6;
785    /// Controls the power-down status of memory group 1.
786    pub u32, pd_mem_g1   , set_pd_mem_g1   : 7;
787    /// Controls the power-down status of memory group 2.
788    pub u32, pd_mem_g2   , set_pd_mem_g2   : 8;
789    /// Controls the power-down status of memory group 3.
790    pub u32, pd_mem_g3   , set_pd_mem_g3   : 9;
791    /// Controls the power-down status of the crystal oscillator.
792    pub u32, pd_xtal     , set_pd_xtal     : 10;
793    /// Controls the power-down status of the fast RC oscillator.
794    pub u32, pd_rc_fast  , set_pd_rc_fast  : 11;
795    /// Controls the power-down status of the 32kHz crystal oscillator.
796    pub u32, pd_xtal32k  , set_pd_xtal32k  : 12;
797    /// Controls the power-down status of the 32kHz RC oscillator.
798    pub u32, pd_rc32k    , set_pd_rc32k    : 13;
799    /// Controls the power-down status of the low-power peripheral domain.
800    pub u32, pd_lp_periph, set_pd_lp_periph: 14;
801}
802
803impl PowerDownFlags {
804    /// Checks whether all memory groups (G0, G1, G2, G3) are powered down.
805    pub fn pd_mem(self) -> bool {
806        self.pd_mem_g0() && self.pd_mem_g1() && self.pd_mem_g2() && self.pd_mem_g3()
807    }
808
809    /// Sets the power-down status for all memory groups (G0, G1, G2, G3) at
810    /// once.
811    pub fn set_pd_mem(&mut self, value: bool) {
812        self.set_pd_mem_g0(value);
813        self.set_pd_mem_g1(value);
814        self.set_pd_mem_g2(value);
815        self.set_pd_mem_g3(value);
816    }
817}
818
819// Constants defined in `PMU_SLEEP_MC_DEFAULT()`
820struct MachineConstants;
821impl MachineConstants {
822    const LP_MIN_SLP_TIME_US: u32 = 450;
823    const LP_WAKEUP_WAIT_CYCLE: u32 = 4;
824    const LP_ANALOG_WAIT_TIME_US: u32 = 154;
825    const LP_XTAL_WAIT_STABLE_TIME_US: u32 = 250;
826    const LP_CLK_SWITCH_CYCLE: u32 = 1;
827    const LP_CLK_POWER_ON_WAIT_CYCLE: u32 = 1;
828    const LP_POWER_SUPPLY_WAIT_TIME_US: u32 = 2;
829    const LP_POWER_UP_WAIT_TIME_US: u32 = 2;
830
831    const HP_MIN_SLP_TIME_US: u32 = 450;
832    const HP_CLOCK_DOMAIN_SYNC_TIME_US: u32 = 150;
833    const HP_SYSTEM_DFS_UP_WORK_TIME_US: u32 = 124;
834    const HP_ANALOG_WAIT_TIME_US: u32 = 154;
835    const HP_POWER_SUPPLY_WAIT_TIME_US: u32 = 2;
836    const HP_POWER_UP_WAIT_TIME_US: u32 = 2;
837    const HP_REGDMA_S2M_WORK_TIME_US: u32 = 172;
838    const HP_REGDMA_S2A_WORK_TIME_US: u32 = 480;
839    const HP_REGDMA_M2A_WORK_TIME_US: u32 = 278;
840    // Unused, but defined in esp-idf. May be needed later.
841    // const HP_REGDMA_A2S_WORK_TIME_US: u32 = 382;
842    const HP_REGDMA_RF_ON_WORK_TIME_US: u32 = 70;
843    // Unused, but defined in esp-idf. May be needed later.
844    // const HP_REGDMA_RF_OFF_WORK_TIME_US: u32 = 23;
845    const HP_XTAL_WAIT_STABLE_TIME_US: u32 = 250;
846    const HP_PLL_WAIT_STABLE_TIME_US: u32 = 1;
847
848    const MODEM_STATE_SKIP_TIME_US: u32 = Self::HP_REGDMA_M2A_WORK_TIME_US
849        + Self::HP_SYSTEM_DFS_UP_WORK_TIME_US
850        + Self::LP_MIN_SLP_TIME_US;
851}
852
853impl RtcSleepConfig {
854    /// Returns whether the device is in deep sleep mode.
855    pub fn deep_slp(&self) -> bool {
856        self.deep
857    }
858
859    /// Configures the device for deep sleep mode with ultra-low power settings.
860    pub fn deep() -> Self {
861        // Set up for ultra-low power sleep. Wakeup sources may modify these settings.
862        Self {
863            deep: true,
864            ..Self::default()
865        }
866    }
867
868    pub(crate) fn base_settings(_rtc: &Rtc<'_>) {
869        Self::wake_io_reset();
870    }
871
872    fn wake_io_reset() {
873        // loosely based on esp_deep_sleep_wakeup_io_reset
874        Ext1WakeupSource::wake_io_reset();
875    }
876
877    /// Finalize power-down flags, apply configuration based on the flags.
878    pub(crate) fn apply(&mut self) {
879        if self.deep {
880            // force-disable certain power domains
881            self.pd_flags.set_pd_top(true);
882            self.pd_flags.set_pd_vddsdio(true);
883            self.pd_flags.set_pd_modem(true);
884            self.pd_flags.set_pd_hp_periph(true);
885            self.pd_flags.set_pd_cpu(true);
886            self.pd_flags.set_pd_mem(true);
887            self.pd_flags.set_pd_xtal(true);
888            self.pd_flags.set_pd_hp_aon(true);
889            self.pd_flags.set_pd_lp_periph(true);
890            self.pd_flags.set_pd_xtal32k(true);
891            self.pd_flags.set_pd_rc32k(true);
892            self.pd_flags.set_pd_rc_fast(true);
893        }
894    }
895
896    /// Configures wakeup options and enters sleep.
897    ///
898    /// This function does not return if deep sleep is requested.
899    pub(crate) fn start_sleep(&self, wakeup_triggers: WakeTriggers) {
900        const PMU_EXT0_WAKEUP_EN: u32 = 1 << 0;
901        const PMU_EXT1_WAKEUP_EN: u32 = 1 << 1;
902        const PMU_GPIO_WAKEUP_EN: u32 = 1 << 2;
903        const PMU_LP_TIMER_WAKEUP_EN: u32 = 1 << 4;
904        const PMU_WIFI_SOC_WAKEUP_EN: u32 = 1 << 5;
905        const PMU_UART0_WAKEUP_EN: u32 = 1 << 6;
906        const PMU_UART1_WAKEUP_EN: u32 = 1 << 7;
907        const PMU_SDIO_WAKEUP_EN: u32 = 1 << 8;
908        const PMU_BLE_SOC_WAKEUP_EN: u32 = 1 << 10;
909        const PMU_LP_CORE_WAKEUP_EN: u32 = 1 << 11;
910        const PMU_USB_WAKEUP_EN: u32 = 1 << 14;
911        const MODEM_REJECT: u32 = 1 << 16;
912
913        const RTC_SLEEP_REJECT_MASK: u32 = PMU_EXT0_WAKEUP_EN
914            | PMU_EXT1_WAKEUP_EN
915            | PMU_GPIO_WAKEUP_EN
916            | PMU_LP_TIMER_WAKEUP_EN
917            | PMU_WIFI_SOC_WAKEUP_EN
918            | PMU_UART0_WAKEUP_EN
919            | PMU_UART1_WAKEUP_EN
920            | PMU_SDIO_WAKEUP_EN
921            | PMU_BLE_SOC_WAKEUP_EN
922            | PMU_LP_CORE_WAKEUP_EN
923            | PMU_USB_WAKEUP_EN;
924
925        let wakeup_mask = wakeup_triggers.0 as u32;
926        let reject_mask = if self.deep {
927            0
928        } else {
929            // TODO: MODEM_REJECT if s_sleep_modem.wifi.phy_link != NULL
930            let reject_mask = RTC_SLEEP_REJECT_MASK | MODEM_REJECT;
931            wakeup_mask & reject_mask
932        };
933
934        let cpu_freq_config = ClockTree::with(|clocks| {
935            let cpu_freq_config = SavedClockConfig::save(clocks);
936            crate::soc::clocks::configure_soc_root_clk(
937                clocks,
938                crate::soc::clocks::SocRootClkConfig::Xtal,
939            );
940            cpu_freq_config
941        });
942
943        // pmu_sleep_config_default + pmu_sleep_init.
944
945        let power = PowerSleepConfig::defaults(self.pd_flags);
946        power.apply();
947
948        // Needs to happen after rtc_clk_cpu_freq_set_xtal
949        let config = if self.deep {
950            SleepTimeConfig::deep_sleep()
951        } else {
952            SleepTimeConfig::light_sleep(self.pd_flags)
953        };
954
955        let mut param =
956            ParamSleepConfig::defaults(config, self.pd_flags, power.hp_sys.xtal.xpd_xtal());
957
958        if self.deep {
959            const PMU_LP_ANALOG_WAIT_TARGET_TIME_DSLP_US: u32 = 500;
960            param.lp_sys.analog_wait_target_cycle =
961                config.us_to_slowclk(PMU_LP_ANALOG_WAIT_TARGET_TIME_DSLP_US) as u8;
962
963            AnalogSleepConfig::defaults_deep_sleep().apply();
964        } else {
965            AnalogSleepConfig::defaults_light_sleep(self.pd_flags).apply();
966            DigitalSleepConfig::defaults_light_sleep(self.pd_flags).apply();
967        }
968
969        param.apply();
970
971        // like esp-idf pmu_sleep_start()
972
973        unsafe {
974            // lp_aon_hal_inform_wakeup_type - tells ROM which wakeup stub to run
975            lp_aon()
976                .store9()
977                .modify(|r, w| w.bits(r.bits() & !0x01 | self.deep as u32));
978
979            // pmu_ll_hp_set_wakeup_enable
980            pmu().slp_wakeup_cntl2().write(|w| w.bits(wakeup_mask));
981
982            // pmu_ll_hp_set_reject_enable
983            pmu().slp_wakeup_cntl1().modify(|_, w| {
984                w.slp_reject_en()
985                    .bit(true)
986                    .sleep_reject_ena()
987                    .bits(reject_mask)
988            });
989
990            // pmu_ll_hp_clear_reject_cause
991            pmu()
992                .slp_wakeup_cntl4()
993                .write(|w| w.slp_reject_cause_clr().bit(true));
994
995            pmu().int_clr().write(|w| {
996                w.sw() // pmu_ll_hp_clear_sw_intr_status
997                    .clear_bit_by_one()
998                    .soc_sleep_reject() // pmu_ll_hp_clear_reject_intr_status
999                    .clear_bit_by_one()
1000                    .soc_wakeup() // pmu_ll_hp_clear_wakeup_intr_status
1001                    .clear_bit_by_one()
1002            });
1003
1004            // misc_modules_sleep_prepare
1005
1006            // TODO: IDF-7370
1007            #[cfg(not(soc_has_pmu))]
1008            if !(self.deep && wakeup_triggers.touch) {
1009                APB_SARADC::regs()
1010                    .ctrl()
1011                    .modify(|_, w| w.saradc2_pwdet_drv().bit(false));
1012            }
1013
1014            // Start entry into sleep mode
1015
1016            // pmu_ll_hp_set_sleep_enable
1017            pmu().slp_wakeup_cntl0().write(|w| w.sleep_req().bit(true));
1018
1019            // In pd_cpu lightsleep and deepsleep mode, we never get here
1020            loop {
1021                let int_raw = pmu().int_raw().read();
1022                if int_raw.soc_wakeup().bit_is_set() || int_raw.soc_sleep_reject().bit_is_set() {
1023                    break;
1024                }
1025            }
1026        }
1027
1028        // esp-idf returns if the sleep was rejected, we don't return anything
1029
1030        ClockTree::with(|clocks| {
1031            cpu_freq_config.restore(clocks);
1032        });
1033    }
1034
1035    /// Cleans up after sleep
1036    pub(crate) fn finish_sleep(&self) {
1037        // like esp-idf pmu_sleep_finish()
1038        // In "pd_cpu lightsleep" and "deepsleep" modes we never get here
1039
1040        // esp-idf returns if the sleep was rejected, we do nothing
1041        // pmu_ll_hp_is_sleep_reject(PMU_instance()->hal->dev)
1042
1043        Self::wake_io_reset();
1044    }
1045}