Imre Kis | ae5c740 | 2025-01-10 17:57:58 +0100 | [diff] [blame] | 1 | // SPDX-FileCopyrightText: Copyright 2023-2025 Arm Limited and/or its affiliates <open-source-office@arm.com> |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 2 | // SPDX-License-Identifier: MIT OR Apache-2.0 |
| 3 | |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 4 | #![no_std] |
Imre Kis | 25115ca | 2025-03-25 14:05:09 +0100 | [diff] [blame] | 5 | #![doc = include_str!("../README.md")] |
| 6 | #![deny(clippy::undocumented_unsafe_blocks)] |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 7 | |
Imre Kis | aa1e7ef | 2025-04-01 14:48:21 +0200 | [diff] [blame^] | 8 | //! ## 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 Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 31 | use bitflags::bitflags; |
Imre Kis | 25115ca | 2025-03-25 14:05:09 +0100 | [diff] [blame] | 32 | pub use safe_mmio::UniqueMmioPointer; |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 33 | use safe_mmio::{ |
| 34 | field, |
| 35 | fields::{ReadPure, ReadPureWrite, WriteOnly}, |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 36 | }; |
| 37 | use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; |
| 38 | |
Imre Kis | 16eb316 | 2025-03-25 12:51:17 +0100 | [diff] [blame] | 39 | /// Control Register |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 40 | #[repr(transparent)] |
| 41 | #[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] |
| 42 | struct ControlRegister(u32); |
| 43 | |
Imre Kis | 16eb316 | 2025-03-25 12:51:17 +0100 | [diff] [blame] | 44 | /// Interrupt register. |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 45 | #[repr(transparent)] |
| 46 | #[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] |
| 47 | struct Interrupts(u32); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 48 | |
| 49 | bitflags! { |
| 50 | /// Control register |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 51 | impl ControlRegister : u32 { |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 52 | /// 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 Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 59 | impl Interrupts : u32 { |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 60 | /// Raw interrupt status from the counter |
| 61 | const WDOGRIS = 1 << 0; |
| 62 | } |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 63 | } |
| 64 | |
Imre Kis | 25115ca | 2025-03-25 14:05:09 +0100 | [diff] [blame] | 65 | /// SP805 Watchdog register map. |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 66 | #[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 67 | #[repr(C, align(4))] |
| 68 | pub struct SP805Registers { |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 69 | /// 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 Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 109 | } |
| 110 | |
Imre Kis | 16eb316 | 2025-03-25 12:51:17 +0100 | [diff] [blame] | 111 | /// SP805 Watchdog driver implementation. |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 112 | pub struct Watchdog<'a> { |
| 113 | regs: UniqueMmioPointer<'a, SP805Registers>, |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 114 | load_value: u32, |
| 115 | } |
| 116 | |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 117 | impl<'a> Watchdog<'a> { |
| 118 | const LOCK: u32 = 0x00000001; |
| 119 | const UNLOCK: u32 = 0x1ACCE551; |
| 120 | |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 121 | /// Create new watchdog instance |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 122 | pub fn new(regs: UniqueMmioPointer<'a, SP805Registers>, load_value: u32) -> Self { |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 123 | Self { regs, load_value } |
| 124 | } |
| 125 | |
| 126 | /// Enable watchdog |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 127 | pub fn enable(&mut self) { |
| 128 | let load_value = self.load_value; |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 129 | |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 130 | self.with_unlock(|mut regs| { |
| 131 | field!(regs, wdog_load).write(load_value); |
| 132 | field!(regs, wdog_intclr).write(1); |
Imre Kis | 25115ca | 2025-03-25 14:05:09 +0100 | [diff] [blame] | 133 | field!(regs, wdog_control).write(ControlRegister::INTEN | ControlRegister::RESEN); |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 134 | }); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 135 | } |
| 136 | |
| 137 | /// Disable watchdog |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 138 | pub fn disable(&mut self) { |
Imre Kis | 25115ca | 2025-03-25 14:05:09 +0100 | [diff] [blame] | 139 | self.with_unlock(|mut regs| field!(regs, wdog_control).write(ControlRegister::empty())); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 140 | } |
| 141 | |
| 142 | /// Update watchdog |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 143 | pub fn update(&mut self) { |
| 144 | let load_value = self.load_value; |
| 145 | |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 146 | self.with_unlock(|mut regs| field!(regs, wdog_load).write(load_value)); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 147 | } |
| 148 | |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 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); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 156 | } |
| 157 | } |
Imre Kis | 16eb316 | 2025-03-25 12:51:17 +0100 | [diff] [blame] | 158 | |
| 159 | #[cfg(test)] |
| 160 | mod tests { |
| 161 | use super::*; |
| 162 | use zerocopy::transmute_mut; |
| 163 | |
Imre Kis | 25115ca | 2025-03-25 14:05:09 +0100 | [diff] [blame] | 164 | const LOAD_VALUE: u32 = 0xabcd_ef01; |
Imre Kis | 16eb316 | 2025-03-25 12:51:17 +0100 | [diff] [blame] | 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() { |
Imre Kis | 25115ca | 2025-03-25 14:05:09 +0100 | [diff] [blame] | 204 | let mut regs = FakeSp805Registers::new(); |
Imre Kis | 16eb316 | 2025-03-25 12:51:17 +0100 | [diff] [blame] | 205 | |
| 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 Kis | 25115ca | 2025-03-25 14:05:09 +0100 | [diff] [blame] | 240 | } |