|
| 1 | +use core::fmt; |
| 2 | + |
| 3 | +use raw::WWDT; |
| 4 | + |
| 5 | +mod sealed { |
| 6 | + pub trait WwdtMode {} |
| 7 | +} |
| 8 | + |
| 9 | +#[non_exhaustive] |
| 10 | +pub struct Inactive; |
| 11 | + |
| 12 | +#[non_exhaustive] |
| 13 | +pub struct Active; |
| 14 | + |
| 15 | +impl sealed::WwdtMode for Inactive {} |
| 16 | +impl sealed::WwdtMode for Active {} |
| 17 | + |
| 18 | +/// Windowed watchdog |
| 19 | +/// |
| 20 | +/// Only when `Enabled` is [`Active`] the watchdog is active |
| 21 | +/// Only when `Resetting` is [`Active`] will the watchdog reset the device when its counter reaches zero |
| 22 | +/// Only when `Protecting` is [`Active`] will the watchdog reset the device when its counter reaches zero |
| 23 | +pub struct Wwdt< |
| 24 | + Enabled: sealed::WwdtMode, |
| 25 | + Resetting: sealed::WwdtMode, |
| 26 | + Protecting: sealed::WwdtMode, |
| 27 | +> { |
| 28 | + wwdt: WWDT, |
| 29 | + _state: (Enabled, Resetting, Protecting), |
| 30 | +} |
| 31 | + |
| 32 | +#[derive(Debug)] |
| 33 | +/// Returned if [`Wwdt::set_timer`][] is given an invalid count |
| 34 | +/// |
| 35 | +/// The count must fit within 24 bits |
| 36 | +pub struct InvalidInterval; |
| 37 | + |
| 38 | +impl fmt::Display for InvalidInterval { |
| 39 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 40 | + f.write_str("The count must fit within 24 bits") |
| 41 | + } |
| 42 | +} |
| 43 | + |
| 44 | +#[derive(Debug)] |
| 45 | +/// Returned if [`Wwdt::set_warning`][] is given an invalid count |
| 46 | +/// |
| 47 | +/// The count must fit within 10 bits |
| 48 | +pub struct InvalidWarnInterval; |
| 49 | + |
| 50 | +impl fmt::Display for InvalidWarnInterval { |
| 51 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 52 | + f.write_str("The count must fit within 10 bits") |
| 53 | + } |
| 54 | +} |
| 55 | + |
| 56 | +#[derive(Debug)] |
| 57 | +pub enum TryNewWatchdogError { |
| 58 | + InvalidState, |
| 59 | + /// Returned if [`Wwdt::try_new`][] is given an invalid divider |
| 60 | + /// |
| 61 | + /// The divider must fit within 10 bits |
| 62 | + InvalidClkDiv, |
| 63 | +} |
| 64 | + |
| 65 | +impl fmt::Display for TryNewWatchdogError { |
| 66 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 67 | + match self { |
| 68 | + Self::InvalidState => f.write_str("Watchdog must be fully inactive for try_new"), |
| 69 | + Self::InvalidClkDiv => f.write_str("The clock divider must fit within 6 bits"), |
| 70 | + } |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +impl Wwdt<Inactive, Inactive, Inactive> { |
| 75 | + pub fn try_new( |
| 76 | + wwdt: WWDT, |
| 77 | + syscon: &crate::Syscon, |
| 78 | + clock_divider: u8, |
| 79 | + ) -> Result<Self, (WWDT, TryNewWatchdogError)> { |
| 80 | + if wwdt.mod_.read().wden().bit_is_set() |
| 81 | + || wwdt.mod_.read().wdprotect().bit_is_set() |
| 82 | + || wwdt.mod_.read().wdreset().bit_is_set() |
| 83 | + { |
| 84 | + return Err((wwdt, TryNewWatchdogError::InvalidState)); |
| 85 | + } |
| 86 | + |
| 87 | + if (clock_divider & 0b11000000) != 0 { |
| 88 | + return Err((wwdt, TryNewWatchdogError::InvalidClkDiv)); |
| 89 | + } |
| 90 | + |
| 91 | + // Safety: divider fits in 5 bits |
| 92 | + syscon.raw.wdtclkdiv.modify(|_, w| unsafe { |
| 93 | + w.reset() |
| 94 | + .released() |
| 95 | + .halt() |
| 96 | + .clear_bit() |
| 97 | + .div() |
| 98 | + .bits(clock_divider) |
| 99 | + }); |
| 100 | + |
| 101 | + syscon.raw.ahbclkctrl0.modify(|_, w| w.wwdt().set_bit()); |
| 102 | + |
| 103 | + Ok(Self { |
| 104 | + wwdt, |
| 105 | + _state: (Inactive, Inactive, Inactive), |
| 106 | + }) |
| 107 | + } |
| 108 | +} |
| 109 | + |
| 110 | +impl<Resetting, Protecting> Wwdt<Inactive, Resetting, Protecting> |
| 111 | +where |
| 112 | + Resetting: sealed::WwdtMode, |
| 113 | + Protecting: sealed::WwdtMode, |
| 114 | +{ |
| 115 | + /// Set the timer to TWDCLK * count * 4 |
| 116 | + /// |
| 117 | + /// `count` must fit within 24 bits |
| 118 | + pub fn set_timer(&mut self, count: u32) -> Result<(), InvalidInterval> { |
| 119 | + if (count & 0xFF000000) != 0 { |
| 120 | + return Err(InvalidInterval); |
| 121 | + } |
| 122 | + // Safety: The count fits in 24 bits and the watchdog is disabled |
| 123 | + self.wwdt.tc.write(|w| unsafe { w.count().bits(count) }); |
| 124 | + Ok(()) |
| 125 | + } |
| 126 | + |
| 127 | + /// Set the counter value |
| 128 | + /// |
| 129 | + /// `at_counter` must fit within 10 bits |
| 130 | + pub fn set_warning(&mut self, at_counter: u16) -> Result<(), InvalidWarnInterval> { |
| 131 | + if (at_counter & 0b1111110000000000) != 0 { |
| 132 | + return Err(InvalidWarnInterval); |
| 133 | + } |
| 134 | + // Safety: counter fits in 10 bits |
| 135 | + self.wwdt |
| 136 | + .warnint |
| 137 | + .modify(|_, w| unsafe { w.warnint().bits(at_counter) }); |
| 138 | + Ok(()) |
| 139 | + } |
| 140 | + |
| 141 | + /// Sets the count for the window of the counter value from which the watchdog can be fed |
| 142 | + /// |
| 143 | + /// The watchdog can only be fed if the counter value is below `count` |
| 144 | + pub fn set_window(&mut self, count: u32) -> Result<(), InvalidInterval> { |
| 145 | + if (count & 0xFF000000) != 0 { |
| 146 | + return Err(InvalidInterval); |
| 147 | + } |
| 148 | + |
| 149 | + // Safety: count fits in 24 bits |
| 150 | + self.wwdt.window.modify(|_, w| unsafe { w.bits(count) }); |
| 151 | + Ok(()) |
| 152 | + } |
| 153 | + |
| 154 | + /// Get the current count of the watchdog |
| 155 | + pub fn current_count(&mut self) -> u32 { |
| 156 | + self.wwdt.tv.read().bits() |
| 157 | + } |
| 158 | +} |
| 159 | + |
| 160 | +impl<Resetting, Protecting> Wwdt<Inactive, Resetting, Protecting> |
| 161 | +where |
| 162 | + Resetting: sealed::WwdtMode, |
| 163 | + Protecting: sealed::WwdtMode, |
| 164 | +{ |
| 165 | + /// Enable the watchdog |
| 166 | + /// |
| 167 | + /// Cannot be reversed without a reset |
| 168 | + pub fn set_enabled(self) -> Wwdt<Active, Resetting, Protecting> { |
| 169 | + self.wwdt.mod_.modify(|_, w| w.wden().bit(true)); |
| 170 | + let this = Wwdt { |
| 171 | + wwdt: self.wwdt, |
| 172 | + _state: (Active, self._state.1, self._state.2), |
| 173 | + }; |
| 174 | + this.feed(); |
| 175 | + this |
| 176 | + } |
| 177 | +} |
| 178 | + |
| 179 | +impl<Enabled, Protecting> Wwdt<Enabled, Inactive, Protecting> |
| 180 | +where |
| 181 | + Enabled: sealed::WwdtMode, |
| 182 | + Protecting: sealed::WwdtMode, |
| 183 | +{ |
| 184 | + /// Trigger a reset when the watchdog counter reaches zero |
| 185 | + /// |
| 186 | + /// Cannot be reversed without a reset |
| 187 | + pub fn set_resetting(self) -> Wwdt<Enabled, Active, Protecting> { |
| 188 | + self.wwdt.mod_.modify(|_, w| w.wdreset().bit(true)); |
| 189 | + Wwdt { |
| 190 | + wwdt: self.wwdt, |
| 191 | + _state: (self._state.0, Active, self._state.2), |
| 192 | + } |
| 193 | + } |
| 194 | +} |
| 195 | + |
| 196 | +impl<Enabled, Resetting> Wwdt<Enabled, Resetting, Inactive> |
| 197 | +where |
| 198 | + Enabled: sealed::WwdtMode, |
| 199 | + Resetting: sealed::WwdtMode, |
| 200 | +{ |
| 201 | + /// When set, the watchdog can only be fed when the counter is below bothe the values passed |
| 202 | + /// to set_window and set_warning |
| 203 | + pub fn set_protecting(self) -> Wwdt<Enabled, Resetting, Active> { |
| 204 | + self.wwdt.mod_.modify(|_, w| w.wdprotect().bit(true)); |
| 205 | + Wwdt { |
| 206 | + wwdt: self.wwdt, |
| 207 | + _state: (self._state.0, self._state.1, Active), |
| 208 | + } |
| 209 | + } |
| 210 | +} |
| 211 | + |
| 212 | +impl<Resetting, Protecting> Wwdt<Active, Resetting, Protecting> |
| 213 | +where |
| 214 | + Resetting: sealed::WwdtMode, |
| 215 | + Protecting: sealed::WwdtMode, |
| 216 | +{ |
| 217 | + pub fn feed(&self) { |
| 218 | + // Safety: The watchdog is enabled |
| 219 | + self.wwdt.feed.write(|w| unsafe { w.feed().bits(0xAA) }); |
| 220 | + self.wwdt.feed.write(|w| unsafe { w.feed().bits(0x55) }); |
| 221 | + } |
| 222 | + |
| 223 | + pub fn timer(&self) -> u32 { |
| 224 | + self.wwdt.tv.read().bits() |
| 225 | + } |
| 226 | +} |
0 commit comments