1
#![cfg_attr(docsrs, procmacros::doc_replace)]
2
//! # Real-Time Control and Low-power Management (RTC_CNTL)
3
//!
4
//! ## Overview
5
//!
6
//! The RTC_CNTL peripheral is responsible for managing the low-power modes on
7
//! the chip.
8
//!
9
//! ## Configuration
10
//!
11
//! It also includes the necessary configurations and constants for clock
12
//! sources and low-power management. The driver provides the following features
13
//! and functionalities:
14
//!
15
//! * Clock Configuration
16
//! * Calibration
17
//! * Low-Power Management
18
//! * Handling Watchdog Timers
19
#![cfg_attr(
20
    lp_timer_driver_supported,
21
    doc = r#"
22
## Examples
23

            
24
### Get time in ms from the RTC Timer
25

            
26
```rust, no_run
27
# {before_snippet}
28
# use esp_hal::{delay::Delay, rtc_cntl::Rtc};
29

            
30
let rtc = Rtc::new(peripherals.RTC_TIMER);
31
let delay = Delay::new();
32

            
33
loop {
34
    // Print the current RTC time in milliseconds
35
    let time_ms = rtc.current_time_us() / 1000;
36
    delay.delay_millis(1000);
37

            
38
    // Set the time to half a second in the past
39
    let new_time = rtc.current_time_us() - 500_000;
40
    rtc.set_current_time_us(new_time);
41
}
42
# }
43
```
44

            
45
### RWDT usage
46
```rust, no_run
47
# {before_snippet}
48
# use core::cell::RefCell;
49
# use critical_section::Mutex;
50
# use esp_hal::delay::Delay;
51
# use esp_hal::rtc_cntl::Rtc;
52
# use esp_hal::rtc_cntl::Rwdt;
53
# use esp_hal::rtc_cntl::RwdtStage;
54
static RWDT: Mutex<RefCell<Option<Rwdt>>> = Mutex::new(RefCell::new(None));
55

            
56
let mut delay = Delay::new();
57
let mut rtc = Rtc::new(peripherals.RTC_TIMER);
58

            
59
rtc.set_interrupt_handler(interrupt_handler);
60
rtc.rwdt
61
    .set_timeout(RwdtStage::Stage0, Duration::from_millis(2000));
62
rtc.rwdt.listen();
63

            
64
critical_section::with(|cs| RWDT.borrow_ref_mut(cs).replace(rtc.rwdt));
65
# {after_snippet}
66

            
67
// Where the `LP_WDT` interrupt handler is defined as:
68
# use core::cell::RefCell;
69
# use critical_section::Mutex;
70
# use esp_hal::rtc_cntl::Rwdt;
71
# use esp_hal::rtc_cntl::RwdtStage;
72
static RWDT: Mutex<RefCell<Option<Rwdt>>> = Mutex::new(RefCell::new(None));
73

            
74
// Handle the corresponding interrupt
75
#[esp_hal::handler]
76
fn interrupt_handler() {
77
    critical_section::with(|cs| {
78
        println!("RWDT Interrupt");
79

            
80
        let mut rwdt = RWDT.borrow_ref_mut(cs);
81
        if let Some(rwdt) = rwdt.as_mut() {
82
            rwdt.clear_interrupt();
83

            
84
            println!("Restarting in 5 seconds...");
85

            
86
            rwdt.set_timeout(RwdtStage::Stage0, Duration::from_millis(5000));
87
            rwdt.unlisten();
88
        }
89
    });
90
}
91
```
92

            
93
### Get time in ms from the RTC Timer
94
```rust, no_run
95
# {before_snippet}
96
# use esp_hal::{delay::Delay, rtc_cntl::Rtc};
97

            
98
let rtc = Rtc::new(peripherals.RTC_TIMER);
99
let delay = Delay::new();
100

            
101
loop {
102
    // Get the current RTC time in milliseconds
103
    let time_ms = rtc.current_time_us() / 1000;
104
    delay.delay_millis(1000);
105

            
106
    // Set the time to half a second in the past
107
    let new_time = rtc.current_time_us() - 500_000;
108
    rtc.set_current_time_us(new_time);
109
}
110
# }
111
```
112
"#
113
)]
114
pub use self::rtc::SocResetReason;
115
#[cfg_attr(not(lp_timer_driver_supported), expect(unused))]
116
use crate::{
117
    interrupt::{self, InterruptHandler},
118
    peripherals::Interrupt,
119
};
120
use crate::{peripherals::RTC_TIMER, system::Cpu, time::Duration};
121

            
122
unstable_module! {
123
    // only include sleep where it's been implemented
124
    #[cfg(sleep_driver_supported)]
125
    pub mod sleep;
126
}
127

            
128
cfg_select! {
129
    supports_cpu_power_down => {
130
        mod cpu_retention;
131
        pub(crate) use cpu_retention::installed_buffer_ptr;
132
        #[instability::unstable]
133
        pub use cpu_retention::memory::{
134
            CpuRetentionMemory,
135
            CpuRetentionMemoryError,
136
            CpuRetentionStorage,
137
        };
138
    }
139
    _ => {}
140
}
141

            
142
cfg_select! {
143
    supports_tagmem_power_down => {
144
        #[path = "cpu_retention/tagmem.rs"]
145
        mod tagmem;
146
        #[instability::unstable]
147
        pub use tagmem::{
148
            CacheTagRetentionMemory,
149
            CacheTagRetentionMemoryError,
150
            CacheTagRetentionStorage,
151
        };
152
    }
153
    _ => {}
154
}
155

            
156
#[cfg_attr(esp32, path = "rtc/esp32.rs")]
157
#[cfg_attr(esp32c2, path = "rtc/esp32c2.rs")]
158
#[cfg_attr(esp32c3, path = "rtc/esp32c3.rs")]
159
#[cfg_attr(esp32c5, path = "rtc/esp32c5.rs")]
160
#[cfg_attr(esp32c6, path = "rtc/esp32c6.rs")]
161
#[cfg_attr(esp32c61, path = "rtc/esp32c61.rs")]
162
#[cfg_attr(esp32h2, path = "rtc/esp32h2.rs")]
163
#[cfg_attr(esp32h4, path = "rtc/esp32h4.rs")]
164
#[cfg_attr(esp32p4, path = "rtc/esp32p4.rs")]
165
#[cfg_attr(esp32s2, path = "rtc/esp32s2.rs")]
166
#[cfg_attr(esp32s3, path = "rtc/esp32s3.rs")]
167
#[cfg_attr(esp32s31, path = "rtc/esp32s31.rs")]
168
pub(crate) mod rtc;
169

            
170
// Trims the RTC bandgap using eFuse calibration data.
171
//
172
// The trim only needs to be applied on power-on; the analog domain retains it
173
// across other resets, so the eFuses are not read again on those paths.
174
//
175
// `ocode` returns the trim value to apply, or `None` if the eFuses hold no
176
// usable calibration data.
177
#[cfg(any(esp32c2, esp32c3, esp32c5, esp32c6, esp32c61, esp32s2, esp32s3))]
178
pub(crate) fn calibrate_ocode(ocode: impl FnOnce() -> Option<u8>) {
179
    if crate::system::reset_reason() != Some(SocResetReason::ChipPowerOn) {
180
        return;
181
    }
182

            
183
    if let Some(ocode) = ocode() {
184
        crate::soc::regi2c::I2C_ULP_EXT_CODE.write_reg(ocode);
185
325
        crate::soc::regi2c::I2C_ULP_IR_FORCE_CODE.write_field(1);
186
    }
187
}
188

            
189
cfg_select! {
190
    esp32s31 => {
191
        use crate::peripherals::{LP_SYS as LP_AON, LP_WDT};
192
    }
193
    soc_has_lp_wdt => {
194
        use crate::peripherals::{LP_AON, LP_WDT};
195
    }
196
    _ => {
197
        use crate::peripherals::{LPWR, LPWR as LP_WDT, LPWR as LP_AON};
198
    }
199
}
200

            
201
#[rustfmt::skip]
202
#[cfg(sleep_driver_supported)]
203
macro_rules! wakeup_docstring {
204
    (Ext0)          => { "EXT0 wakeup" };
205
    (Ext1)          => { "EXT1 wakeup, via one or more RTC GPIOs (`RTC_CNTL`)." };
206
    (Gpio)          => { "GPIO wakeup." };
207
    (Timer)         => { "Timer wakeup." };
208
    (Sdio)          => { "SDIO wakeup." };
209
    (Wifi)          => { "Wi-Fi (MAC) wakeup." };
210
    (WifiBeacon)    => { "Wi-Fi beacon wakeup." };
211
    (Uart0)         => { "UART0 wakeup." };
212
    (Uart1)         => { "UART1 wakeup." };
213
    (Uart2)         => { "UART2 wakeup." };
214
    (Uart3)         => { "UART3 wakeup." };
215
    (Uart4)         => { "UART4 wakeup." };
216
    (Touch)         => { "Touch sensor wakeup." };
217
    (Ulp)           => { "ULP wakeup." };
218
    (UlpRiscv)      => { "ULP RISC-V coprocessor wakeup." };
219
    (UlpRiscvTrap)  => { "ULP RISC-V coprocessor trap (crash) wakeup." };
220
    (Bt)            => { "Bluetooth wakeup." };
221
    (LpCore)        => { "LP core wakeup." };
222
    (Usb)           => { "USB wakeup." };
223
    ($($other:tt)*) => { compile_error!(concat!("Unknown wakeup source: ", stringify!($($other)*))) };
224
}
225

            
226
#[cfg(sleep_driver_supported)]
227
for_each_wakeup_source! {
228
    (all $( ($variant:ident, $bit:literal) ),*) => {
229
        /// A source that can wake the chip from sleep.
230
        ///
231
        /// Each variant models a single bit in the wakeup registers. Internal representation shared
232
        /// by [`WakeTriggers`][crate::rtc_cntl::sleep::WakeTriggers] (the sources configured to
233
        /// end a sleep) and [`WakeupReason`] (the source(s) that caused the most recent wakeup,
234
        /// see [`wakeup_cause`])
235
        ///
236
        /// The available variants depend on the target chip.
237
        #[derive(Debug, enumset::EnumSetType)]
238
        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
239
        pub enum WakeupSource {
240
            $(
241
                #[doc = wakeup_docstring!($variant)]
242
                $variant = $bit,
243
            )*
244
        }
245
    };
246
}
247

            
248
/// The source(s) that caused the most recent wakeup from sleep.
249
///
250
/// Returned by [`wakeup_cause`]. This is a thin wrapper around the set of [`WakeupSource`]s that
251
/// were reported by the hardware. The set is empty if the chip was not woken from sleep.
252
///
253
/// The available sources depend on the target chip.
254
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
255
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
256
#[cfg(sleep_driver_supported)]
257
pub struct WakeupReason(enumset::EnumSet<WakeupSource>);
258

            
259
#[cfg(sleep_driver_supported)]
260
impl WakeupReason {
261
    /// Returns whether the chip was not woken from sleep by any source.
262
    pub fn is_empty(&self) -> bool {
263
        self.0.is_empty()
264
    }
265

            
266
    /// Returns whether the given source caused the most recent wakeup.
267
    pub fn contains(&self, source: WakeupSource) -> bool {
268
        self.0.contains(source)
269
    }
270

            
271
    /// Returns an iterator over the sources that caused the most recent wakeup.
272
    pub fn iter(&self) -> impl Iterator<Item = WakeupSource> {
273
        self.0.iter()
274
    }
275
}
276

            
277
/// RTC clock.
278
pub struct Rtc<'d> {
279
    // This field holds the peripheral, which prevents a second `Rtc`. The code uses
