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 | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 8 | use bitflags::bitflags; |
Imre Kis | 25115ca | 2025-03-25 14:05:09 +0100 | [diff] [blame^] | 9 | pub use safe_mmio::UniqueMmioPointer; |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 10 | use safe_mmio::{ |
| 11 | field, |
| 12 | fields::{ReadPure, ReadPureWrite, WriteOnly}, |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 13 | }; |
| 14 | use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; |
| 15 | |
Imre Kis | 16eb316 | 2025-03-25 12:51:17 +0100 | [diff] [blame] | 16 | /// Control Register |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 17 | #[repr(transparent)] |
| 18 | #[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] |
| 19 | struct ControlRegister(u32); |
| 20 | |
Imre Kis | 16eb316 | 2025-03-25 12:51:17 +0100 | [diff] [blame] | 21 | /// Interrupt register. |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 22 | #[repr(transparent)] |
| 23 | #[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] |
| 24 | struct Interrupts(u32); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 25 | |
| 26 | bitflags! { |
| 27 | /// Control register |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 28 | impl ControlRegister : u32 { |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 29 | /// 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 Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 36 | impl Interrupts : u32 { |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 37 | /// Raw interrupt status from the counter |
| 38 | const WDOGRIS = 1 << 0; |
| 39 | } |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 40 | } |
| 41 | |
Imre Kis | 25115ca | 2025-03-25 14:05:09 +0100 | [diff] [blame^] | 42 | /// SP805 Watchdog register map. |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 43 | #[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 44 | #[repr(C, align(4))] |
| 45 | pub struct SP805Registers { |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 46 | /// 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 Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 86 | } |
| 87 | |
Imre Kis | 16eb316 | 2025-03-25 12:51:17 +0100 | [diff] [blame] | 88 | /// SP805 Watchdog driver implementation. |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 89 | pub struct Watchdog<'a> { |
| 90 | regs: UniqueMmioPointer<'a, SP805Registers>, |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 91 | load_value: u32, |
| 92 | } |
| 93 | |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 94 | impl<'a> Watchdog<'a> { |
| 95 | const LOCK: u32 = 0x00000001; |
| 96 | const UNLOCK: u32 = 0x1ACCE551; |
| 97 | |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 98 | /// Create new watchdog instance |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 99 | pub fn new(regs: UniqueMmioPointer<'a, SP805Registers>, load_value: u32) -> Self { |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 100 | Self { regs, load_value } |
| 101 | } |
| 102 | |
| 103 | /// Enable watchdog |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 104 | pub fn enable(&mut self) { |
| 105 | let load_value = self.load_value; |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 106 | |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 107 | self.with_unlock(|mut regs| { |
| 108 | field!(regs, wdog_load).write(load_value); |
| 109 | field!(regs, wdog_intclr).write(1); |
Imre Kis | 25115ca | 2025-03-25 14:05:09 +0100 | [diff] [blame^] | 110 | field!(regs, wdog_control).write(ControlRegister::INTEN | ControlRegister::RESEN); |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 111 | }); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 112 | } |
| 113 | |
| 114 | /// Disable watchdog |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 115 | pub fn disable(&mut self) { |
Imre Kis | 25115ca | 2025-03-25 14:05:09 +0100 | [diff] [blame^] | 116 | self.with_unlock(|mut regs| field!(regs, wdog_control).write(ControlRegister::empty())); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 117 | } |
| 118 | |
| 119 | /// Update watchdog |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 120 | pub fn update(&mut self) { |
| 121 | let load_value = self.load_value; |
| 122 | |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 123 | self.with_unlock(|mut regs| field!(regs, wdog_load).write(load_value)); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 124 | } |
| 125 | |
Imre Kis | 826b861 | 2025-03-12 13:07:15 +0100 | [diff] [blame] | 126 | 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 Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 133 | } |
| 134 | } |
Imre Kis | 16eb316 | 2025-03-25 12:51:17 +0100 | [diff] [blame] | 135 | |
| 136 | #[cfg(test)] |
| 137 | mod tests { |
| 138 | use super::*; |
| 139 | use zerocopy::transmute_mut; |
| 140 | |
Imre Kis | 25115ca | 2025-03-25 14:05:09 +0100 | [diff] [blame^] | 141 | const LOAD_VALUE: u32 = 0xabcd_ef01; |
Imre Kis | 16eb316 | 2025-03-25 12:51:17 +0100 | [diff] [blame] | 142 | |
| 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 Kis | 25115ca | 2025-03-25 14:05:09 +0100 | [diff] [blame^] | 181 | let mut regs = FakeSp805Registers::new(); |
Imre Kis | 16eb316 | 2025-03-25 12:51:17 +0100 | [diff] [blame] | 182 | |
| 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 Kis | 25115ca | 2025-03-25 14:05:09 +0100 | [diff] [blame^] | 217 | } |