1
//! # RTC Control Sleep Module
2
//!
3
//! ## Overview
4
//! The `sleep` module allows configuring various wakeup sources and setting up
5
//! the sleep behavior based on those sources. The supported wakeup sources
6
//! include:
7
//!    * `GPIO` pins - light sleep only
8
//!    * timers
9
//!    * `SDIO (Secure Digital Input/Output) - light sleep only`
10
//!    * `MAC (Media Access Control)` wake - light sleep only
11
//!    * `UART0` - light sleep only
12
//!    * `UART1` - light sleep only
13
//!    * `touch`
14
//!    * `ULP (Ultra-Low Power)` wake
15
//!    * `BT (Bluetooth) wake` - light sleep only
16

            
17
use esp_sync::raw::{RawLock, SingleCoreInterruptLock};
18

            
19
use crate::{
20
    gpio,
21
    peripherals::LPWR,
22
    rtc_cntl::{Rtc, WakeupSource},
23
};
24

            
25
#[cfg(soc_has_pmu)]
26
pub(crate) mod pmu_common;
27

            
28
#[cfg_attr(esp32, path = "esp32.rs")]
29
#[cfg_attr(esp32s2, path = "esp32s2.rs")]
30
#[cfg_attr(esp32s3, path = "esp32s3.rs")]
31
#[cfg_attr(esp32c3, path = "esp32c3.rs")]
32
#[cfg_attr(esp32c5, path = "esp32c5.rs")]
33
#[cfg_attr(esp32c61, path = "esp32c61.rs")]
34
#[cfg_attr(esp32c6, path = "esp32c6.rs")]
35
#[cfg_attr(esp32c2, path = "esp32c2.rs")]
36
#[cfg_attr(esp32h2, path = "esp32h2.rs")]
37
#[cfg_attr(esp32p4, path = "esp32p4.rs")]
38
#[cfg_attr(esp32s31, path = "esp32s31.rs")]
39
mod sleep_impl;
40
pub use sleep_impl::*;
41

            
42
#[cfg(sleep_has_wakeup_source_timer)]
43
mod timer;
44

            
45
mod wakeup;
46
pub(crate) use wakeup::*;
47
#[instability::unstable]
48
pub use wakeup::{SleepEntryHook, SleepExitHook, WrappedSleepConfig};
49

            
50
/// The result of a light sleep.
51
#[cfg(sleep_light_sleep)]
52
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
54
#[instability::unstable]
55
pub enum LightSleep {
56
    /// The call returned from the sleep.
57
    ///
58
    /// The sleep may also never have started, because no wakeup source was enabled, because the
59
    /// hardware rejected the request, or because a sleep limit was too short for the transition to
60
    /// catch. This result does not tell those apart from a sleep that a wakeup source ended.
61
    Ended,
62

            
63
    /// A wakeup source refused the sleep, so the chip stayed awake.
64
    ///
65
    /// The refusal holds until the state that caused it changes, so a second call refuses too.
66
    /// Wait for an interrupt instead.
67
    Refused,
68
}
69

            
70
#[cfg(supports_cpu_power_down)]
71
use crate::rtc_cntl::cpu_retention;
72

            
73
/// The handler that answers whether this core still agrees to a light sleep, or null.
74
///
75
/// Only the rendezvous of the software retention path reads this, which is why the read lives
76
/// there.
77
#[cfg(all(multi_core, sleep_driver_supported))]
78
pub(crate) static CAN_SLEEP_HANDLER: portable_atomic::AtomicPtr<()> =
79
    portable_atomic::AtomicPtr::new(core::ptr::null_mut());
