Skip to content

Commit

Permalink
Add watchdog driver
Browse files Browse the repository at this point in the history
  • Loading branch information
sosthene-nitrokey committed Jan 17, 2025
1 parent 316c97c commit 841c6b2
Show file tree
Hide file tree
Showing 2 changed files with 227 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/peripherals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ pub mod syscon;
pub mod usbfs;
pub mod usbhs;
pub mod utick;
pub mod wwdt;
226 changes: 226 additions & 0 deletions src/peripherals/wwdt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
use core::fmt;

use raw::WWDT;

mod sealed {
pub trait WwdtMode {}
}

#[non_exhaustive]
pub struct Inactive;

#[non_exhaustive]
pub struct Active;

impl sealed::WwdtMode for Inactive {}
impl sealed::WwdtMode for Active {}

/// Windowed watchdog
///
/// Only when `Enabled` is [`Active`] the watchdog is active
/// Only when `Resetting` is [`Active`] will the watchdog reset the device when its counter reaches zero
/// Only when `Protecting` is [`Active`] will the watchdog reset the device when its counter reaches zero
pub struct Wwdt<
Enabled: sealed::WwdtMode,
Resetting: sealed::WwdtMode,
Protecting: sealed::WwdtMode,
> {
wwdt: WWDT,
_state: (Enabled, Resetting, Protecting),
}

#[derive(Debug)]
/// Returned if [`Wwdt::set_timer`][] is given an invalid count
///
/// The count must fit within 24 bits
pub struct InvalidInterval;

impl fmt::Display for InvalidInterval {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("The count must fit within 24 bits")
}
}

#[derive(Debug)]
/// Returned if [`Wwdt::set_warning`][] is given an invalid count
///
/// The count must fit within 10 bits
pub struct InvalidWarnInterval;

impl fmt::Display for InvalidWarnInterval {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("The count must fit within 10 bits")
}
}

#[derive(Debug)]
pub enum TryNewWatchdogError {
InvalidState,
/// Returned if [`Wwdt::try_new`][] is given an invalid divider
///
/// The divider must fit within 10 bits
InvalidClkDiv,
}

impl fmt::Display for TryNewWatchdogError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidState => f.write_str("Watchdog must be fully inactive for try_new"),
Self::InvalidClkDiv => f.write_str("The clock divider must fit within 6 bits"),
}
}
}

impl Wwdt<Inactive, Inactive, Inactive> {
pub fn try_new(
wwdt: WWDT,
syscon: &crate::Syscon,
clock_divider: u8,
) -> Result<Self, (WWDT, TryNewWatchdogError)> {
if wwdt.mod_.read().wden().bit_is_set()
|| wwdt.mod_.read().wdprotect().bit_is_set()
|| wwdt.mod_.read().wdreset().bit_is_set()
{
return Err((wwdt, TryNewWatchdogError::InvalidState));
}

if (clock_divider & 0b11000000) != 0 {
return Err((wwdt, TryNewWatchdogError::InvalidClkDiv));
}

// Safety: divider fits in 5 bits
syscon.raw.wdtclkdiv.modify(|_, w| unsafe {
w.reset()
.released()
.halt()
.clear_bit()
.div()
.bits(clock_divider)
});

syscon.raw.ahbclkctrl0.modify(|_, w| w.wwdt().set_bit());

Ok(Self {
wwdt,
_state: (Inactive, Inactive, Inactive),
})
}
}

impl<Resetting, Protecting> Wwdt<Inactive, Resetting, Protecting>
where
Resetting: sealed::WwdtMode,
Protecting: sealed::WwdtMode,
{
/// Set the timer to TWDCLK * count * 4
///
/// `count` must fit within 24 bits
pub fn set_timer(&mut self, count: u32) -> Result<(), InvalidInterval> {
if (count & 0xFF000000) != 0 {
return Err(InvalidInterval);
}
// Safety: The count fits in 24 bits and the watchdog is disabled
self.wwdt.tc.write(|w| unsafe { w.count().bits(count) });
Ok(())
}

/// Set the counter value
///
/// `at_counter` must fit within 10 bits
pub fn set_warning(&mut self, at_counter: u16) -> Result<(), InvalidWarnInterval> {
if (at_counter & 0b1111110000000000) != 0 {
return Err(InvalidWarnInterval);
}
// Safety: counter fits in 10 bits
self.wwdt
.warnint
.modify(|_, w| unsafe { w.warnint().bits(at_counter) });
Ok(())
}

/// Sets the count for the window of the counter value from which the watchdog can be fed
///
/// The watchdog can only be fed if the counter value is below `count`
pub fn set_window(&mut self, count: u32) -> Result<(), InvalidInterval> {
if (count & 0xFF000000) != 0 {
return Err(InvalidInterval);
}

// Safety: count fits in 24 bits
self.wwdt.window.modify(|_, w| unsafe { w.bits(count) });
Ok(())
}

/// Get the current count of the watchdog
pub fn current_count(&mut self) -> u32 {
self.wwdt.tv.read().bits()
}
}

impl<Resetting, Protecting> Wwdt<Inactive, Resetting, Protecting>
where
Resetting: sealed::WwdtMode,
Protecting: sealed::WwdtMode,
{
/// Enable the watchdog
///
/// Cannot be reversed without a reset
pub fn set_enabled(self) -> Wwdt<Active, Resetting, Protecting> {
self.wwdt.mod_.modify(|_, w| w.wden().bit(true));
let this = Wwdt {
wwdt: self.wwdt,
_state: (Active, self._state.1, self._state.2),
};
this.feed();
this
}
}

impl<Enabled, Protecting> Wwdt<Enabled, Inactive, Protecting>
where
Enabled: sealed::WwdtMode,
Protecting: sealed::WwdtMode,
{
/// Trigger a reset when the watchdog counter reaches zero
///
/// Cannot be reversed without a reset
pub fn set_resetting(self) -> Wwdt<Enabled, Active, Protecting> {
self.wwdt.mod_.modify(|_, w| w.wdreset().bit(true));
Wwdt {
wwdt: self.wwdt,
_state: (self._state.0, Active, self._state.2),
}
}
}

impl<Enabled, Resetting> Wwdt<Enabled, Resetting, Inactive>
where
Enabled: sealed::WwdtMode,
Resetting: sealed::WwdtMode,
{
/// When set, the watchdog can only be fed when the counter is below bothe the values passed
/// to set_window and set_warning
pub fn set_protecting(self) -> Wwdt<Enabled, Resetting, Active> {
self.wwdt.mod_.modify(|_, w| w.wdprotect().bit(true));
Wwdt {
wwdt: self.wwdt,
_state: (self._state.0, self._state.1, Active),
}
}
}

impl<Resetting, Protecting> Wwdt<Active, Resetting, Protecting>
where
Resetting: sealed::WwdtMode,
Protecting: sealed::WwdtMode,
{
pub fn feed(&self) {
// Safety: The watchdog is enabled
self.wwdt.feed.write(|w| unsafe { w.feed().bits(0xAA) });
self.wwdt.feed.write(|w| unsafe { w.feed().bits(0x55) });
}

pub fn timer(&self) -> u32 {
self.wwdt.tv.read().bits()
}
}

0 comments on commit 841c6b2

Please sign in to comment.