1use core::cell::Cell;
8
9use crate::pm::{self, Clock, PBDClock};
10
11use cortexm4::support;
12
13use kernel::utilities::math::log_base_two_u64;
14use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
15use kernel::utilities::registers::{
16 register_bitfields, FieldValue, ReadOnly, ReadWrite, WriteOnly,
17};
18use kernel::utilities::StaticRef;
19
20#[repr(C)]
21pub struct WdtRegisters {
22 cr: ReadWrite<u32, Control::Register>,
23 clr: WriteOnly<u32, Clear::Register>,
24 sr: ReadOnly<u32, Status::Register>,
25 ier: WriteOnly<u32, Interrupt::Register>,
26 idr: WriteOnly<u32, Interrupt::Register>,
27 imr: ReadOnly<u32, Interrupt::Register>,
28 isr: ReadOnly<u32, Interrupt::Register>,
29 icr: WriteOnly<u32, Interrupt::Register>,
30}
31
32register_bitfields![u32,
33 Control [
34 KEY OFFSET(24) NUMBITS(8) [
36 KEY1 = 0x55,
37 KEY2 = 0xAA
38 ],
39 TBAN OFFSET(18) NUMBITS(5) [],
41 CSSEL OFFSET(17) NUMBITS(1) [
43 RCSYS = 0,
44 OSC32K = 1
45 ],
46 CEN OFFSET(16) NUMBITS(1) [
48 ClockDisable = 0,
49 ClockEnable = 1
50 ],
51 PSEL OFFSET(8) NUMBITS(5) [],
53 FCD OFFSET(7) NUMBITS(1) [
55 RedoCalibration = 0,
56 DoNotRedoCalibration = 1
57 ],
58 IM OFFSET(4) NUMBITS(1) [
60 InterruptModeDisabled = 0,
61 InterruptModeEnabled = 1
62 ],
63 SFV OFFSET(3) NUMBITS(1) [
65 NotLocked = 0,
66 Locked = 1
67 ],
68 MODE OFFSET(2) NUMBITS(1) [
70 Basic = 0,
71 Window = 1
72 ],
73 DAR OFFSET(1) NUMBITS(1) [
75 EnableAfterReset = 0,
76 DisableAfterReset = 1
77 ],
78 EN OFFSET(0) NUMBITS(1) [
80 Disable = 0,
81 Enable = 1
82 ]
83 ],
84
85 Clear [
86 KEY OFFSET(24) NUMBITS(8) [
88 KEY1 = 0x55,
89 KEY2 = 0xAA
90 ],
91 WDTCLR OFFSET(0) NUMBITS(1) []
93 ],
94
95 Status [
96 CLEARED 1,
98 WINDOW 0
100 ],
101
102 Interrupt [
103 WINT 2
104 ]
105];
106
107const WDT_BASE: *mut WdtRegisters = 0x400F0C00 as *mut WdtRegisters;
109const WDT_REGS: StaticRef<WdtRegisters> = unsafe { StaticRef::new(WDT_BASE.cast_const()) };
110
111pub struct Wdt {
112 enabled: Cell<bool>,
113}
114
115#[derive(Copy, Clone)]
116pub enum WdtClockSource {
117 ClockRCSys = 0,
118 ClockOsc32 = 1,
119}
120
121impl From<WdtClockSource> for FieldValue<u32, Control::Register> {
122 fn from(clock: WdtClockSource) -> Self {
123 match clock {
124 WdtClockSource::ClockRCSys => Control::CSSEL::RCSYS,
125 WdtClockSource::ClockOsc32 => Control::CSSEL::OSC32K,
126 }
127 }
128}
129
130impl Wdt {
131 pub const fn new() -> Wdt {
132 Wdt {
133 enabled: Cell::new(false),
134 }
135 }
136
137 fn write_cr(&self, control: FieldValue<u32, Control::Register>) {
144 WDT_REGS.cr.modify(Control::KEY::KEY1 + control);
145 WDT_REGS.cr.modify(Control::KEY::KEY2 + control);
146
147 for _ in 0..10000 {
154 support::nop();
155 }
156 }
157
158 fn select_clock(&self, clock: WdtClockSource) {
159 if !(WDT_REGS.cr.matches_all(From::from(clock))) {
160 let clock_enabled = WDT_REGS.cr.is_set(Control::CEN);
161
162 if clock_enabled {
163 self.write_cr(Control::CEN::CLEAR);
165 while WDT_REGS.cr.is_set(Control::CEN) {}
166 }
167
168 self.write_cr(From::from(clock));
170
171 if clock_enabled {
172 self.write_cr(Control::CEN::SET);
174 while !WDT_REGS.cr.is_set(Control::CEN) {}
175 }
176 }
177 }
178
179 fn start(&self, period: usize) {
180 self.enabled.set(true);
181
182 pm::enable_clock(Clock::PBD(PBDClock::WDT));
183
184 self.select_clock(WdtClockSource::ClockOsc32);
189
190 let f_clk_khz: u64 = if WDT_REGS.cr.matches_all(Control::CSSEL::RCSYS) {
198 115
199 } else {
200 32
202 };
203 let mult: u64 = f_clk_khz * (period as u64);
204 let scaler = log_base_two_u64(mult); let control = Control::CEN::ClockEnable
207 + Control::PSEL.val(scaler)
208 + Control::FCD::DoNotRedoCalibration
209 + Control::DAR::DisableAfterReset
210 + Control::EN::Enable;
211 self.write_cr(control);
212 }
213
214 fn stop(&self) {
215 self.write_cr(Control::EN::CLEAR);
216
217 pm::disable_clock(Clock::PBD(PBDClock::WDT));
218
219 self.enabled.set(false);
220 }
221
222 fn tickle(&self) {
223 WDT_REGS.clr.write(Clear::KEY::KEY1 + Clear::WDTCLR::SET);
225 WDT_REGS.clr.write(Clear::KEY::KEY2 + Clear::WDTCLR::SET);
226 }
227}
228
229impl kernel::platform::watchdog::WatchDog for Wdt {
230 fn setup(&self) {
231 self.start(100);
233 }
234
235 fn tickle(&self) {
236 self.tickle();
237 }
238
239 fn suspend(&self) {
240 self.stop();
241 }
242}