280
    // `RTC_TIMER::regs()` for the registers, because the sleep alarm needs them without an `Rtc`.
281
    _rtc_timer: RTC_TIMER<'d>,
282
    /// Resets Watchdog Timer.
283
    pub rwdt: Rwdt,
284
    /// Super Watchdog
285
    #[cfg(soc_has_swd_watchdog)]
286
    pub swd: Swd,
287
}
288

            
289
impl<'d> Rtc<'d> {
290
    /// Creates a new instance in [crate::Blocking] mode.
291
    ///
292
    /// Optionally an interrupt handler can be bound.
293
    pub fn new(rtc_timer: RTC_TIMER<'d>) -> Self {
294
        Self {
295
            _rtc_timer: rtc_timer,
296
            rwdt: Rwdt,
297
            #[cfg(soc_has_swd_watchdog)]
298
            swd: Swd,
299
        }
300
    }
301

            
302
    /// Returns the time since boot in the raw register units.
303
    #[cfg(lp_timer_driver_supported)]
304
    fn time_since_boot_raw(&self) -> u64 {
305
2
        time_since_boot_raw()
306
    }
307

            
308
    /// Returns the time elapsed since the last power-on reset.
309
    ///
310
    /// Any reset or sleep, other than a power-up reset, will not stop or
311
    /// reset the RTC timer.
312
    #[cfg(lp_timer_driver_supported)]
313
2
    pub fn time_since_power_up(&self) -> Duration {
314
        Duration::from_micros(crate::clock::rtc_ticks_to_us(self.time_since_boot_raw()))
315
2
    }
316

            
317
    /// Reads the current value of the boot time registers in microseconds.
318
    #[cfg(lp_timer_driver_supported)]
319
    fn boot_time_us(&self) -> u64 {
320
        // For more info on about how RTC setting works and what it has to do with boot time, see https://github.com/esp-rs/esp-hal/pull/1883
321
        let (low_reg, high_reg) =
322
            cfg_select! {
323
                esp32s31 => (LP_AON::regs().lp_store(2), LP_AON::regs().lp_store(3)),
324
                esp32p4 => (LP_AON::regs().lp_store2(), LP_AON::regs().lp_store3()),
325
                _ => (LP_AON::regs().store2(), LP_AON::regs().store3()),
326
            };
327

            
328
        let l = low_reg.read().bits() as u64;
329
        let h = high_reg.read().bits() as u64;
330

            
331
        // https://github.com/espressif/esp-idf/blob/23e4823f17a8349b5e03536ff7653e3e584c9351/components/newlib/port/esp_time_impl.c#L115
332
        l + (h << 32)
333
    }
334

            
335
    /// Sets the current value of the boot time registers in microseconds.
336
    #[cfg(lp_timer_driver_supported)]
337
    fn set_boot_time_us(&self, boot_time_us: u64) {
338
        // Please see `boot_time_us` for documentation on registers and peripherals
339
        // used for certain SOCs.
340

            
341
        let (low_reg, high_reg) =
342
            cfg_select! {
343
                esp32s31 => (LP_AON::regs().lp_store(2), LP_AON::regs().lp_store(3)),
344
                esp32p4 => (LP_AON::regs().lp_store2(), LP_AON::regs().lp_store3()),
345
                _ => (LP_AON::regs().store2(), LP_AON::regs().store3()),
346
            };
347

            
348
        // https://github.com/espressif/esp-idf/blob/23e4823/components/newlib/port/esp_time_impl.c#L102-L103
349

            
350
        low_reg // Low bits
351
            .write(|w| unsafe { w.bits((boot_time_us & 0xffff_ffff) as u32) });
352
        high_reg // High bits
353
            .write(|w| unsafe { w.bits((boot_time_us >> 32) as u32) });
354
    }
355

            
356
    #[procmacros::doc_replace]
357
    /// Returns the current time in microseconds.
358
    ///
359
    /// # Examples
360
    ///
361
    /// This example shows how to get the weekday of the current time in
362
    /// New York using the `jiff` crate. This example works in core-only
363
    /// environments without dynamic memory allocation.
364
    ///
365
    /// ```rust, no_run
366
    /// # {before_snippet}
367
    /// # use esp_hal::rtc_cntl::Rtc;
368
    /// use jiff::{
369
    ///     Timestamp,
370
    ///     tz::{self, TimeZone},
371
    /// };
372
    ///
373
    /// static TZ: TimeZone = tz::get!("America/New_York");
374
    ///
375
    /// let rtc = Rtc::new(peripherals.RTC_TIMER);
376
    /// let now = Timestamp::from_microsecond(rtc.current_time_us() as i64)?;
377
    /// let weekday_in_new_york = now.to_zoned(TZ.clone()).weekday();
378
    /// # {after_snippet}
379
    /// ```
380
    #[cfg(lp_timer_driver_supported)]
381
    pub fn current_time_us(&self) -> u64 {
382
        // Current time is boot time + time since boot
383

            
384
        let rtc_time_us = self.time_since_power_up().as_micros();
385
        let boot_time_us = self.boot_time_us();
386
        let wrapped_boot_time_us = u64::MAX - boot_time_us;
387

            
388
        // We can detect if we wrapped the boot time by checking if rtc time is greater
389
        // than the amount of time we would've wrapped.
390
        if rtc_time_us > wrapped_boot_time_us {
391
            // We also just checked that this won't overflow
392
            rtc_time_us - wrapped_boot_time_us
393
        } else {
394
            boot_time_us + rtc_time_us
395
        }
396
    }
397

            
398
    /// Sets the current time in microseconds.
399
    #[cfg(lp_timer_driver_supported)]
400
    pub fn set_current_time_us(&self, current_time_us: u64) {
401
        // Current time is boot time + time since boot (rtc time)
402
        // So boot time = current time - time since boot (rtc time)
403

            
404
        let rtc_time_us = self.time_since_power_up().as_micros();
405
        if current_time_us < rtc_time_us {
406
            // An overflow would happen if we subtracted rtc_time_us from current_time_us.
407
            // To work around this, we can wrap around u64::MAX by subtracting the
408
            // difference between the current time and the time since boot.
409
            // Subtracting time since boot and adding current new time is equivalent and
410
            // avoids overflow. We just checked that rtc_time_us is less than time_us
411
            // so this won't overflow.
412
            self.set_boot_time_us(u64::MAX - rtc_time_us + current_time_us)
413
        } else {
414
            self.set_boot_time_us(current_time_us - rtc_time_us)
415
        }
416
    }
417

            
418
    pub(crate) const RTC_DISABLE_ROM_LOG: u32 = 1;
419

            
420
    /// Temporarily disable log messages of the ROM bootloader.
421
    ///
422
    /// To permanently disable the ROM bootloader messages, set the corresponding eFuse.
423
    pub fn disable_rom_message_printing(&self) {
424
        // Corresponding documentation:
425
        // ESP32-S3: TRM v1.5 chapter 8.3
426
        // ESP32-H2: TRM v0.5 chapter 8.2.3
427

            
428
        let reg = cfg_select! {
429
            esp32s31 => LP_AON::regs().lp_store(4),
430
            esp32p4 => LP_AON::regs().lp_store4(),
431
            _ => LP_AON::regs().store4(),
432
        };
433
        let disable_mask = cfg_select! {
434
            any(esp32p4, esp32s31) => {
435
                Self::RTC_DISABLE_ROM_LOG | (Self::RTC_DISABLE_ROM_LOG << 16)
436
            }
437
            _ => Self::RTC_DISABLE_ROM_LOG,
438
        };
439
        reg.modify(|r, w| unsafe { w.bits(r.bits() | disable_mask) });
440
    }
441

            
442
    /// Registers an interrupt handler for the RTC.
443
    ///
444
    /// Replaces any previously registered interrupt handlers.
445
    #[instability::unstable]
446
    #[cfg(lp_timer_driver_supported)]
447
    pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
448
        let interrupt = cfg_select! {
449
            esp32p4 => Interrupt::LP_TIMER0,
450
            soc_has_lp_wdt => Interrupt::LP_WDT,
451
            _ => Interrupt::RTC_CORE,
452
        };
453
        for core in crate::system::Cpu::other() {
454
            crate::interrupt::disable(core, interrupt);
455
        }
456
        interrupt::bind_handler(interrupt, handler);
457
    }
