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