1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
//! # WDOG - Watchdog Timer
//!
//! This peripheral runs from an independent timer, and resets the MCU when
//! that timer overflows. Clearing the count value of this timer ensures that
//! software does not leave the cpu stuck in an infinite loop or execute from
//! unknown data.
//!
//! ** NOTE: ** The watchdog does not function well with the debugger active.
//! writing to it is only successful in odd cases, with little effect.
//! Attempting to unlock will trigger a reset. I pulled my hair out over a long
//! weekend before I realized this. Yes, there is a DEBUG mode to set according
//! the KEA64RM. It never works as expected.
//!
//! ## Usage
//!
//! ```rust
//! #![no_main]
//! #![no_std]
//!
//! use kea_hal as hal;
//!
//! use cortex_m_rt::entry;
//! use hal::{pac, prelude::*, system};
//! use panic_halt as _;
//!
//! #[entry]
//! fn main() -> ! {
//!     //println!("Hello, world!");
//!     let _cp = cortex_m::Peripherals::take().unwrap();
//!     let dp = pac::Peripherals::take().unwrap();
//!
//!     let watchdog = dp.WDOG.split();
//!     let mut config = watchdog.configuration();
//!     // Reset every 0xBEEF/32kHz seconds.
//!     config.period = 0xBEEF;
//!     config.clock = system::watchdog::WDogClock::IntRefClock;
//!
//!     // Trigger an interrupt before reset to log the error (or something).
//!     config.interrupt = true;
//!
//!     // load new settings (watchdog will determine if needs to unlock or
//!     // not)
//!     let watchdog = watchdog.configure(config);
//!
//!     // Seal the watchdog so that it cannot be modified until reset
//!     let watchdog.into_sealed();
//! }
//! ```
//!
//!
//! ## Clock Sources
//!
//! * Bus Clock
//! * 1kHz Clock
//! * 32kHz Clock
//! * Ext Clock
//!
//! ## Programmable Timeout Perioid
//!
//! 16 bit timeout value, with optional, fixed, 1/256 prescaler for longer
//!    timeout periods.
//!
//! ## Servicing the Watchdog.
//!
//! Call the [Watchdog.service] method to reset the countdown. This is often
//! known as petting the watchdog.
//!
//! Refresh write sequence: 0x2A6 and then 0x80B4 within 16 bus clocks.
//!
//! ## Windowed refresh
//!
//! Triggers a reset if refresh comes before expected. 16 bit programmable
//! window value. "Provides robust check that program flow is faster than
//! expected".
//!
//! *Implementer's Note*
//!
//! This seems useful for asm sequences, but it seems like writing in a high
//! level language would make determining "how soon is too soon" rather
//! difficult.
//!
//! ## Watchdog Interrupt
//!
//! Allows some post-processing to be done after the watchdog triggers, but
//! before the reset. Reset happens 128 bus clocks after the interrupt vector
//! is fetched.
//!
//! ## Configuration
//!
//! Configuration register fields are write-once after reset to prevent
//! accidental modification. These fields can be unlocked for updates by
//! writing 0x20C5 and then 0x28D9 (within 16 bus clocks of each other).
//! Updates must be written within 128 bus clocks after unlocking.

use crate::cortex_m::interrupt;
use crate::{pac::WDOG, HALExt};
use core::marker::PhantomData;

#[inline(always)]
fn unlock(_cs: &interrupt::CriticalSection) {
    let peripheral = unsafe { &(*WDOG::ptr()) };
    peripheral
        .wdog_cnt()
        .write(|w| unsafe { w.bits(0x20C5).bits(0x28D9) });
}

impl HALExt for WDOG {
    type T = WatchDog<Enabled, Unlocked>;
    fn split(self) -> WatchDog<Enabled, Unlocked> {
        WatchDog {
            _enable: PhantomData,
            _update: PhantomData,
            peripheral: self,
        }
    }
}

/// Enumeration of watchdog clocks
#[derive(Clone, Debug)]
#[repr(u8)]
pub enum WDogClock {
    /// Bus Clock.
    ///
    /// Note that using this clock disables the watchdog's backup reset
    /// functionality. The Watchdog periphal uses the bus block internally to
    /// operate. If the bus clock is lost and the WDogClock continues to
    /// increment the counter (i.e. it's not also set to bus clock), after the
    /// counter overflows twice the backup reset functionality kicks in to
    /// reset the MCU.
    BusClock = 0,
    /// Internal 1kHz Low Power Oscillator
    LpoClock = 1,
    /// 32kHz Internal Reference Clock
    IntRefClock = 2,
    /// External Reference Clock
    ExtRefClock = 3,
}

