use crate::{
gpio,
peripherals::LPWR,
rtc_cntl::{Rtc, WakeupSource},
};
#[cfg(soc_has_pmu)]
mod pmu_common;
#[cfg_attr(esp32, path = "esp32.rs")]
#[cfg_attr(esp32s2, path = "esp32s2.rs")]
#[cfg_attr(esp32s3, path = "esp32s3.rs")]
#[cfg_attr(esp32c3, path = "esp32c3.rs")]
#[cfg_attr(esp32c5, path = "esp32c5.rs")]
#[cfg_attr(esp32c61, path = "esp32c61.rs")]
#[cfg_attr(esp32c6, path = "esp32c6.rs")]
#[cfg_attr(esp32c2, path = "esp32c2.rs")]
#[cfg_attr(esp32h2, path = "esp32h2.rs")]
#[cfg_attr(esp32p4, path = "esp32p4.rs")]
mod sleep_impl;
pub use sleep_impl::*;
#[cfg(sleep_has_wakeup_source_timer)]
mod timer;
mod wakeup;
pub(crate) use wakeup::*;
pub(crate) fn init(rtc: &Rtc<'_>) {
gpio::wakeup::record_wakeup();
if super::reset_reason(crate::system::Cpu::ProCpu) == Some(super::SocResetReason::CoreDeepSleep)
&& gpio::wakeup::wake_enabled()
{
gpio::wakeup::wake_io_reset();
}
RtcSleepConfig::base_settings(rtc);
set_mask(0);
}
#[instability::unstable]
pub struct LowPower<'d> {
_inner: LPWR<'d>,
}
impl<'d> LowPower<'d> {
pub fn new(lpwr: LPWR<'d>) -> Self {
Self { _inner: lpwr }
}
#[cfg(sleep_has_wakeup_source_timer)]
pub fn set_wakeup_deadline(&mut self, deadline: crate::time::Instant) {
timer::set_deadline(deadline);
}
#[cfg(sleep_has_wakeup_source_timer)]
pub fn clear_wakeup_deadline(&mut self) {
timer::clear_deadline();
}
#[cfg(sleep_deep_sleep)]
pub fn sleep_deep(&mut self, config: RtcSleepConfig) -> ! {
#[cfg(sleep_has_wakeup_source_timer)]
if enabled_sources().contains(WakeupSource::Timer) {
assert!(
!timer::deadline_missed(),
"the wakeup deadline is too near to be caught by the sleep transition"
);
}
self.sleep(config, SleepKind::Deep, false);
unreachable!("deep sleep without rejection cannot return")
}
#[cfg(sleep_deep_sleep)]
pub fn sleep_deep_with_rejection(&mut self, config: RtcSleepConfig) {
self.sleep(config, SleepKind::Deep, true);
}
#[cfg(sleep_light_sleep)]
pub fn sleep_light(&mut self, config: RtcSleepConfig) {
self.sleep(config, SleepKind::Light, true);
}
#[cfg(sleep_driver_supported)]
#[crate::ram]
fn sleep(&mut self, config: RtcSleepConfig, kind: SleepKind, allow_reject: bool) {
let rtc = Rtc::new(unsafe { crate::peripherals::RTC_TIMER::steal() });
let mut config = config;
config.set_sleep_kind(kind);
run_entry_hooks(&mut config);
config.apply();
let wakeup_mask = mask();
if wakeup_mask == 0 {
match kind {
SleepKind::Light => return,
SleepKind::Deep => {
panic!("no wakeup source is enabled, so nothing could end the sleep")
}
}
}
let reject_mask = if allow_reject { reject_mask() } else { 0 };
sleep_uart_prepare();
#[cfg(sleep_deep_sleep_needs_gpio_isolation)]
if kind == SleepKind::Deep {
gpio::wakeup::isolate_pads_for_deep_sleep();
}
let before_ticks = crate::time::implem::raw_counter();
let before = rtc.time_since_boot_raw();
let _uart0_sclk_guard = crate::system::ensure_uart0_sclk_enabled();
let rejected = {
#[allow(clippy::let_unit_value)]
let _sleep_guard = config.start_sleep(wakeup_mask, reject_mask);
let rejected = wait_for_sleep_result();
if config.is_deep_sleep() && !rejected {
loop {
core::hint::spin_loop();
}
}
rejected
};
config.finish_sleep();
let after = rtc.time_since_boot_raw();
let slept_us = crate::clock::rtc_ticks_to_us(after.wrapping_sub(before));
let slept_ticks = crate::time::implem::us_to_ticks(slept_us);
unsafe { crate::time::implem::update_counter(before_ticks + slept_ticks) };
sleep_uart_resume();
run_exit_hooks();
if !config.is_deep_sleep() && !rejected {
super::LIGHT_SLEEP_WAKEUP.store(true, portable_atomic::Ordering::Relaxed);
}
gpio::wakeup::record_wakeup();
}
}
#[cfg(sleep_driver_supported)]
fn wait_for_sleep_result() -> bool {
loop {
cfg_select! {
soc_has_pmu => {
let int_raw = crate::peripherals::PMU::regs().int_raw().read();
if int_raw.soc_wakeup().bit_is_set() || int_raw.soc_sleep_reject().bit_is_set() {
return int_raw.soc_sleep_reject().bit_is_set();
}
}
_ => {
let int_raw = LPWR::regs().int_raw().read();
if int_raw.slp_wakeup().bit_is_set() || int_raw.slp_reject().bit_is_set() {
return int_raw.slp_reject().bit_is_set();
}
}
}
}
}
#[cfg(sleep_driver_supported)]
fn sleep_uart_prepare() {
use crate::uart::Instance;
for_each_uart! {
($id:literal, $inst:ident, $peri:ident, $rxd:ident, $txd:ident, $cts:ident, $rts:ident, wakeup_source = $_:literal) => {
unsafe {
crate::peripherals::$inst::steal().info().suspend_for_sleep();
}
};
}
}
#[cfg(sleep_driver_supported)]
fn sleep_uart_resume() {
use crate::uart::Instance;
for_each_uart! {
($id:literal, $inst:ident, $peri:ident, $rxd:ident, $txd:ident, $cts:ident, $rts:ident, wakeup_source = $_:literal) => {
unsafe {
crate::peripherals::$inst::steal().info().resume_from_sleep();
}
};
}
}