80

            
81
/// The parts of the sleep path that an RTOS fills in.
82
#[cfg(all(multi_core, sleep_driver_supported))]
83
#[doc(hidden)]
84
#[instability::unstable]
85
pub mod __rtos_implementation {
86
    use portable_atomic::Ordering;
87

            
88
    /// Registers the function that answers whether the core that runs it agrees to a light sleep.
89
    ///
90
    /// A light sleep that powers the CPU domain down needs every running core to save itself, so
91
    /// the core that calls the sleep function asks the other core to join it. The core that calls
92
    /// decides for itself. The other core answers this function, and a `false` answer stops the
93
    /// sleep. Either core can be the one that answers.
94
    ///
95
    /// The answer holds for the whole sleep. The core that gives it has its interrupts disabled
96
    /// already, so nothing can give that core work while the sleep runs.
97
    ///
98
    /// # Safety
99
    ///
100
    /// The handler runs in an interrupt handler, with interrupts disabled and with the caches of
101
    /// the sleep path. It must return, it must take no lock that a core can hold across a sleep,
102
    /// and it must stay callable for the life of the program.
103
    pub unsafe fn set_can_sleep_handler(handler: fn() -> bool) {
104
        super::CAN_SLEEP_HANDLER.store(handler as *mut (), Ordering::Release);
105
    }
106
}
107

            
108
/// Prepares the sleep hardware, and clears the wakeup sources of the previous run.
109
///
110
/// The wakeup-enable mask survives a deep-sleep wake, so here it still holds the request of the run
111
/// that went to sleep. Two steps need the mask, in this order. First, the code releases the pads
112
/// that the previous run armed. Then it clears the mask, because a program starts with no wakeup
113
/// sources, and the drivers of the new run build the mask again.
114
pub(crate) fn init(rtc: &Rtc<'_>) {
115
    // First, because the pads that ended a deep sleep are readable only until the code below
116
    // changes a path.
117
325
    gpio::wakeup::record_wakeup();
118

            
119
    // Release the pads after a deep-sleep wake only, and only if the previous
120
    // run armed an IO wake source.
121
    if super::reset_reason(crate::system::Cpu::ProCpu) == Some(super::SocResetReason::CoreDeepSleep)
122
        && gpio::wakeup::wake_enabled()
123
    {
124
        gpio::wakeup::wake_io_reset();
125
    }
126

            
127
    RtcSleepConfig::base_settings(rtc);
128

            
129
    set_mask(0);
130
}
131

            
132
/// Low-power management.
133
///
134
/// The sleep calls do not take the wakeup sources that end the sleep. Each driver enables the
135
/// source that it owns, and the hardware wakeup-enable mask keeps that request until the driver
136
/// clears it. The mask keeps it through a light sleep, and through a deep-sleep wake. A sleep call
137
/// reads the mask back, and calculates the rest of the configuration from it.
138
#[instability::unstable]
139
pub struct LowPower<'d> {
140
    _inner: LPWR<'d>,
