blob: da2994d2e8bb4abc5abdaecc504952b1dff648a2 [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 Kisae5c7402025-01-10 17:57:58 +010010use core::ops::{Deref, DerefMut};
Imre Kiscd007ad2024-08-14 15:53:11 +020011
12use bitflags::bitflags;
Imre Kiscd007ad2024-08-14 15:53:11 +020013
14bitflags! {
15 /// Control register
16 #[repr(transparent)]
17 #[derive(Copy, Clone)]
18 struct ControlRegister : u32 {
19 /// Enable Watchdog module reset output
20 const RESEN = 1 << 1;
21 /// Break error
22 const INTEN = 1 << 0;
23 }
24
25 /// Raw Interrupt Status Register
26 #[repr(transparent)]
27 #[derive(Copy, Clone)]
28 struct RawInterruptStatusRegister : u32 {
29 /// Raw interrupt status from the counter
30 const WDOGRIS = 1 << 0;
31 }
32
33 /// Masked Interrupt Status Register
34 struct MaskedInterruptStatusRegister : u32 {
35 /// Enabled interrupt status from the counter
36 const WDOGMIS = 1 << 0;
37 }
38}
39
40/// SP805 register map
41#[repr(C, align(4))]
42pub struct SP805Registers {
Imre Kise05daa42024-09-26 11:20:06 +020043 wdog_load: u32, // 0x000 Load Register
44 wdog_value: u32, // 0x004 Value Register
45 wdog_control: u32, // 0x008 Control register
46 wdog_intclr: u32, // 0x00c Interrupt Clear Register
47 wdog_ris: u32, // 0x010 Raw Interrupt Status Register
48 wdog_mis: u32, // 0x014 Masked Interrupt Status Register
Imre Kiscd007ad2024-08-14 15:53:11 +020049 reserved_18: [u32; 762], // 0x018 - 0xbfc
Imre Kise05daa42024-09-26 11:20:06 +020050 wdog_lock: u32, // 0xc00 Lock Register
Imre Kiscd007ad2024-08-14 15:53:11 +020051 reserved_c04: [u32; 191], // 0xc04 - 0xefc
Imre Kise05daa42024-09-26 11:20:06 +020052 wdog_itcr: u32, // 0xf00 Integration Test Control Register,
53 wdog_itop: u32, // 0xf04 Integration Test Output Set
Imre Kiscd007ad2024-08-14 15:53:11 +020054 reserved_f08: [u32; 54], // 0xf08 - 0xfdc
Imre Kise05daa42024-09-26 11:20:06 +020055 wdog_periph_id0: u32, // 0xfe0 Peripheral Identification Register 0
56 wdog_periph_id1: u32, // 0xfe4 Peripheral Identification Register 1
57 wdog_periph_id2: u32, // 0xfe8 Peripheral Identification Register 2
58 wdog_periph_id3: u32, // 0xfec Peripheral Identification Register 3
59 wdog_pcell_id0: u32, // 0xff0 PrimeCell Identification Register 0
60 wdog_pcell_id1: u32, // 0xff4 PrimeCell Identification Register 1
61 wdog_pcell_id2: u32, // 0xff8 PrimeCell Identification Register 2
62 wdog_pcell_id3: u32, // 0xffc PrimeCell Identification Register 3
Imre Kiscd007ad2024-08-14 15:53:11 +020063}
64
65struct WatchdogUnlockGuard<'a, R>
66where
Imre Kise05daa42024-09-26 11:20:06 +020067 R: DerefMut<Target = SP805Registers>,
Imre Kiscd007ad2024-08-14 15:53:11 +020068{
Imre Kise05daa42024-09-26 11:20:06 +020069 regs: &'a mut R,
Imre Kiscd007ad2024-08-14 15:53:11 +020070}
71
72impl<'a, R> WatchdogUnlockGuard<'a, R>
73where
Imre Kise05daa42024-09-26 11:20:06 +020074 R: DerefMut<Target = SP805Registers>,
Imre Kiscd007ad2024-08-14 15:53:11 +020075{
76 const LOCK: u32 = 0x00000001;
77 const UNLOCK: u32 = 0x1ACCE551;
78
Imre Kise05daa42024-09-26 11:20:06 +020079 pub fn new(regs: &'a mut R) -> Self {
Imre Kis03f96f02024-10-02 14:11:25 +020080 // SAFETY: regs can be dereferenced as a valid SP805 register block
Imre Kiscd007ad2024-08-14 15:53:11 +020081 unsafe {
Imre Kisae5c7402025-01-10 17:57:58 +010082 (&raw mut regs.wdog_lock).write_volatile(Self::UNLOCK);
Imre Kiscd007ad2024-08-14 15:53:11 +020083 }
84 Self { regs }
85 }
86}
87
Imre Kisae5c7402025-01-10 17:57:58 +010088impl<R> Deref for WatchdogUnlockGuard<'_, R>
Imre Kiscd007ad2024-08-14 15:53:11 +020089where
Imre Kise05daa42024-09-26 11:20:06 +020090 R: DerefMut<Target = SP805Registers>,
Imre Kiscd007ad2024-08-14 15:53:11 +020091{
92 type Target = R;
93
94 fn deref(&self) -> &Self::Target {
95 self.regs
96 }
97}
98
Imre Kisae5c7402025-01-10 17:57:58 +010099impl<R> DerefMut for WatchdogUnlockGuard<'_, R>
Imre Kise05daa42024-09-26 11:20:06 +0200100where
101 R: DerefMut<Target = SP805Registers>,
102{
103 fn deref_mut(&mut self) -> &mut Self::Target {
104 self.regs
105 }
106}
107
Imre Kisae5c7402025-01-10 17:57:58 +0100108impl<R> Drop for WatchdogUnlockGuard<'_, R>
Imre Kiscd007ad2024-08-14 15:53:11 +0200109where
Imre Kise05daa42024-09-26 11:20:06 +0200110 R: DerefMut<Target = SP805Registers>,
Imre Kiscd007ad2024-08-14 15:53:11 +0200111{
112 fn drop(&mut self) {
Imre Kis03f96f02024-10-02 14:11:25 +0200113 // SAFETY: self.regs can be dereferenced as a valid SP805 register block
Imre Kiscd007ad2024-08-14 15:53:11 +0200114 unsafe {
Imre Kisae5c7402025-01-10 17:57:58 +0100115 (&raw mut self.regs.wdog_lock).write_volatile(Self::LOCK);
Imre Kiscd007ad2024-08-14 15:53:11 +0200116 }
117 }
118}
119
120/// SP805 Watchdog implementation
121pub struct Watchdog<R>
122where
123 R: Deref<Target = SP805Registers>,
124{
125 regs: R,
126 load_value: u32,
127}
128
129impl<R> Watchdog<R>
130where
Imre Kise05daa42024-09-26 11:20:06 +0200131 R: DerefMut<Target = SP805Registers>,
Imre Kiscd007ad2024-08-14 15:53:11 +0200132{
133 /// Create new watchdog instance
134 pub fn new(regs: R, load_value: u32) -> Self {
135 Self { regs, load_value }
136 }
137
138 /// Enable watchdog
Imre Kise05daa42024-09-26 11:20:06 +0200139 pub fn enable(&mut self) {
140 let load_value = self.load_value;
141 let mut regs = self.unlock();
Imre Kiscd007ad2024-08-14 15:53:11 +0200142
Imre Kis03f96f02024-10-02 14:11:25 +0200143 // SAFETY: self.regs can be dereferenced as a valid SP805 register block
Imre Kiscd007ad2024-08-14 15:53:11 +0200144 unsafe {
Imre Kisae5c7402025-01-10 17:57:58 +0100145 (&raw mut regs.wdog_load).write_volatile(load_value);
146 (&raw mut regs.wdog_intclr).write_volatile(1);
147 (&raw mut regs.wdog_control)
Imre Kise05daa42024-09-26 11:20:06 +0200148 .write_volatile((ControlRegister::INTEN | ControlRegister::RESEN).bits());
Imre Kiscd007ad2024-08-14 15:53:11 +0200149 }
150 }
151
152 /// Disable watchdog
Imre Kise05daa42024-09-26 11:20:06 +0200153 pub fn disable(&mut self) {
Imre Kis03f96f02024-10-02 14:11:25 +0200154 // SAFETY: self.regs can be dereferenced as a valid SP805 register block
Imre Kiscd007ad2024-08-14 15:53:11 +0200155 unsafe {
Imre Kisae5c7402025-01-10 17:57:58 +0100156 (&raw mut self.unlock().wdog_control).write_volatile(ControlRegister::empty().bits());
Imre Kiscd007ad2024-08-14 15:53:11 +0200157 }
158 }
159
160 /// Update watchdog
Imre Kise05daa42024-09-26 11:20:06 +0200161 pub fn update(&mut self) {
162 let load_value = self.load_value;
163
Imre Kis03f96f02024-10-02 14:11:25 +0200164 // SAFETY: self.regs can be dereferenced as a valid SP805 register block
Imre Kiscd007ad2024-08-14 15:53:11 +0200165 unsafe {
Imre Kisae5c7402025-01-10 17:57:58 +0100166 (&raw mut self.unlock().wdog_load).write_volatile(load_value);
Imre Kiscd007ad2024-08-14 15:53:11 +0200167 }
168 }
169
Imre Kise05daa42024-09-26 11:20:06 +0200170 fn unlock(&mut self) -> WatchdogUnlockGuard<R> {
171 WatchdogUnlockGuard::new(&mut self.regs)
Imre Kiscd007ad2024-08-14 15:53:11 +0200172 }
173}