Skip to main content

esp_hal/rtc_cntl/sleep/
esp32p4.rs

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