From 1a25fc366013503b46af938646c88aed4e36d74c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 15 Jan 2025 17:18:58 +0100 Subject: [PATCH] Add watchdog driver --- src/peripherals.rs | 1 + src/peripherals/wwdt.rs | 226 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+) create mode 100644 src/peripherals/wwdt.rs diff --git a/src/peripherals.rs b/src/peripherals.rs index afb24e5..421b441 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -42,3 +42,4 @@ pub mod syscon; pub mod usbfs; pub mod usbhs; pub mod utick; +pub mod wwdt; diff --git a/src/peripherals/wwdt.rs b/src/peripherals/wwdt.rs new file mode 100644 index 0000000..25baea0 --- /dev/null +++ b/src/peripherals/wwdt.rs @@ -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 { + pub fn try_new( + wwdt: WWDT, + syscon: &crate::Syscon, + clock_divider: u8, + ) -> Result { + 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 Wwdt +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 Wwdt +where + Resetting: sealed::WwdtMode, + Protecting: sealed::WwdtMode, +{ + /// Enable the watchdog + /// + /// Cannot be reversed without a reset + pub fn set_enabled(self) -> Wwdt { + 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 Wwdt +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 { + self.wwdt.mod_.modify(|_, w| w.wdreset().bit(true)); + Wwdt { + wwdt: self.wwdt, + _state: (self._state.0, Active, self._state.2), + } + } +} + +impl Wwdt +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 { + self.wwdt.mod_.modify(|_, w| w.wdprotect().bit(true)); + Wwdt { + wwdt: self.wwdt, + _state: (self._state.0, self._state.1, Active), + } + } +} + +impl Wwdt +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() + } +}