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 | |
| 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 | ae5c740 | 2025-01-10 17:57:58 +0100 | [diff] [blame^] | 10 | use core::ops::{Deref, DerefMut}; |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 11 | |
| 12 | use bitflags::bitflags; |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 13 | |
| 14 | bitflags! { |
| 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))] |
| 42 | pub struct SP805Registers { |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 43 | 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 Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 49 | reserved_18: [u32; 762], // 0x018 - 0xbfc |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 50 | wdog_lock: u32, // 0xc00 Lock Register |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 51 | reserved_c04: [u32; 191], // 0xc04 - 0xefc |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 52 | wdog_itcr: u32, // 0xf00 Integration Test Control Register, |
| 53 | wdog_itop: u32, // 0xf04 Integration Test Output Set |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 54 | reserved_f08: [u32; 54], // 0xf08 - 0xfdc |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 55 | 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 Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 63 | } |
| 64 | |
| 65 | struct WatchdogUnlockGuard<'a, R> |
| 66 | where |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 67 | R: DerefMut<Target = SP805Registers>, |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 68 | { |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 69 | regs: &'a mut R, |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 70 | } |
| 71 | |
| 72 | impl<'a, R> WatchdogUnlockGuard<'a, R> |
| 73 | where |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 74 | R: DerefMut<Target = SP805Registers>, |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 75 | { |
| 76 | const LOCK: u32 = 0x00000001; |
| 77 | const UNLOCK: u32 = 0x1ACCE551; |
| 78 | |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 79 | pub fn new(regs: &'a mut R) -> Self { |
Imre Kis | 03f96f0 | 2024-10-02 14:11:25 +0200 | [diff] [blame] | 80 | // SAFETY: regs can be dereferenced as a valid SP805 register block |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 81 | unsafe { |
Imre Kis | ae5c740 | 2025-01-10 17:57:58 +0100 | [diff] [blame^] | 82 | (&raw mut regs.wdog_lock).write_volatile(Self::UNLOCK); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 83 | } |
| 84 | Self { regs } |
| 85 | } |
| 86 | } |
| 87 | |
Imre Kis | ae5c740 | 2025-01-10 17:57:58 +0100 | [diff] [blame^] | 88 | impl<R> Deref for WatchdogUnlockGuard<'_, R> |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 89 | where |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 90 | R: DerefMut<Target = SP805Registers>, |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 91 | { |
| 92 | type Target = R; |
| 93 | |
| 94 | fn deref(&self) -> &Self::Target { |
| 95 | self.regs |
| 96 | } |
| 97 | } |
| 98 | |
Imre Kis | ae5c740 | 2025-01-10 17:57:58 +0100 | [diff] [blame^] | 99 | impl<R> DerefMut for WatchdogUnlockGuard<'_, R> |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 100 | where |
| 101 | R: DerefMut<Target = SP805Registers>, |
| 102 | { |
| 103 | fn deref_mut(&mut self) -> &mut Self::Target { |
| 104 | self.regs |
| 105 | } |
| 106 | } |
| 107 | |
Imre Kis | ae5c740 | 2025-01-10 17:57:58 +0100 | [diff] [blame^] | 108 | impl<R> Drop for WatchdogUnlockGuard<'_, R> |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 109 | where |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 110 | R: DerefMut<Target = SP805Registers>, |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 111 | { |
| 112 | fn drop(&mut self) { |
Imre Kis | 03f96f0 | 2024-10-02 14:11:25 +0200 | [diff] [blame] | 113 | // SAFETY: self.regs can be dereferenced as a valid SP805 register block |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 114 | unsafe { |
Imre Kis | ae5c740 | 2025-01-10 17:57:58 +0100 | [diff] [blame^] | 115 | (&raw mut self.regs.wdog_lock).write_volatile(Self::LOCK); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 116 | } |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | /// SP805 Watchdog implementation |
| 121 | pub struct Watchdog<R> |
| 122 | where |
| 123 | R: Deref<Target = SP805Registers>, |
| 124 | { |
| 125 | regs: R, |
| 126 | load_value: u32, |
| 127 | } |
| 128 | |
| 129 | impl<R> Watchdog<R> |
| 130 | where |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 131 | R: DerefMut<Target = SP805Registers>, |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 132 | { |
| 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 Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 139 | pub fn enable(&mut self) { |
| 140 | let load_value = self.load_value; |
| 141 | let mut regs = self.unlock(); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 142 | |
Imre Kis | 03f96f0 | 2024-10-02 14:11:25 +0200 | [diff] [blame] | 143 | // SAFETY: self.regs can be dereferenced as a valid SP805 register block |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 144 | unsafe { |
Imre Kis | ae5c740 | 2025-01-10 17:57:58 +0100 | [diff] [blame^] | 145 | (&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 Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 148 | .write_volatile((ControlRegister::INTEN | ControlRegister::RESEN).bits()); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 149 | } |
| 150 | } |
| 151 | |
| 152 | /// Disable watchdog |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 153 | pub fn disable(&mut self) { |
Imre Kis | 03f96f0 | 2024-10-02 14:11:25 +0200 | [diff] [blame] | 154 | // SAFETY: self.regs can be dereferenced as a valid SP805 register block |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 155 | unsafe { |
Imre Kis | ae5c740 | 2025-01-10 17:57:58 +0100 | [diff] [blame^] | 156 | (&raw mut self.unlock().wdog_control).write_volatile(ControlRegister::empty().bits()); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 157 | } |
| 158 | } |
| 159 | |
| 160 | /// Update watchdog |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 161 | pub fn update(&mut self) { |
| 162 | let load_value = self.load_value; |
| 163 | |
Imre Kis | 03f96f0 | 2024-10-02 14:11:25 +0200 | [diff] [blame] | 164 | // SAFETY: self.regs can be dereferenced as a valid SP805 register block |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 165 | unsafe { |
Imre Kis | ae5c740 | 2025-01-10 17:57:58 +0100 | [diff] [blame^] | 166 | (&raw mut self.unlock().wdog_load).write_volatile(load_value); |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 167 | } |
| 168 | } |
| 169 | |
Imre Kis | e05daa4 | 2024-09-26 11:20:06 +0200 | [diff] [blame] | 170 | fn unlock(&mut self) -> WatchdogUnlockGuard<R> { |
| 171 | WatchdogUnlockGuard::new(&mut self.regs) |
Imre Kis | cd007ad | 2024-08-14 15:53:11 +0200 | [diff] [blame] | 172 | } |
| 173 | } |