141
}
142

            
143
impl<'d> LowPower<'d> {
144
    /// Creates a new `LowPower` driver.
145
    pub fn new(lpwr: LPWR<'d>) -> Self {
146
        Self { _inner: lpwr }
147
    }
148

            
149
    /// Arms the sleep alarm for `deadline`, and enables the timer wakeup source.
150
    ///
151
    /// The deadline is absolute, so the time between this call and the sleep does not make the
152
    /// sleep shorter. The deadline is a standing request. The wake that it causes does not
153
    /// disarm it, a later call replaces it, and [`Self::clear_wakeup_deadline`] removes it.
154
    ///
155
    /// A deadline in the past ends a light sleep immediately, and makes [`Self::sleep_deep`] panic.
156
    ///
157
    /// A wakeup source can limit one sleep to an earlier wake. The limit does not replace this
158
    /// deadline. The deadline stays armed for the next sleep.
159
    #[cfg(sleep_has_wakeup_source_timer)]
160
    pub fn set_wakeup_deadline(&mut self, deadline: crate::time::Instant) {
161
        timer::set_deadline(deadline);
162
    }
163

            
164
    /// Disarms the sleep alarm, and disables the timer wakeup source.
165
    #[cfg(sleep_has_wakeup_source_timer)]
166
    pub fn clear_wakeup_deadline(&mut self) {
167
        timer::clear_deadline();
168
    }
169

            
170
    /// Enters deep sleep, and does not return.
171
    ///
172
    /// In deep sleep the CPUs, most of the RAM, and all digital peripherals that are clocked from
173
    /// APB_CLK are powered off. The wake resets the chip, so use the
174
    /// [`#[esp_hal::ram(persistent)]`][procmacros::ram] attribute to keep a variable through the
175
    /// sleep.
176
    ///
177
    /// The hardware cannot reject this sleep, because the function cannot return to report the
178
    /// rejection. Use [`Self::sleep_deep_with_rejection`] for that.
179
    ///
180
    /// A wakeup source cannot refuse this sleep. The wake resets the chip, so the call cannot
181
    /// return a refusal. [`Self::sleep_light`] reports a refusal.
182
    ///
183
    /// # Panics
184
    ///
185
    /// Panics if no wakeup source is enabled, because then nothing can end the sleep. Panics also
186
    /// if the armed wakeup deadline is too near for the sleep transition to catch it, or if a
187
    /// wakeup source limits the sleep to less time than the transition can catch. In these cases
188
    /// the chip never wakes again, and it gives no report of the cause.
189
    #[cfg(sleep_deep_sleep)]
190
    #[cfg(feature = "rt")]
191
    pub fn sleep_deep(&mut self, config: RtcSleepConfig) -> ! {
192
        #[cfg(sleep_has_wakeup_source_timer)]
193
        if enabled_sources().contains(WakeupSource::Timer) {
194
            assert!(
195
                !timer::deadline_missed(),
196
                "the wakeup deadline is too near to be caught by the sleep transition"
197
            );
198
        }
199

            
200
        self.sleep(config, SleepKind::Deep, false);
201

            
202
        unreachable!("deep sleep without rejection cannot return")
203
    }
204

            
205
    /// Enters deep sleep, and returns only if the hardware rejects the request.
206
    ///
207
    /// The hardware rejects a sleep if one of its wakeup sources is already asserted. Without the
208
    /// rejection, the chip sleeps through the event that the caller wants to wake on. The return of
209
    /// this function is the complete report, so it gives no other result.
210
    ///
211
    /// A wakeup source cannot refuse this sleep. A wakeup source can limit it. The function also
212
    /// returns when that limit is too short for the sleep transition to catch. The chip stays
213
    /// awake. The return does not say which case it was.
214
    ///
215
    /// A rejected request returns the wake pads to their drivers, but it cannot return every pad.
216
    /// Sleep entry disconnects the pads that no hold keeps, on the chips that need that step to
217
    /// reach the deep-sleep current, and it cannot know their earlier configuration. Configure
218
    /// those pads again if this function returns. ESP-IDF has the same limit in
219
    /// `esp_deep_sleep_try_to_start`.
220
    ///
221
    /// # Panics
222
    ///
223
    /// Panics if no wakeup source is enabled.
224
    #[cfg(sleep_deep_sleep)]
225
    #[cfg(feature = "rt")]
226
    pub fn sleep_deep_with_rejection(&mut self, config: RtcSleepConfig) {
227
        self.sleep(config, SleepKind::Deep, true);
228
    }
229

            
230
    /// Enters light sleep, and returns when a wakeup source ends it.
231
    ///
232
    /// Light sleep keeps the state of the digital domain, so the program continues at the same
233
    /// place.
234
    ///
235
    /// A wakeup source can refuse the sleep, which keeps the chip awake. A direct call and
236
    /// automatic light sleep both honor a refusal. The return reports it, because the caller then
237
    /// has to wait for the event itself.
238
    ///
239
    /// A wakeup source can also limit the length of the sleep. The chip wakes at the sooner of that
240
    /// limit and the armed wakeup deadline. When the timer source is not enabled, the limit enables
241
    /// it for this sleep and disables it again afterwards. The sleep does not start when that wake
242
    /// is already due, or when the transition cannot catch it. The result is [`LightSleep::Ended`],
243
    /// not [`LightSleep::Refused`].
244
    ///
245
    /// The function also returns without a sleep if no wakeup source is enabled, or if the
246
    /// hardware rejects the request because a wakeup source is already asserted. It reports
247
    /// neither case. For the caller, a rejected sleep and a very short sleep have the same
248
    /// result.
249
    #[cfg(sleep_light_sleep)]
250
    #[cfg(feature = "rt")]
251
    pub fn sleep_light(&mut self, config: RtcSleepConfig) -> LightSleep {
252
        if self.sleep(config, SleepKind::Light, true) {
253
            LightSleep::Refused
254
        } else {
255
            LightSleep::Ended
256
        }
257
    }
258

            
259
    /// Calculates the sleep configuration from the wakeup-enable mask, and enters the sleep.
260
    ///
261
    /// Returns whether a wakeup source refused a light sleep. A deep sleep ignores a refusal,
262
    /// so this function does not return `true` for a deep sleep.
263
    ///
264
    /// A sleep limit can end the call before the sleep starts. That result is `false`. It is not a
265
    /// refusal. A deep sleep whose limit the transition cannot catch panics when the caller cannot
266
    /// accept a rejection.
267
    #[cfg(sleep_driver_supported)]
268
    #[crate::ram]
269
    #[cfg(feature = "rt")]
270
    pub(crate) fn sleep(
271
        &mut self,
272
        config: RtcSleepConfig,
273
        kind: SleepKind,
274
        allow_reject: bool,
275
    ) -> bool {
276
        // ESP-IDF runs `esp_light_sleep_start` inside `portENTER_CRITICAL(&light_sleep_lock)`.
277
        // A sleep ends when `wait_for_sleep_result` sees a wakeup or reject bit, so nothing in
278
        // this path needs interrupts.
279
        let irq_token = unsafe { SingleCoreInterruptLock.enter() };
280
        let refused = cfg_select! {
281
            all(cpu_retention = "software", multi_core) => {
282
                // The PRO CPU requests every light sleep, because the wake releases the APP CPU
283
                // from the PRO CPU.
284
                match (kind, crate::system::Cpu::current()) {
285
                    (SleepKind::Light, crate::system::Cpu::ProCpu) => {
286
                        cpu_retention::rendezvous::take_request();
287
                        let refused =
288
                            self.sleep_with_interrupts_disabled(config, kind, allow_reject);
289
                        cpu_retention::rendezvous::release_app_cpu();
290
                        refused
291
                    }
292
                    // The APP CPU does not learn whether a wakeup source refused the sleep.
293
                    (SleepKind::Light, _) => {
294
                        cpu_retention::rendezvous::sleep_from_app_cpu(config);
295
                        false
296
                    }
297
                    _ => self.sleep_with_interrupts_disabled(config, kind, allow_reject),
298
                }
299
            }
300
            _ => self.sleep_with_interrupts_disabled(config, kind, allow_reject),
301
        };
302
        unsafe { SingleCoreInterruptLock.exit(irq_token) };
303
        refused
304
    }
305

            
306
    #[cfg(sleep_driver_supported)]
307
    #[crate::ram]
308
    #[cfg(feature = "rt")]
309
    fn sleep_with_interrupts_disabled(
310
        &mut self,
311
        config: RtcSleepConfig,
312
        kind: SleepKind,
313
        allow_reject: bool,
314
    ) -> bool {
315
        let rtc = Rtc::new(unsafe { crate::peripherals::RTC_TIMER::steal() });
316

            
317
        let mut config = config;
318
        config.set_sleep_kind(kind);
319

            
320
        // Retention serves light sleep only. Deep sleep resets the chip, so it has no CPU state to
321
        // bring back, and an armed descriptor would outlive the sleep in the RTC domain. esp-idf
322
        // arms retention from its light sleep path alone.
323
        //
324
        // The buffer is read once, because the rendezvous and the power-down must agree on it. A
325
        // buffer that another core installs during this sleep serves the next one.
326
        #[cfg(supports_cpu_power_down)]
327
        let retention_buffer = match kind {
328
            SleepKind::Light => crate::rtc_cntl::installed_buffer_ptr(),
329
            SleepKind::Deep => None,
330
        };
331

            
332
        // A chip that retains the CPU in software needs every running core to save itself, so the
333
        // rendezvous comes first, before any step that a refusal would have to undo. A return
334
        // after it must call `rendezvous::finish`.
335
        #[cfg(all(cpu_retention = "software", multi_core))]
336
        if retention_buffer.is_some() && !cpu_retention::rendezvous::engage() {
337
            return false;
338
        }
339

            
340
        // The hooks run before `apply`, so that a request to keep a power domain powered reaches
341
        // the hardware. They also run before the last read of the mask, because a hook can
342
        // enable another source. The GPIO hook does this while it allocates its pins to the
343
        // paths.
344
        let entry = run_entry_hooks(&mut config);
345

            
346
        // Stop before `apply` when a hook refused a light sleep. `apply` writes the sleep
347
        // configuration, and the chip must stay awake with that configuration unchanged.
348
        // Entry hooks can already have moved a pad. The exit hooks put the pad back.
349
        // A deep sleep ignores the refusal. The wake resets the chip, so there is no caller.
350
        if kind == SleepKind::Light && entry.refused {
351
            #[cfg(sleep_has_wakeup_source_timer)]
352
            if let Some(clamp) = entry.clamp {
353
                timer::restore_after_limit(clamp);
354
            }
355
            run_exit_hooks();
356
            #[cfg(all(cpu_retention = "software", multi_core))]
357
            cpu_retention::rendezvous::finish();
358
            return true;
359
        }
360

            
361
        // A limit inside the transition window cannot wake the chip. esp32 cannot reject on the
362
        // timer, so the hardware does not report the miss. Stay awake. The exit hooks undo pad
363
        // changes from the entry hooks. The comparator was not written.
364
        #[cfg(sleep_has_wakeup_source_timer)]
365
        if matches!(&entry.clamp, Some(timer::LimitClamp::TooShort)) {
366
            run_exit_hooks();
367
            #[cfg(all(cpu_retention = "software", multi_core))]
368
            cpu_retention::rendezvous::finish();
369
            if kind == SleepKind::Deep && !allow_reject {
370
                panic!("the wakeup deadline is too near to be caught by the sleep transition");
371
            }
372
            return false;
373
        }
374

            
375
        // A deep sleep keeps what `RtcSleepConfig::deep` asked for, because the wake resets the
376
        // chip and keeps no CPU state to lose.
377
        #[cfg(supports_cpu_power_down)]
378
        if kind == SleepKind::Light {
379
            cpu_retention::configure_cpu_retention(&mut config, retention_buffer);
380
        }
381

            
382
        // The PMU chips write the configuration when the sleep starts.
383
        #[cfg(not(soc_has_pmu))]
384
        config.apply();
385

            
386
        // A sleep with no wakeup source never ends. No counter overflow ends it either.
387
        let wakeup_mask = mask();
388
        if wakeup_mask == 0 {
389
            // No sleep starts, so a clamp must not keep the comparator.
390
            #[cfg(sleep_has_wakeup_source_timer)]
391
            if let Some(clamp) = entry.clamp {
392
                timer::restore_after_limit(clamp);
393
            }
394
            #[cfg(all(cpu_retention = "software", multi_core))]
395
            cpu_retention::rendezvous::finish();
396

            
397
            match kind {
398
                // Not a software refusal. Light sleep does not report a missing wakeup source.
399
                SleepKind::Light => return false,
400
                SleepKind::Deep => {
401
                    panic!("no wakeup source is enabled, so nothing could end the sleep")
402
                }
403
            }
404
        }
405

            
406
        let reject_mask = if allow_reject { reject_mask() } else { 0 };
407

            
408
        sleep_uart_prepare();
409

            
410
        // Latch the systimer value *before* sleeping. The systimer keeps running during
411
        // the sleep enter/exit sequences, so we must not advance from the post-wake
412
        // value (that would count the enter/exit time twice). Instead we set an absolute
413
        // target of `before + slept`, measured by the always-running LP timer.
414
        let before_ticks = crate::time::implem::raw_counter();
415
        let before = rtc.time_since_boot_raw();
416

            
417
        let _uart0_sclk_guard = crate::system::ensure_uart0_sclk_enabled();
418

            
419
        #[cfg(multi_core)]
420
        let parked = match kind {
421
            SleepKind::Light => park_other_cores(),
422
            // A deep sleep resets the chip, so the other core needs no stall, and nothing thaws
423
            // it afterwards.
424
            SleepKind::Deep => 0,
425
        };
426

            
427
        let rejected = {
428
            // A chip can keep a guard for the length of the sleep, to restore what sleep entry
429
            // changed for the sleep only. The guard must therefore outlive the wait below.
430
            // ESP-IDF arms RTC_CNTL retention in `misc_modules_sleep_prepare`, before it arms the
431
            // wakeup sources.
432

            
433
            #[cfg(cpu_retention = "rtc_cntl")]
434
            cpu_retention::prepare_cpu_retention(retention_buffer);
435

            
436
            #[allow(clippy::let_unit_value)]
437
            let _sleep_guard = config.start_sleep(wakeup_mask, reject_mask);
438

            
439
            // Last, because this step takes the pads away from the peripherals that drove them. The
440
            // wakeup sources have their holds now, and no later step needs a pad.
441
            #[cfg(any(
442
                sleep_deep_sleep_needs_gpio_isolation,
443
                gpio_need_soft_isolate_during_pd
444
            ))]
