1#![no_std]
5#![doc = include_str!("../README.md")]
6#![deny(clippy::undocumented_unsafe_blocks)]
7
8use bitflags::bitflags;
32pub use safe_mmio::UniqueMmioPointer;
33use safe_mmio::{
34 field,
35 fields::{ReadPure, ReadPureWrite, WriteOnly},
36};
37use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
38
39#[repr(transparent)]
41#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
42struct ControlRegister(u32);
43
44#[repr(transparent)]
46#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
47struct Interrupts(u32);
48
49bitflags! {
50 impl ControlRegister : u32 {
52 const RESEN = 1 << 1;
54 const INTEN = 1 << 0;
56 }
57
58 impl Interrupts : u32 {
60 const WDOGRIS = 1 << 0;
62 }
63}
64
65#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
67#[repr(C, align(4))]
68pub struct SP805Registers {
69 wdog_load: ReadPureWrite<u32>,
71 wdog_value: ReadPure<u32>,
73 wdog_control: ReadPureWrite<ControlRegister>,
75 wdog_intclr: WriteOnly<u32>,
77 wdog_ris: ReadPure<Interrupts>,
79 wdog_mis: ReadPure<Interrupts>,
81 reserved_18: [u32; 762],
83 wdog_lock: ReadPureWrite<u32>,
85 reserved_c04: [u32; 191],
87 wdog_itcr: ReadPureWrite<u32>,
89 wdog_itop: WriteOnly<u32>,
91 reserved_f08: [u32; 54],
93 wdog_periph_id0: ReadPure<u32>,
95 wdog_periph_id1: ReadPure<u32>,
97 wdog_periph_id2: ReadPure<u32>,
99 wdog_periph_id3: ReadPure<u32>,
101 wdog_pcell_id0: ReadPure<u32>,
103 wdog_pcell_id1: ReadPure<u32>,
105 wdog_pcell_id2: ReadPure<u32>,
107 wdog_pcell_id3: ReadPure<u32>,
109}
110
111pub struct Watchdog<'a> {
113 regs: UniqueMmioPointer<'a, SP805Registers>,
114 load_value: u32,
115}
116
117impl<'a> Watchdog<'a> {
118 const LOCK: u32 = 0x00000001;
119 const UNLOCK: u32 = 0x1ACCE551;
120
121 pub fn new(regs: UniqueMmioPointer<'a, SP805Registers>, load_value: u32) -> Self {
123 Self { regs, load_value }
124 }
125
126 pub fn enable(&mut self) {
128 let load_value = self.load_value;
129
130 self.with_unlock(|mut regs| {
131 field!(regs, wdog_load).write(load_value);
132 field!(regs, wdog_intclr).write(1);
133 field!(regs, wdog_control).write(ControlRegister::INTEN | ControlRegister::RESEN);
134 });
135 }
136
137 pub fn disable(&mut self) {
139 self.with_unlock(|mut regs| field!(regs, wdog_control).write(ControlRegister::empty()));
140 }
141
142 pub fn update(&mut self) {
144 let load_value = self.load_value;
145
146 self.with_unlock(|mut regs| field!(regs, wdog_load).write(load_value));
147 }
148
149 fn with_unlock<F>(&mut self, f: F)
150 where
151 F: FnOnce(&mut UniqueMmioPointer<SP805Registers>),
152 {
153 field!(self.regs, wdog_lock).write(Self::UNLOCK);
154 f(&mut self.regs);
155 field!(self.regs, wdog_lock).write(Self::LOCK);
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162 use zerocopy::transmute_mut;
163
164 const LOAD_VALUE: u32 = 0xabcd_ef01;
165
166 #[repr(align(4096))]
167 pub struct FakeSp805Registers {
168 regs: [u32; 1024],
169 }
170
171 impl FakeSp805Registers {
172 pub fn new() -> Self {
173 Self { regs: [0u32; 1024] }
174 }
175
176 pub fn clear(&mut self) {
177 self.regs.fill(0);
178 }
179
180 pub fn reg_write(&mut self, offset: usize, value: u32) {
181 self.regs[offset / 4] = value;
182 }
183
184 pub fn reg_read(&self, offset: usize) -> u32 {
185 self.regs[offset / 4]
186 }
187
188 fn get(&mut self) -> UniqueMmioPointer<'_, SP805Registers> {
189 UniqueMmioPointer::from(transmute_mut!(&mut self.regs))
190 }
191
192 pub fn system_for_test(&mut self) -> Watchdog<'_> {
193 Watchdog::new(self.get(), LOAD_VALUE)
194 }
195 }
196
197 #[test]
198 fn register_block_size() {
199 assert_eq!(0x1000, core::mem::size_of::<SP805Registers>());
200 }
201
202 #[test]
203 fn enable() {
204 let mut regs = FakeSp805Registers::new();
205
206 {
207 regs.reg_write(0x0c, 0xffff_ffff);
209 let mut wdt = regs.system_for_test();
210 wdt.enable();
211 }
212
213 assert_eq!(LOAD_VALUE, regs.reg_read(0x00));
214 assert_eq!(0x0000_0003, regs.reg_read(0x08));
215 assert!(regs.reg_read(0x0c) != 0);
216 assert_eq!(Watchdog::LOCK, regs.reg_read(0xc00));
217
218 regs.clear();
219
220 {
221 regs.reg_write(0x08, 0x0000_0003);
223 let mut wdt = regs.system_for_test();
224 wdt.disable();
225 }
226
227 assert_eq!(0x0000_0000, regs.reg_read(0x08));
228 assert_eq!(Watchdog::LOCK, regs.reg_read(0xc00));
229
230 regs.clear();
231
232 {
233 let mut wdt = regs.system_for_test();
235 wdt.update();
236 }
237
238 assert_eq!(LOAD_VALUE, regs.reg_read(0x00));
239 }
240}