/// The Watchdog interface presented to the user.
pub struct WatchDog<State, UpdateState> {
    _enable: PhantomData<State>,
    _update: PhantomData<UpdateState>,
    peripheral: WDOG,
}
/// Holds watchdog configuration.
///
/// Generated by [WatchDog::configuration] and consumed by
/// [WatchDog::configure].
#[derive(Debug)]
pub struct WDogConfig {
    /// Watchdog Generates Intterupts
    pub interrupt: bool,
    /// Watchdog Operates in Debug mode
    pub debug_mode: bool,
    /// Watchdog Operates in Wait mode
    pub wait_mode: bool,
    /// Watchdog Operates in Stop mode
    pub stop_mode: bool,
    /// Windowed Watchdog Mode
    pub windowed: bool,
    /// Use watchdog clock prescaler (fixed 1:256)
    pub prescale: bool,
    /// Set the clock used by the watchdog
    pub clock: WDogClock,
    /// When counter >= period, reset.
    pub period: u16,
    /// Refresh window
    ///
    /// If windowed is set and the watchdog is serviced while counter <= window
    /// reset. In other words, in windowed mode, the watchdog will trigger a
    /// reset unless serviced when window < counter < period.
    pub window: u16,
}

// This state is the on-reset state
impl WatchDog<Enabled, Unlocked> {
    /// Load a configuration and start the watchdog
    ///
    /// per KEA64RM 16.3.2 pg193-194, all registers except count must be
    /// written to for configuration to take effect. The Window register may be
    /// omited if not in windowed mode.
    ///
    /// Note: Configuring in an unlocked state with the debugger is attached
    /// will have little useful effect.
    pub fn configure(self, config: WDogConfig) -> WatchDog<Enabled, Locked> {
        // The 16bit watchdog registers are in big-endian format
        self.peripheral
            .wdog_toval()
            .write(|w| unsafe { w.bits(config.period.swap_bytes()) });
        if config.windowed {
            self.peripheral
                .wdog_win()
                .write(|w| unsafe { w.bits(config.window.swap_bytes()) });
        }
        self.peripheral.cs2.modify(|_, w| {
            w.win()
                .bit(config.windowed)
                .pres()
                .bit(config.prescale)
                .clk()
                .bits(config.clock.clone() as u8) // why does only this one move from config?
        });
        self.peripheral.cs1.modify(|_, w| {
            w.int()
                .bit(config.interrupt)
                .dbg()
                .bit(config.debug_mode)
                .wait()
                .bit(config.wait_mode)
                .stop()
                .bit(config.stop_mode)
                .en() // Enable the Watchdog
                ._1()
                .update() // Allow to be updateable (locked, not sealed)
                ._1()
        });

        WatchDog {
            _enable: PhantomData,
            _update: PhantomData,
            peripheral: self.peripheral,
        }
    }

    /// Disable the WatchDog
    pub fn into_disabled(self) -> WatchDog<Disabled, Locked> {
        // Write everything. CS1 last
        self.peripheral
            .wdog_toval()
            .modify(|r, w| unsafe { w.bits(r.bits()) });

        // update window register if window is set
        if self.peripheral.cs2.read().win().bit() {
            self.peripheral
                .wdog_win()
                .modify(|r, w| unsafe { w.bits(r.bits()) });
        }
        self.peripheral
            .cs2
            .modify(|r, w| unsafe { w.bits(r.bits()) });
        self.peripheral
            .cs1
            .modify(|r, w| unsafe { w.bits(r.bits()).en()._0() });

        WatchDog {
            _enable: PhantomData,
            _update: PhantomData,
            peripheral: self.peripheral,
        }
    }
}

impl WatchDog<Enabled, Locked> {
    /// Unlock and disable the WatchDog
    pub fn into_disabled(self) -> WatchDog<Disabled, Locked> {
        interrupt::free(|cs| {
            unlock(cs);
            WatchDog::<Enabled, Unlocked> {
                _enable: PhantomData,
                _update: PhantomData,
                peripheral: self.peripheral,
            }
            .into_disabled()
        })
    }
}