445
            if kind == SleepKind::Deep {
446
                gpio::wakeup::isolate_pads_for_deep_sleep();
447
            }
448

            
449
            let rejected = cfg_select! {
450
                cpu_retention = "software" => {
451
                    // The software chips save the CPU inside the request, so the request belongs to
452
                    // them. ESP-IDF wraps `pmu_sleep_start` the same way (`sleep_modes.c:963-964`).
453
                    cpu_retention::enter_sleep_with_retention(&config, retention_buffer)
454
                }
455
                _ => config.enter_sleep(),
456
            };
457

            
458
            if config.is_deep_sleep() && !rejected {
459
                // The chip is entering deep sleep, and the wake resets it. Because RTC is in a
460
                // slower clock domain than the CPU, the power-down can take several CPU cycles.
461
                loop {
462
                    core::hint::spin_loop();
463
                }
464
            }
465

            
466
            rejected
467
        };
468

            
469
        #[cfg(supports_cpu_power_down)]
470
        if kind == SleepKind::Light && retention_buffer.is_some() {
471
            // First, because the other core comes back through the wake stub that the next call
472
            // disarms.
473
            #[cfg(all(cpu_retention = "software", multi_core))]
474
            cpu_retention::rendezvous::finish();
475

            
476
            cpu_retention::finish_cpu_retention(rejected);
477
        }
