lowrisc/aon_timer.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.
//! AON/Watchdog Timer Driver
use kernel::platform;
use kernel::utilities::registers::interfaces::{Readable, Writeable};
use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite, WriteOnly};
use kernel::utilities::StaticRef;
// Based on the latest commit of OpenTitan supported by tock:
// Refer: https://github.com/lowRISC/opentitan/blob/217a0168ba118503c166a9587819e3811eeb0c0c/hw/ip/aon_timer/rtl/aon_timer_reg_pkg.sv#L136
register_structs! {
pub AonTimerRegisters {
//AON_TIMER: Alert Test Register
(0x000 => alert_test: WriteOnly<u32, ALERT_TEST::Register>),
//AON_TIMER: Wakeup Timer Control Register
(0x004 => wkup_ctrl: ReadWrite<u32, WKUP_CTRL::Register>),
//AON_TIMER: Wakeup Timer Threshold Register
(0x008 => wkup_thold: ReadWrite<u32, THRESHOLD::Register>),
//AON_TIMER: Wakeup Timer Count Register
(0x00C => wkup_count: ReadWrite<u32, WKUP_COUNT::Register>),
//AON_TIMER: Watchdog Timer Write Enable Register [rw0c]
(0x010 => wdog_regwen: ReadWrite<u32, WDOG_REGWEN::Register>),
//AON_TIMER: Watchdog Timer Control register
(0x014 => wdog_ctrl: ReadWrite<u32, WDOG_CTRL::Register>),
//AON_TIMER: Watchdog Timer Bark Threshold Register
(0x018 => wdog_bark_thold: ReadWrite<u32, THRESHOLD::Register>),
//AON_TIMER: Watchdog Timer Bite Threshold Register
(0x01C => wdog_bite_thold: ReadWrite<u32, THRESHOLD::Register>),
//AON_TIMER: Watchdog Timer Count Register
(0x020 => wdog_count: ReadWrite<u32, WDOG_COUNT::Register>),
//AON_TIMER: Interrupt State Register [rw1c]
(0x024 => intr_state: ReadWrite<u32, INTR::Register>),
//AON_TIMER: Interrupt Test Reigster
(0x028 => intr_test: WriteOnly<u32, INTR::Register>),
//AON_TIMER: Wakeup Request Status [rw0c]
(0x02C => wkup_cause: ReadWrite<u32, WKUP_CAUSE::Register>),
(0x030 => @END),
}
}
register_bitfields![u32,
ALERT_TEST[
FATAL_FAULT OFFSET(0) NUMBITS(1) []
],
WKUP_CTRL[
ENABLE OFFSET(0) NUMBITS(1) [],
PRESCALER OFFSET(1) NUMBITS(12) []
],
THRESHOLD[
THRESHOLD OFFSET(0) NUMBITS(32) []
],
WKUP_COUNT[
COUNT OFFSET(0) NUMBITS(32) []
],
WDOG_REGWEN[
REGWEN OFFSET(0) NUMBITS(1) []
],
WDOG_CTRL[
ENABLE OFFSET(0) NUMBITS(1) [],
PAUSE_IN_SLEEP OFFSET(1) NUMBITS(1) []
],
WDOG_COUNT[
COUNT OFFSET(0) NUMBITS(32) [],
],
INTR[
WKUP_TIMER_EXPIRED OFFSET(0) NUMBITS(1) [],
WDOG_TIMER_BARK OFFSET(1) NUMBITS(1) []
],
WKUP_CAUSE[
CAUSE OFFSET(0) NUMBITS(1) [],
]
];
pub struct AonTimer {
registers: StaticRef<AonTimerRegisters>,
aon_clk_freq: u32, //Hz, this differs for FPGA/Verilator
}
impl AonTimer {
pub const fn new(base: StaticRef<AonTimerRegisters>, aon_clk_freq: u32) -> AonTimer {
AonTimer {
registers: base,
aon_clk_freq,
}
}
/// Reset both watch dog and wake up timer count values.
fn reset_timers(&self) {
let regs = self.registers;
regs.wkup_count.set(0x00);
regs.wdog_count.set(0x00);
}
/// Start the watchdog counter with pause in sleep
/// i.e wdog timer is paused when system is sleeping
fn wdog_start_count(&self) {
self.registers
.wdog_ctrl
.write(WDOG_CTRL::ENABLE::SET + WDOG_CTRL::PAUSE_IN_SLEEP::SET);
}
/// Program the desired thresholds in WKUP_THOLD, WDOG_BARK_THOLD and WDOG_BITE_THOLD
fn set_wdog_thresh(&self) {
let regs = self.registers;
// Watchdog period may need to be revised with kernel changes/updates
// since the watchdog is `tickled()` at the start of every kernel loop
// see: https://github.com/tock/tock/blob/eb3f7ce59434b7ac1b77ef1ab7dd2afad1a62ac5/kernel/src/kernel.rs#L448
let bark_cycles = self.ms_to_cycles(500);
// ~1000ms bite period
let bite_cycles = bark_cycles.saturating_mul(2);
regs.wdog_bark_thold
.write(THRESHOLD::THRESHOLD.val(bark_cycles));
regs.wdog_bite_thold
.write(THRESHOLD::THRESHOLD.val(bite_cycles));
}
// Reset watch dog timer
fn wdog_pet(&self) {
self.registers.wdog_count.set(0x00);
}
fn wdog_suspend(&self) {
self.registers.wdog_ctrl.write(WDOG_CTRL::ENABLE::CLEAR);
}
fn wdog_resume(&self) {
self.registers.wdog_ctrl.write(WDOG_CTRL::ENABLE::SET);
}
/// Locks further config to WDOG until next system reset
fn lock_wdog_conf(&self) {
self.registers.wdog_regwen.write(WDOG_REGWEN::REGWEN::SET)
}
/// Convert microseconds to cycles
fn ms_to_cycles(&self, ms: u32) -> u32 {
// 250kHZ CW130 or 125kHz Verilator (as specified in chip config)
ms.saturating_mul(self.aon_clk_freq).saturating_div(1000)
}
fn reset_wkup_count(&self) {
self.registers.wkup_count.set(0x00);
}
pub fn handle_interrupt(&self) {
let regs = self.registers;
let intr = self.registers.intr_state.extract();
if intr.is_set(INTR::WKUP_TIMER_EXPIRED) {
// Wake up timer has expired, sw must ack and clear
regs.wkup_cause.set(0x00);
regs.wkup_count.set(0x00); // To avoid re-triggers
self.reset_wkup_count();
// RW1C, clear the interrupt
regs.intr_state.write(INTR::WKUP_TIMER_EXPIRED::SET);
}
if intr.is_set(INTR::WDOG_TIMER_BARK) {
// Clear the bark (RW1C) and pet doggo
regs.intr_state.write(INTR::WDOG_TIMER_BARK::SET);
self.wdog_pet();
}
}
}
impl platform::watchdog::WatchDog for AonTimer {
/// The always-on timer will run on a ~125KHz (Verilator) or ~250kHz clock.
/// The timers themselves are 32b wide, giving a maximum timeout
/// window of roughly ~6 hours. For the wakeup timer, the pre-scaler
/// extends the maximum timeout to ~1000 days.
///
/// The AON HW_IP has a watchdog and a wake-up timer (counts independantly of eachother),
/// although struct `AonTimer` implements the wakeup timer functionality,
/// we only start and use the watchdog in the code below.
fn setup(&self) {
// 1. Clear Timers
self.reset_timers();
// 2. Set thresholds.
self.set_wdog_thresh();
// 3. Commence gaurd duty...
self.wdog_start_count();
// 4. Lock watchdog config
// Preventing firmware from accidentally or maliciously disabling the watchdog,
// until next system reset.
self.lock_wdog_conf();
}
fn tickle(&self) {
// Nothing to worry about, good dog...
self.wdog_pet();
}
fn suspend(&self) {
self.wdog_suspend();
}
fn resume(&self) {
self.wdog_resume();
}
}