esp_hal/rtc_cntl/sleep/
esp32c6.rs

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