458
}
459

            
460
impl crate::private::Sealed for Rtc<'_> {}
461

            
462
#[instability::unstable]
463
#[cfg(lp_timer_driver_supported)]
464
impl crate::interrupt::InterruptConfigurable for Rtc<'_> {
465
    fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
466
        self.set_interrupt_handler(handler);
467
    }
468
}
469

            
470
/// Behavior of the RWDT stage if it times out.
471
#[allow(unused)]
472
#[derive(Debug, Clone, Copy)]
473
pub enum RwdtStageAction {
474
    /// No effect on the system.
475
    Off         = 0,
476
    /// Triggers an interrupt.
477
    Interrupt   = 1,
478
    /// Resets the CPU core.
479
    ResetCpu    = 2,
480
    /// Resets the main system.
481
    /// The power management unit and RTC peripherals will not be reset.
482
    ResetCore   = 3,
483
    /// Resets the main system, power management unit and RTC peripherals.
484
    ResetSystem = 4,
485
}
486

            
487
/// RWDT stages.
488
///
489
/// Timer stages allow for a timer to have a series of different timeout values
490
/// and corresponding expiry action.
491
#[derive(Debug, Clone, Copy)]
492
pub enum RwdtStage {
493
    /// RWDT stage 0.
494
    Stage0,
495
    /// RWDT stage 1.
496
    Stage1,
497
    /// RWDT stage 2.
498
    Stage2,
499
    /// RWDT stage 3.
500
    Stage3,
501
}
502

            
503
/// RTC Watchdog Timer.
504
#[non_exhaustive]
505
pub struct Rwdt;
506

            
507
/// RTC Watchdog Timer driver.
508
impl Rwdt {
509
    /// Enables the watchdog timer instance.
510
    /// Watchdog starts with default settings (`stage 0` resets the system, the
511
    /// others are deactivated).
512
1
    pub fn enable(&mut self) {
513
        self.set_enabled(true);
514
1
    }
515

            
516
    /// Disables the watchdog timer instance.
517
1
    pub fn disable(&mut self) {
518
        self.set_enabled(false);
519
1
    }
520

            
521
    fn set_listen(&mut self, enable: bool) {
522
        let regs = LP_WDT::regs();
523

            
524
        self.set_write_protection(false);
525

            
526
        // Configure STAGE0 to trigger an interrupt upon expiration
527
        let cfg_reg = cfg_select! {
528
            esp32p4 => regs.config0(),
529
            _ => regs.wdtconfig0(),
530
        };
531
        cfg_reg.modify(|_, w| unsafe {
532
            w.wdt_stg0().bits(if enable {
533
                RwdtStageAction::Interrupt as u8
534
            } else {
535
                RwdtStageAction::ResetSystem as u8
536
            })
537
        });
538

            
539
        regs.int_ena().modify(|_, w| {
540
            cfg_select! {
541
                esp32p4 => w.lp_wdt().bit(enable),
542
                _ => w.wdt().bit(enable),
543
            }
544
        });
545

            
546
        self.set_write_protection(true);
547
    }
548

            
549
    /// Listens for interrupts on stage 0.
550
    pub fn listen(&mut self) {
551
        self.set_listen(true);
552
    }
553

            
554
    /// Stops listening for interrupts on stage 0.
555
    pub fn unlisten(&mut self) {
556
        self.set_listen(false);
557
    }
558

            
559
    /// Clears interrupt.
560
    pub fn clear_interrupt(&mut self) {
561
        self.set_write_protection(false);
562

            
563
        LP_WDT::regs().int_clr().write(|w| {
564
            cfg_select! {
565
                esp32p4 => w.lp_wdt().clear_bit_by_one(),
566
                _ => w.wdt().clear_bit_by_one(),
567
            }
568
        });
569

            
570
        self.set_write_protection(true);
571
    }
572

            
573
    /// Returns whether the interrupt is set.
574
    pub fn is_interrupt_set(&self) -> bool {
575
        cfg_select! {
576
            esp32p4 => LP_WDT::regs().int_st().read().lp_wdt().bit_is_set(),
577
            _ => LP_WDT::regs().int_st().read().wdt().bit_is_set(),
578
        }
579
    }
580

            
581
    /// Feeds the watchdog timer.
582
6
    pub fn feed(&mut self) {
583
        self.set_write_protection(false);
584

            
585
        cfg_select! {
586
            esp32p4 => LP_WDT::regs().feed().write(|w| w.feed().set_bit()),
587
            _ => LP_WDT::regs().wdtfeed().write(|w| w.wdt_feed().set_bit()),
588
        };
589

            
590
        self.set_write_protection(true);
591
6
    }
592

            
593
    fn set_write_protection(&mut self, enable: bool) {
594
        let wkey = if enable { 0u32 } else { 0x50D8_3AA1 };
595
        let reg = cfg_select! {
596
            esp32p4 => LP_WDT::regs().wprotect(),
597
            _ => LP_WDT::regs().wdtwprotect(),
598
        };
599
        reg.write(|w| unsafe { w.bits(wkey) });
600
    }
601

            
602
    fn set_enabled(&mut self, enable: bool) {
603
        self.set_write_protection(false);
604

            
605
        let regs = LP_WDT::regs();
606
        let config0 = cfg_select! {
607
            esp32p4 => regs.config0(),
608
            _ => regs.wdtconfig0(),
609
        };
610

            
611
        config0.write(|w| unsafe {
612
            if enable {
613
                w.wdt_flashboot_mod_en().bit(false);
614
                w.wdt_pause_in_slp().set_bit();
615
                w.wdt_cpu_reset_length().bits(7);
616
                w.wdt_sys_reset_length().bits(7);
617
                w.wdt_stg0().bits(RwdtStageAction::ResetSystem as u8);
618
                w.wdt_stg1().bits(RwdtStageAction::Off as u8);
619
                w.wdt_stg2().bits(RwdtStageAction::Off as u8);
620
                w.wdt_stg3().bits(RwdtStageAction::Off as u8);
621
                w.wdt_en().set_bit()
622
            } else {
623
                w.bits(0)
624
            }
625
        });
626

            
627
        self.set_write_protection(true);
628
    }
629

            
630
    /// Configures timeout value for the selected stage.
631
1
    pub fn set_timeout(&mut self, stage: RwdtStage, timeout: Duration) {
632
        let timeout_raw = crate::clock::us_to_rtc_ticks(timeout.as_micros()) as u32;
633

            
634
        #[cfg(not(esp32))]
635
1
        let timeout_raw = timeout_raw >> (1 + crate::efuse::rwdt_multiplier());
636

            
637
        self.set_write_protection(false);
638

            
639
        let regs = LP_WDT::regs();
640
        let config_reg = cfg_select! {
641
            esp32p4 => regs.config(stage as usize),
642
            _ => regs.wdtconfig(stage as usize),
643
        };
644

            
645
        config_reg.modify(|_, w| unsafe { w.hold().bits(timeout_raw) });
646

            
647
        self.set_write_protection(true);
648
1
    }
649

            
650
    /// Sets the action for a specific stage.
651
    pub fn set_stage_action(&mut self, stage: RwdtStage, action: RwdtStageAction) {
652
        self.set_write_protection(false);
653

            
654
        let regs = LP_WDT::regs();
655
        let cfg_reg = cfg_select! {
656
            esp32p4 => regs.config0(),
657
            _ => regs.wdtconfig0(),
658
        };
659
        cfg_reg.modify(|_, w| unsafe {
660
            match stage {
661
                RwdtStage::Stage0 => w.wdt_stg0().bits(action as u8),
662
                RwdtStage::Stage1 => w.wdt_stg1().bits(action as u8),
663
                RwdtStage::Stage2 => w.wdt_stg2().bits(action as u8),
664
                RwdtStage::Stage3 => w.wdt_stg3().bits(action as u8),
665
            }
666
        });
667

            
668
        self.set_write_protection(true);
669
    }
670
}
671

            
672
/// Super Watchdog
673
#[cfg(soc_has_swd_watchdog)]
674
#[non_exhaustive]
675
pub struct Swd;
676

            
677
/// Super Watchdog driver
678
#[cfg(soc_has_swd_watchdog)]
679
impl Swd {
680
    /// Enables the watchdog timer instance.
681
    pub fn enable(&mut self) {
682
        self.set_enabled(true);
683
    }
684

            
685
    /// Disables the watchdog timer instance.
686
    pub fn disable(&mut self) {
687
        self.set_enabled(false);
688
    }
689

            
690
    /// Enables or disables write protection for WDT registers.
691
    fn set_write_protection(&mut self, enable: bool) {
692
        let wkey = if enable {
693
            0u32
694
        } else if cfg!(any(esp32c2, esp32c3, esp32s2, esp32s3)) {
695
            0x8F1D_312A
696
        } else {
697
            0x50D8_3AA1
698
        };
699

            
700
        LP_WDT::regs()
701
            .swd_wprotect()
702
            .write(|w| unsafe { w.swd_wkey().bits(wkey) });
703
    }
704

            
705
    fn set_enabled(&mut self, enable: bool) {
706
        self.set_write_protection(false);
707

            
708
        let reg = cfg_select! {
709
            esp32p4 => LP_WDT::regs().swd_config(),
710
            _ => LP_WDT::regs().swd_conf(),
711
        };
712
        reg.write(|w| w.swd_auto_feed_en().bit(!enable));
713

            
714
        self.set_write_protection(true);
715
    }
716
}
717

            
718
/// Reads the LP timer counter, in its own tick units.
719
///
720
/// `Rtc` owns `RTC_TIMER`, but the sleep alarm needs the same counter without the peripheral. This
721
/// read is therefore a free function. A read first writes a register to make a snapshot, and then
722
/// reads the snapshot from other registers. Two callers that do this at the same time can read the
723
/// snapshot of the other caller, and the lock prevents that. The lock keeps interrupts disabled for
724
/// the time of the snapshot, which is up to approximately 20 µs on esp32. That chip needs the
725
/// update request set for one slow-clock period.
726
#[cfg(lp_timer_driver_supported)]
727
2
pub(crate) fn time_since_boot_raw() -> u64 {
728
    static SNAPSHOT: esp_sync::NonReentrantMutex<()> = esp_sync::NonReentrantMutex::new(());
729

            
730
    SNAPSHOT.with(|()| {
731
        let rtc = RTC_TIMER::regs();
732

            
733
        // Load counter value
734
        cfg_select! {
735
            any(esp32, esp32s2, esp32s3, esp32c2, esp32c3) => {
736
                // Keep update high for at least one RTC slowclk period, assumes 150k RTC_SLOWCLK
737
                // Without this, this function may return a stale value
738
                const UPDATE_COUNT: usize = if cfg!(esp32) { 20 } else { 10 };
739
                for _ in 0..UPDATE_COUNT {
740
                    rtc.time_update().write(|w| w.time_update().set_bit());
741
                    crate::rom::ets_delay_us(1);
742
                }
743
            }
744
            _ => {
745
                rtc.update().write(|w| w.main_timer_update().set_bit());
746
            }
747
        }
748

            
749
        // Read counter value
750
        cfg_select! {
751
            esp32 => {
752
                while rtc.time_update().read().time_valid().bit_is_clear() {
753
                    // Might take 1 RTC slowclk period, don't flood RTC bus
754
                    crate::rom::ets_delay_us(1);
755
                }
756

            
757
                let h = rtc.time1().read().time_hi().bits();
758
                let l = rtc.time0().read().time_lo().bits();
759
            }
760
            any(esp32s2, esp32s3, esp32c2, esp32c3) => {
761
                let h = rtc.time_high0().read().timer_value0_high().bits();
762
                let l = rtc.time_low0().read().timer_value0_low().bits();
763
            }
764
            _ => {
765
                let h = rtc.main_buf0_high().read().main_timer_buf0_high().bits();
766
                let l = rtc.main_buf0_low().read().main_timer_buf0_low().bits();
767
            }
768
        }
769

            
770
2
        ((h as u64) << 32) | (l as u64)
771
    })
772
2
}
773

            
774
/// Returns reset reason.
775
pub fn reset_reason(cpu: Cpu) -> Option<SocResetReason> {
776
    let reason = crate::rom::rtc_get_reset_reason(cpu as u32);
777

            
778
    SocResetReason::from_repr(reason as usize)
779
}
780

            
781
/// Tracks whether the chip has just returned from a light sleep.
782
#[cfg(sleep_driver_supported)]
783
static LIGHT_SLEEP_WAKEUP: portable_atomic::AtomicBool = portable_atomic::AtomicBool::new(false);
784

            
785
/// Returns the cause(s) of the most recent wakeup.
786
///
787
/// A sleep can be ended by more than one source simultaneously, so all matching sources are
788
/// returned. The result is empty if the chip was not woken from sleep (for example on a cold boot,
789
/// or after a reset unrelated to deep sleep).
790
#[cfg(sleep_driver_supported)]
791
pub fn wakeup_cause() -> WakeupReason {
792
    if reset_reason(Cpu::ProCpu) != Some(SocResetReason::CoreDeepSleep)
793
325
        && !LIGHT_SLEEP_WAKEUP.load(portable_atomic::Ordering::Relaxed)
794
    {
795
        return WakeupReason::default();
796
    }
797

            
798
    let reg = cfg_select! {
799
        soc_has_pmu => crate::peripherals::PMU::regs().slp_wakeup_status0(),
800
        esp32 => LPWR::regs().wakeup_state(),
801
        _ => LPWR::regs().slp_wakeup_cause(),
802
    };
803

            
804
    #[allow(clippy::unnecessary_cast)]
805
    let wakeup_cause_bits = reg.read().wakeup_cause().bits() as u32;
806

            
807
    WakeupReason(enumset::EnumSet::from_u32_truncated(wakeup_cause_bits))
808
}
809

            
810
#[cfg(sleep_light_sleep)]
811
cfg_select! {
812
    feature = "rt" => {
813
        #[unsafe(no_mangle)]
814
        static ESP_HAL_WAKE_LOCK_COUNT: portable_atomic::AtomicUsize =
815
            portable_atomic::AtomicUsize::new(0);
816

            
817
        fn wake_lock_count() -> &'static portable_atomic::AtomicUsize {
818
            &ESP_HAL_WAKE_LOCK_COUNT
819
        }
820
    }
