| // SPDX-FileCopyrightText: Copyright 2023-2025 Arm Limited and/or its affiliates <open-source-office@arm.com> |
| // SPDX-License-Identifier: MIT OR Apache-2.0 |
| |
| //! Arm Watchdog Module (SP805) driver |
| //! |
| //! Driver implementation for the [SP805 watchdog module](https://developer.arm.com/documentation/ddi0270/latest/). |
| |
| #![no_std] |
| |
| use bitflags::bitflags; |
| use safe_mmio::{ |
| field, |
| fields::{ReadPure, ReadPureWrite, WriteOnly}, |
| UniqueMmioPointer, |
| }; |
| use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; |
| |
| #[repr(transparent)] |
| #[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] |
| struct ControlRegister(u32); |
| |
| #[repr(transparent)] |
| #[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] |
| struct Interrupts(u32); |
| |
| bitflags! { |
| /// Control register |
| impl ControlRegister : u32 { |
| /// Enable Watchdog module reset output |
| const RESEN = 1 << 1; |
| /// Break error |
| const INTEN = 1 << 0; |
| } |
| |
| /// Raw Interrupt Status Register |
| impl Interrupts : u32 { |
| /// Raw interrupt status from the counter |
| const WDOGRIS = 1 << 0; |
| } |
| } |
| |
| /// SP805 register map |
| #[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] |
| #[repr(C, align(4))] |
| pub struct SP805Registers { |
| /// 0x000 Load Register |
| wdog_load: ReadPureWrite<u32>, |
| /// 0x004 Value Register |
| wdog_value: ReadPure<u32>, |
| /// 0x008 Control register |
| wdog_control: ReadPureWrite<ControlRegister>, |
| /// 0x00c Interrupt Clear Register |
| wdog_intclr: WriteOnly<u32>, |
| /// 0x010 Raw Interrupt Status Register |
| wdog_ris: ReadPure<Interrupts>, |
| /// 0x014 Masked Interrupt Status Register |
| wdog_mis: ReadPure<Interrupts>, |
| /// 0x018 - 0xbfc |
| reserved_18: [u32; 762], |
| /// 0xc00 Lock Register |
| wdog_lock: ReadPureWrite<u32>, |
| /// 0xc04 - 0xefc |
| reserved_c04: [u32; 191], |
| /// 0xf00 Integration Test Control Register, |
| wdog_itcr: ReadPureWrite<u32>, |
| /// 0xf04 Integration Test Output Set |
| wdog_itop: WriteOnly<u32>, |
| /// 0xf08 - 0xfdc |
| reserved_f08: [u32; 54], |
| /// 0xfe0 Peripheral Identification Register 0 |
| wdog_periph_id0: ReadPure<u32>, |
| /// 0xfe4 Peripheral Identification Register 1 |
| wdog_periph_id1: ReadPure<u32>, |
| /// 0xfe8 Peripheral Identification Register 2 |
| wdog_periph_id2: ReadPure<u32>, |
| /// 0xfec Peripheral Identification Register 3 |
| wdog_periph_id3: ReadPure<u32>, |
| /// 0xff0 PrimeCell Identification Register 0 |
| wdog_pcell_id0: ReadPure<u32>, |
| /// 0xff4 PrimeCell Identification Register 1 |
| wdog_pcell_id1: ReadPure<u32>, |
| /// 0xff8 PrimeCell Identification Register 2 |
| wdog_pcell_id2: ReadPure<u32>, |
| /// 0xffc PrimeCell Identification Register 3 |
| wdog_pcell_id3: ReadPure<u32>, |
| } |
| |
| /// SP805 Watchdog implementation |
| pub struct Watchdog<'a> { |
| regs: UniqueMmioPointer<'a, SP805Registers>, |
| load_value: u32, |
| } |
| |
| impl<'a> Watchdog<'a> { |
| const LOCK: u32 = 0x00000001; |
| const UNLOCK: u32 = 0x1ACCE551; |
| |
| /// Create new watchdog instance |
| pub fn new(regs: UniqueMmioPointer<'a, SP805Registers>, load_value: u32) -> Self { |
| Self { regs, load_value } |
| } |
| |
| /// Enable watchdog |
| pub fn enable(&mut self) { |
| let load_value = self.load_value; |
| |
| self.with_unlock(|mut regs| { |
| field!(regs, wdog_load).write(load_value); |
| field!(regs, wdog_intclr).write(1); |
| field!(regs, wdog_control) |
| .write(ControlRegister::INTEN | ControlRegister::RESEN); |
| }); |
| } |
| |
| /// Disable watchdog |
| pub fn disable(&mut self) { |
| self.with_unlock(|mut regs| { |
| field!(regs, wdog_control).write(ControlRegister::empty()) |
| }); |
| } |
| |
| /// Update watchdog |
| pub fn update(&mut self) { |
| let load_value = self.load_value; |
| |
| self.with_unlock(|mut regs| field!(regs, wdog_load).write(load_value)); |
| } |
| |
| fn with_unlock<F>(&mut self, f: F) |
| where |
| F: FnOnce(&mut UniqueMmioPointer<SP805Registers>), |
| { |
| field!(self.regs, wdog_lock).write(Self::UNLOCK); |
| f(&mut self.regs); |
| field!(self.regs, wdog_lock).write(Self::LOCK); |
| } |
| } |