Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add watchdog driver #76

Merged
merged 2 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ pub struct Peripherals {
/// System Control Block (SCB) - core peripheral
pub SCB: raw::SCB,

/// Watchdog
pub wwdt: raw::WWDT,

#[cfg(not(feature = "rtic-peripherals"))]
/// SysTick: System Timer - core peripheral
#[cfg(not(feature = "rtic-peripherals"))]
Expand Down Expand Up @@ -415,6 +418,7 @@ impl From<(raw::Peripherals, raw::CorePeripherals)> for Peripherals {
FLASH_CFPA0: p.FLASH_CFPA0,
SAU: p.SAU,
SCT0: p.SCT0,
wwdt: p.WWDT,

// Core peripherals
CPUID: cp.CPUID,
Expand Down
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()
}
}
Loading