478

            
479
        config.finish_sleep();
480

            
481
        #[cfg(multi_core)]
482
        unpark_cores(parked);
483

            
484
        let after = rtc.time_since_boot_raw();
485

            
486
        let slept_us = crate::clock::rtc_ticks_to_us(after.wrapping_sub(before));
487
        let slept_ticks = crate::time::implem::us_to_ticks(slept_us);
488

            
489
        unsafe { crate::time::implem::update_counter(before_ticks + slept_ticks) };
490
        sleep_uart_resume();
491

            
492
        run_exit_hooks();
493

            
494
        // Unlike deep sleep, light sleep does not reset the chip, so `wakeup_cause` cannot rely on
495
        // the reset reason to tell whether a wakeup occurred. A rejected request is not a wakeup,
496
        // and it must not name a wakeup source.
497
        // https://github.com/espressif/esp-idf/blob/a45d713b03fd96d8805d1cc116f02a4415b360c7/components/esp_hw_support/sleep_modes.c#L2158
498
        if !config.is_deep_sleep() && !rejected {
499
            super::LIGHT_SLEEP_WAKEUP.store(true, portable_atomic::Ordering::Relaxed);
500
        }
501

            
502
        // Last, because this call reads the wakeup cause, and after a light sleep the cause is
503
        // available only after the line above.
