1use core::marker::PhantomData;
4
5use cortex_m::asm;
6use embassy_hal_internal::{Peri, PeripheralType, interrupt::InterruptExt as _};
7
8use crate::{
9 event_link::{IcuInterrupt as _, InterruptEvent},
10 interrupt::{
11 self,
12 typelevel::{Handler as InterruptHandler, Interrupt as InterruptType},
13 },
14 pac::{
15 self,
16 dbg::vals::DbgstopWdt,
17 osm::vals::StartMode,
18 wdt::vals::{Cks, Rpes, Rpss, Tops},
19 },
20 watchdog::Action,
21};
22
23#[cfg_attr(feature = "defmt", derive(defmt::Format))]
25#[derive(Copy, Clone)]
26pub enum ClockDivider {
27 Div4,
29
30 Div64,
32
33 Div128,
35
36 Div512,
38
39 Div2048,
41
42 Div8192,
44}
45
46#[cfg_attr(feature = "defmt", derive(defmt::Format))]
51#[non_exhaustive]
52pub struct Config {
53 #[allow(missing_docs)]
54 pub action: Action,
55 #[allow(missing_docs)]
56 pub divider: ClockDivider,
57 #[allow(missing_docs)]
58 pub period: TimeoutPeriod,
59}
60
61#[cfg_attr(feature = "defmt", derive(defmt::Format))]
63#[derive(Copy, Clone)]
64pub enum TimeoutPeriod {
65 _1024,
67
68 _4096,
70
71 _8192,
73
74 _16384,
76}
77
78pub struct Watchdog<'d, I: Instance> {
80 phantom: PhantomData<&'d I>,
81}
82
83pub struct WdtInterruptHandler<I: Instance> {
85 _phantom: PhantomData<I>,
86}
87
88#[allow(private_bounds)]
90pub trait Instance: SealedInstance + PeripheralType {}
91
92pub(crate) trait SealedInstance {
93 fn regs() -> pac::wdt::Wdt;
94}
95
96impl From<ClockDivider> for u16 {
97 #[inline]
98 fn from(value: ClockDivider) -> Self {
99 match value {
100 ClockDivider::Div4 => 4,
101 ClockDivider::Div64 => 64,
102 ClockDivider::Div128 => 128,
103 ClockDivider::Div512 => 512,
104 ClockDivider::Div2048 => 2048,
105 ClockDivider::Div8192 => 8192,
106 }
107 }
108}
109
110impl From<ClockDivider> for Cks {
111 #[inline]
112 fn from(value: ClockDivider) -> Self {
113 match value {
114 ClockDivider::Div4 => Cks::Pclkb4,
115 ClockDivider::Div64 => Cks::Pclkb64,
116 ClockDivider::Div128 => Cks::Pclkb128,
117 ClockDivider::Div512 => Cks::Pclkb512,
118 ClockDivider::Div2048 => Cks::Pclkb2048,
119 ClockDivider::Div8192 => Cks::Pclkb8192,
120 }
121 }
122}
123
124impl From<TimeoutPeriod> for Tops {
125 #[inline]
126 fn from(value: TimeoutPeriod) -> Self {
127 match value {
128 TimeoutPeriod::_1024 => Self::_1024Cycles,
129 TimeoutPeriod::_4096 => Self::_4096Cycles,
130 TimeoutPeriod::_8192 => Self::_8192Cycles,
131 TimeoutPeriod::_16384 => Self::_16384Cycles,
132 }
133 }
134}
135
136impl From<TimeoutPeriod> for u16 {
137 #[inline]
138 fn from(value: TimeoutPeriod) -> Self {
139 match value {
140 TimeoutPeriod::_1024 => 1024,
141 TimeoutPeriod::_4096 => 4096,
142 TimeoutPeriod::_8192 => 8192,
143 TimeoutPeriod::_16384 => 16384,
144 }
145 }
146}
147
148impl Default for Config {
149 fn default() -> Self {
151 Self {
152 divider: ClockDivider::Div8192,
153 period: TimeoutPeriod::_8192,
154 action: Action::Interrupt,
155 }
156 }
157}
158
159impl<'d, I: Instance> Watchdog<'d, I> {
160 fn new_inner(peri: Peri<'d, I>, config: Config) -> Self {
161 let _ = peri;
162
163 {
164 let osm = pac::OSM;
165 let ofs0 = osm.ofs0().read();
166
167 if ofs0.wdtstrt() == StartMode::AutoStart {
168 debug!("WDT: Auto-start mode selected, timing changes will be ignored.");
169 }
170 }
171
172 let mut reset = false;
173 match config.action {
174 Action::Interrupt => {
175 debug!("WDT: Enabling NMI");
176
177 cfg_select! {
178 not(ra8m1) => {
179 let icu = pac::ICU;
180
181 icu.nmicr().write(|r| r.set_nflten(false));
182 icu.nmicr().write(|r| r.set_nmimd(true));
183 icu.nmiclr().write(|r| r.set_nmiclr(true));
184 icu.nmier().write(|r| r.set_wdten(true));
185 },
186 ra8m1 => {
187 let icu = pac::ICU;
188 let icu_common = pac::ICU_COMMON;
189
190 icu_common.nmicr().write(|r| r.set_nflten(false));
191 icu_common.nmicr().write(|r| r.set_nmimd(true));
192 icu.nmiclr().write(|r| r.set_nmiclr(true));
193 icu.nmier().write(|r| r.set_wdten(true));
194 },
195 }
196 }
197 Action::Reset => {
198 reset = true;
199 }
200 Action::Handler => {}
201 }
202
203 let dbg = pac::DBG;
206 dbg.dbgstopcr()
207 .write(|r| r.set_dbgstop_wdt(DbgstopWdt::Enable));
208
209 let wdt = I::regs();
210
211 wdt.wdtcr().write(|r| {
212 r.set_tops(config.period.into());
213 r.set_cks(config.divider.into());
214 r.set_rpes(Rpes::None);
215 r.set_rpss(Rpss::None);
216 });
217
218 {
219 let config = wdt.wdtcr().read();
220 let clocks = crate::clock::clock_status();
221 let divider = match config.cks() {
222 Cks::Pclkb4 => 4.0,
223 Cks::Pclkb64 => 64.0,
224 Cks::Pclkb128 => 128.0,
225 Cks::Pclkb512 => 512.0,
226 Cks::Pclkb2048 => 2048.0,
227 Cks::Pclkb8192 => 8192.0,
228 _ => unreachable!(),
229 };
230 let period = match config.tops() {
231 Tops::_1024Cycles => 1024.0,
232 Tops::_4096Cycles => 4096.0,
233 Tops::_8192Cycles => 8192.0,
234 Tops::_16384Cycles => 16384.0,
235 };
236
237 let timeout = period / ((clocks.peripheral_b.to_Hz() as f32) / divider);
238 debug!("WDT: timeout: {} sec", timeout);
239 }
240
241 wdt.wdtrcr().write(|r| r.set_rstirqs(reset));
242 wdt.wdtcstpr().write(|r| r.set_slcstp(false));
243
244 let mut this = Self {
245 phantom: PhantomData,
246 };
247
248 this.refresh();
249
250 this
251 }
252
253 #[inline]
261 pub fn new(peri: Peri<'d, I>, config: Config) -> Self {
262 Self::new_inner(peri, config)
263 }
264
265 #[inline]
270 pub fn new_handler<WdtInt: InterruptType>(
271 peri: Peri<'d, I>,
272 config: Config,
273 irq: impl interrupt::typelevel::Binding<WdtInt, WdtInterruptHandler<I>>,
274 ) -> Self {
275 let _ = peri;
276 let _ = irq;
277
278 assert_eq!(config.action, Action::Handler);
279
280 unsafe {
282 WdtInt::IRQ.enable();
283 WdtInt::IRQ.icu_enable(InterruptEvent::WdtUnderflow);
284 }
285
286 Self::new_inner(peri, config)
287 }
288
289 #[inline]
291 pub fn refresh(&mut self) {
292 let wdt = I::regs();
293
294 asm::dsb();
295 wdt.wdtrr().write_value(0x00);
296 asm::dsb();
297 wdt.wdtrr().write_value(0xFF);
298 }
299
300 #[inline]
302 pub fn value(&self) -> u16 {
303 let wdt = I::regs();
304 let status = wdt.wdtsr().read();
305
306 status.cntval()
307 }
308}
309
310impl Instance for crate::peripherals::WDT {}
311
312impl SealedInstance for crate::peripherals::WDT {
313 #[inline(always)]
314 fn regs() -> pac::wdt::Wdt {
315 pac::WDT
316 }
317}
318
319impl<I: Instance, WdtInt: InterruptType> InterruptHandler<WdtInt> for WdtInterruptHandler<I> {
320 unsafe fn on_interrupt() {
321 WdtInt::IRQ.icu_unpend();
322
323 panic!("Watchdog underflow");
324 }
325}