Skip to main content

esp_hal/rtc_cntl/rtc/
esp32p4.rs

1use strum::FromRepr;
2
3use crate::{
4    peripherals::PMU,
5    soc::{clocks::ClockConfig, regi2c},
6};
7
8// `dig_sysclk_sel` codes (SOC_CPU_CLK_SRC_*).
9const HP_SYSCLK_XTAL: u8 = 0;
10
11/// SOC Reset Reason.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)]
13#[repr(usize)]
14#[cfg_attr(feature = "defmt", derive(defmt::Format))]
15pub enum SocResetReason {
16    /// Power on reset
17    ChipPowerOn   = 0x01,
18    /// Software resets the digital core
19    CoreSw        = 0x03,
20    /// Deep sleep reset the digital core
21    CoreDeepSleep = 0x05,
22    /// SDIO Core reset
23    CoreSDIO      = 0x06,
24    /// Main watch dog 0 resets digital core
25    CoreMwdt0     = 0x07,
26    /// Main watch dog 1 resets digital core
27    CoreMwdt1     = 0x08,
28    /// RTC watch dog resets digital core
29    CoreRtcWdt    = 0x09,
30    /// Main watch dog 0 resets CPU 0
31    Cpu0Mwdt0     = 0x0B,
32    /// Software resets CPU 0
33    Cpu0Sw        = 0x0C,
34    /// RTC watch dog resets CPU 0
35    Cpu0RtcWdt    = 0x0D,
36    /// VDD voltage is not stable and resets the digital core
37    SysBrownOut   = 0x0F,
38    /// RTC watch dog resets digital core and rtc module
39    SysRtcWdt     = 0x10,
40    /// Main watch dog 1 resets CPU 0
41    Cpu0Mwdt1     = 0x11,
42    /// Super watch dog resets the digital core and rtc module
43    SysSuperWdt   = 0x12,
44    /// eFuse CRC error resets the digital core
45    CoreEfuseCrc  = 0x14,
46    /// USB UART resets the digital core
47    CoreUsbUart   = 0x15,
48    /// USB JTAG resets the digital core
49    CoreUsbJtag   = 0x16,
50    /// JTAG resets CPU
51    Cpu0JtagCpu   = 0x18,
52}
53
54/// Clear all force flags on PMU power domains to allow normal power management.
55///
56/// eco5 power domains (verified against PAC):
57///   - TOP: power_pd_top_cntl (system top-level)
58///   - CNNT: power_pd_cnnt_cntl (connectivity, eco5 new -- replaces hpaon/hpcpu/hpwifi)
59///   - HPMEM: power_pd_hpmem_cntl (HP memory)
60///   - LPPERI: power_pd_lpperi_cntl (LP peripherals, eco5 new)
61fn pmu_power_domain_force_default() {
62    let pmu = PMU::regs();
63
64    // PMU_HP_PD_TOP
65    pmu.power_pd_top_cntl().modify(|_, w| {
66        w.force_top_reset().bit(false);
67        w.force_top_iso().bit(false);
68        w.force_top_pu().bit(false);
69        w.force_top_no_reset().bit(false);
70        w.force_top_no_iso().bit(false);
71        w.force_top_pd().bit(false)
72    });
73
74    // PMU_HP_PD_CNNT (eco5: replaces hpaon + hpcpu + hpwifi from eco4)
75    pmu.power_pd_cnnt_cntl().modify(|_, w| {
76        w.force_cnnt_reset().bit(false);
77        w.force_cnnt_iso().bit(false);
78        w.force_cnnt_pu().bit(false);
79        w.force_cnnt_no_reset().bit(false);
80        w.force_cnnt_no_iso().bit(false);
81        w.force_cnnt_pd().bit(false)
82    });
83
84    // PMU_HP_PD_HPMEM
85    pmu.power_pd_hpmem_cntl().modify(|_, w| {
86        w.force_hp_mem_reset().bit(false);
87        w.force_hp_mem_iso().bit(false);
88        w.force_hp_mem_pu().bit(false);
89        w.force_hp_mem_no_reset().bit(false);
90        w.force_hp_mem_no_iso().bit(false);
91        w.force_hp_mem_pd().bit(false)
92    });
93
94    // PMU_LP_PD_LPPERI (eco5 new)
95    pmu.power_pd_lpperi_cntl().modify(|_, w| {
96        w.force_lp_peri_reset().bit(false);
97        w.force_lp_peri_iso().bit(false);
98        w.force_lp_peri_pu().bit(false);
99        w.force_lp_peri_no_reset().bit(false);
100        w.force_lp_peri_no_iso().bit(false);
101        w.force_lp_peri_pd().bit(false)
102    });
103}
104
105// ---------------------------------------------------------------------------
106// PMU register data structures.
107//
108// These mirror the `pmu_*_t` structs in esp-idf. The bit positions match the
109// ESP32-P4 PMU PAC register layouts so that the power configs can be written as
110// raw `u32` values, while the analog/syscntl configs are written field-by-field
111// from the sleep driver.
112// ---------------------------------------------------------------------------
113
114bitfield::bitfield! {
115    #[derive(Clone, Copy, Default)]
116    // pmu_hp_power_t.0 (hp_*_dig_power)
117    pub struct HpDigPower(u32);
118
119    pub bool, dcdc_switch_pd_en, set_dcdc_switch_pd_en: 21;
120    pub bool, mem_dslp          , set_mem_dslp          : 22;
121    pub bool, mem_pd_en         , set_mem_pd_en         : 23;
122    pub bool, cpu_pd_en         , set_cpu_pd_en         : 29;
123    pub bool, cnnt_pd_en        , set_cnnt_pd_en        : 30;
124    pub bool, top_pd_en         , set_top_pd_en         : 31;
125}
126
127bitfield::bitfield! {
128    #[derive(Clone, Copy, Default)]
129    // pmu_hp_power_t.1 (hp_*_hp_ck_power)
130    pub struct HpClkPower(u32);
131
132    pub bool, i2c_iso_en   , set_i2c_iso_en   : 21;
133    pub bool, i2c_retention, set_i2c_retention: 22;
134    pub u8,   xpd_pll_i2c  , set_xpd_pll_i2c  : 26, 23;
135    pub u8,   xpd_pll      , set_xpd_pll      : 30, 27;
136}
137
138bitfield::bitfield! {
139    #[derive(Clone, Copy, Default)]
140    // pmu_hp_power_t.2 (hp_*_xtal)
141    pub struct HpXtalPower(u32);
142
143    pub bool, xpd_xtal, set_xpd_xtal: 31;
144}
145
146#[derive(Clone, Copy, Default)]
147// pmu_sleep_power_config_t.0
148pub struct HpSysPower {
149    pub dig_power: HpDigPower,
150    pub clk: HpClkPower,
151    pub xtal: HpXtalPower,
152}
153
154bitfield::bitfield! {
155    #[derive(Clone, Copy, Default)]
156    // pmu_hp_sys_cntl_reg_t (hp_*_hp_sys_cntl)
157    pub struct HpSysCntlReg(u32);
158
159    pub bool, uart_wakeup_en , set_uart_wakeup_en : 24;
160    pub bool, lp_pad_hold_all, set_lp_pad_hold_all: 25;
161    pub bool, hp_pad_hold_all, set_hp_pad_hold_all: 26;
162    pub bool, dig_pad_slp_sel, set_dig_pad_slp_sel: 27;
163    pub bool, dig_pause_wdt  , set_dig_pause_wdt  : 28;
164    pub bool, dig_cpu_stall  , set_dig_cpu_stall  : 29;
165}
166
167bitfield::bitfield! {
168    #[derive(Clone, Copy, Default)]
169    // pmu_hp_sysclk_reg_t (hp_*_sysclk)
170    pub struct HpSysclk(u32);
171
172    pub bool, dig_sysclk_nodiv, set_dig_sysclk_nodiv: 26;
173    pub bool, icg_sysclk_en   , set_icg_sysclk_en   : 27;
174    pub bool, sysclk_slp_sel  , set_sysclk_slp_sel  : 28;
175    pub bool, icg_slp_sel     , set_icg_slp_sel     : 29;
176    pub u8,   dig_sysclk_sel  , set_dig_sysclk_sel  : 31, 30;
177}
178
179bitfield::bitfield! {
180    #[derive(Clone, Copy, Default)]
181    // pmu_hp_analog_t.0 (hp_*_bias) -- note P4-only dcm fields.
182    pub struct HpAnalogBias(u32);
183
184    pub u8,   dcm_vset  , set_dcm_vset  : 22, 18;
185    pub u8,   dcm_mode  , set_dcm_mode  : 24, 23;
186    pub bool, xpd_bias  , set_xpd_bias  : 25;
187    pub u8,   dbg_atten , set_dbg_atten : 29, 26;
188    pub bool, pd_cur    , set_pd_cur    : 30;
189    pub bool, bias_sleep, set_bias_sleep: 31;
190}
191
192bitfield::bitfield! {
193    #[derive(Clone, Copy, Default)]
194    // pmu_hp_analog_t.1 (hp_*_hp_regulator0)
195    pub struct HpAnalogRegulator0(u32);
196
197    pub bool, slp_mem_xpd    , set_slp_mem_xpd    : 16;
198    pub bool, slp_logic_xpd  , set_slp_logic_xpd  : 17;
199    pub bool, xpd            , set_xpd            : 18;
200    pub u8,   slp_mem_dbias  , set_slp_mem_dbias  : 22, 19;
201    pub u8,   slp_logic_dbias, set_slp_logic_dbias: 26, 23;
202    pub u8,   dbias          , set_dbias          : 31, 27;
203}
204
205bitfield::bitfield! {
206    #[derive(Clone, Copy, Default)]
207    // pmu_hp_analog_t.2 (hp_*_hp_regulator1)
208    pub struct HpAnalogRegulator1(u32);
209
210    pub u8, drv_b, set_drv_b: 31, 26;
211}
212
213#[derive(Clone, Copy, Default)]
214// pmu_hp_analog_t
215pub struct HpAnalog {
216    pub bias: HpAnalogBias,
217    pub regulator0: HpAnalogRegulator0,
218    pub regulator1: HpAnalogRegulator1,
219}
220
221bitfield::bitfield! {
222    #[derive(Clone, Copy, Default)]
223    // pmu_lp_power_t.0 (lp dig_power)
224    pub struct LpDigPower(u32);
225
226    pub bool, lp_pad_slp_sel, set_lp_pad_slp_sel: 26;
227    pub bool, bod_source_sel, set_bod_source_sel: 27;
228    pub u8,   vddbat_mode   , set_vddbat_mode   : 29, 28;
229    pub bool, mem_dslp      , set_mem_dslp      : 30;
230    pub bool, peri_pd_en    , set_peri_pd_en    : 31;
231}
232
233bitfield::bitfield! {
234    #[derive(Clone, Copy, Default)]
235    // pmu_lp_power_t.1 (lp clk_power)
236    pub struct LpClkPower(u32);
237
238    pub bool, xpd_lppll  , set_xpd_lppll  : 27;
239    pub bool, xpd_xtal32k, set_xpd_xtal32k: 28;
240    pub bool, xpd_rc32k  , set_xpd_rc32k  : 29;
241    pub bool, xpd_fosc   , set_xpd_fosc   : 30;
242    pub bool, pd_osc     , set_pd_osc     : 31;
243}
244
245bitfield::bitfield! {
246    #[derive(Clone, Copy, Default)]
247    // pmu_lp_power_t.2 (lp xtal)
248    pub struct LpXtalPower(u32);
249
250    pub bool, xpd_xtal, set_xpd_xtal: 31;
251}
252
253#[derive(Clone, Copy, Default)]
254// pmu_sleep_power_config_t.1
255pub struct LpSysPower {
256    pub dig_power: LpDigPower,
257    pub clk_power: LpClkPower,
258    pub xtal: LpXtalPower,
259}
260
261bitfield::bitfield! {
262    #[derive(Clone, Copy, Default)]
263    // pmu_lp_analog_t.0 (lp_*_bias)
264    pub struct LpAnalogBias(u32);
265
266    pub bool, xpd_bias  , set_xpd_bias  : 25;
267    pub u8,   dbg_atten , set_dbg_atten : 29, 26;
268    pub bool, pd_cur    , set_pd_cur    : 30;
269    pub bool, bias_sleep, set_bias_sleep: 31;
270}
271
272bitfield::bitfield! {
273    #[derive(Clone, Copy, Default)]
274    // pmu_lp_analog_t.1 (lp_*_lp_regulator0)
275    pub struct LpAnalogRegulator0(u32);
276
277    pub bool, slp_xpd  , set_slp_xpd  : 21;
278    pub bool, xpd      , set_xpd      : 22;
279    pub u8,   slp_dbias, set_slp_dbias: 26, 23;
280    pub u8,   dbias    , set_dbias    : 31, 27;
281}
282
283bitfield::bitfield! {
284    #[derive(Clone, Copy, Default)]
285    // pmu_lp_analog_t.2 (lp_*_lp_regulator1)
286    pub struct LpAnalogRegulator1(u32);
287
288    pub u8, drv_b, set_drv_b: 31, 26;
289}
290
291#[derive(Clone, Copy, Default)]
292// pmu_lp_analog_t
293pub struct LpAnalog {
294    pub bias: LpAnalogBias,
295    pub regulator0: LpAnalogRegulator0,
296    pub regulator1: LpAnalogRegulator1,
297}
298
299/// Configures the default per-mode HP system PMU registers (HP_ACTIVE and
300/// HP_SLEEP). Mirrors `pmu_hp_system_init()` in esp-idf. The HP_MODEM mode is
301/// skipped (P4 has no wireless modem).
302fn pmu_hp_system_init() {
303    let pmu = PMU::regs();
304
305    // ---- HP_ACTIVE ----
306    pmu.hp_active_dig_power().write(|w| unsafe { w.bits(0) });
307    pmu.hp_active_hp_ck_power().write(|w| unsafe {
308        w.hp_active_i2c_iso_en().bit(false);
309        w.hp_active_i2c_retention().bit(false);
310        w.hp_active_xpd_pll_i2c().bits(0xf);
311        w.hp_active_xpd_pll().bits(0xf)
312    });
313    pmu.hp_active_xtal()
314        .modify(|_, w| w.hp_active_xpd_xtal().bit(true));
315
316    pmu.hp_active_icg_hp_func()
317        .write(|w| unsafe { w.bits(0xffff_ffff) });
318    pmu.hp_active_icg_hp_apb()
319        .write(|w| unsafe { w.bits(0xffff_ffff) });
320    pmu.hp_active_icg_modem()
321        .write(|w| unsafe { w.hp_active_dig_icg_modem_code().bits(0) });
322    pmu.hp_active_sysclk().modify(|_, w| unsafe {
323        w.hp_active_dig_sys_clk_no_div().bit(false);
324        w.hp_active_icg_sys_clock_en().bit(true);
325        w.hp_active_sys_clk_slp_sel().bit(false);
326        w.hp_active_icg_slp_sel().bit(false);
327        w.hp_active_dig_sys_clk_sel().bits(HP_SYSCLK_XTAL)
328    });
329    pmu.hp_active_hp_sys_cntl().modify(|_, w| {
330        w.hp_active_uart_wakeup_en().bit(false);
331        w.hp_active_lp_pad_hold_all().bit(false);
332        w.hp_active_hp_pad_hold_all().bit(false);
333        w.hp_active_dig_pad_slp_sel().bit(false);
334        w.hp_active_dig_pause_wdt().bit(false);
335        w.hp_active_dig_cpu_stall().bit(false)
336    });
337    pmu.hp_active_bias().modify(|_, w| unsafe {
338        w.hp_active_dcm_vset().bits(27);
339        w.hp_active_dcm_mode().bits(1);
340        w.hp_active_xpd_bias().bit(true);
341        w.hp_active_dbg_atten().bits(0);
342        w.hp_active_pd_cur().bit(false);
343        w.sleep().bit(false)
344    });
345    // NOTE: do NOT reprogram the HP active regulator xpd/dbias here. The
346    // bootloader already applied the efuse-calibrated active voltage
347    // (get_act_hp_dbias()); overriding it with a fixed default under-volts the
348    // HP domain and hangs the running CPU. IDF's pmu_hp_system_init() only
349    // touches the sleep-related regulator sub-fields (pmu_init.c).
350    pmu.hp_active_hp_regulator0().modify(|_, w| unsafe {
351        w.hp_active_hp_regulator_slp_mem_xpd().bit(false);
352        w.hp_active_hp_regulator_slp_logic_xpd().bit(false);
353        w.hp_active_hp_regulator_slp_logic_dbias().bits(0)
354    });
355    pmu.hp_active_backup().write(|w| unsafe { w.bits(0) });
356    pmu.hp_active_backup_clk()
357        .write(|w| unsafe { w.bits(0xffff_ffff) });
358
359    // ---- HP_SLEEP ----
360    pmu.hp_sleep_dig_power()
361        .write(|w| w.hp_sleep_dcdc_switch_pd_en().bit(true));
362    pmu.hp_sleep_hp_ck_power().write(|w| unsafe {
363        w.hp_sleep_i2c_iso_en().bit(true);
364        w.hp_sleep_i2c_retention().bit(true);
365        w.hp_sleep_xpd_pll_i2c().bits(1);
366        w.hp_sleep_xpd_pll().bits(0)
367    });
368    pmu.hp_sleep_xtal()
369        .modify(|_, w| w.hp_sleep_xpd_xtal().bit(false));
370
371    pmu.hp_sleep_icg_hp_func().write(|w| unsafe { w.bits(0) });
372    pmu.hp_sleep_icg_hp_apb().write(|w| unsafe { w.bits(0) });
373    pmu.hp_sleep_icg_modem()
374        .write(|w| unsafe { w.hp_sleep_dig_icg_modem_code().bits(0) });
375    pmu.hp_sleep_sysclk().modify(|_, w| unsafe {
376        w.hp_sleep_dig_sys_clk_no_div().bit(false);
377        w.hp_sleep_icg_sys_clock_en().bit(false);
378        w.hp_sleep_sys_clk_slp_sel().bit(true);
379        w.hp_sleep_icg_slp_sel().bit(true);
380        w.hp_sleep_dig_sys_clk_sel().bits(HP_SYSCLK_XTAL)
381    });
382    pmu.hp_sleep_hp_sys_cntl().modify(|_, w| {
383        w.hp_sleep_uart_wakeup_en().bit(true);
384        w.hp_sleep_lp_pad_hold_all().bit(false);
385        w.hp_sleep_hp_pad_hold_all().bit(false);
386        w.hp_sleep_dig_pad_slp_sel().bit(false);
387        w.hp_sleep_dig_pause_wdt().bit(true);
388        w.hp_sleep_dig_cpu_stall().bit(true)
389    });
390    pmu.hp_sleep_bias().modify(|_, w| unsafe {
391        w.hp_sleep_dcm_vset().bits(0);
392        w.hp_sleep_dcm_mode().bits(1);
393        w.hp_sleep_xpd_bias().bit(false);
394        w.hp_sleep_dbg_atten().bits(0);
395        w.hp_sleep_pd_cur().bit(true);
396        w.sleep().bit(true)
397    });
398    pmu.hp_sleep_hp_regulator0().modify(|_, w| unsafe {
399        w.hp_sleep_hp_regulator_slp_mem_xpd().bit(false);
400        w.hp_sleep_hp_regulator_slp_logic_xpd().bit(false);
401        w.hp_sleep_hp_regulator_xpd().bit(true);
402        w.hp_sleep_hp_regulator_slp_mem_dbias().bits(1);
403        w.hp_sleep_hp_regulator_slp_logic_dbias().bits(0)
404    });
405    pmu.hp_sleep_backup().write(|w| unsafe { w.bits(0) });
406    pmu.hp_sleep_backup_clk()
407        .write(|w| unsafe { w.bits(0xffff_ffff) });
408
409    // Some PMU initial parameter configuration (force update ICG/sysclk).
410    pmu.imm_modem_icg()
411        .write(|w| w.update_dig_icg_modem_en().bit(true));
412    pmu.imm_sleep_sysclk()
413        .write(|w| w.update_dig_icg_switch().bit(true));
414
415    // PMU_SLEEP_PROTECT_HP_LP_SLEEP
416    const PMU_SLEEP_PROTECT_HP_LP_SLEEP: u8 = 2;
417    pmu.slp_wakeup_cntl3()
418        .modify(|_, w| unsafe { w.sleep_prt_sel().bits(PMU_SLEEP_PROTECT_HP_LP_SLEEP) });
419}
420
421/// Configures the default per-mode LP system PMU registers (LP_ACTIVE which
422/// lives in the `hp_sleep_lp_*` group, and LP_SLEEP in the `lp_sleep_*` group).
423/// Mirrors `pmu_lp_system_init()` in esp-idf.
424fn pmu_lp_system_init() {
425    let pmu = PMU::regs();
426
427    // ---- LP_ACTIVE (hp_sleep_lp_*) ----
428    pmu.hp_sleep_lp_dig_power().write(|w| unsafe { w.bits(0) });
429    pmu.hp_sleep_lp_ck_power().write(|w| {
430        w.hp_sleep_xpd_lppll().bit(false);
431        w.hp_sleep_xpd_xtal32k().bit(true);
432        w.hp_sleep_xpd_rc32k().bit(true);
433        w.hp_sleep_xpd_fosc_clk().bit(true);
434        w.hp_sleep_pd_osc_clk().bit(false)
435    });
436    // Likewise, leave the LP active regulator voltage (xpd/dbias) as the
437    // bootloader calibrated it (get_act_lp_dbias()); IDF's pmu_lp_system_init()
438    // only defaults the sleep sub-fields here.
439    pmu.hp_sleep_lp_regulator0().modify(|_, w| unsafe {
440        w.hp_sleep_lp_regulator_slp_xpd().bit(false);
441        w.hp_sleep_lp_regulator_slp_dbias().bits(0)
442    });
443    pmu.hp_sleep_lp_regulator1()
444        .modify(|_, w| unsafe { w.hp_sleep_lp_regulator_drv_b().bits(0) });
445
446    // ---- LP_SLEEP (lp_sleep_*) ----
447    pmu.lp_sleep_lp_dig_power().write(|w| unsafe { w.bits(0) });
448    pmu.lp_sleep_lp_ck_power().write(|w| unsafe { w.bits(0) });
449    pmu.lp_sleep_xtal()
450        .modify(|_, w| w.lp_sleep_xpd_xtal().bit(false));
451    pmu.lp_sleep_bias().modify(|_, w| unsafe {
452        w.lp_sleep_xpd_bias().bit(false);
453        w.lp_sleep_dbg_atten().bits(0);
454        w.lp_sleep_pd_cur().bit(true);
455        w.sleep().bit(true)
456    });
457    pmu.lp_sleep_lp_regulator0().modify(|_, w| unsafe {
458        w.lp_sleep_lp_regulator_slp_xpd().bit(false);
459        w.lp_sleep_lp_regulator_xpd().bit(true);
460        w.lp_sleep_lp_regulator_slp_dbias().bits(0);
461        w.lp_sleep_lp_regulator_dbias().bits(12)
462    });
463    pmu.lp_sleep_lp_regulator1()
464        .modify(|_, w| unsafe { w.lp_sleep_lp_regulator_drv_b().bits(0) });
465}
466
467/// Minimal init for bare-metal boot.
468pub(crate) fn init(_config: &ClockConfig) {
469    pmu_power_domain_force_default();
470
471    // Force the analog dig regulators on via regi2c (XPD off => regulated by
472    // PMU) and configure the default per-mode PMU system state (HP/LP active +
473    // sleep). Required before the PMU can perform a clean sleep/wake transition.
474    // Ref: IDF rtc_clk_init.c (esp32p4).
475    regi2c::I2C_DIG_REG_FORCE_RTC_DREG.write_field(1);
476    regi2c::I2C_DIG_REG_FORCE_DIG_DREG.write_field(1);
477    regi2c::I2C_DIG_REG_XPD_RTC_REG.write_field(0);
478    regi2c::I2C_DIG_REG_XPD_DIG_REG.write_field(0);
479    pmu_hp_system_init();
480    pmu_lp_system_init();
481
482    // Clear DCDC switch force flags PMU_POWER_DCDC_SWITCH_REG (offset 0x10c)
483    PMU::regs().power_dcdc_switch().modify(|_, w| {
484        w.force_dcdc_switch_pu().bit(false);
485        w.force_dcdc_switch_pd().bit(false)
486    });
487
488    // PAC: tie_high_xpd_pll is 4-bit field (one bit per PLL: CPLL/SPLL/MPLL/PLLA).
489    // Set all bits to enable all PLLs. Same for pll_i2c.
490    PMU::regs().imm_hp_ck_power().write(|w| unsafe {
491        w.tie_high_xpd_pll().bits(0xF);
492        w.tie_high_xpd_pll_i2c().bits(0xF)
493    });
494}