504
        gpio::wakeup::record_wakeup();
505

            
506
        // After the cause is recorded. A limit must not keep the comparator, or the next sleep
507
        // inherits a deadline that no hook requested.
508
        #[cfg(sleep_has_wakeup_source_timer)]
509
        if let Some(clamp) = entry.clamp {
510
            timer::restore_after_limit(clamp);
511
        }
512

            
513
        false
514
    }
515
}
516

            
517
/// Hardware-stalls the other running cores for the length of a light sleep, or thaws them.
518
///
519
/// A stall is enough where the retention DMA saves the CPU domain, because the domain holds every
520
/// core and the hardware brings the stalled core back with this one. esp-idf stalls the other core
521
/// the same way and adds nothing more (`sleep_modes.c`, where the SMP retention work is gated on
522
/// `SOC_PM_CPU_RETENTION_BY_SW`).
523
///
524
/// FIXME: a stall freezes the other core wherever it stands, which can be inside an interrupt
525
/// handler that holds a cross-core lock, such as the clock tree, the peripheral reference counts,
526
/// or the UART locks that this path takes. This core then spins for that lock for ever, because the
527
/// frozen core cannot release it.
528
///
529
/// A chip that retains the CPU in software needs more than a stall, because each core saves
530
/// itself, and a frozen core saves nothing. The rendezvous of that path keeps the other core
531
/// running, so this function stalls nothing while that core waits in the rendezvous.
532
///
533
/// Returns the cores that it stalled, as a bit for each [`Cpu`], for [`unpark_cores`]. A core that
534
/// the program stalled before the sleep stays stalled after it.
535
#[cfg(all(multi_core, sleep_driver_supported))]
536
#[crate::ram]
537
fn park_other_cores() -> u8 {
538
    // A core that saves itself in the rendezvous must keep running, because a stalled core saves
539
    // nothing.
540
    #[cfg(all(cpu_retention = "software", multi_core, feature = "rt"))]
541
    if cpu_retention::rendezvous::helper_saved() {
542
        return 0;
543
    }
544

            
545
    let mut parked = 0;
546
    for cpu in crate::system::Cpu::other() {
547
        if crate::soc::cpu_control::is_running(cpu) {
548
            // SAFETY: [`unpark_cores`] runs before this function returns to its caller.
549
            unsafe { crate::soc::cpu_control::internal_park_core(cpu, true) };
550
            parked |= 1 << cpu as u8;
551
        }
552
    }
553
    parked
554
}
555

            
556
/// Thaws the cores that [`park_other_cores`] stalled.
557
#[cfg(all(multi_core, sleep_driver_supported))]
558
#[crate::ram]
559
fn unpark_cores(parked: u8) {
560
    for cpu in crate::system::Cpu::other() {
561
        if parked & (1 << cpu as u8) != 0 {
562
            // SAFETY: this core stalled that core for the sleep, and the sleep is over.
563
            unsafe { crate::soc::cpu_control::internal_park_core(cpu, false) };
564
        }
565
    }
566
}
567

            
568
/// Waits for the hardware to report the result of the sleep request, and returns whether the
569
/// hardware rejected the request.
570
///
571
/// A deep sleep powers the CPU down inside this loop, and a light sleep stops the CPU here until a
572
/// wakeup source ends the sleep. A rejected request does neither, so the reject interrupt is the
573
/// only report of that case. ESP-IDF waits in the same place, in `rtc_sleep_start` and in
574
/// `pmu_sleep_start`.
575
#[cfg(sleep_driver_supported)]
576
#[inline(always)]
577
pub(crate) fn wait_for_sleep_result() -> bool {
578
    loop {
579
        cfg_select! {
580
            soc_has_pmu => {
581
                let int_raw = crate::peripherals::PMU::regs().int_raw().read();
582
                let rejected = int_raw.soc_sleep_reject().bit_is_set();
583
                let wakeup = int_raw.soc_wakeup().bit_is_set();
584
            }
585
            _ => {
586
                let int_raw = LPWR::regs().int_raw().read();
587
                let rejected = int_raw.slp_reject().bit_is_set();
588
                let wakeup = int_raw.slp_wakeup().bit_is_set();
589
            }
590
        }
591

            
592
        if wakeup || rejected {
593
            return rejected;
594
        }
595
    }
596
}
597

            
598
#[cfg(sleep_driver_supported)]
599
fn sleep_uart_prepare() {
600
    use crate::uart::Instance;
601
    for_each_uart! {
602
        ($id:literal, $inst:ident, $peri:ident, $rxd:ident, $txd:ident, $cts:ident, $rts:ident, wakeup_source = $_:literal) => {
603
            unsafe {
604
                crate::peripherals::$inst::steal().info().suspend_for_sleep();
605
            }
606
        };
607
    }
608
}
609

            
610
#[cfg(sleep_driver_supported)]
611
fn sleep_uart_resume() {
612
    use crate::uart::Instance;
613
    for_each_uart! {
614
        ($id:literal, $inst:ident, $peri:ident, $rxd:ident, $txd:ident, $cts:ident, $rts:ident, wakeup_source = $_:literal) => {
615
            unsafe {
616
                crate::peripherals::$inst::steal().info().resume_from_sleep();
617
            }
618
        };
619
    }
620
}