impl<UpdateState> WatchDog<Enabled, UpdateState> {
    /// Service the Watchdog
    ///
    /// Restart the countdown for MCU reset. This is often called petting,
    /// feeding, or kicking the watchdog.
    pub fn service(&self) {
        interrupt::free(|_| {
            self.peripheral
                .wdog_cnt()
                .write(|w| unsafe { w.cnt().bits(0x02A6) });
            self.peripheral
                .wdog_cnt()
                .write(|w| unsafe { w.cnt().bits(0x80B4) });
        });
    }

    /// Return the current value of the watchdog's counter
    ///
    /// This function swaps the bytes from the big endian registers to little
    /// endian representation used.
    pub fn counts(&self) -> u16 {
        self.peripheral.wdog_cnt().read().bits().swap_bytes()
    }
}

impl<State> WatchDog<State, Locked> {
    /// Unlock, enable, and reconfigure
    ///
    /// Note: Configuring in a locked state with the debugger attached will
    /// trigger an immediate reset.
    pub fn configure(self, config: WDogConfig) -> WatchDog<Enabled, Locked> {
        interrupt::free(|cs| {
            unlock(cs);
            WatchDog::<Enabled, Unlocked> {
                _enable: PhantomData,
                _update: PhantomData,
                peripheral: self.peripheral,
            }
            .configure(config)
        })
    }

    /// Seals the WatchDog peripheral.
    ///
    /// This prevents any further modifications to the watchdog, aside from
    /// servicing as needed.
    pub fn into_sealed(self) -> WatchDog<State, Sealed> {
        interrupt::free(|cs| {
            unlock(cs);

            // Relock everything with current value, except unset update
            self.peripheral
                .wdog_toval()
                .modify(|r, w| unsafe { w.bits(r.bits()) });
            if self.peripheral.cs2.read().win().bit() {
                self.peripheral
                    .wdog_win()
                    .modify(|r, w| unsafe { w.bits(r.bits()) });
            }
            self.peripheral
                .cs2
                .modify(|r, w| unsafe { w.bits(r.bits()) });

            // update_A::_0 is what seals.
            self.peripheral
                .cs1
                .modify(|r, w| unsafe { w.bits(r.bits()).update()._0() });
        });
        WatchDog {
            _enable: PhantomData,
            _update: PhantomData,
            peripheral: self.peripheral,
        }
    }
}

impl<State, UpdateState> WatchDog<State, UpdateState> {
    /// Returns a [WDogConfig] containing the current state of the peripheral
    pub fn configuration(&self) -> WDogConfig {
        WDogConfig {
            interrupt: self.peripheral.cs1.read().int().bit(),
            debug_mode: self.peripheral.cs1.read().dbg().bit(),
            wait_mode: self.peripheral.cs1.read().wait().bit(),
            stop_mode: self.peripheral.cs1.read().stop().bit(),
            windowed: self.peripheral.cs2.read().win().bit(),
            prescale: self.peripheral.cs2.read().pres().bit(),
            clock: match self.peripheral.cs2.read().clk().bits() {
                0 => WDogClock::BusClock,
                1 => WDogClock::LpoClock,
                2 => WDogClock::IntRefClock,
                _ => WDogClock::ExtRefClock,
            },
            period: self.peripheral.wdog_toval().read().bits(),
            window: self.peripheral.wdog_win().read().bits(),
        }
    }

    /// Checks if the interrupt_ran.
    ///
    /// If acknowledge argument is true, then clear the interrupt flag if it is
    /// set.
    pub fn interrupt_ran(&self, acknowledge: bool) -> bool {
        let ret_val: bool = self.peripheral.cs2.read().flg().bit();
        if acknowledge && ret_val {
            self.peripheral.cs2.modify(|_, w| w.flg()._1());
        }
        ret_val
    }
}

/// Unlocked state, modifiable.
pub struct Unlocked;
/// Locked state, must unlock before modifying
pub struct Locked;
/// Locked and sealed state; Device must be reset to unlock.
pub struct Sealed;

/// Enabled state
pub struct Enabled;
/// Disabled state
pub struct Disabled;