blob: 0126c94febfdded8b04d8e4a84877acda9db6ad7 [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
4//! Arm Watchdog Module (SP805) driver
5//!
6//! Driver implementation for the [SP805 watchdog module](https://developer.arm.com/documentation/ddi0270/latest/).
7
8#![no_std]
9
Imre Kiscd007ad2024-08-14 15:53:11 +020010use bitflags::bitflags;
Imre Kis826b8612025-03-12 13:07:15 +010011use safe_mmio::{
12 field,
13 fields::{ReadPure, ReadPureWrite, WriteOnly},
14 UniqueMmioPointer,
15};
16use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
17
Imre Kis16eb3162025-03-25 12:51:17 +010018/// Control Register
Imre Kis826b8612025-03-12 13:07:15 +010019#[repr(transparent)]
20#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
21struct ControlRegister(u32);
22
Imre Kis16eb3162025-03-25 12:51:17 +010023/// Interrupt register.
Imre Kis826b8612025-03-12 13:07:15 +010024#[repr(transparent)]
25#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
26struct Interrupts(u32);
Imre Kiscd007ad2024-08-14 15:53:11 +020027
28bitflags! {
29 /// Control register
Imre Kis826b8612025-03-12 13:07:15 +010030 impl ControlRegister : u32 {
Imre Kiscd007ad2024-08-14 15:53:11 +020031 /// Enable Watchdog module reset output
32 const RESEN = 1 << 1;
33 /// Break error
34 const INTEN = 1 << 0;
35 }
36
37 /// Raw Interrupt Status Register
Imre Kis826b8612025-03-12 13:07:15 +010038 impl Interrupts : u32 {
Imre Kiscd007ad2024-08-14 15:53:11 +020039 /// Raw interrupt status from the counter
40 const WDOGRIS = 1 << 0;
41 }
Imre Kiscd007ad2024-08-14 15:53:11 +020042}
43
Imre Kis16eb3162025-03-25 12:51:17 +010044/// SP805 register map.
Imre Kis826b8612025-03-12 13:07:15 +010045#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
Imre Kiscd007ad2024-08-14 15:53:11 +020046#[repr(C, align(4))]
47pub struct SP805Registers {
Imre Kis826b8612025-03-12 13:07:15 +010048 /// 0x000 Load Register
49 wdog_load: ReadPureWrite<u32>,
50 /// 0x004 Value Register
51 wdog_value: ReadPure<u32>,
52 /// 0x008 Control register
53 wdog_control: ReadPureWrite<ControlRegister>,
54 /// 0x00c Interrupt Clear Register
55 wdog_intclr: WriteOnly<u32>,
56 /// 0x010 Raw Interrupt Status Register
57 wdog_ris: ReadPure<Interrupts>,
58 /// 0x014 Masked Interrupt Status Register
59 wdog_mis: ReadPure<Interrupts>,
60 /// 0x018 - 0xbfc
61 reserved_18: [u32; 762],
62 /// 0xc00 Lock Register
63 wdog_lock: ReadPureWrite<u32>,
64 /// 0xc04 - 0xefc
65 reserved_c04: [u32; 191],
66 /// 0xf00 Integration Test Control Register,
67 wdog_itcr: ReadPureWrite<u32>,
68 /// 0xf04 Integration Test Output Set
69 wdog_itop: WriteOnly<u32>,
70 /// 0xf08 - 0xfdc
71 reserved_f08: [u32; 54],
72 /// 0xfe0 Peripheral Identification Register 0
73 wdog_periph_id0: ReadPure<u32>,
74 /// 0xfe4 Peripheral Identification Register 1
75 wdog_periph_id1: ReadPure<u32>,
76 /// 0xfe8 Peripheral Identification Register 2
77 wdog_periph_id2: ReadPure<u32>,
78 /// 0xfec Peripheral Identification Register 3
79 wdog_periph_id3: ReadPure<u32>,
80 /// 0xff0 PrimeCell Identification Register 0
81 wdog_pcell_id0: ReadPure<u32>,
82 /// 0xff4 PrimeCell Identification Register 1
83 wdog_pcell_id1: ReadPure<u32>,
84 /// 0xff8 PrimeCell Identification Register 2
85 wdog_pcell_id2: ReadPure<u32>,
86 /// 0xffc PrimeCell Identification Register 3
87 wdog_pcell_id3: ReadPure<u32>,
Imre Kiscd007ad2024-08-14 15:53:11 +020088}
89
Imre Kis16eb3162025-03-25 12:51:17 +010090/// SP805 Watchdog driver implementation.
Imre Kis826b8612025-03-12 13:07:15 +010091pub struct Watchdog<'a> {
92 regs: UniqueMmioPointer<'a, SP805Registers>,
Imre Kiscd007ad2024-08-14 15:53:11 +020093 load_value: u32,
94}
95
Imre Kis826b8612025-03-12 13:07:15 +010096impl<'a> Watchdog<'a> {
97 const LOCK: u32 = 0x00000001;
98 const UNLOCK: u32 = 0x1ACCE551;
99
Imre Kiscd007ad2024-08-14 15:53:11 +0200100 /// Create new watchdog instance
Imre Kis826b8612025-03-12 13:07:15 +0100101 pub fn new(regs: UniqueMmioPointer<'a, SP805Registers>, load_value: u32) -> Self {
Imre Kiscd007ad2024-08-14 15:53:11 +0200102 Self { regs, load_value }
103 }
104
105 /// Enable watchdog
Imre Kise05daa42024-09-26 11:20:06 +0200106 pub fn enable(&mut self) {
107 let load_value = self.load_value;
Imre Kiscd007ad2024-08-14 15:53:11 +0200108
Imre Kis826b8612025-03-12 13:07:15 +0100109 self.with_unlock(|mut regs| {
110 field!(regs, wdog_load).write(load_value);
111 field!(regs, wdog_intclr).write(1);
112 field!(regs, wdog_control)
113 .write(ControlRegister::INTEN | ControlRegister::RESEN);
114 });
Imre Kiscd007ad2024-08-14 15:53:11 +0200115 }
116
117 /// Disable watchdog
Imre Kise05daa42024-09-26 11:20:06 +0200118 pub fn disable(&mut self) {
Imre Kis826b8612025-03-12 13:07:15 +0100119 self.with_unlock(|mut regs| {
120 field!(regs, wdog_control).write(ControlRegister::empty())
121 });
Imre Kiscd007ad2024-08-14 15:53:11 +0200122 }
123
124 /// Update watchdog
Imre Kise05daa42024-09-26 11:20:06 +0200125 pub fn update(&mut self) {
126 let load_value = self.load_value;
127
Imre Kis826b8612025-03-12 13:07:15 +0100128 self.with_unlock(|mut regs| field!(regs, wdog_load).write(load_value));
Imre Kiscd007ad2024-08-14 15:53:11 +0200129 }
130
Imre Kis826b8612025-03-12 13:07:15 +0100131 fn with_unlock<F>(&mut self, f: F)
132 where
133 F: FnOnce(&mut UniqueMmioPointer<SP805Registers>),
134 {
135 field!(self.regs, wdog_lock).write(Self::UNLOCK);
136 f(&mut self.regs);
137 field!(self.regs, wdog_lock).write(Self::LOCK);
Imre Kiscd007ad2024-08-14 15:53:11 +0200138 }
139}
Imre Kis16eb3162025-03-25 12:51:17 +0100140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use zerocopy::transmute_mut;
145
146 const LOAD_VALUE : u32 = 0xabcd_ef01;
147
148 #[repr(align(4096))]
149 pub struct FakeSp805Registers {
150 regs: [u32; 1024],
151 }
152
153 impl FakeSp805Registers {
154 pub fn new() -> Self {
155 Self { regs: [0u32; 1024] }
156 }
157
158 pub fn clear(&mut self) {
159 self.regs.fill(0);
160 }
161
162 pub fn reg_write(&mut self, offset: usize, value: u32) {
163 self.regs[offset / 4] = value;
164 }
165
166 pub fn reg_read(&self, offset: usize) -> u32 {
167 self.regs[offset / 4]
168 }
169
170 fn get(&mut self) -> UniqueMmioPointer<SP805Registers> {
171 UniqueMmioPointer::from(transmute_mut!(&mut self.regs))
172 }
173
174 pub fn system_for_test(&mut self) -> Watchdog {
175 Watchdog::new(self.get(), LOAD_VALUE)
176 }
177 }
178
179 #[test]
180 fn register_block_size() {
181 assert_eq!(0x1000, core::mem::size_of::<SP805Registers>());
182 }
183
184 #[test]
185 fn enable() {
186 let mut regs = FakeSp805Registers::new();
187
188 {
189 // Enable
190 regs.reg_write(0x0c, 0xffff_ffff);
191 let mut wdt = regs.system_for_test();
192 wdt.enable();
193 }
194
195 assert_eq!(LOAD_VALUE, regs.reg_read(0x00));
196 assert_eq!(0x0000_0003, regs.reg_read(0x08));
197 assert!(regs.reg_read(0x0c) != 0);
198 assert_eq!(Watchdog::LOCK, regs.reg_read(0xc00));
199
200 regs.clear();
201
202 {
203 // Disable
204 regs.reg_write(0x08, 0x0000_0003);
205 let mut wdt = regs.system_for_test();
206 wdt.disable();
207 }
208
209 assert_eq!(0x0000_0000, regs.reg_read(0x08));
210 assert_eq!(Watchdog::LOCK, regs.reg_read(0xc00));
211
212 regs.clear();
213
214 {
215 // Update
216 let mut wdt = regs.system_for_test();
217 wdt.update();
218 }
219
220 assert_eq!(LOAD_VALUE, regs.reg_read(0x00));
221 }
222}