Skip to main content

esp_hal/rtc_cntl/sleep/
esp32p4.rs

1//! Light- and deep-sleep support for the ESP32-P4.
2
3use core::{
4    ops::Not,
5    sync::atomic::{AtomicBool, Ordering},
6};
7
8use crate::{
9    peripherals::{HP_SYS_CLKRST, LP_AON_CLKRST, PMU, USB_DEVICE},
10    private::DropGuard,
11    rtc_cntl::{
12        Rtc,
13        rtc::{HpAnalog, HpSysCntlReg, HpSysPower, LpAnalog, LpSysPower},
14        sleep::{
15            SleepKind,
16            pmu_common::{SleepTimeConfig, request_sleep},
17        },
18    },
19    soc::clocks::{self, ClockTree, CpuRootClkConfig},
20};
21
22// ----------------------------------------------------------------------------
23// USB-Serial-JTAG pad handling across light sleep.
24//
25// In light sleep the HP peripheral domain stays powered, so the USJ PHY keeps
26// driving its pads (including the D+ pull-up) while the peripheral is clock
27// gated. The host therefore keeps the device enumerated but unresponsive, and
28// the link does not recover after wake. Mirroring esp-idf `sleep_console.c`,
29// disable the USJ pad (releasing the pull-up so the host sees a clean
30// disconnect) on light-sleep entry and restore it on wake. ESP32-P4 does not
31// support keeping USJ alive across light sleep, so this is unconditional.
32// ----------------------------------------------------------------------------
33
34static USJ_CLOCK_WAS_ENABLED: AtomicBool = AtomicBool::new(false);
35static USJ_PAD_WAS_ENABLED: AtomicBool = AtomicBool::new(false);
36
37fn usj_module_is_enabled() -> bool {
38    let clkrst = HP_SYS_CLKRST::regs();
39    let aon = LP_AON_CLKRST::regs();
40    clkrst
41        .soc_clk_ctrl2()
42        .read()
43        .usb_device_apb_clk_en()
44        .bit_is_set()
45        && aon
46            .lp_aonclkrst_hp_usb_clkrst_ctrl1()
47            .read()
48            .lp_aonclkrst_rst_en_usb_device()
49            .bit_is_clear()
50}
51
52fn usj_enable_bus_clock(enable: bool) {
53    HP_SYS_CLKRST::regs()
54        .soc_clk_ctrl2()
55        .modify(|_, w| w.usb_device_apb_clk_en().bit(enable));
56    // PHY 48 MHz clock for USB FSLS PHY 0.
57    LP_AON_CLKRST::regs()
58        .lp_aonclkrst_hp_usb_clkrst_ctrl0()
59        .modify(|_, w| w.lp_aonclkrst_usb_device_48m_clk_en().bit(enable));
60}
61
62fn usj_reset_register() {
63    let aon = LP_AON_CLKRST::regs();
64    aon.lp_aonclkrst_hp_usb_clkrst_ctrl1()
65        .modify(|_, w| w.lp_aonclkrst_rst_en_usb_device().set_bit());
66    aon.lp_aonclkrst_hp_usb_clkrst_ctrl1()
67        .modify(|_, w| w.lp_aonclkrst_rst_en_usb_device().clear_bit());
68}
69
70fn usj_set_pad_enable(enable: bool) {
71    USB_DEVICE::regs()
72        .conf0()
73        .modify(|_, w| w.usb_pad_enable().bit(enable));
74}
75
76fn usj_pad_is_enabled() -> bool {
77    USB_DEVICE::regs()
78        .conf0()
79        .read()
80        .usb_pad_enable()
81        .bit_is_set()
82}
83
84/// Backup and disable the USJ pad on light-sleep entry. esp-idf
85/// `sleep_console_usj_pad_backup_and_disable`.
86fn usj_pad_backup_and_disable() {
87    let clock_enabled = usj_module_is_enabled();
88    let pad_enabled = if clock_enabled {
89        usj_pad_is_enabled()
90    } else {
91        // Bring the register block up so we can touch the pad enable.
92        usj_enable_bus_clock(true);
93        usj_reset_register();
94        false
95    };
96    usj_set_pad_enable(false);
97    usj_enable_bus_clock(false);
98
99    USJ_CLOCK_WAS_ENABLED.store(clock_enabled, Ordering::Relaxed);
100    USJ_PAD_WAS_ENABLED.store(pad_enabled, Ordering::Relaxed);
101}
102
103/// Restores the USJ pad on wake. esp-idf `sleep_console_usj_pad_restore`.
104fn usj_pad_restore() {
105    usj_enable_bus_clock(true);
106    usj_set_pad_enable(USJ_PAD_WAS_ENABLED.load(Ordering::Relaxed));
107    if !USJ_CLOCK_WAS_ENABLED.load(Ordering::Relaxed) {
108        usj_enable_bus_clock(false);
109    }
110}
111
112/// LP SPM RAM base. On rev-3.0 the deep-sleep wake reset vector can be
113/// redirected here (see [`install_mspi_workaround_stub`]).
114const P4_LP_RAM_BOOT_ADDR: usize = 0x5010_8000;
115
116/// Returns whether this silicon is ESP32-P4 rev 3.0 (ECO5), the only revision
117/// affected by the "MSPI crash after power up" deep-sleep erratum (fixed in
118/// rev 3.1). Mirrors esp-idf's `efuse_hal_chip_revision() == 300` gate.
119fn is_rev3_mspi_workaround_needed() -> bool {
120    crate::efuse::chip_revision() == crate::efuse::ChipRevision::from_combined(300)
121}
122
123// ESP32-P4 rev-3.0 deep-sleep wake stub (esp-idf
124// `p4_rev3_mspi_workaround.S`). On wake the chip powers up and would crash on
125// the first flash fetch; this stub runs from LP RAM, stabilizes the MSPI/flash
126// cache, resets the MSPI AXI/APB interfaces and then jumps to the HP ROM
127// first-stage boot. It is position-independent (only absolute `li`/`jr` and a
128// PC-relative delay loop), so it can be copied to `P4_LP_RAM_BOOT_ADDR` and run
129// from there. Absolute register addresses are computed from the rev-3.x
130// `reg_base.h` (HPPERIPH0 = 0x5000_0000, HPPERIPH1 = 0x500C_0000,
131// LPAON = 0x5011_0000).
132core::arch::global_asm!(
133    ".pushsection .rodata.p4_rev3_mspi_wa, \"a\"",
134    ".option push",
135    ".option norelax",
136    ".option norvc",
137    ".balign 4",
138    ".global _p4_rev3_mspi_wa_start",
139    ".global _p4_rev3_mspi_wa_end",
140    "_p4_rev3_mspi_wa_start:",
141    // Recover the reset vector to HP ROM: LP_CLKRST_HPCPU_RESET_CTRL0 |= STAT_VECTOR_SEL
142    "li   a0, 0x50111014",
143    "li   a1, 0x8000",
144    "lw   a2, 0(a0)",
145    "or   a2, a2, a1",
146    "sw   a2, 0(a0)",
147    // SPI_MEM_C_CACHE_FCTRL &= ~CLOSE_AXI_INF_EN
148    "li   a0, 0x5008C03C",
149    "li   a1, 0x80000000",
150    "not  a1, a1",
151    "lw   a2, 0(a0)",
152    "and  a2, a2, a1",
153    "sw   a2, 0(a0)",
154    // SPI_MEM_C_CACHE_FCTRL |= AXI_REQ_EN
155    "li   a1, 0x1",
156    "lw   a2, 0(a0)",
157    "or   a2, a2, a1",
158    "sw   a2, 0(a0)",
159    // One MSPI MMU entry mapping AXI addr -> flash addr.
160    "li   a0, 0x5008C380", // MMU_ITEM_INDEX = 0
161    "sw   zero, 0(a0)",
162    "li   a0, 0x5008C37C", // MMU_ITEM_CONTENT = 0x1000
163    "li   a1, 0x1000",
164    "sw   a1, 0(a0)",
165    // Disable cpu error response: CORE_ERR_RESP_DIS = 0x7
166    "li   a0, 0x500E51A4",
167    "li   a1, 0x7",
168    "sw   a1, 0(a0)",
169    // Two dummy flash reads to stabilize MSPI.
170    "li   a0, 0x80000000",
171    "lw   a1, 0(a0)",
172    "li   a0, 0x80000080",
173    "lw   a1, 0(a0)",
174    // Delay ~1us (CPU runs at 40 MHz right after reset).
175    "li   t3, 40",
176    "csrr t0, cycle",
177    "add  t1, t0, t3",
178    "100:",
179    "csrr t2, cycle",
180    "blt  t2, t1, 100b",
181    // Re-enable cpu error response: CORE_ERR_RESP_DIS = 0
182    "li   a0, 0x500E51A4",
183    "sw   zero, 0(a0)",
184    // Reset MSPI AXI + APB interfaces, then release.
185    "li   a0, 0x500E60C0",
186    "li   a1, 0x400000", // RST_EN_MSPI_AXI
187    "lw   a2, 0(a0)",
188    "or   a2, a2, a1",
189    "sw   a2, 0(a0)",
190    "li   a1, 0x1000000", // RST_EN_MSPI_APB
191    "lw   a2, 0(a0)",
192    "or   a2, a2, a1",
193    "sw   a2, 0(a0)",
194    "li   a1, 0x400000",
195    "not  a1, a1",
196    "lw   a2, 0(a0)",
197    "and  a2, a2, a1",
198    "sw   a2, 0(a0)",
199    "li   a1, 0x1000000",
200    "not  a1, a1",
201    "lw   a2, 0(a0)",
202    "and  a2, a2, a1",
203    "sw   a2, 0(a0)",
204    // Jump to HP ROM first-stage boot.
205    "li   a5, 0x4FC00000",
206    "jr   a5",
207    "_p4_rev3_mspi_wa_end:",
208    ".option pop",
209    ".popsection",
210);
211
212/// Copies the rev-3.0 MSPI workaround wake stub into LP RAM at
213/// [`P4_LP_RAM_BOOT_ADDR`]. The stub is position-independent, so a plain word
214/// copy is sufficient. The first 0x100 bytes of `RTC_FAST` are reserved for it
215/// in the linker script.
216fn install_mspi_workaround_stub() {
217    unsafe extern "C" {
218        static _p4_rev3_mspi_wa_start: u8;
219        static _p4_rev3_mspi_wa_end: u8;
220    }
221
222    let src = &raw const _p4_rev3_mspi_wa_start;
223    let end = &raw const _p4_rev3_mspi_wa_end;
224    let len = end as usize - src as usize;
225    let words = len.div_ceil(4);
226
227    let src = src as *const u32;
228    let dst = P4_LP_RAM_BOOT_ADDR as *mut u32;
229    for i in 0..words {
230        unsafe { dst.add(i).write_volatile(src.add(i).read_volatile()) };
231    }
232}
233
234/// Redirects (or restores) the HP-core wake reset vector.
235///
236/// `lp_clkrst_ll_boot_from_lp_ram`: `hpcore0_stat_vector_sel = !boot_from_lp_ram`
237/// (0 -> boot from LP SPM RAM 0x50108000, 1 -> boot from HP ROM 0x4FC00000).
238fn set_boot_from_lp_ram(boot_from_lp_ram: bool) {
239    LP_AON_CLKRST::regs()
240        .lp_aonclkrst_hpcpu_reset_ctrl0()
241        .modify(|_, w| {
242            w.lp_aonclkrst_hpcore0_stat_vector_sel()
243                .bit(!boot_from_lp_ram)
244        });
245}
246
247/// ESP32-P4 deep-sleep DCDC -> LDO supply handover.
248///
249/// The HP digital rail is normally supplied by the on-chip DCDC converter. On
250/// deep-sleep entry the PMU FSM powers down the DCDC switch; if the DCDC is
251/// still actively regulating at that point, the rail glitches when the LDO has
252/// to take over on wake-up and the chip fails to reboot (it appears to "never
253/// wake"). esp-idf avoids this by raising the HP LDO so it can take over
254/// (`pmu_sleep_increase_ldo_volt`), pre-lowering the DCDC set-point to limit the
255/// hand-over overshoot, waiting for the LDO to settle, then disabling the DCDC
256/// (`pmu_sleep_shutdown_dcdc`). The DCDC is re-enabled by the bootloader on
257/// wake. C-series parts have no DCDC and do not need this.
258fn pmu_sleep_dcdc_to_ldo_handover() {
259    // esp-idf: LDO_POWER_TAKEOVER_PREPARATION_TIME_US.
260    const LDO_TAKEOVER_PREPARATION_TIME_US: u32 = 185;
261    // esp-idf: HP_CALI_ACTIVE_DBIAS_DEFAULT.
262    const HP_CALI_ACTIVE_DBIAS: u8 = 24;
263    // esp-idf pmu_sleep_increase_ldo_volt() constants.
264    const LDO_TAKEOVER_DBIAS: u8 = 30;
265    const LDO_TAKEOVER_DCM_VSET: u8 = 24;
266
267    // pmu_sleep_increase_ldo_volt(): raise the HP LDO and pre-lower the DCDC
268    // voltage so the LDO can take over without overshoot.
269    PMU::regs()
270        .hp_active_hp_regulator0()
271        .modify(|_, w| unsafe { w.hp_active_hp_regulator_dbias().bits(LDO_TAKEOVER_DBIAS) });
272    PMU::regs()
273        .hp_active_hp_regulator0()
274        .modify(|_, w| w.hp_active_hp_regulator_xpd().set_bit());
275    PMU::regs()
276        .hp_active_bias()
277        .modify(|_, w| unsafe { w.hp_active_dcm_vset().bits(LDO_TAKEOVER_DCM_VSET) });
278
279    crate::rom::ets_delay_us(LDO_TAKEOVER_PREPARATION_TIME_US);
280
281    // pmu_sleep_shutdown_dcdc(): request the DCDC off (done_force latches it off,
282    // the dcdc_switch stays on and is disabled by the PMU when sleep is entered)
283    // and drop the HP LDO back to the active default voltage.
284    PMU::regs().dcm_ctrl().modify(|_, w| {
285        w.dcdc_off_req().set_bit();
286        w.dcdc_done_force().set_bit()
287    });
288    PMU::regs()
289        .hp_active_hp_regulator0()
290        .modify(|_, w| unsafe { w.hp_active_hp_regulator_dbias().bits(HP_CALI_ACTIVE_DBIAS) });
291}
292
293/// Configuration controlling the analog behavior during sleep.
294#[derive(Clone, Copy)]
295// pmu_sleep_analog_config_t
296pub struct AnalogSleepConfig {
297    /// High-power system analog configuration.
298    pub hp_sys: HpAnalog,
299    /// Low-power system analog configuration (LP_SLEEP).
300    pub lp_sys_sleep: LpAnalog,
301}
302
303impl AnalogSleepConfig {
304    fn defaults_deep_sleep() -> Self {
305        // PMU_SLEEP_ANALOG_DSLP_CONFIG_DEFAULT
306        Self {
307            hp_sys: {
308                let mut cfg = HpAnalog::default();
309                cfg.bias.set_dcm_mode(0);
310                cfg.bias.set_pd_cur(true); // PMU_PD_CUR_SLEEP_DEFAULT
311                cfg.bias.set_bias_sleep(true); // PMU_BIASSLP_SLEEP_DEFAULT
312                cfg.regulator0.set_xpd(false); // PMU_HP_XPD_DEEPSLEEP
313                cfg.bias.set_dbg_atten(0); // PMU_DBG_HP_DEEPSLEEP
314                cfg
315            },
316            lp_sys_sleep: {
317                let mut cfg = LpAnalog::default();
318                cfg.regulator1.set_drv_b(0);
319                cfg.bias.set_pd_cur(true);
320                cfg.bias.set_bias_sleep(true);
321                cfg.regulator0.set_slp_xpd(false);
322                cfg.regulator0.set_slp_dbias(0);
323                cfg.regulator0.set_xpd(true);
324                cfg.bias.set_dbg_atten(12); // PMU_DBG_ATTEN_DEEPSLEEP_DEFAULT
325                cfg.regulator0.set_dbias(23); // PMU_LP_DBIAS_DEEPSLEEP_0V7
326                cfg
327            },
328        }
329    }
330
331    fn defaults_light_sleep(pd_flags: PowerDownFlags) -> Self {
332        // PMU_SLEEP_ANALOG_LSLP_CONFIG_DEFAULT
333        let mut this = Self {
334            hp_sys: {
335                let mut cfg = HpAnalog::default();
336                cfg.bias.set_dcm_mode(1);
337                cfg.bias.set_dcm_vset(DCM_VSET_IN_SLEEP);
338                cfg.regulator1.set_drv_b(0); // PMU_HP_DRVB_LIGHTSLEEP
339                cfg.bias.set_pd_cur(true); // PMU_PD_CUR_SLEEP_DEFAULT
340                cfg.bias.set_bias_sleep(true); // PMU_BIASSLP_SLEEP_DEFAULT
341                cfg.regulator0.set_xpd(false); // PMU_HP_XPD_LIGHTSLEEP (use DCDC)
342                cfg.bias.set_dbg_atten(0); // PMU_DBG_ATTEN_LIGHTSLEEP_DEFAULT
343                cfg.regulator0.set_dbias(1); // PMU_HP_DBIAS_LIGHTSLEEP_0V6
344                cfg
345            },
346            lp_sys_sleep: {
347                let mut cfg = LpAnalog::default();
348                cfg.regulator1.set_drv_b(0);
349                cfg.bias.set_pd_cur(true);
350                cfg.bias.set_bias_sleep(true);
351                cfg.regulator0.set_slp_xpd(false);
352                cfg.regulator0.set_slp_dbias(0);
353                cfg.regulator0.set_xpd(true);
354                cfg.bias.set_dbg_atten(0);
355                cfg.regulator0.set_dbias(12); // PMU_LP_DBIAS_LIGHTSLEEP_0V7
356                cfg
357            },
358        };
359
360        // When the main XTAL stays powered during sleep, the analog domain must
361        // be kept in its active operating point (esp-idf pmu_sleep_config_default).
362        if !pd_flags.pd_xtal() {
363            this.hp_sys.bias.set_pd_cur(false);
364            this.hp_sys.bias.set_bias_sleep(false);
365            this.hp_sys.bias.set_dbg_atten(0);
366            this.hp_sys.regulator0.set_dbias(HP_CALI_ACTIVE_DBIAS);
367
368            this.lp_sys_sleep.bias.set_pd_cur(false);
369            this.lp_sys_sleep.bias.set_bias_sleep(false);
370            this.lp_sys_sleep.bias.set_dbg_atten(0);
371        }
372
373        this
374    }
375
376    fn apply(&self, dslp: bool) {
377        // pmu_sleep_analog_init
378
379        // HP_ACTIVE dcm_mode (deep sleep forces 0, otherwise 1).
380        PMU::regs()
381            .hp_active_bias()
382            .modify(|_, w| unsafe { w.hp_active_dcm_mode().bits(if dslp { 0 } else { 1 }) });
383
384        PMU::regs().hp_sleep_bias().modify(|_, w| unsafe {
385            w.hp_sleep_dcm_mode().bits(self.hp_sys.bias.dcm_mode());
386            w.hp_sleep_dcm_vset().bits(self.hp_sys.bias.dcm_vset());
387            w.hp_sleep_dbg_atten().bits(self.hp_sys.bias.dbg_atten());
388            w.hp_sleep_pd_cur().bit(self.hp_sys.bias.pd_cur());
389            w.sleep().bit(self.hp_sys.bias.bias_sleep())
390        });
391        PMU::regs().hp_sleep_hp_regulator0().modify(|_, w| unsafe {
392            w.hp_sleep_hp_regulator_slp_mem_xpd()
393                .bit(self.hp_sys.regulator0.slp_mem_xpd());
394            w.hp_sleep_hp_regulator_slp_logic_xpd()
395                .bit(self.hp_sys.regulator0.slp_logic_xpd());
396            w.hp_sleep_hp_regulator_xpd()
397                .bit(self.hp_sys.regulator0.xpd());
398            w.hp_sleep_hp_regulator_slp_logic_dbias()
399                .bits(self.hp_sys.regulator0.slp_logic_dbias());
400            w.hp_sleep_hp_regulator_dbias()
401                .bits(self.hp_sys.regulator0.dbias())
402        });
403        PMU::regs().hp_sleep_hp_regulator1().modify(|_, w| unsafe {
404            w.hp_sleep_hp_regulator_drv_b()
405                .bits(self.hp_sys.regulator1.drv_b())
406        });
407
408        // LP_SLEEP
409        PMU::regs().lp_sleep_bias().modify(|_, w| unsafe {
410            w.lp_sleep_dbg_atten()
411                .bits(self.lp_sys_sleep.bias.dbg_atten());
412            w.lp_sleep_pd_cur().bit(self.lp_sys_sleep.bias.pd_cur());
413            w.sleep().bit(self.lp_sys_sleep.bias.bias_sleep())
414        });
415        PMU::regs().lp_sleep_lp_regulator0().modify(|_, w| unsafe {
416            w.lp_sleep_lp_regulator_slp_xpd()
417                .bit(self.lp_sys_sleep.regulator0.slp_xpd());
418            w.lp_sleep_lp_regulator_xpd()
419                .bit(self.lp_sys_sleep.regulator0.xpd());
420            w.lp_sleep_lp_regulator_slp_dbias()
421                .bits(self.lp_sys_sleep.regulator0.slp_dbias());
422            w.lp_sleep_lp_regulator_dbias()
423                .bits(self.lp_sys_sleep.regulator0.dbias())
424        });
425        PMU::regs().lp_sleep_lp_regulator1().modify(|_, w| unsafe {
426            w.lp_sleep_lp_regulator_drv_b()
427                .bits(self.lp_sys_sleep.regulator1.drv_b())
428        });
429    }
430}
431
432/// Configuration controlling digital peripherals during sleep.
433#[derive(Clone, Copy)]
434// pmu_sleep_digital_config_t
435pub struct DigitalSleepConfig {
436    /// High-power system control register configuration.
437    pub syscntl: HpSysCntlReg,
438}
439
440impl DigitalSleepConfig {
441    fn defaults_deep_sleep(pd_flags: PowerDownFlags) -> Self {
442        let mut syscntl = HpSysCntlReg::default();
443        syscntl.set_dig_pad_slp_sel(false);
444        syscntl.set_lp_pad_hold_all(pd_flags.pd_lp_periph());
445
446        Self { syscntl }
447    }
448
449    fn defaults_light_sleep(pd_flags: PowerDownFlags) -> Self {
450        // PMU_SLEEP_DIGITAL_LSLP_CONFIG_DEFAULT
451        Self {
452            syscntl: {
453                let mut cfg = HpSysCntlReg::default();
454                cfg.set_dig_pad_slp_sel(false);
455                // Hold the LP pads if the LP peripheral domain is powered down.
456                cfg.set_lp_pad_hold_all(pd_flags.pd_lp_periph());
457                cfg.set_dig_pause_wdt(true);
458                cfg
459            },
460        }
461    }
462
463    fn apply(&self) {
464        // pmu_sleep_digital_init
465
466        PMU::regs().hp_sleep_hp_sys_cntl().modify(|_, w| {
467            w.hp_sleep_dig_pad_slp_sel()
468                .bit(self.syscntl.dig_pad_slp_sel());
469            w.hp_sleep_lp_pad_hold_all()
470                .bit(self.syscntl.lp_pad_hold_all());
471            w.hp_sleep_dig_pause_wdt().bit(self.syscntl.dig_pause_wdt());
472            w.hp_sleep_dig_cpu_stall().bit(true)
473        });
474    }
475}
476
477/// Configuration controlling the power state of the HP and LP systems during
478/// sleep.
479#[derive(Clone, Copy)]
480// pmu_sleep_power_config_t
481pub struct PowerSleepConfig {
482    /// Power configuration for the high-power system during sleep.
483    pub hp_sys: HpSysPower,
484    /// Power configuration for the low-power system when active.
485    pub lp_sys_active: LpSysPower,
486    /// Power configuration for the low-power system during sleep.
487    pub lp_sys_sleep: LpSysPower,
488}
489
490impl PowerSleepConfig {
491    fn defaults(pd_flags: PowerDownFlags) -> Self {
492        let mut this = Self {
493            hp_sys: HpSysPower::default(),
494            lp_sys_active: LpSysPower::default(),
495            lp_sys_sleep: LpSysPower::default(),
496        };
497        this.apply_flags(pd_flags);
498        this
499    }
500
501    fn apply_flags(&mut self, pd_flags: PowerDownFlags) {
502        // PMU_HP_SLEEP_POWER_CONFIG_DEFAULT + flag overrides.
503        // `dcdc_switch_pd_en` is powered down only in deep sleep (which sets
504        // `pd_vddsdio`); light sleep keeps the DCDC switch so the DCDC can supply
505        // the HP domain at the light-sleep voltage.
506        self.hp_sys
507            .dig_power
508            .set_dcdc_switch_pd_en(pd_flags.pd_vddsdio());
509        self.hp_sys.dig_power.set_cnnt_pd_en(pd_flags.pd_modem());
510        self.hp_sys.dig_power.set_cpu_pd_en(pd_flags.pd_cpu());
511        self.hp_sys.dig_power.set_top_pd_en(pd_flags.pd_top());
512        self.hp_sys.dig_power.set_mem_pd_en(pd_flags.pd_mem());
513
514        self.hp_sys.clk.set_i2c_iso_en(true);
515        self.hp_sys.clk.set_i2c_retention(true);
516        self.hp_sys.clk.set_xpd_pll_i2c(0);
517        self.hp_sys.clk.set_xpd_pll(0);
518
519        self.hp_sys.xtal.set_xpd_xtal(pd_flags.pd_xtal().not());
520
521        self.lp_sys_active.clk_power.set_xpd_lppll(true);
522        self.lp_sys_active.clk_power.set_xpd_xtal32k(true);
523        self.lp_sys_active.clk_power.set_xpd_rc32k(true);
524        self.lp_sys_active.clk_power.set_xpd_fosc(true);
525
526        self.lp_sys_sleep
527            .dig_power
528            .set_peri_pd_en(pd_flags.pd_lp_periph());
529
530        self.lp_sys_sleep
531            .clk_power
532            .set_xpd_xtal32k(pd_flags.pd_xtal32k().not());
533        self.lp_sys_sleep
534            .clk_power
535            .set_xpd_rc32k(pd_flags.pd_rc32k().not());
536        self.lp_sys_sleep
537            .clk_power
538            .set_xpd_fosc(pd_flags.pd_rc_fast().not());
539
540        self.lp_sys_sleep
541            .xtal
542            .set_xpd_xtal(pd_flags.pd_xtal().not());
543    }
544
545    fn apply(&self) {
546        // pmu_sleep_power_init
547
548        // HP_SLEEP
549        PMU::regs()
550            .hp_sleep_dig_power()
551            .modify(|_, w| unsafe { w.bits(self.hp_sys.dig_power.0) });
552        PMU::regs()
553            .hp_sleep_hp_ck_power()
554            .modify(|_, w| unsafe { w.bits(self.hp_sys.clk.0) });
555        PMU::regs()
556            .hp_sleep_xtal()
557            .modify(|_, w| w.hp_sleep_xpd_xtal().bit(self.hp_sys.xtal.xpd_xtal()));
558
559        // LP_ACTIVE (hp_sleep_lp_*)
560        PMU::regs()
561            .hp_sleep_lp_dig_power()
562            .modify(|_, w| unsafe { w.bits(self.lp_sys_active.dig_power.0) });
563        PMU::regs()
564            .hp_sleep_lp_ck_power()
565            .modify(|_, w| unsafe { w.bits(self.lp_sys_active.clk_power.0) });
566
567        // LP_SLEEP
568        PMU::regs()
569            .lp_sleep_lp_dig_power()
570            .modify(|_, w| unsafe { w.bits(self.lp_sys_sleep.dig_power.0) });
571        PMU::regs()
572            .lp_sleep_lp_ck_power()
573            .modify(|_, w| unsafe { w.bits(self.lp_sys_sleep.clk_power.0) });
574        PMU::regs()
575            .lp_sleep_xtal()
576            .modify(|_, w| w.lp_sleep_xpd_xtal().bit(self.lp_sys_sleep.xtal.xpd_xtal()));
577    }
578}
579
580/// High-power system sleep timing parameters (pmu_hp_param_t subset).
581#[derive(Clone, Copy, Default)]
582pub struct HpParam {
583    analog_wait_target_cycle: u16,
584    digital_power_supply_wait_cycle: u16,
585    digital_power_up_wait_cycle: u16,
586    pll_stable_wait_cycle: u16,
587    min_slp_slow_clk_cycle: u8,
588}
589
590/// Low-power system sleep timing parameters (pmu_lp_param_t subset).
591#[derive(Clone, Copy, Default)]
592pub struct LpParam {
593    digital_power_supply_wait_cycle: u16,
594    min_slp_slow_clk_cycle: u8,
595    analog_wait_target_cycle: u8,
596    digital_power_up_wait_cycle: u16,
597}
598
599/// Shared HP/LP sleep timing parameters.
600#[derive(Clone, Copy, Default)]
601pub struct HpLpParam {
602    xtal_stable_wait_cycle: u16,
603}
604
605/// Sleep timing parameter configuration (pmu_sleep_param_config_t).
606#[derive(Clone, Copy)]
607pub struct ParamSleepConfig {
608    hp_sys: HpParam,
609    lp_sys: LpParam,
610    hp_lp: HpLpParam,
611}
612
613impl ParamSleepConfig {
614    fn apply(&self) {
615        // pmu_sleep_param_init
616        PMU::regs().slp_wakeup_cntl3().modify(|_, w| unsafe {
617            w.hp_min_slp_val().bits(self.hp_sys.min_slp_slow_clk_cycle);
618            w.lp_min_slp_val().bits(self.lp_sys.min_slp_slow_clk_cycle)
619        });
620
621        PMU::regs().slp_wakeup_cntl7().modify(|_, w| unsafe {
622            w.ana_wait_target()
623                .bits(self.hp_sys.analog_wait_target_cycle)
624        });
625
626        PMU::regs().power_wait_timer0().modify(|_, w| unsafe {
627            w.dg_hp_wait_timer()
628                .bits(self.hp_sys.digital_power_supply_wait_cycle);
629            w.dg_hp_powerup_timer()
630                .bits(self.hp_sys.digital_power_up_wait_cycle)
631        });
632
633        PMU::regs().power_wait_timer1().modify(|_, w| unsafe {
634            w.dg_lp_wait_timer()
635                .bits(self.lp_sys.digital_power_supply_wait_cycle);
636            w.dg_lp_powerup_timer()
637                .bits(self.lp_sys.digital_power_up_wait_cycle)
638        });
639
640        PMU::regs().slp_wakeup_cntl5().modify(|_, w| unsafe {
641            w.lp_ana_wait_target()
642                .bits(self.lp_sys.analog_wait_target_cycle)
643        });
644
645        PMU::regs().power_ck_wait_cntl().modify(|_, w| unsafe {
646            w.pmu_wait_xtl_stable()
647                .bits(self.hp_lp.xtal_stable_wait_cycle);
648            w.pmu_wait_pll_stable()
649                .bits(self.hp_sys.pll_stable_wait_cycle)
650        });
651    }
652
653    fn defaults(config: SleepTimeConfig, pd_flags: PowerDownFlags, pd_xtal: bool) -> Self {
654        // pmu_sleep_param_config_default
655        let hp_analog_wait_time_us = if pd_flags.pd_top() {
656            MachineConstants::HP_ANA_WAIT_TIME_PD_TOP_US
657        } else {
658            MachineConstants::HP_ANA_WAIT_TIME_PU_TOP_US
659        };
660
661        let hp_sys = HpParam {
662            min_slp_slow_clk_cycle: config.us_to_slowclk(MachineConstants::HP_MIN_SLP_TIME_US)
663                as u8,
664            analog_wait_target_cycle: config.us_to_slowclk(hp_analog_wait_time_us) as u16,
665            digital_power_supply_wait_cycle: config
666                .us_to_fastclk(MachineConstants::HP_POWER_SUPPLY_WAIT_TIME_US)
667                as u16,
668            digital_power_up_wait_cycle: config
669                .us_to_fastclk(MachineConstants::HP_POWER_UP_WAIT_TIME_US)
670                as u16,
671            pll_stable_wait_cycle: config
672                .us_to_fastclk(MachineConstants::HP_PLL_WAIT_STABLE_TIME_US)
673                as u16,
674        };
675
676        let lp_sys = LpParam {
677            min_slp_slow_clk_cycle: config.us_to_slowclk(MachineConstants::LP_MIN_SLP_TIME_US)
678                as u8,
679            analog_wait_target_cycle: config.us_to_slowclk(MachineConstants::LP_ANALOG_WAIT_TIME_US)
680                as u8,
681            digital_power_supply_wait_cycle: config
682                .us_to_fastclk(MachineConstants::LP_POWER_SUPPLY_WAIT_TIME_US)
683                as u16,
684            digital_power_up_wait_cycle: config
685                .us_to_fastclk(MachineConstants::LP_POWER_UP_WAIT_TIME_US)
686                as u16,
687        };
688
689        let xtal_stable_wait_cycle = if pd_xtal {
690            config.us_to_slowclk(MachineConstants::LP_XTAL_WAIT_STABLE_TIME_US) as u16
691        } else {
692            config.us_to_fastclk(MachineConstants::HP_XTAL_WAIT_STABLE_TIME_US) as u16
693        };
694
695        Self {
696            hp_sys,
697            lp_sys,
698            hp_lp: HpLpParam {
699                xtal_stable_wait_cycle,
700            },
701        }
702    }
703}
704
705impl SleepTimeConfig {
706    pub(crate) const CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ: u32 = 360;
707    pub(crate) const LIGHT_SLEEP_TIME_OVERHEAD_US: u32 = 56;
708
709    pub(crate) fn pmu_sleep_calculate_hw_wait_time(&self, pd_flags: PowerDownFlags) -> u32 {
710        // LP core hardware wait time, microseconds.
711        let lp_wakeup_wait_time_us = self.slowclk_to_us(MachineConstants::LP_WAKEUP_WAIT_CYCLE);
712        let lp_clk_switch_time_us = self.slowclk_to_us(MachineConstants::LP_CLK_SWITCH_CYCLE);
713        // XTAL is not used as the RTC_FAST source here, so the clock power-on
714        // wait is measured in slow-clock cycles.
715        let lp_clk_power_on_wait_time_us =
716            self.slowclk_to_us(MachineConstants::LP_CLK_POWER_ON_WAIT_CYCLE);
717
718        let lp_hw_wait_time_us = MachineConstants::LP_MIN_SLP_TIME_US
719            + MachineConstants::LP_ANALOG_WAIT_TIME_US
720            + lp_clk_power_on_wait_time_us
721            + lp_wakeup_wait_time_us
722            + lp_clk_switch_time_us
723            + MachineConstants::LP_POWER_SUPPLY_WAIT_TIME_US
724            + MachineConstants::LP_POWER_UP_WAIT_TIME_US;
725
726        // HP core hardware wait time, microseconds.
727        let hp_analog_wait_time_us = if pd_flags.pd_top() {
728            MachineConstants::HP_ANA_WAIT_TIME_PD_TOP_US
729        } else {
730            MachineConstants::HP_ANA_WAIT_TIME_PU_TOP_US
731        };
732        let hp_digital_power_up_wait_time_us = MachineConstants::HP_POWER_SUPPLY_WAIT_TIME_US
733            + MachineConstants::HP_POWER_UP_WAIT_TIME_US;
734        // No regdma retention in v1, so the regdma wait time is 0.
735        let hp_regdma_wait_time_us = 0;
736        // XTAL is powered down but not used as RTC_FAST, so wait for it to
737        // stabilize on wake along with the PLL.
738        let hp_clock_wait_time_us = if pd_flags.pd_xtal() {
739            MachineConstants::HP_XTAL_WAIT_STABLE_TIME_US
740                + MachineConstants::HP_PLL_WAIT_STABLE_TIME_US
741        } else {
742            MachineConstants::HP_PLL_WAIT_STABLE_TIME_US
743        };
744
745        let hp_hw_wait_time_us = hp_analog_wait_time_us
746            + hp_digital_power_up_wait_time_us
747            + hp_regdma_wait_time_us
748            + hp_clock_wait_time_us;
749
750        lp_hw_wait_time_us + hp_hw_wait_time_us
751    }
752}
753
754/// Configuration for the RTC sleep behavior.
755#[derive(Clone, Copy)]
756pub struct RtcSleepConfig {
757    /// Deep sleep flag.
758    pub deep: bool,
759    /// Power-down flags.
760    pub pd_flags: PowerDownFlags,
761}
762
763impl Default for RtcSleepConfig {
764    fn default() -> Self {
765        Self {
766            deep: false,
767            pd_flags: PowerDownFlags(0),
768        }
769    }
770}
771
772bitfield::bitfield! {
773    #[derive(Clone, Copy)]
774    /// Power domains to be powered down during sleep.
775    pub struct PowerDownFlags(u32);
776
777    /// Controls the power-down status of the top power domain.
778    pub u32, pd_top      , set_pd_top      : 0;
779    /// Controls the power-down status of the VDD_SDIO / DCDC switch.
780    pub u32, pd_vddsdio  , set_pd_vddsdio  : 1;
781    /// Controls the power-down status of the connectivity power domain.
782    pub u32, pd_modem    , set_pd_modem    : 2;
783    /// Controls the power-down status of the high-performance peripheral domain.
784    pub u32, pd_hp_periph, set_pd_hp_periph: 3;
785    /// Controls the power-down status of the CPU power domain.
786    ///
787    /// Crate-private, because a light sleep needs CPU retention to power this domain down. A
788    /// power-down without retention loses the CPU state.
789    pub(crate) u32, pd_cpu, set_pd_cpu: 4;
790    /// Controls the power-down status of the high-performance always-on domain.
791    pub u32, pd_hp_aon   , set_pd_hp_aon   : 5;
792    /// Controls the power-down status of memory group 0.
793    pub u32, pd_mem_g0   , set_pd_mem_g0   : 6;
794    /// Controls the power-down status of memory group 1.
795    pub u32, pd_mem_g1   , set_pd_mem_g1   : 7;
796    /// Controls the power-down status of memory group 2.
797    pub u32, pd_mem_g2   , set_pd_mem_g2   : 8;
798    /// Controls the power-down status of memory group 3.
799    pub u32, pd_mem_g3   , set_pd_mem_g3   : 9;
800    /// Controls the power-down status of the crystal oscillator.
801    pub u32, pd_xtal     , set_pd_xtal     : 10;
802    /// Controls the power-down status of the fast RC oscillator.
803    pub u32, pd_rc_fast  , set_pd_rc_fast  : 11;
804    /// Controls the power-down status of the 32 kHz crystal oscillator.
805    pub u32, pd_xtal32k  , set_pd_xtal32k  : 12;
806    /// Controls the power-down status of the 32 kHz RC oscillator.
807    pub u32, pd_rc32k    , set_pd_rc32k    : 13;
808    /// Controls the power-down status of the low-power peripheral domain.
809    pub u32, pd_lp_periph, set_pd_lp_periph: 14;
810}
811
812impl PowerDownFlags {
813    /// Returns whether all memory groups are powered down.
814    pub fn pd_mem(self) -> bool {
815        self.pd_mem_g0() && self.pd_mem_g1() && self.pd_mem_g2() && self.pd_mem_g3()
816    }
817
818    /// Sets the power-down status for all memory groups at once.
819    pub fn set_pd_mem(&mut self, value: bool) {
820        self.set_pd_mem_g0(value);
821        self.set_pd_mem_g1(value);
822        self.set_pd_mem_g2(value);
823        self.set_pd_mem_g3(value);
824    }
825}
826
827// Default DCDC voltage parameter during sleep (Kconfig
828// CONFIG_ESP_SLEEP_DCM_VSET_VAL_IN_SLEEP default).
829const DCM_VSET_IN_SLEEP: u8 = 14;
830// HP active calibration dbias (esp-idf HP_CALI_ACTIVE_DBIAS_DEFAULT).
831const HP_CALI_ACTIVE_DBIAS: u8 = 24;
832
833// Constants from `PMU_SLEEP_MC_DEFAULT()` in esp-idf pmu_param.h.
834struct MachineConstants;
835impl MachineConstants {
836    const LP_MIN_SLP_TIME_US: u32 = 450;
837    const LP_WAKEUP_WAIT_CYCLE: u32 = 4;
838    const LP_ANALOG_WAIT_TIME_US: u32 = 154;
839    const LP_XTAL_WAIT_STABLE_TIME_US: u32 = 250;
840    const LP_CLK_SWITCH_CYCLE: u32 = 1;
841    const LP_CLK_POWER_ON_WAIT_CYCLE: u32 = 1;
842    const LP_POWER_SUPPLY_WAIT_TIME_US: u32 = 2;
843    const LP_POWER_UP_WAIT_TIME_US: u32 = 2;
844
845    const HP_MIN_SLP_TIME_US: u32 = 450;
846    // analog_wait_time depends on whether TOP is powered down.
847    const HP_ANA_WAIT_TIME_PD_TOP_US: u32 = 260;
848    const HP_REGDMA_S2A_WORK_TIME_US: u32 = 685;
849    const HP_ANA_WAIT_TIME_PU_TOP_US: u32 =
850        Self::HP_ANA_WAIT_TIME_PD_TOP_US + Self::HP_REGDMA_S2A_WORK_TIME_US;
851    const HP_POWER_SUPPLY_WAIT_TIME_US: u32 = 2;
852    const HP_POWER_UP_WAIT_TIME_US: u32 = 26;
853    const HP_XTAL_WAIT_STABLE_TIME_US: u32 = 250;
854    const HP_PLL_WAIT_STABLE_TIME_US: u32 = 50;
855}
856
857impl RtcSleepConfig {
858    /// Returns whether the device is in deep sleep mode.
859    pub fn deep_slp(&self) -> bool {
860        self.deep
861    }
862
863    /// Configures the device for deep sleep mode.
864    pub fn deep() -> Self {
865        Self {
866            deep: true,
867            ..Self::default()
868        }
869    }
870
871    pub(crate) fn is_deep_sleep(&self) -> bool {
872        self.deep_slp()
873    }
874
875    pub(crate) fn base_settings(_rtc: &Rtc<'_>) {}
876
877    /// Selects the kind of the sleep, and what the sleep powers down.
878    ///
879    /// Sleep entry calls this before the hooks of the wakeup sources, so that a source can keep
880    /// powered what it needs.
881    pub(crate) fn set_sleep_kind(&mut self, kind: SleepKind) {
882        self.deep = kind == SleepKind::Deep;
883
884        let lp_slow_uses_xtal32k = cfg_select! {
885            use_xtal32k => ClockTree::with(|clocks| {
886                matches!(
887                    clocks::lp_slow_clk_config(clocks),
888                    Some(clocks::LpSlowClkConfig::Xtal32k)
889                )
890            }),
891            _ => false,
892        };
893
894        if self.deep {
895            self.pd_flags.set_pd_top(true);
896            self.pd_flags.set_pd_vddsdio(true);
897            self.pd_flags.set_pd_modem(true);
898            self.pd_flags.set_pd_hp_periph(true);
899            self.pd_flags.set_pd_cpu(true);
900            self.pd_flags.set_pd_mem(true);
901            self.pd_flags.set_pd_xtal(true);
902            self.pd_flags.set_pd_hp_aon(true);
903            self.pd_flags.set_pd_lp_periph(true);
904            self.pd_flags.set_pd_xtal32k(!lp_slow_uses_xtal32k);
905            self.pd_flags.set_pd_rc32k(true);
906            self.pd_flags.set_pd_rc_fast(true);
907        } else {
908            // Light sleep: the digital domain stays powered (DCDC-supplied at the
909            // light-sleep voltage) and only clock-gated, so execution resumes in
910            // place. Power down the analog clock sources nothing needs while the
911            // core is gated. Powering down XTAL also makes the analog config use
912            // the 0.6 V light-sleep operating point.
913            self.pd_flags.set_pd_xtal(true);
914            self.pd_flags.set_pd_rc_fast(true);
915            self.pd_flags.set_pd_xtal32k(!lp_slow_uses_xtal32k);
916        }
917    }
918
919    /// Configures the wakeup and reject sources of the sleep.
920    ///
921    /// [`Self::enter_sleep`] requests the sleep after this call. The return value is a guard that
922    /// restores what sleep entry changed for the sleep only, so the caller keeps it until the
923    /// sleep ends.
924    #[crate::ram]
925    pub(crate) fn start_sleep(&self, wakeup_mask: u32, reject_mask: u32) -> impl Sized {
926        // Switch the CPU root clock to XTAL for the duration of sleep.
927        let restore_clock_config = ClockTree::with(|clocks| {
928            let old_cpu_root_clk = clocks.cpu_root_clk();
929
930            clocks::configure_cpu_root_clk(clocks, CpuRootClkConfig::Xtal);
931
932            // Restore the old clock settings when we return
933            DropGuard::new((), move |_| {
934                ClockTree::with(|clocks| {
935                    if let Some(old) = old_cpu_root_clk {
936                        clocks::configure_cpu_root_clk(clocks, old);
937                    }
938                });
939            })
940        });
941
942        let power = PowerSleepConfig::defaults(self.pd_flags);
943        power.apply();
944
945        let config = if self.deep {
946            SleepTimeConfig::deep_sleep()
947        } else {
948            SleepTimeConfig::light_sleep(self.pd_flags)
949        };
950
951        // `pd_xtal` here means "the main XTAL is powered down during sleep", which
952        // selects the slow-clock xtal-stable wait on wake. That is exactly
953        // `pd_flags.pd_xtal()`; passing `xpd_xtal` (its inverse) used the fast-clock
954        // wait and produced a ~120x too-long wake-up xtal wait.
955        let mut param = ParamSleepConfig::defaults(config, self.pd_flags, self.pd_flags.pd_xtal());
956
957        if self.deep {
958            const PMU_LP_ANALOG_WAIT_TARGET_TIME_DSLP_US: u32 = 500;
959            param.lp_sys.analog_wait_target_cycle =
960                config.us_to_slowclk(PMU_LP_ANALOG_WAIT_TARGET_TIME_DSLP_US) as u8;
961
962            DigitalSleepConfig::defaults_deep_sleep(self.pd_flags).apply();
963            AnalogSleepConfig::defaults_deep_sleep().apply(true);
964        } else {
965            AnalogSleepConfig::defaults_light_sleep(self.pd_flags).apply(false);
966            DigitalSleepConfig::defaults_light_sleep(self.pd_flags).apply();
967        }
968
969        param.apply();
970
971        // ESP32-P4 rev 3.0 (ECO5) "MSPI crash after power up" deep-sleep
972        // erratum: redirect the wake reset vector to a stub in LP RAM that
973        // recovers MSPI before the first flash fetch (esp-idf pmu_sleep.c).
974        let mspi_workaround = self.deep && is_rev3_mspi_workaround_needed();
975        if mspi_workaround {
976            install_mspi_workaround_stub();
977            set_boot_from_lp_ram(true);
978        }
979
980        // The wake stub itself restores the vector on a real wake, so this guard runs only if the
981        // hardware rejects the sleep. It points the vector back at the HP ROM, so that a later
982        // reset boots normally.
983        let restore_boot_vector = DropGuard::new((), move |_| {
984            if mspi_workaround {
985                set_boot_from_lp_ram(false);
986            }
987        });
988
989        // like esp-idf pmu_sleep_start()
990
991        // lp_aon_hal_inform_wakeup_type: on P4 RTC_SLEEP_MODE_REG is
992        // LP_SYSTEM_REG_LP_STORE8 (bit0 = run deep-sleep wake stub). The ROM
993        // reads this on wake to pick the deep vs light wake path.
994        crate::peripherals::LP_AON::regs()
995            .lp_store8()
996            .modify(|r, w| unsafe { w.bits(r.bits() & !0x01 | self.deep as u32) });
997
998        // The wakeup enable field is bits 30:0 here, unlike the other PMU chips where it is the
999        // whole register. Bit 31 is reserved and reads 0, so a whole-register write is correct and
1000        // saves the read.
1001        PMU::regs()
1002            .slp_wakeup_cntl2()
1003            .write(|w| unsafe { w.bits(wakeup_mask) });
1004
1005        PMU::regs().slp_wakeup_cntl1().modify(|_, w| unsafe {
1006            w.slp_reject_en().bit(reject_mask != 0);
1007            w.sleep_reject_ena().bits(reject_mask)
1008        });
1009
1010        PMU::regs()
1011            .slp_wakeup_cntl4()
1012            .write(|w| w.slp_reject_cause_clr().bit(true));
1013
1014        PMU::regs().int_clr().write(|w| {
1015            w.sw().clear_bit_by_one();
1016            w.soc_sleep_reject().clear_bit_by_one();
1017            w.soc_wakeup().clear_bit_by_one()
1018        });
1019
1020        // ESP32-P4 deep-sleep DCDC -> LDO supply handover. The HP digital rail
1021        // is normally fed by the on-chip DCDC; if it is left running while the
1022        // PMU powers down the DCDC switch on deep-sleep entry, the rail glitches
1023        // when the LDO takes over on wake-up and the chip fails to reboot (it
1024        // looks like it "never wakes"). esp-idf raises the HP LDO so it can take
1025        // over, waits for it to settle, then disables the DCDC before entering
1026        // deep sleep (pmu_sleep_increase_ldo_volt + pmu_sleep_shutdown_dcdc).
1027        // C-series parts have no DCDC and skip this.
1028        if self.deep {
1029            pmu_sleep_dcdc_to_ldo_handover();
1030        }
1031
1032        // Light sleep keeps the HP domain (and thus the USJ PHY) powered, so
1033        // de-enumerate USB-Serial-JTAG cleanly before sleeping and restore it
1034        // in `finish_sleep`. Deep sleep powers the PHY off on its own.
1035        if !self.deep {
1036            usj_pad_backup_and_disable();
1037        }
1038
1039        // The PMU FSM switches the pads to their sleep setting and holds IOs
1040        // at the same stage; trigger the pad sleep selection first so the IOs
1041        // do not get held in an indeterminate state.
1042
1043        PMU::regs()
1044            .imm_pad_hold_all()
1045            .write(|w| w.tie_high_pad_slp_sel().set_bit());
1046
1047        // FIXME HERE
1048
1049        // Start entry into sleep mode.
1050
1051        (restore_clock_config, restore_boot_vector)
1052    }
1053
1054    /// Requests the sleep.
1055    ///
1056    /// The caller waits for the result of the request.
1057    #[inline(always)]
1058    pub(crate) fn enter_sleep(&self) -> bool {
1059        request_sleep()
1060    }
1061
1062    /// Cleans up after sleep.
1063    #[crate::ram]
1064    pub(crate) fn finish_sleep(&self) {
1065        // like esp-idf pmu_sleep_finish(): switch the pad configuration back from
1066        // the sleep state to the active state. In deep sleep we never get here.
1067
1068        PMU::regs()
1069            .imm_pad_hold_all()
1070            .write(|w| w.tie_low_pad_slp_sel().set_bit());
1071
1072        // The post-wake hook of the GPIO driver releases the pads that the sleep armed. Only that
1073        // driver knows which pads it prepared.
1074
1075        // Re-enumerate USB-Serial-JTAG (only disabled for light sleep; in deep
1076        // sleep we never reach here).
1077        if !self.deep {
1078            usj_pad_restore();
1079        }
1080    }
1081}