esp_hal/rtc_cntl/sleep/mod.rs
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
17use esp_sync::raw::{RawLock, SingleCoreInterruptLock};
18
19#[cfg(sleep_deep_sleep)]
20use crate::rtc_cntl::WakeupSource;
21use crate::{gpio, peripherals::LPWR, rtc_cntl::Rtc};
22
23#[cfg(soc_has_pmu)]
24pub(crate) mod pmu_common;
25
26#[cfg_attr(esp32, path = "esp32.rs")]
27#[cfg_attr(esp32s2, path = "esp32s2.rs")]
28#[cfg_attr(esp32s3, path = "esp32s3.rs")]
29#[cfg_attr(esp32c3, path = "esp32c3.rs")]
30#[cfg_attr(esp32c5, path = "esp32c5.rs")]
31#[cfg_attr(esp32c61, path = "esp32c61.rs")]
32#[cfg_attr(esp32c6, path = "esp32c6.rs")]
33#[cfg_attr(esp32c2, path = "esp32c2.rs")]
34#[cfg_attr(esp32h2, path = "esp32h2.rs")]
35#[cfg_attr(esp32h4, path = "esp32h4.rs")]
36#[cfg_attr(esp32p4, path = "esp32p4.rs")]
37#[cfg_attr(esp32s31, path = "esp32s31.rs")]
38mod sleep_impl;
39pub use sleep_impl::*;
40
41#[cfg(sleep_has_wakeup_source_timer)]
42mod timer;
43
44mod wakeup;
45pub(crate) use wakeup::*;
46#[instability::unstable]
47pub use wakeup::{SleepEntryHook, SleepExitHook, WrappedSleepConfig};
48
49/// The result of a light sleep.
50#[cfg(sleep_light_sleep)]
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52#[cfg_attr(feature = "defmt", derive(defmt::Format))]
53#[instability::unstable]
54pub enum LightSleep {
55 /// The call returned from the sleep.
56 ///
57 /// The sleep may also never have started, because no wakeup source was enabled, because the
58 /// hardware rejected the request, or because a sleep limit was too short for the transition to
59 /// catch. This result does not tell those apart from a sleep that a wakeup source ended.
60 Ended,
61
62 /// A wakeup source refused the sleep, so the chip stayed awake.
63 ///
64 /// The refusal holds until the state that caused it changes, so a second call refuses too.
65 /// Wait for an interrupt instead.
66 Refused,
67}
68
69#[cfg(supports_cpu_power_down)]
70use crate::rtc_cntl::cpu_retention;
71
72/// The handler that answers whether this core still agrees to a light sleep, or null.
73///
74/// Only the rendezvous of the software retention path reads this, which is why the read lives
75/// there.
76#[cfg(all(multi_core, sleep_driver_supported))]
77pub(crate) static CAN_SLEEP_HANDLER: portable_atomic::AtomicPtr<()> =
78 portable_atomic::AtomicPtr::new(core::ptr::null_mut());
79
80/// The parts of the sleep path that an RTOS fills in.
81#[cfg(all(multi_core, sleep_driver_supported))]
82#[doc(hidden)]
83#[instability::unstable]
84pub mod __rtos_implementation {
85 use portable_atomic::Ordering;
86
87 /// Registers the function that answers whether the core that runs it agrees to a light sleep.
88 ///
89 /// A light sleep that powers the CPU domain down needs every running core to save itself, so
90 /// the core that calls the sleep function asks the other core to join it. The core that calls
91 /// decides for itself. The other core answers this function, and a `false` answer stops the
92 /// sleep. Either core can be the one that answers.
93 ///
94 /// The answer holds for the whole sleep. The core that gives it has its interrupts disabled
95 /// already, so nothing can give that core work while the sleep runs.
96 ///
97 /// # Safety
98 ///
99 /// The handler runs in an interrupt handler, with interrupts disabled and with the caches of
100 /// the sleep path. It must return, it must take no lock that a core can hold across a sleep,
101 /// and it must stay callable for the life of the program.
102 pub unsafe fn set_can_sleep_handler(handler: fn() -> bool) {
103 super::CAN_SLEEP_HANDLER.store(handler as *mut (), Ordering::Release);
104 }
105}
106
107/// Prepares the sleep hardware, and clears the wakeup sources of the previous run.
108///
109/// The wakeup-enable mask survives a deep-sleep wake, so here it still holds the request of the run
110/// that went to sleep. Two steps need the mask, in this order. First, the code releases the pads
111/// that the previous run armed. Then it clears the mask, because a program starts with no wakeup
112/// sources, and the drivers of the new run build the mask again.
113pub(crate) fn init(rtc: &Rtc<'_>) {
114 // First, because the pads that ended a deep sleep are readable only until the code below
115 // changes a path.
116 gpio::wakeup::record_wakeup();
117
118 // Release the pads after a deep-sleep wake only, and only if the previous
119 // run armed an IO wake source.
120 if super::reset_reason(crate::system::Cpu::ProCpu) == Some(super::SocResetReason::CoreDeepSleep)
121 && gpio::wakeup::wake_enabled()
122 {
123 gpio::wakeup::wake_io_reset();
124 }
125
126 RtcSleepConfig::base_settings(rtc);
127
128 set_mask(0);
129}
130
131/// Low-power management.
132///
133/// The sleep calls do not take the wakeup sources that end the sleep. Each driver enables the
134/// source that it owns, and the hardware wakeup-enable mask keeps that request until the driver
135/// clears it. The mask keeps it through a light sleep, and through a deep-sleep wake. A sleep call
136/// reads the mask back, and calculates the rest of the configuration from it.
137#[instability::unstable]
138pub struct LowPower<'d> {
139 _inner: LPWR<'d>,
140}
141
142impl<'d> LowPower<'d> {
143 /// Creates a new `LowPower` driver.
144 pub fn new(lpwr: LPWR<'d>) -> Self {
145 Self { _inner: lpwr }
146 }
147
148 /// Arms the sleep alarm for `deadline`, and enables the timer wakeup source.
149 ///
150 /// The deadline is absolute, so the time between this call and the sleep does not make the
151 /// sleep shorter. The deadline is a standing request. The wake that it causes does not
152 /// disarm it, a later call replaces it, and [`Self::clear_wakeup_deadline`] removes it.
153 ///
154 /// A deadline in the past ends a light sleep immediately, and makes [`Self::sleep_deep`] panic.
155 ///
156 /// A wakeup source can limit one sleep to an earlier wake. The limit does not replace this
157 /// deadline. The deadline stays armed for the next sleep.
158 #[cfg(sleep_has_wakeup_source_timer)]
159 pub fn set_wakeup_deadline(&mut self, deadline: crate::time::Instant) {
160 timer::set_deadline(deadline);
161 }
162
163 /// Disarms the sleep alarm, and disables the timer wakeup source.
164 #[cfg(sleep_has_wakeup_source_timer)]
165 pub fn clear_wakeup_deadline(&mut self) {
166 timer::clear_deadline();
167 }
168
169 /// Enters deep sleep, and does not return.
170 ///
171 /// In deep sleep the CPUs, most of the RAM, and all digital peripherals that are clocked from
172 /// APB_CLK are powered off. The wake resets the chip, so use the
173 /// [`#[esp_hal::ram(persistent)]`][procmacros::ram] attribute to keep a variable through the
174 /// sleep.
175 ///
176 /// The hardware cannot reject this sleep, because the function cannot return to report the
177 /// rejection. Use [`Self::sleep_deep_with_rejection`] for that.
178 ///
179 /// A wakeup source cannot refuse this sleep. The wake resets the chip, so the call cannot
180 /// return a refusal. [`Self::sleep_light`] reports a refusal.
181 ///
182 /// # Panics
183 ///
184 /// Panics if no wakeup source is enabled, because then nothing can end the sleep. Panics also
185 /// if the armed wakeup deadline is too near for the sleep transition to catch it, or if a
186 /// wakeup source limits the sleep to less time than the transition can catch. In these cases
187 /// the chip never wakes again, and it gives no report of the cause.
188 #[cfg(sleep_deep_sleep)]
189 #[cfg(feature = "rt")]
190 pub fn sleep_deep(&mut self, config: RtcSleepConfig) -> ! {
191 #[cfg(sleep_has_wakeup_source_timer)]
192 if enabled_sources().contains(WakeupSource::Timer) {
193 assert!(
194 !timer::deadline_missed(),
195 "the wakeup deadline is too near to be caught by the sleep transition"
196 );
197 }
198
199 self.sleep(config, SleepKind::Deep, false);
200
201 unreachable!("deep sleep without rejection cannot return")
202 }
203
204 /// Enters deep sleep, and returns only if the hardware rejects the request.
205 ///
206 /// The hardware rejects a sleep if one of its wakeup sources is already asserted. Without the
207 /// rejection, the chip sleeps through the event that the caller wants to wake on. The return of
208 /// this function is the complete report, so it gives no other result.
209 ///
210 /// A wakeup source cannot refuse this sleep. A wakeup source can limit it. The function also
211 /// returns when that limit is too short for the sleep transition to catch. The chip stays
212 /// awake. The return does not say which case it was.
213 ///
214 /// A rejected request returns the wake pads to their drivers, but it cannot return every pad.
215 /// Sleep entry disconnects the pads that no hold keeps, on the chips that need that step to
216 /// reach the deep-sleep current, and it cannot know their earlier configuration. Configure
217 /// those pads again if this function returns. ESP-IDF has the same limit in
218 /// `esp_deep_sleep_try_to_start`.
219 ///
220 /// # Panics
221 ///
222 /// Panics if no wakeup source is enabled.
223 #[cfg(sleep_deep_sleep)]
224 #[cfg(feature = "rt")]
225 pub fn sleep_deep_with_rejection(&mut self, config: RtcSleepConfig) {
226 self.sleep(config, SleepKind::Deep, true);
227 }
228
229 /// Enters light sleep, and returns when a wakeup source ends it.
230 ///
231 /// Light sleep keeps the state of the digital domain, so the program continues at the same
232 /// place.
233 ///
234 /// A wakeup source can refuse the sleep, which keeps the chip awake. A direct call and
235 /// automatic light sleep both honor a refusal. The return reports it, because the caller then
236 /// has to wait for the event itself.
237 ///
238 /// A wakeup source can also limit the length of the sleep. The chip wakes at the sooner of that
239 /// limit and the armed wakeup deadline. When the timer source is not enabled, the limit enables
240 /// it for this sleep and disables it again afterwards. The sleep does not start when that wake
241 /// is already due, or when the transition cannot catch it. The result is [`LightSleep::Ended`],
242 /// not [`LightSleep::Refused`].
243 ///
244 /// The function also returns without a sleep if no wakeup source is enabled, or if the
245 /// hardware rejects the request because a wakeup source is already asserted. It reports
246 /// neither case. For the caller, a rejected sleep and a very short sleep have the same
247 /// result.
248 #[cfg(sleep_light_sleep)]
249 #[cfg(feature = "rt")]
250 pub fn sleep_light(&mut self, config: RtcSleepConfig) -> LightSleep {
251 if self.sleep(config, SleepKind::Light, true) {
252 LightSleep::Refused
253 } else {
254 LightSleep::Ended
255 }
256 }
257
258 /// Calculates the sleep configuration from the wakeup-enable mask, and enters the sleep.
259 ///
260 /// Returns whether a wakeup source refused a light sleep. A deep sleep ignores a refusal,
261 /// so this function does not return `true` for a deep sleep.
262 ///
263 /// A sleep limit can end the call before the sleep starts. That result is `false`. It is not a
264 /// refusal. A deep sleep whose limit the transition cannot catch panics when the caller cannot
265 /// accept a rejection.
266 #[cfg(sleep_driver_supported)]
267 #[crate::ram]
268 #[cfg(feature = "rt")]
269 pub(crate) fn sleep(
270 &mut self,
271 config: RtcSleepConfig,
272 kind: SleepKind,
273 allow_reject: bool,
274 ) -> bool {
275 // ESP-IDF runs `esp_light_sleep_start` inside `portENTER_CRITICAL(&light_sleep_lock)`.
276 // A sleep ends when `wait_for_sleep_result` sees a wakeup or reject bit, so nothing in
277 // this path needs interrupts.
278 let irq_token = unsafe { SingleCoreInterruptLock.enter() };
279 let refused = cfg_select! {
280 all(cpu_retention = "software", multi_core) => {
281 // The PRO CPU requests every light sleep, because the wake releases the APP CPU
282 // from the PRO CPU.
283 match (kind, crate::system::Cpu::current()) {
284 (SleepKind::Light, crate::system::Cpu::ProCpu) => {
285 cpu_retention::rendezvous::take_request();
286 let refused =
287 self.sleep_with_interrupts_disabled(config, kind, allow_reject);
288 cpu_retention::rendezvous::release_app_cpu();
289 refused
290 }
291 // The APP CPU does not learn whether a wakeup source refused the sleep.
292 (SleepKind::Light, _) => {
293 cpu_retention::rendezvous::sleep_from_app_cpu(config);
294 false
295 }
296 _ => self.sleep_with_interrupts_disabled(config, kind, allow_reject),
297 }
298 }
299 _ => self.sleep_with_interrupts_disabled(config, kind, allow_reject),
300 };
301 unsafe { SingleCoreInterruptLock.exit(irq_token) };
302 refused
303 }
304
305 #[cfg(sleep_driver_supported)]
306 #[crate::ram]
307 #[cfg(feature = "rt")]
308 fn sleep_with_interrupts_disabled(
309 &mut self,
310 config: RtcSleepConfig,
311 kind: SleepKind,
312 allow_reject: bool,
313 ) -> bool {
314 let rtc = Rtc::new(unsafe { crate::peripherals::RTC_TIMER::steal() });
315
316 let mut config = config;
317 config.set_sleep_kind(kind);
318
319 // Retention serves light sleep only. Deep sleep resets the chip, so it has no CPU state to
320 // bring back, and an armed descriptor would outlive the sleep in the RTC domain. esp-idf
321 // arms retention from its light sleep path alone.
322 //
323 // The buffer is read once, because the rendezvous and the power-down must agree on it. A
324 // buffer that another core installs during this sleep serves the next one.
325 #[cfg(supports_cpu_power_down)]
326 let retention_buffer = match kind {
327 SleepKind::Light => crate::rtc_cntl::installed_buffer_ptr(),
328 SleepKind::Deep => None,
329 };
330
331 // A chip that retains the CPU in software needs every running core to save itself, so the
332 // rendezvous comes first, before any step that a refusal would have to undo. A return
333 // after it must call `rendezvous::finish`.
334 #[cfg(all(cpu_retention = "software", multi_core))]
335 if retention_buffer.is_some() && !cpu_retention::rendezvous::engage() {
336 return false;
337 }
338
339 // The hooks run before `apply`, so that a request to keep a power domain powered reaches
340 // the hardware. They also run before the last read of the mask, because a hook can
341 // enable another source. The GPIO hook does this while it allocates its pins to the
342 // paths.
343 let entry = run_entry_hooks(&mut config);
344
345 // Stop before `apply` when a hook refused a light sleep. `apply` writes the sleep
346 // configuration, and the chip must stay awake with that configuration unchanged.
347 // Entry hooks can already have moved a pad. The exit hooks put the pad back.
348 // A deep sleep ignores the refusal. The wake resets the chip, so there is no caller.
349 if kind == SleepKind::Light && entry.refused {
350 #[cfg(sleep_has_wakeup_source_timer)]
351 if let Some(clamp) = entry.clamp {
352 timer::restore_after_limit(clamp);
353 }
354 run_exit_hooks();
355 #[cfg(all(cpu_retention = "software", multi_core))]
356 cpu_retention::rendezvous::finish();
357 return true;
358 }
359
360 // A limit inside the transition window cannot wake the chip. esp32 cannot reject on the
361 // timer, so the hardware does not report the miss. Stay awake. The exit hooks undo pad
362 // changes from the entry hooks. The comparator was not written.
363 #[cfg(sleep_has_wakeup_source_timer)]
364 if matches!(&entry.clamp, Some(timer::LimitClamp::TooShort)) {
365 run_exit_hooks();
366 #[cfg(all(cpu_retention = "software", multi_core))]
367 cpu_retention::rendezvous::finish();
368 if kind == SleepKind::Deep && !allow_reject {
369 panic!("the wakeup deadline is too near to be caught by the sleep transition");
370 }
371 return false;
372 }
373
374 // A deep sleep keeps what `RtcSleepConfig::deep` asked for, because the wake resets the
375 // chip and keeps no CPU state to lose.
376 #[cfg(supports_cpu_power_down)]
377 if kind == SleepKind::Light {
378 cpu_retention::configure_cpu_retention(&mut config, retention_buffer);
379 }
380
381 // The PMU chips write the configuration when the sleep starts.
382 #[cfg(not(soc_has_pmu))]
383 config.apply();
384
385 // A sleep with no wakeup source never ends. No counter overflow ends it either.
386 let wakeup_mask = mask();
387 if wakeup_mask == 0 {
388 // No sleep starts, so a clamp must not keep the comparator.
389 #[cfg(sleep_has_wakeup_source_timer)]
390 if let Some(clamp) = entry.clamp {
391 timer::restore_after_limit(clamp);
392 }
393 #[cfg(all(cpu_retention = "software", multi_core))]
394 cpu_retention::rendezvous::finish();
395
396 match kind {
397 // Not a software refusal. Light sleep does not report a missing wakeup source.
398 SleepKind::Light => return false,
399 SleepKind::Deep => {
400 panic!("no wakeup source is enabled, so nothing could end the sleep")
401 }
402 }
403 }
404
405 let reject_mask = if allow_reject { reject_mask() } else { 0 };
406
407 sleep_uart_prepare();
408
409 // Latch the systimer value *before* sleeping. The systimer keeps running during
410 // the sleep enter/exit sequences, so we must not advance from the post-wake
411 // value (that would count the enter/exit time twice). Instead we set an absolute
412 // target of `before + slept`, measured by the always-running LP timer.
413 let before_ticks = crate::time::implem::raw_counter();
414 let before = rtc.time_since_boot_raw();
415
416 let _uart0_sclk_guard = crate::system::ensure_uart0_sclk_enabled();
417
418 #[cfg(multi_core)]
419 let parked = match kind {
420 SleepKind::Light => park_other_cores(),
421 // A deep sleep resets the chip, so the other core needs no stall, and nothing thaws
422 // it afterwards.
423 SleepKind::Deep => 0,
424 };
425
426 let rejected = {
427 // A chip can keep a guard for the length of the sleep, to restore what sleep entry
428 // changed for the sleep only. The guard must therefore outlive the wait below.
429 // ESP-IDF arms RTC_CNTL retention in `misc_modules_sleep_prepare`, before it arms the
430 // wakeup sources.
431
432 #[cfg(cpu_retention = "rtc_cntl")]
433 cpu_retention::prepare_cpu_retention(retention_buffer);
434
435 #[allow(clippy::let_unit_value)]
436 let _sleep_guard = config.start_sleep(wakeup_mask, reject_mask);
437
438 // Last, because this step takes the pads away from the peripherals that drove them. The
439 // wakeup sources have their holds now, and no later step needs a pad.
440 #[cfg(any(
441 sleep_deep_sleep_needs_gpio_isolation,
442 gpio_need_soft_isolate_during_pd
443 ))]
444 if kind == SleepKind::Deep {
445 gpio::wakeup::isolate_pads_for_deep_sleep();
446 }
447
448 let rejected = cfg_select! {
449 cpu_retention = "software" => {
450 // The software chips save the CPU inside the request, so the request belongs to
451 // them. ESP-IDF wraps `pmu_sleep_start` the same way (`sleep_modes.c:963-964`).
452 cpu_retention::enter_sleep_with_retention(&config, retention_buffer)
453 }
454 _ => config.enter_sleep(),
455 };
456
457 if config.is_deep_sleep() && !rejected {
458 // The chip is entering deep sleep, and the wake resets it. Because RTC is in a
459 // slower clock domain than the CPU, the power-down can take several CPU cycles.
460 loop {
461 core::hint::spin_loop();
462 }
463 }
464
465 rejected
466 };
467
468 #[cfg(supports_cpu_power_down)]
469 if kind == SleepKind::Light && retention_buffer.is_some() {
470 // First, because the other core comes back through the wake stub that the next call
471 // disarms.
472 #[cfg(all(cpu_retention = "software", multi_core))]
473 cpu_retention::rendezvous::finish();
474
475 cpu_retention::finish_cpu_retention(rejected);
476 }
477
478 config.finish_sleep();
479
480 #[cfg(multi_core)]
481 unpark_cores(parked);
482
483 let after = rtc.time_since_boot_raw();
484
485 let slept_us = crate::clock::rtc_ticks_to_us(after.wrapping_sub(before));
486 let slept_ticks = crate::time::implem::us_to_ticks(slept_us);
487
488 unsafe { crate::time::implem::update_counter(before_ticks + slept_ticks) };
489 sleep_uart_resume();
490
491 run_exit_hooks();
492
493 // Unlike deep sleep, light sleep does not reset the chip, so `wakeup_cause` cannot rely on
494 // the reset reason to tell whether a wakeup occurred. A rejected request is not a wakeup,
495 // and it must not name a wakeup source.
496 // https://github.com/espressif/esp-idf/blob/a45d713b03fd96d8805d1cc116f02a4415b360c7/components/esp_hw_support/sleep_modes.c#L2158
497 if !config.is_deep_sleep() && !rejected {
498 super::LIGHT_SLEEP_WAKEUP.store(true, portable_atomic::Ordering::Relaxed);
499 }
500
501 // Last, because this call reads the wakeup cause, and after a light sleep the cause is
502 // available only after the line above.
503 gpio::wakeup::record_wakeup();
504
505 // After the cause is recorded. A limit must not keep the comparator, or the next sleep
506 // inherits a deadline that no hook requested.
507 #[cfg(sleep_has_wakeup_source_timer)]
508 if let Some(clamp) = entry.clamp {
509 timer::restore_after_limit(clamp);
510 }
511
512 false
513 }
514}
515
516/// Hardware-stalls the other running cores for the length of a light sleep, or thaws them.
517///
518/// A stall is enough where the retention DMA saves the CPU domain, because the domain holds every
519/// core and the hardware brings the stalled core back with this one. esp-idf stalls the other core
520/// the same way and adds nothing more (`sleep_modes.c`, where the SMP retention work is gated on
521/// `SOC_PM_CPU_RETENTION_BY_SW`).
522///
523/// FIXME: a stall freezes the other core wherever it stands, which can be inside an interrupt
524/// handler that holds a cross-core lock, such as the clock tree, the peripheral reference counts,
525/// or the UART locks that this path takes. This core then spins for that lock for ever, because the
526/// frozen core cannot release it.
527///
528/// A chip that retains the CPU in software needs more than a stall, because each core saves
529/// itself, and a frozen core saves nothing. The rendezvous of that path keeps the other core
530/// running, so this function stalls nothing while that core waits in the rendezvous.
531///
532/// Returns the cores that it stalled, as a bit for each [`Cpu`], for [`unpark_cores`]. A core that
533/// the program stalled before the sleep stays stalled after it.
534#[cfg(all(multi_core, sleep_driver_supported))]
535#[crate::ram]
536fn park_other_cores() -> u8 {
537 // A core that saves itself in the rendezvous must keep running, because a stalled core saves
538 // nothing.
539 #[cfg(all(cpu_retention = "software", multi_core, feature = "rt"))]
540 if cpu_retention::rendezvous::helper_saved() {
541 return 0;
542 }
543
544 let mut parked = 0;
545 for cpu in crate::system::Cpu::other() {
546 if crate::soc::cpu_control::is_running(cpu) {
547 // SAFETY: [`unpark_cores`] runs before this function returns to its caller.
548 unsafe { crate::soc::cpu_control::internal_park_core(cpu, true) };
549 parked |= 1 << cpu as u8;
550 }
551 }
552 parked
553}
554
555/// Thaws the cores that [`park_other_cores`] stalled.
556#[cfg(all(multi_core, sleep_driver_supported))]
557#[crate::ram]
558fn unpark_cores(parked: u8) {
559 for cpu in crate::system::Cpu::other() {
560 if parked & (1 << cpu as u8) != 0 {
561 // SAFETY: this core stalled that core for the sleep, and the sleep is over.
562 unsafe { crate::soc::cpu_control::internal_park_core(cpu, false) };
563 }
564 }
565}
566
567/// Waits for the hardware to report the result of the sleep request, and returns whether the
568/// hardware rejected the request.
569///
570/// A deep sleep powers the CPU down inside this loop, and a light sleep stops the CPU here until a
571/// wakeup source ends the sleep. A rejected request does neither, so the reject interrupt is the
572/// only report of that case. ESP-IDF waits in the same place, in `rtc_sleep_start` and in
573/// `pmu_sleep_start`.
574#[cfg(sleep_driver_supported)]
575#[inline(always)]
576pub(crate) fn wait_for_sleep_result() -> bool {
577 loop {
578 cfg_select! {
579 soc_has_pmu => {
580 let int_raw = crate::peripherals::PMU::regs().int_raw().read();
581 let rejected = int_raw.soc_sleep_reject().bit_is_set();
582 let wakeup = int_raw.soc_wakeup().bit_is_set();
583 }
584 _ => {
585 let int_raw = LPWR::regs().int_raw().read();
586 let rejected = int_raw.slp_reject().bit_is_set();
587 let wakeup = int_raw.slp_wakeup().bit_is_set();
588 }
589 }
590
591 if wakeup || rejected {
592 return rejected;
593 }
594 }
595}
596
597#[cfg(sleep_driver_supported)]
598fn sleep_uart_prepare() {
599 use crate::uart::Instance;
600 for_each_uart! {
601 ($id:literal, $inst:ident, $peri:ident, $rxd:ident, $txd:ident, $cts:ident, $rts:ident, wakeup_source = $_:literal) => {
602 unsafe {
603 crate::peripherals::$inst::steal().info().suspend_for_sleep();
604 }
605 };
606 }
607}
608
609#[cfg(sleep_driver_supported)]
610fn sleep_uart_resume() {
611 use crate::uart::Instance;
612 for_each_uart! {
613 ($id:literal, $inst:ident, $peri:ident, $rxd:ident, $txd:ident, $cts:ident, $rts:ident, wakeup_source = $_:literal) => {
614 unsafe {
615 crate::peripherals::$inst::steal().info().resume_from_sleep();
616 }
617 };
618 }
619}