Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 1 | // SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates <open-source-office@arm.com> |
| 2 | // 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 Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 10 | use core::{ |
| 11 | ops::{Deref, DerefMut}, |
| 12 | ptr::addr_of_mut, |
| 13 | }; |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 14 | |
| 15 | use bitflags::bitflags; |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 16 | |
| 17 | bitflags! { |
| 18 | /// Control register |
| 19 | #[repr(transparent)] |
| 20 | #[derive(Copy, Clone)] |
| 21 | struct ControlRegister : u32 { |
| 22 | /// Enable Watchdog module reset output |
| 23 | const RESEN = 1 << 1; |
| 24 | /// Break error |
| 25 | const INTEN = 1 << 0; |
| 26 | } |
| 27 | |
| 28 | /// Raw Interrupt Status Register |
| 29 | #[repr(transparent)] |
| 30 | #[derive(Copy, Clone)] |
| 31 | struct RawInterruptStatusRegister : u32 { |
| 32 | /// Raw interrupt status from the counter |
| 33 | const WDOGRIS = 1 << 0; |
| 34 | } |
| 35 | |
| 36 | /// Masked Interrupt Status Register |
| 37 | struct MaskedInterruptStatusRegister : u32 { |
| 38 | /// Enabled interrupt status from the counter |
| 39 | const WDOGMIS = 1 << 0; |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | /// SP805 register map |
| 44 | #[repr(C, align(4))] |
| 45 | pub struct SP805Registers { |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 46 | wdog_load: u32, // 0x000 Load Register |
| 47 | wdog_value: u32, // 0x004 Value Register |
| 48 | wdog_control: u32, // 0x008 Control register |
| 49 | wdog_intclr: u32, // 0x00c Interrupt Clear Register |
| 50 | wdog_ris: u32, // 0x010 Raw Interrupt Status Register |
| 51 | wdog_mis: u32, // 0x014 Masked Interrupt Status Register |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 52 | reserved_18: [u32; 762], // 0x018 - 0xbfc |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 53 | wdog_lock: u32, // 0xc00 Lock Register |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 54 | reserved_c04: [u32; 191], // 0xc04 - 0xefc |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 55 | wdog_itcr: u32, // 0xf00 Integration Test Control Register, |
| 56 | wdog_itop: u32, // 0xf04 Integration Test Output Set |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 57 | reserved_f08: [u32; 54], // 0xf08 - 0xfdc |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 58 | wdog_periph_id0: u32, // 0xfe0 Peripheral Identification Register 0 |
| 59 | wdog_periph_id1: u32, // 0xfe4 Peripheral Identification Register 1 |
| 60 | wdog_periph_id2: u32, // 0xfe8 Peripheral Identification Register 2 |
| 61 | wdog_periph_id3: u32, // 0xfec Peripheral Identification Register 3 |
| 62 | wdog_pcell_id0: u32, // 0xff0 PrimeCell Identification Register 0 |
| 63 | wdog_pcell_id1: u32, // 0xff4 PrimeCell Identification Register 1 |
| 64 | wdog_pcell_id2: u32, // 0xff8 PrimeCell Identification Register 2 |
| 65 | wdog_pcell_id3: u32, // 0xffc PrimeCell Identification Register 3 |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 66 | } |
| 67 | |
| 68 | struct WatchdogUnlockGuard<'a, R> |
| 69 | where |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 70 | R: DerefMut<Target = SP805Registers>, |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 71 | { |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 72 | regs: &'a mut R, |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 73 | } |
| 74 | |
| 75 | impl<'a, R> WatchdogUnlockGuard<'a, R> |
| 76 | where |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 77 | R: DerefMut<Target = SP805Registers>, |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 78 | { |
| 79 | const LOCK: u32 = 0x00000001; |
| 80 | const UNLOCK: u32 = 0x1ACCE551; |
| 81 | |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 82 | pub fn new(regs: &'a mut R) -> Self { |
Imre Kis | 03f96f0 | 2024-10-02 14:11:25 +0200 | [diff] [blame^] | 83 | // SAFETY: regs can be dereferenced as a valid SP805 register block |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 84 | unsafe { |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 85 | addr_of_mut!(regs.wdog_lock).write_volatile(Self::UNLOCK); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 86 | } |
| 87 | Self { regs } |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | impl<'a, R> Deref for WatchdogUnlockGuard<'a, R> |
| 92 | where |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 93 | R: DerefMut<Target = SP805Registers>, |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 94 | { |
| 95 | type Target = R; |
| 96 | |
| 97 | fn deref(&self) -> &Self::Target { |
| 98 | self.regs |
| 99 | } |
| 100 | } |
| 101 | |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 102 | impl<'a, R> DerefMut for WatchdogUnlockGuard<'a, R> |
| 103 | where |
| 104 | R: DerefMut<Target = SP805Registers>, |
| 105 | { |
| 106 | fn deref_mut(&mut self) -> &mut Self::Target { |
| 107 | self.regs |
| 108 | } |
| 109 | } |
| 110 | |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 111 | impl<'a, R> Drop for WatchdogUnlockGuard<'a, R> |
| 112 | where |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 113 | R: DerefMut<Target = SP805Registers>, |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 114 | { |
| 115 | fn drop(&mut self) { |
Imre Kis | 03f96f0 | 2024-10-02 14:11:25 +0200 | [diff] [blame^] | 116 | // SAFETY: self.regs can be dereferenced as a valid SP805 register block |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 117 | unsafe { |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 118 | addr_of_mut!(self.regs.wdog_lock).write_volatile(Self::LOCK); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 119 | } |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | /// SP805 Watchdog implementation |
| 124 | pub struct Watchdog<R> |
| 125 | where |
| 126 | R: Deref<Target = SP805Registers>, |
| 127 | { |
| 128 | regs: R, |
| 129 | load_value: u32, |
| 130 | } |
| 131 | |
| 132 | impl<R> Watchdog<R> |
| 133 | where |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 134 | R: DerefMut<Target = SP805Registers>, |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 135 | { |
| 136 | /// Create new watchdog instance |
| 137 | pub fn new(regs: R, load_value: u32) -> Self { |
| 138 | Self { regs, load_value } |
| 139 | } |
| 140 | |
| 141 | /// Enable watchdog |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 142 | pub fn enable(&mut self) { |
| 143 | let load_value = self.load_value; |
| 144 | let mut regs = self.unlock(); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 145 | |
Imre Kis | 03f96f0 | 2024-10-02 14:11:25 +0200 | [diff] [blame^] | 146 | // SAFETY: self.regs can be dereferenced as a valid SP805 register block |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 147 | unsafe { |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 148 | addr_of_mut!(regs.wdog_load).write_volatile(load_value); |
| 149 | addr_of_mut!(regs.wdog_intclr).write_volatile(1); |
| 150 | addr_of_mut!(regs.wdog_control) |
| 151 | .write_volatile((ControlRegister::INTEN | ControlRegister::RESEN).bits()); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 152 | } |
| 153 | } |
| 154 | |
| 155 | /// Disable watchdog |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 156 | pub fn disable(&mut self) { |
Imre Kis | 03f96f0 | 2024-10-02 14:11:25 +0200 | [diff] [blame^] | 157 | // SAFETY: self.regs can be dereferenced as a valid SP805 register block |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 158 | unsafe { |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 159 | addr_of_mut!(self.unlock().wdog_control) |
| 160 | .write_volatile(ControlRegister::empty().bits()); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 161 | } |
| 162 | } |
| 163 | |
| 164 | /// Update watchdog |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 165 | pub fn update(&mut self) { |
| 166 | let load_value = self.load_value; |
| 167 | |
Imre Kis | 03f96f0 | 2024-10-02 14:11:25 +0200 | [diff] [blame^] | 168 | // SAFETY: self.regs can be dereferenced as a valid SP805 register block |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 169 | unsafe { |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 170 | addr_of_mut!(self.unlock().wdog_load).write_volatile(load_value); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 171 | } |
| 172 | } |
| 173 | |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 174 | fn unlock(&mut self) -> WatchdogUnlockGuard<R> { |
| 175 | WatchdogUnlockGuard::new(&mut self.regs) |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 176 | } |
| 177 | } |