821
    _ => {
822
        unsafe extern "Rust" {
823
            static ESP_HAL_WAKE_LOCK_COUNT: portable_atomic::AtomicUsize;
824
        }
825

            
826
        fn wake_lock_count() -> &'static portable_atomic::AtomicUsize {
827
            // use of extern static is unsafe and requires unsafe block
828
            unsafe { &ESP_HAL_WAKE_LOCK_COUNT }
829
        }
830
    }
831
}
832

            
833
/// A guard that prevents the system from entering automatic light sleep.
834
///
835
/// While at least one `WakeLock` is held, [`WakeLock::is_active`] returns `true`
836
/// and the auto-lightsleep idle hook will not put the chip to sleep. The lock is
837
/// released when the guard is dropped.
838
#[cfg_attr(
839
    not(sleep_light_sleep),
840
    doc = r"
841

            
842
Note: This chip does not support automatic light sleep. On this chip, `WakeLock` does nothing."
843
)]
844
#[instability::unstable]
845
#[non_exhaustive]
846
pub struct WakeLock;
847

            
848
impl WakeLock {
849
    /// Acquires a wake lock, preventing automatic light sleep until it is dropped.
850
39
    pub fn new() -> Self {
851
        Self::acquire();
852
        Self
853
39
    }
854

            
855
    /// Acquires a wake lock, preventing automatic light sleep.
856
    pub fn acquire() {
857
        #[cfg(sleep_light_sleep)]
858
        wake_lock_count().fetch_add(1, portable_atomic::Ordering::AcqRel);
859
    }
860

            
861
    /// Releases a wake lock, allowing automatic light sleep when no wake locks are held.
862
    ///
863
    /// Must only be called to release a wake lock acquired via [`Self::acquire`]
864
    pub fn release() {
865
        #[cfg(sleep_light_sleep)]
866
        {
867
            let previous = wake_lock_count().fetch_sub(1, portable_atomic::Ordering::AcqRel);
868
847
            debug_assert_ne!(previous, 0, "wake lock counter underflow");
869
        }
870
    }
871

            
872
    /// Returns whether at least one wake lock is currently held.
873
    #[instability::unstable]
874
    pub fn is_active() -> bool {
875
        cfg_select! {
876
            sleep_light_sleep => wake_lock_count().load(portable_atomic::Ordering::Acquire) != 0,
877
            _ => true,
878
        }
879
    }
880
}
881

            
882
#[instability::unstable]
883
impl Clone for WakeLock {
884
    fn clone(&self) -> Self {
885
        Self::new()
886
    }
887
}
888

            
889
#[instability::unstable]
890
impl Default for WakeLock {
891
    fn default() -> Self {
892
        Self::new()
893
    }
894
}
895

            
896
impl Drop for WakeLock {
897
261
    fn drop(&mut self) {
898
        Self::release();
899
261
    }
900
}