use super::{Armed, LP_NUMBERS, NO_LP_NUMBER, prepare_pad};
use crate::{
gpio::{Level, lp_io::low_level},
peripherals::{LPWR, RTC_IO},
rtc_cntl::{
WakeupReason,
WakeupSource,
sleep::{SleepResource, WrappedSleepConfig},
},
};
pub(super) fn disable() {
WakeupSource::Ext0.disable();
WakeupSource::Ext1.disable();
}
pub(super) fn allocate(armed: &[Armed], config: &mut WrappedSleepConfig<'_>) {
clear_per_pin();
let deep = config.is_deep_sleep();
let shared = shared_level(armed);
let mut leftover = false;
let mut ext0_taken = false;
for pin in armed {
if Some(pin.level) == shared {
continue;
}
leftover = true;
if !ext0_taken {
arm_ext0(pin, deep);
ext0_taken = true;
} else {
arm_per_pin(pin, deep);
}
}
if !ext0_taken {
WakeupSource::Ext0.disable();
}
if leftover {
config.keep_alive(SleepResource::LpPeripherals);
}
match shared {
Some(level) => {
let group = |pin: &&Armed| pin.level == level;
arm_ext1(armed.iter().filter(group), level, deep);
}
None => disarm_ext1(),
}
}
fn shared_level(armed: &[Armed]) -> Option<Level> {
fn group(armed: &[Armed], level: Level) -> (usize, u8) {
let mut count = 0;
let mut lowest = u8::MAX;
for pin in armed.iter().filter(|pin| pin.level == level) {
count += 1;
lowest = lowest.min(pin.gpio);
}
(count, lowest)
}
let high = group(armed, Level::High);
let mut low = group(armed, Level::Low);
if cfg!(esp32) && low.0 > 1 {
low = (0, u8::MAX);
}
match (high.0, low.0) {
(0, 0) => None,
(_, 0) => Some(Level::High),
(0, _) => Some(Level::Low),
_ if high.0 > low.0 => Some(Level::High),
_ if low.0 > high.0 => Some(Level::Low),
_ if high.1 < low.1 => Some(Level::High),
_ => Some(Level::Low),
}
}
fn arm_ext1<'a>(group: impl Iterator<Item = &'a Armed>, level: Level, deep: bool) {
let mut pads = 0;
for pin in group {
pads |= 1 << pin.lp;
prepare_pad(pin, deep);
}
write_ext1(pads, level);
WakeupSource::Ext1.enable();
}
fn disarm_ext1() {
write_ext1(0, Level::Low);
WakeupSource::Ext1.disable();
}
fn write_ext1(pads: u32, level: Level) {
LPWR::regs()
.ext_wakeup1()
.modify(|_, w| w.status_clr().set_bit());
LPWR::regs()
.ext_wakeup1()
.modify(|_, w| unsafe { w.sel().bits(pads) });
LPWR::regs()
.ext_wakeup_conf()
.modify(|_, w| w.ext_wakeup1_lv().bit(level == Level::High));
}
fn arm_ext0(pin: &Armed, deep: bool) {
prepare_pad(pin, deep);
RTC_IO::regs()
.ext_wakeup0()
.modify(|_, w| unsafe { w.sel().bits(pin.lp) });
LPWR::regs()
.ext_wakeup_conf()
.modify(|_, w| w.ext_wakeup0_lv().bit(pin.level == Level::High));
WakeupSource::Ext0.enable();
}
pub(super) fn caused_wakeup(gpio: u8, cause: WakeupReason) -> bool {
let Some(lp) = super::lp_number(gpio) else {
return false;
};
let ext1 = cause.contains(WakeupSource::Ext1)
&& LPWR::regs()
.ext_wakeup1_status()
.read()
.ext_wakeup1_status()
.bits()
& (1 << lp)
!= 0;
let ext0 = cause.contains(WakeupSource::Ext0)
&& RTC_IO::regs().ext_wakeup0().read().sel().bits() == lp;
let per_pin = cause.contains(WakeupSource::Gpio) && low_level::wakeup_status() & (1 << lp) != 0;
ext1 || ext0 || per_pin
}
pub(super) fn wake_io_reset() {
let ext1 = LPWR::regs().ext_wakeup1().read().sel().bits();
let ext0 = RTC_IO::regs().ext_wakeup0().read().sel().bits();
let ext0_armed = crate::rtc_cntl::sleep::enabled_sources().contains(WakeupSource::Ext0);
let per_pin = low_level::wakeup_enabled_mask();
for lp in super::low_power_numbers() {
if ext1 & (1 << lp) != 0 || (ext0_armed && ext0 == lp) || per_pin & (1 << lp) != 0 {
low_level::pad_hold(lp, false);
}
}
}
fn clear_per_pin() {
for &lp in LP_NUMBERS.iter().filter(|&&lp| lp != NO_LP_NUMBER) {
low_level::apply_wakeup(lp, false, Level::Low);
}
low_level::clear_wakeup_status();
}
fn arm_per_pin(pin: &Armed, deep: bool) {
prepare_pad(pin, deep);
low_level::apply_wakeup(pin.lp, true, pin.level);
}