#![macro_use]
use core::hint::unreachable_unchecked;
use embassy_hal_internal::PeripheralType;
use crate::pac::wdt::vals;
pub use crate::pac::wdt::vals::{Halt as HaltConfig, Sleep as SleepConfig};
use crate::{Peri, interrupt, pac, peripherals};
const MIN_TICKS: u32 = 15;
#[non_exhaustive]
pub struct Config {
pub timeout_ticks: u32,
pub action_during_sleep: SleepConfig,
pub action_during_debug_halt: HaltConfig,
}
impl Config {
pub fn try_new<T: Instance>(_wdt: &Peri<'_, T>) -> Option<Self> {
let r = T::REGS;
#[cfg(not(any(feature = "_nrf91", feature = "_nrf5340", feature = "_nrf54l")))]
let runstatus = r.runstatus().read().runstatus();
#[cfg(any(feature = "_nrf91", feature = "_nrf5340", feature = "_nrf54l"))]
let runstatus = r.runstatus().read().runstatuswdt();
if runstatus {
let config = r.config().read();
Some(Self {
timeout_ticks: r.crv().read(),
action_during_sleep: config.sleep(),
action_during_debug_halt: config.halt(),
})
} else {
None
}
}
}
impl Default for Config {
fn default() -> Self {
Self {
timeout_ticks: 32768, action_during_debug_halt: HaltConfig::RUN,
action_during_sleep: SleepConfig::RUN,
}
}
}
pub struct Watchdog {
r: pac::wdt::Wdt,
}
impl Watchdog {
#[inline]
pub fn try_new<T: Instance, const N: usize>(
wdt: Peri<'static, T>,
config: Config,
) -> Result<(Self, [WatchdogHandle; N]), Peri<'static, T>> {
assert!(N >= 1 && N <= 8);
let r = T::REGS;
let crv = config.timeout_ticks.max(MIN_TICKS);
let rren = crate::pac::wdt::regs::Rren((1u32 << N) - 1);
#[cfg(not(any(feature = "_nrf91", feature = "_nrf5340", feature = "_nrf54l")))]
let runstatus = r.runstatus().read().runstatus();
#[cfg(any(feature = "_nrf91", feature = "_nrf5340", feature = "_nrf54l"))]
let runstatus = r.runstatus().read().runstatuswdt();
if runstatus {
let curr_config = r.config().read();
if curr_config.halt() != config.action_during_debug_halt
|| curr_config.sleep() != config.action_during_sleep
|| r.crv().read() != crv
|| r.rren().read() != rren
{
return Err(wdt);
}
} else {
r.config().write(|w| {
w.set_sleep(config.action_during_sleep);
w.set_halt(config.action_during_debug_halt);
});
r.intenset().write(|w| w.set_timeout(true));
r.crv().write_value(crv);
r.rren().write_value(rren);
r.tasks_start().write_value(1);
}
let this = Self { r: T::REGS };
let mut handles = [const { WatchdogHandle { index: 0 } }; N];
for i in 0..N {
handles[i] = unsafe { WatchdogHandle::steal::<T>(i as u8) };
handles[i].pet();
}
Ok((this, handles))
}
#[inline(always)]
pub fn enable_interrupt(&mut self) {
self.r.intenset().write(|w| w.set_timeout(true));
}
#[inline(always)]
pub fn disable_interrupt(&mut self) {
self.r.intenclr().write(|w| w.set_timeout(true));
}
#[inline(always)]
pub fn awaiting_pets(&self) -> bool {
let enabled = self.r.rren().read().0;
let status = self.r.reqstatus().read().0;
(status & enabled) == 0
}
}
pub struct WatchdogHandle {
index: u8,
}
impl WatchdogHandle {
fn regs(&self) -> pac::wdt::Wdt {
match self.index / 8 {
#[cfg(not(feature = "_multi_wdt"))]
peripherals::WDT::INDEX => peripherals::WDT::REGS,
#[cfg(feature = "_multi_wdt")]
peripherals::WDT0::INDEX => peripherals::WDT0::REGS,
#[cfg(feature = "_multi_wdt")]
peripherals::WDT1::INDEX => peripherals::WDT1::REGS,
_ => unsafe { unreachable_unchecked() },
}
}
fn rr_index(&self) -> usize {
usize::from(self.index % 8)
}
#[inline]
pub fn pet(&mut self) {
let r = self.regs();
r.rr(self.rr_index()).write(|w| w.set_rr(vals::Rr::RELOAD));
}
pub fn is_pet(&self) -> bool {
let r = self.regs();
!r.reqstatus().read().rr(self.rr_index())
}
pub unsafe fn steal<T: Instance>(index: u8) -> Self {
Self {
index: T::INDEX * 8 + index,
}
}
}
pub(crate) trait SealedInstance {
const REGS: pac::wdt::Wdt;
const INDEX: u8;
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_wdt {
($type:ident, $pac_type:ident, $irq:ident, $index:literal) => {
impl crate::wdt::SealedInstance for peripherals::$type {
const REGS: pac::wdt::Wdt = pac::$pac_type;
const INDEX: u8 = $index;
}
impl crate::wdt::Instance for peripherals::$type {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}