blob: 2540880cdcf31efda306f95eb8d485676edaf840 [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 Kisaa1e7ef2025-04-01 14:48:21 +02008//! ## Example
9//! ```rust
10//! use arm_sp805::{SP805Registers, Watchdog, UniqueMmioPointer};
11//! use core::ptr::NonNull;
12//! # use zerocopy::transmute_mut;
13//! # let mut fake_registers = [0u32; 1024];
14//! # let WATCHDOG_ADDRESS : *mut SP805Registers = transmute_mut!(&mut fake_registers);
15//! # fn handler() {}
16//!
17//! // SAFETY: `WATCHDOG_ADDRESS` is the base address of a SP805 watchdog register block. It remains
18//! // valid for the lifetime of the application and nothing else references this address range.
19//! let watchdog_pointer = unsafe { UniqueMmioPointer::new(NonNull::new(WATCHDOG_ADDRESS).unwrap()) };
20//!
21//! let mut watchdog = Watchdog::new(watchdog_pointer, 0x0001_0000);
22//! watchdog.enable();
23//!
24//! loop {
25//! handler();
26//! watchdog.update();
27//! # break
28//! }
29//! ```
30
Imre Kiscd007ad2024-08-14 15:53:11 +020031use bitflags::bitflags;
Imre Kis25115ca2025-03-25 14:05:09 +010032pub use safe_mmio::UniqueMmioPointer;
Imre Kis826b8612025-03-12 13:07:15 +010033use safe_mmio::{
34 field,
35 fields::{ReadPure, ReadPureWrite, WriteOnly},
Imre Kis826b8612025-03-12 13:07:15 +010036};
37use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
38
Imre Kis16eb3162025-03-25 12:51:17 +010039/// Control Register
Imre Kis826b8612025-03-12 13:07:15 +010040#[repr(transparent)]
41#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
42struct ControlRegister(u32);
43
Imre Kis16eb3162025-03-25 12:51:17 +010044/// Interrupt register.
Imre Kis826b8612025-03-12 13:07:15 +010045#[repr(transparent)]
46#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
47struct Interrupts(u32);
Imre Kiscd007ad2024-08-14 15:53:11 +020048
49bitflags! {
50 /// Control register
Imre Kis826b8612025-03-12 13:07:15 +010051 impl ControlRegister : u32 {
Imre Kiscd007ad2024-08-14 15:53:11 +020052 /// Enable Watchdog module reset output
53 const RESEN = 1 << 1;
54 /// Break error
55 const INTEN = 1 << 0;
56 }
57
58 /// Raw Interrupt Status Register
Imre Kis826b8612025-03-12 13:07:15 +010059 impl Interrupts : u32 {
Imre Kiscd007ad2024-08-14 15:53:11 +020060 /// Raw interrupt status from the counter
61 const WDOGRIS = 1 << 0;
62 }
Imre Kiscd007ad2024-08-14 15:53:11 +020063}
64
Imre Kis25115ca2025-03-25 14:05:09 +010065/// SP805 Watchdog register map.
Imre Kis826b8612025-03-12 13:07:15 +010066#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
Imre Kiscd007ad2024-08-14 15:53:11 +020067#[repr(C, align(4))]
68pub struct SP805Registers {
Imre Kis826b8612025-03-12 13:07:15 +010069 /// 0x000 Load Register
70 wdog_load: ReadPureWrite<u32>,
71 /// 0x004 Value Register
72 wdog_value: ReadPure<u32>,
73 /// 0x008 Control register
74 wdog_control: ReadPureWrite<ControlRegister>,
75 /// 0x00c Interrupt Clear Register
76 wdog_intclr: WriteOnly<u32>,
77 /// 0x010 Raw Interrupt Status Register
78 wdog_ris: ReadPure<Interrupts>,
79 /// 0x014 Masked Interrupt Status Register
80 wdog_mis: ReadPure<Interrupts>,
81 /// 0x018 - 0xbfc
82 reserved_18: [u32; 762],
83 /// 0xc00 Lock Register
84 wdog_lock: ReadPureWrite<u32>,
85 /// 0xc04 - 0xefc
86 reserved_c04: [u32; 191],
87 /// 0xf00 Integration Test Control Register,
88 wdog_itcr: ReadPureWrite<u32>,
89 /// 0xf04 Integration Test Output Set
90 wdog_itop: WriteOnly<u32>,
91 /// 0xf08 - 0xfdc
92 reserved_f08: [u32; 54],
93 /// 0xfe0 Peripheral Identification Register 0
94 wdog_periph_id0: ReadPure<u32>,
95 /// 0xfe4 Peripheral Identification Register 1
96 wdog_periph_id1: ReadPure<u32>,
97 /// 0xfe8 Peripheral Identification Register 2
98 wdog_periph_id2: ReadPure<u32>,
99 /// 0xfec Peripheral Identification Register 3
100 wdog_periph_id3: ReadPure<u32>,
101 /// 0xff0 PrimeCell Identification Register 0
102 wdog_pcell_id0: ReadPure<u32>,
103 /// 0xff4 PrimeCell Identification Register 1
104 wdog_pcell_id1: ReadPure<u32>,
105 /// 0xff8 PrimeCell Identification Register 2
106 wdog_pcell_id2: ReadPure<u32>,
107 /// 0xffc PrimeCell Identification Register 3
108 wdog_pcell_id3: ReadPure<u32>,
Imre Kiscd007ad2024-08-14 15:53:11 +0200109}
110
Imre Kis16eb3162025-03-25 12:51:17 +0100111/// SP805 Watchdog driver implementation.
Imre Kis826b8612025-03-12 13:07:15 +0100112pub struct Watchdog<'a> {
113 regs: UniqueMmioPointer<'a, SP805Registers>,
Imre Kiscd007ad2024-08-14 15:53:11 +0200114 load_value: u32,
115}
116
Imre Kis826b8612025-03-12 13:07:15 +0100117impl<'a> Watchdog<'a> {
118 const LOCK: u32 = 0x00000001;
119 const UNLOCK: u32 = 0x1ACCE551;
120
Imre Kiscd007ad2024-08-14 15:53:11 +0200121 /// Create new watchdog instance
Imre Kis826b8612025-03-12 13:07:15 +0100122 pub fn new(regs: UniqueMmioPointer<'a, SP805Registers>, load_value: u32) -> Self {
Imre Kiscd007ad2024-08-14 15:53:11 +0200123 Self { regs, load_value }
124 }
125
126 /// Enable watchdog
Imre Kise05daa42024-09-26 11:20:06 +0200127 pub fn enable(&mut self) {
128 let load_value = self.load_value;
Imre Kiscd007ad2024-08-14 15:53:11 +0200129
Imre Kis826b8612025-03-12 13:07:15 +0100130 self.with_unlock(|mut regs| {
131 field!(regs, wdog_load).write(load_value);
132 field!(regs, wdog_intclr).write(1);
Imre Kis25115ca2025-03-25 14:05:09 +0100133 field!(regs, wdog_control).write(ControlRegister::INTEN | ControlRegister::RESEN);
Imre Kis826b8612025-03-12 13:07:15 +0100134 });
Imre Kiscd007ad2024-08-14 15:53:11 +0200135 }
136
137 /// Disable watchdog
Imre Kise05daa42024-09-26 11:20:06 +0200138 pub fn disable(&mut self) {
Imre Kis25115ca2025-03-25 14:05:09 +0100139 self.with_unlock(|mut regs| field!(regs, wdog_control).write(ControlRegister::empty()));
Imre Kiscd007ad2024-08-14 15:53:11 +0200140 }
141
142 /// Update watchdog
Imre Kise05daa42024-09-26 11:20:06 +0200143 pub fn update(&mut self) {
144 let load_value = self.load_value;
145
Imre Kis826b8612025-03-12 13:07:15 +0100146 self.with_unlock(|mut regs| field!(regs, wdog_load).write(load_value));
Imre Kiscd007ad2024-08-14 15:53:11 +0200147 }
148
Imre Kis826b8612025-03-12 13:07:15 +0100149 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);
Imre Kiscd007ad2024-08-14 15:53:11 +0200156 }
157}
Imre Kis16eb3162025-03-25 12:51:17 +0100158
159#[cfg(test)]
160mod tests {
161 use super::*;
162 use zerocopy::transmute_mut;
163
Imre Kis25115ca2025-03-25 14:05:09 +0100164 const LOAD_VALUE: u32 = 0xabcd_ef01;
Imre Kis16eb3162025-03-25 12:51:17 +0100165
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() {
Imre Kis25115ca2025-03-25 14:05:09 +0100204 let mut regs = FakeSp805Registers::new();
Imre Kis16eb3162025-03-25 12:51:17 +0100205
206 {
207 // Enable
208 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 // Disable
222 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 // Update
234 let mut wdt = regs.system_for_test();
235 wdt.update();
236 }
237
238 assert_eq!(LOAD_VALUE, regs.reg_read(0x00));
239 }
Imre Kis25115ca2025-03-25 14:05:09 +0100240}