blob: b328a86afc73bb35f5fb90e8a06167b7b4f44b1e [file] [log] [blame]
Imre Kisae5c7402025-01-10 17:57:58 +01001// SPDX-FileCopyrightText: Copyright 2023-2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
Imre Kiscd007ad2024-08-14 15:53:11 +02002// SPDX-License-Identifier: MIT OR Apache-2.0
3
Imre Kiscd007ad2024-08-14 15:53:11 +02004#![no_std]
Imre Kis25115ca2025-03-25 14:05:09 +01005#![doc = include_str!("../README.md")]
6#![deny(clippy::undocumented_unsafe_blocks)]
Imre Kiscd007ad2024-08-14 15:53:11 +02007
Imre Kiscd007ad2024-08-14 15:53:11 +02008use bitflags::bitflags;
Imre Kis25115ca2025-03-25 14:05:09 +01009pub use safe_mmio::UniqueMmioPointer;
Imre Kis826b8612025-03-12 13:07:15 +010010use safe_mmio::{
11 field,
12 fields::{ReadPure, ReadPureWrite, WriteOnly},
Imre Kis826b8612025-03-12 13:07:15 +010013};
14use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
15
Imre Kis16eb3162025-03-25 12:51:17 +010016/// Control Register
Imre Kis826b8612025-03-12 13:07:15 +010017#[repr(transparent)]
18#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
19struct ControlRegister(u32);
20
Imre Kis16eb3162025-03-25 12:51:17 +010021/// Interrupt register.
Imre Kis826b8612025-03-12 13:07:15 +010022#[repr(transparent)]
23#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
24struct Interrupts(u32);
Imre Kiscd007ad2024-08-14 15:53:11 +020025
26bitflags! {
27 /// Control register
Imre Kis826b8612025-03-12 13:07:15 +010028 impl ControlRegister : u32 {
Imre Kiscd007ad2024-08-14 15:53:11 +020029 /// Enable Watchdog module reset output
30 const RESEN = 1 << 1;
31 /// Break error
32 const INTEN = 1 << 0;
33 }
34
35 /// Raw Interrupt Status Register
Imre Kis826b8612025-03-12 13:07:15 +010036 impl Interrupts : u32 {
Imre Kiscd007ad2024-08-14 15:53:11 +020037 /// Raw interrupt status from the counter
38 const WDOGRIS = 1 << 0;
39 }
Imre Kiscd007ad2024-08-14 15:53:11 +020040}
41
Imre Kis25115ca2025-03-25 14:05:09 +010042/// SP805 Watchdog register map.
Imre Kis826b8612025-03-12 13:07:15 +010043#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
Imre Kiscd007ad2024-08-14 15:53:11 +020044#[repr(C, align(4))]
45pub struct SP805Registers {
Imre Kis826b8612025-03-12 13:07:15 +010046 /// 0x000 Load Register
47 wdog_load: ReadPureWrite<u32>,
48 /// 0x004 Value Register
49 wdog_value: ReadPure<u32>,
50 /// 0x008 Control register
51 wdog_control: ReadPureWrite<ControlRegister>,
52 /// 0x00c Interrupt Clear Register
53 wdog_intclr: WriteOnly<u32>,
54 /// 0x010 Raw Interrupt Status Register
55 wdog_ris: ReadPure<Interrupts>,
56 /// 0x014 Masked Interrupt Status Register
57 wdog_mis: ReadPure<Interrupts>,
58 /// 0x018 - 0xbfc
59 reserved_18: [u32; 762],
60 /// 0xc00 Lock Register
61 wdog_lock: ReadPureWrite<u32>,
62 /// 0xc04 - 0xefc
63 reserved_c04: [u32; 191],
64 /// 0xf00 Integration Test Control Register,
65 wdog_itcr: ReadPureWrite<u32>,
66 /// 0xf04 Integration Test Output Set
67 wdog_itop: WriteOnly<u32>,
68 /// 0xf08 - 0xfdc
69 reserved_f08: [u32; 54],
70 /// 0xfe0 Peripheral Identification Register 0
71 wdog_periph_id0: ReadPure<u32>,
72 /// 0xfe4 Peripheral Identification Register 1
73 wdog_periph_id1: ReadPure<u32>,
74 /// 0xfe8 Peripheral Identification Register 2
75 wdog_periph_id2: ReadPure<u32>,
76 /// 0xfec Peripheral Identification Register 3
77 wdog_periph_id3: ReadPure<u32>,
78 /// 0xff0 PrimeCell Identification Register 0
79 wdog_pcell_id0: ReadPure<u32>,
80 /// 0xff4 PrimeCell Identification Register 1
81 wdog_pcell_id1: ReadPure<u32>,
82 /// 0xff8 PrimeCell Identification Register 2
83 wdog_pcell_id2: ReadPure<u32>,
84 /// 0xffc PrimeCell Identification Register 3
85 wdog_pcell_id3: ReadPure<u32>,
Imre Kiscd007ad2024-08-14 15:53:11 +020086}
87
Imre Kis16eb3162025-03-25 12:51:17 +010088/// SP805 Watchdog driver implementation.
Imre Kis826b8612025-03-12 13:07:15 +010089pub struct Watchdog<'a> {
90 regs: UniqueMmioPointer<'a, SP805Registers>,
Imre Kiscd007ad2024-08-14 15:53:11 +020091 load_value: u32,
92}
93
Imre Kis826b8612025-03-12 13:07:15 +010094impl<'a> Watchdog<'a> {
95 const LOCK: u32 = 0x00000001;
96 const UNLOCK: u32 = 0x1ACCE551;
97
Imre Kiscd007ad2024-08-14 15:53:11 +020098 /// Create new watchdog instance
Imre Kis826b8612025-03-12 13:07:15 +010099 pub fn new(regs: UniqueMmioPointer<'a, SP805Registers>, load_value: u32) -> Self {
Imre Kiscd007ad2024-08-14 15:53:11 +0200100 Self { regs, load_value }
101 }
102
103 /// Enable watchdog
Imre Kise05daa42024-09-26 11:20:06 +0200104 pub fn enable(&mut self) {
105 let load_value = self.load_value;
Imre Kiscd007ad2024-08-14 15:53:11 +0200106
Imre Kis826b8612025-03-12 13:07:15 +0100107 self.with_unlock(|mut regs| {
108 field!(regs, wdog_load).write(load_value);
109 field!(regs, wdog_intclr).write(1);
Imre Kis25115ca2025-03-25 14:05:09 +0100110 field!(regs, wdog_control).write(ControlRegister::INTEN | ControlRegister::RESEN);
Imre Kis826b8612025-03-12 13:07:15 +0100111 });
Imre Kiscd007ad2024-08-14 15:53:11 +0200112 }
113
114 /// Disable watchdog
Imre Kise05daa42024-09-26 11:20:06 +0200115 pub fn disable(&mut self) {
Imre Kis25115ca2025-03-25 14:05:09 +0100116 self.with_unlock(|mut regs| field!(regs, wdog_control).write(ControlRegister::empty()));
Imre Kiscd007ad2024-08-14 15:53:11 +0200117 }
118
119 /// Update watchdog
Imre Kise05daa42024-09-26 11:20:06 +0200120 pub fn update(&mut self) {
121 let load_value = self.load_value;
122
Imre Kis826b8612025-03-12 13:07:15 +0100123 self.with_unlock(|mut regs| field!(regs, wdog_load).write(load_value));
Imre Kiscd007ad2024-08-14 15:53:11 +0200124 }
125
Imre Kis826b8612025-03-12 13:07:15 +0100126 fn with_unlock<F>(&mut self, f: F)
127 where
128 F: FnOnce(&mut UniqueMmioPointer<SP805Registers>),
129 {
130 field!(self.regs, wdog_lock).write(Self::UNLOCK);
131 f(&mut self.regs);
132 field!(self.regs, wdog_lock).write(Self::LOCK);
Imre Kiscd007ad2024-08-14 15:53:11 +0200133 }
134}
Imre Kis16eb3162025-03-25 12:51:17 +0100135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use zerocopy::transmute_mut;
140
Imre Kis25115ca2025-03-25 14:05:09 +0100141 const LOAD_VALUE: u32 = 0xabcd_ef01;
Imre Kis16eb3162025-03-25 12:51:17 +0100142
143 #[repr(align(4096))]
144 pub struct FakeSp805Registers {
145 regs: [u32; 1024],
146 }
147
148 impl FakeSp805Registers {
149 pub fn new() -> Self {
150 Self { regs: [0u32; 1024] }
151 }
152
153 pub fn clear(&mut self) {
154 self.regs.fill(0);
155 }
156
157 pub fn reg_write(&mut self, offset: usize, value: u32) {
158 self.regs[offset / 4] = value;
159 }
160
161 pub fn reg_read(&self, offset: usize) -> u32 {
162 self.regs[offset / 4]
163 }
164
165 fn get(&mut self) -> UniqueMmioPointer<SP805Registers> {
166 UniqueMmioPointer::from(transmute_mut!(&mut self.regs))
167 }
168
169 pub fn system_for_test(&mut self) -> Watchdog {
170 Watchdog::new(self.get(), LOAD_VALUE)
171 }
172 }
173
174 #[test]
175 fn register_block_size() {
176 assert_eq!(0x1000, core::mem::size_of::<SP805Registers>());
177 }
178
179 #[test]
180 fn enable() {
Imre Kis25115ca2025-03-25 14:05:09 +0100181 let mut regs = FakeSp805Registers::new();
Imre Kis16eb3162025-03-25 12:51:17 +0100182
183 {
184 // Enable
185 regs.reg_write(0x0c, 0xffff_ffff);
186 let mut wdt = regs.system_for_test();
187 wdt.enable();
188 }
189
190 assert_eq!(LOAD_VALUE, regs.reg_read(0x00));
191 assert_eq!(0x0000_0003, regs.reg_read(0x08));
192 assert!(regs.reg_read(0x0c) != 0);
193 assert_eq!(Watchdog::LOCK, regs.reg_read(0xc00));
194
195 regs.clear();
196
197 {
198 // Disable
199 regs.reg_write(0x08, 0x0000_0003);
200 let mut wdt = regs.system_for_test();
201 wdt.disable();
202 }
203
204 assert_eq!(0x0000_0000, regs.reg_read(0x08));
205 assert_eq!(Watchdog::LOCK, regs.reg_read(0xc00));
206
207 regs.clear();
208
209 {
210 // Update
211 let mut wdt = regs.system_for_test();
212 wdt.update();
213 }
214
215 assert_eq!(LOAD_VALUE, regs.reg_read(0x00));
216 }
Imre Kis25115ca2025-03-25 14:05:09 +0100217}