blob: c99684e0326ee983a5e2876221c737bffb411ea5 [file] [log] [blame]
// SPDX-FileCopyrightText: Copyright The arm-generic-timer Contributors.
// SPDX-License-Identifier: MIT OR Apache-2.0
#![no_std]
#![doc = include_str!("../README.md")]
#![deny(clippy::undocumented_unsafe_blocks)]
#![deny(unsafe_op_in_unsafe_fn)]
use core::time::Duration;
use bitflags::bitflags;
use safe_mmio::{
field, field_shared,
fields::{ReadPure, ReadPureWrite},
UniqueMmioPointer,
};
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
/// Counter Control Register
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
pub struct CntCr(u32);
/// Counter Status Register
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
pub struct CntSr(u32);
/// Counter Identification Register.
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
pub struct CntId(u32);
/// Counter-timer Access Control Register.
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
pub struct CntAcr(u32);
/// Timer feature bits, defined at I5.7.16 CNTTIDR, Counter-timer Timer ID Register description.
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
pub struct Features(u8);
/// Counter-timer EL0 Access Control Register.
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
pub struct CntEl0Acr(u32);
/// Common control register of the physical and virtual timers. Defined at I5.7.10 CNTP_CTL,
/// Counter-timer Physical Timer Control and at CNTV_CTL, Counter-timer Virtual Timer Control.
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
pub struct TimerControl(u32);
bitflags! {
impl CntCr: u32 {
/// Scaling is enabled. This bit depends on the presence of FEAT_CNTSC.
const SCEN = 1 << 2;
/// Halt-on-debug.
const HDBG = 1 << 1;
/// System counter enabled.
const EN = 1 << 0;
}
impl CntSr: u32 {
/// Halt-on-debug.
const HDBG = 1 << 1;
}
impl CntAcr: u32 {
/// Read/write access to the EL1 Physical Timer registers
const RWPT = 1 << 5;
/// Read/write access to the Virtual Timer registers
const RWVT = 1 << 4;
/// Read-only access to CNTVOFF
const RVOFF = 1 << 3;
/// Read-only access to CNTFRQ
const RFRQ = 1 << 2;
/// Read-only access to CNTVCT
const RVCT = 1 << 1;
/// Read-only access to CNTPCT
const RPCT = 1 << 0;
}
impl Features: u8 {
/// Frame<n> has a second view, CNTEL0Base<n>.
const CNTEL0BASE = 1 << 2;
/// Frame<n> has virtual capability. The virtual time and offset registers are implemented.
const VIRTUAL = 1 << 1;
/// Frame<n> is implemented.
const IMPLEMENTED = 1 << 0;
}
impl CntEl0Acr: u32 {
/// Second view read access control for CNTP_CVAL, CNTP_TVAL, and CNTP_CTL.
const EL0PTEN = 1 << 9;
/// Second view read access control for CNTV_CVAL, CNTV_TVAL, and CNTV_CTL.
const EL0VTEN = 1 << 8;
/// Second view read access control for CNTVCT and CNTFRQ.
const EL0VCTEN = 1 << 1;
/// Second view read access control for CNTPCT and CNTFRQ.
const EL0PCTEN = 1 << 0;
}
impl TimerControl: u32 {
/// Timer condition is met.
const ISTATUS = 1 << 2;
/// Timer interrupt is masked.
const IMASK = 1 << 1;
/// Timer enabled.
const ENABLE = 1 << 0;
}
}
impl CntCr {
const FCREQ_MASK: u32 = 0x0000_03ff;
const FCREQ_SHIFT: u32 = 8;
/// Write FCREQ field of the register.
pub fn set_fcreq(&mut self, index: usize) {
let mut value = self.0 & !(Self::FCREQ_MASK << Self::FCREQ_SHIFT);
value |= ((index as u32) & Self::FCREQ_MASK) << Self::FCREQ_SHIFT;
self.0 = value;
}
}
impl CntSr {
const FCACK_MASK: u32 = 0x0000_03ff;
const FCACK_SHIFT: u32 = 8;
/// Read FCACK field of the register.
pub fn fcack(&self) -> usize {
((self.0 >> Self::FCACK_SHIFT) & Self::FCACK_MASK) as usize
}
}
impl CntId {
const CNTSC_MASK: u32 = 0b1111;
const CNTSC_IMPLEMENTED: u32 = 0b0001;
/// Indicates whether Counter Scaling is implemented.
pub fn scaling_implemented(&self) -> bool {
self.0 & Self::CNTSC_MASK == Self::CNTSC_IMPLEMENTED
}
}
/// Table I2-1 CNTControlBase memory map
#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
#[repr(C, align(4))]
pub struct CntControlBase {
/// 0x000 Counter Control Register
cntcr: ReadPureWrite<CntCr>,
/// 0x004 Counter Status Register
cntsr: ReadPure<CntSr>,
/// 0x008 Counter Count Value register
cntcv: ReadPureWrite<u64>,
/// 0x010 Counter Counter Scale register
cntscr: ReadPureWrite<u32>,
reserved_14: [u32; 2],
/// 0x01c Counter ID register
cntid: ReadPure<CntId>,
/// 0x020 Counter Frequency IDs
cntfid: [ReadPureWrite<u32>; 40],
/// 0x0c0 Implementation defined
impdef_0c0: [u32; 16],
reserved_100: [u32; 948],
/// 0xfd0 Counter ID registers
counter_id: [ReadPure<u32>; 12],
}
/// Table I2-2 CNTReadBase memory map
#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
#[repr(C, align(4))]
pub struct CntReadBase {
/// 0x000 Counter Count Value register
cntcv: ReadPure<u64>,
reserved_8: [u32; 1010],
/// 0xfd0 Counter ID registers
counter_id: [ReadPure<u32>; 12],
}
/// Table I2-3 CNTCTLBase memory map
#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
#[repr(C, align(4))]
pub struct CntCtlBase {
/// 0x000 Counter-timer Frequency
cntfrq: ReadPureWrite<u32>,
/// 0x004 Counter-timer Non-secure Access Register
cntnsar: ReadPureWrite<u32>,
/// 0x008 Counter-timer Timer ID Register
cnttidr: ReadPure<u32>,
reserved_00c: [u32; 13],
/// 0x040 Counter-timer Access Control Registers
cntacr: [ReadPureWrite<CntAcr>; 8],
reserved_060: [u32; 8],
/// 0x080 Counter-timer Virtual Offsets
cntvoff: [ReadPureWrite<u64>; 8],
reserved_0c0: [u32; 16],
/// 0x100 Implementation defined
impdef_100: [u32; 448],
reserved_800: [u32; 496],
impdef_fc0: [u32; 4],
/// 0xfd0 Counter ID registers
counter_id: [ReadPure<u32>; 12],
}
/// Repeated subset of register that describe a physical or virtual timer in the CntBase or
/// CntEl0Base blocks.
#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
#[repr(C, align(4))]
pub struct TimerRegs {
/// 0x000 Counter-timer Timer CompareValue
cval: ReadPureWrite<u64>,
/// 0x008 Counter-timer Timer TimerValue
tval: ReadPureWrite<u32>,
/// 0x00c Counter-timer Timer Control
ctl: ReadPureWrite<TimerControl>,
}
/// Table I2-4 CNTBaseN memory map
#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
#[repr(C, align(4))]
pub struct CntBase {
/// 0x000 Counter-timer Physical Count
cntpct: ReadPure<u64>,
/// 0x008 Counter-timer Virtual Count
cntvct: ReadPure<u64>,
/// 0x010 Counter-timer Frequency
cntfrq: ReadPure<u32>,
/// 0x014 Counter-timer EL0 Access Control Register
cntel0acr: ReadPureWrite<CntEl0Acr>,
/// 0x018 Counter-timer Virtual Offset
cntvoff: ReadPure<u64>,
/// 0x020-0x02c Physical timer block
cntp: TimerRegs,
/// 0x030-0x03c Virtual timer block
cntv: TimerRegs,
reserved: [u32; 996],
/// 0xfd0 Counter ID registers
counter_id: [ReadPure<u32>; 12],
}
/// CntEl0Base frame is identical to the CntBase frame, except that CNTVOFF, CNTEL0ACR registers are
/// never visible and CNTEL0ACR of the corresponding CntBase controls the access of the physical and
/// virtual timer registers.
#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
#[repr(C, align(4))]
pub struct CntEl0Base {
/// 0x000 Counter-timer Physical Count
cntpct: ReadPure<u64>,
/// 0x008 Counter-timer Virtual Count
cntvct: ReadPure<u64>,
/// 0x010 Counter-timer Frequency
cntfrq: ReadPure<u32>,
reserved_014: [u32; 3],
/// 0x020-0x02c Physical timer block
cntp: TimerRegs,
/// 0x030-0x03c Virtual timer block
cntv: TimerRegs,
reserved: [u32; 996],
/// 0xfd0 Counter ID registers
counter_id: [ReadPure<u32>; 12],
}
/// Driver for the CNTControlBase block.
pub struct GenericTimerControl<'a> {
regs: UniqueMmioPointer<'a, CntControlBase>,
}
impl<'a> GenericTimerControl<'a> {
/// Creates new instance.
pub fn new(regs: UniqueMmioPointer<'a, CntControlBase>) -> Self {
Self { regs }
}
/// Enables or disables the timer.
pub fn set_enable(&mut self, enable: bool) {
let mut cntcr = field!(self.regs, cntcr).read();
cntcr.set(CntCr::EN, enable);
field!(self.regs, cntcr).write(cntcr);
}
/// Sets the number of the entry in the Frequency modes table to select.
pub fn request_frequency(&mut self, index: usize) {
let mut cntcr = field!(self.regs, cntcr).read();
cntcr.set_fcreq(index);
field!(self.regs, cntcr).write(cntcr);
}
/// Gets currently selected entry index in the Frequency modes table.
pub fn frequency_index(&self) -> usize {
field_shared!(self.regs, cntsr).read().fcack()
}
/// Gets timer count value.
pub fn count(&self) -> u64 {
field_shared!(self.regs, cntcv).read()
}
/// Sets timer count value.
pub fn set_count(&mut self, count: u64) {
field!(self.regs, cntcv).write(count);
}
/// Checks whether scaling is implemented by the timer.
pub fn scaling_implemented(&self) -> bool {
field_shared!(self.regs, cntid).read().scaling_implemented()
}
/// Gets scale value.
pub fn scale(&self) -> u32 {
field_shared!(self.regs, cntscr).read()
}
/// Sets scale and enable scaling.
pub fn enable_scaling(&mut self, scale: u32) {
field!(self.regs, cntscr).write(scale);
let cntcr = field!(self.regs, cntcr).read();
field!(self.regs, cntcr).write(cntcr | CntCr::SCEN);
}
/// Disables scaling.
pub fn disable_scaling(&mut self) {
let cntcr = field!(self.regs, cntcr).read();
field!(self.regs, cntcr).write(cntcr - CntCr::SCEN);
field!(self.regs, cntscr).write(0);
}
/// Indicates the base frequency of the system counter in Hz.
pub fn base_frequency(&self) -> u32 {
field_shared!(self.regs, cntfid).get(0).unwrap().read()
}
/// Gets frequency mode of the given index in Hz. The availablity of the frequency mode is
/// implementation defined.
pub fn frequency_mode(&self, index: usize) -> Option<u32> {
let frequency = field_shared!(self.regs, cntfid).get(index).unwrap().read();
if frequency != 0 {
Some(frequency)
} else {
None
}
}
/// Sets frequency mode of the given index. The availablity of the frequency mode is
/// implementation defined.
pub fn set_frequency_mode(&mut self, index: usize, frequency: u32) {
field!(self.regs, cntfid)
.get(index)
.unwrap()
.write(frequency)
}
}
/// Driver for the CNTCTLBase block.
pub struct GenericTimerCtl<'a> {
regs: UniqueMmioPointer<'a, CntCtlBase>,
}
impl<'a> GenericTimerCtl<'a> {
/// Creates new instance.
pub fn new(regs: UniqueMmioPointer<'a, CntCtlBase>) -> Self {
Self { regs }
}
/// Gets counter frequency in Hz.
pub fn frequency(&self) -> u32 {
field_shared!(self.regs, cntfrq).read()
}
/// Sets counter frequency in Hz.
pub fn set_frequency(&mut self, frequency: u32) {
field!(self.regs, cntfrq).write(frequency);
}
/// Gets non-secure access state.
pub fn non_secure_access(&self, index: usize) -> bool {
assert!(index < 8);
let cntnsar = field_shared!(self.regs, cntnsar).read();
cntnsar & (1 << index) != 0
}
/// Provides the highest-level control of whether frames CNTBaseN and CNTEL0BaseN are accessible
/// by Non-secure accesses.
pub fn set_non_secure_access(&mut self, index: usize, enable: bool) {
assert!(index < 8);
let mut cntnsar = field_shared!(self.regs, cntnsar).read();
if enable {
cntnsar |= 1 << index;
} else {
cntnsar &= !(1 << index);
}
field!(self.regs, cntnsar).write(cntnsar);
}
/// Queries features of the timer.
pub fn features(&self, index: usize) -> Features {
assert!(index < 8);
let cnttidr = field_shared!(self.regs, cnttidr).read();
Features::from_bits_truncate((cnttidr >> (index * 8)) as u8)
}
/// Gets current top-level access controls for the elements of a timer frame.
pub fn access_control(&self, index: usize) -> CntAcr {
field_shared!(self.regs, cntacr).get(index).unwrap().read()
}
/// Sets top-level access controls for the elements of a timer frame.
pub fn set_access_control(&mut self, index: usize, cntacr: CntAcr) {
field!(self.regs, cntacr).get(index).unwrap().write(cntacr);
}
/// Gets the 64-bit virtual offset for frame CNTBase.
pub fn virtual_offset(&self, index: usize) -> u64 {
field_shared!(self.regs, cntvoff).get(index).unwrap().read()
}
/// Sets the 64-bit virtual offset for frame CNTBase. This is the offset between real time
/// and virtual time.
pub fn set_virtual_offset(&mut self, index: usize, offset: u64) {
field!(self.regs, cntvoff).get(index).unwrap().write(offset);
}
}
/// Driver for the physical or virtual timer instance of the CNTBase block.
pub struct Timer<'a> {
regs: UniqueMmioPointer<'a, TimerRegs>,
frequency: u32,
}
impl<'a> Timer<'a> {
/// Creates new instance.
pub fn new(regs: UniqueMmioPointer<'a, TimerRegs>, frequency: u32) -> Self {
Self { regs, frequency }
}
/// Sets up timer to generate an interrupt after the given duration.
///
/// # Safety
///
/// The system must be prepared to take an interrupt. The vector table has to be set and the
/// interrupt controller must be configured properly.
pub unsafe fn generate_interrupt_after(&mut self, duration: Duration) {
self.set_deadline(duration);
self.set_control(TimerControl::ENABLE);
}
/// Disables the timer and masks the interrupt.
pub fn cancel_interrupt(&mut self) {
self.set_control(TimerControl::IMASK);
}
/// Blocking waits for a duration.
pub fn wait(&mut self, duration: Duration) {
self.set_deadline(duration);
self.set_control(TimerControl::ENABLE | TimerControl::IMASK);
while !self.control().contains(TimerControl::ISTATUS) {
core::hint::spin_loop();
}
}
/// Sets the compare register to trigger after the given duration.
fn set_deadline(&mut self, duration: Duration) {
let increment = self.frequency as u64 * duration.as_micros() as u64 / 1_000_000;
let value = field!(self.regs, cval).read();
field!(self.regs, cval).write(value + increment);
}
/// Reads CTL register.
fn control(&self) -> TimerControl {
field_shared!(self.regs, ctl).read()
}
/// Sets CTL register.
fn set_control(&mut self, control: TimerControl) {
field!(self.regs, ctl).write(control)
}
}
/// Driver for the CNTBase timer block.
pub struct GenericTimerCnt<'a> {
regs: UniqueMmioPointer<'a, CntBase>,
}
impl<'a> GenericTimerCnt<'a> {
/// Creates new instance.
pub fn new(regs: UniqueMmioPointer<'a, CntBase>) -> Self {
Self { regs }
}
/// Gets physical count.
pub fn physical_count(&self) -> u64 {
field_shared!(self.regs, cntpct).read()
}
/// Gets virtual count.
pub fn virtual_count(&self) -> u64 {
field_shared!(self.regs, cntvct).read()
}
/// Gets frequency in Hz.
pub fn frequency(&self) -> u32 {
field_shared!(self.regs, cntfrq).read()
}
/// Gets second view access rights.
pub fn el0_access(&self) -> CntEl0Acr {
field_shared!(self.regs, cntel0acr).read()
}
/// Sets second view access rights.
pub fn set_el0_access(&mut self, value: CntEl0Acr) {
field!(self.regs, cntel0acr).write(value)
}
/// Gets the 64-bit virtual offset for frame CNTBase.
pub fn virtual_offset(&self) -> u64 {
field_shared!(self.regs, cntvoff).read()
}
/// Gets physical timer.
pub fn physical_timer(&mut self) -> Timer<'_> {
let frequency = self.frequency();
Timer::new(field!(self.regs, cntp), frequency)
}
/// Gets virtual timer.
pub fn virtual_timer(&mut self) -> Timer<'_> {
let frequency = self.frequency();
Timer::new(field!(self.regs, cntv), frequency)
}
}
/// Driver for the CNTEL0Base timer block.
pub struct GenericTimerCntEl0<'a> {
regs: UniqueMmioPointer<'a, CntEl0Base>,
}
impl<'a> GenericTimerCntEl0<'a> {
/// Creates new instance.
pub fn new(regs: UniqueMmioPointer<'a, CntEl0Base>) -> Self {
Self { regs }
}
/// Gets physical count.
pub fn physical_count(&self) -> u64 {
field_shared!(self.regs, cntpct).read()
}
/// Gets virtual count.
pub fn virtual_count(&self) -> u64 {
field_shared!(self.regs, cntvct).read()
}
/// Gets frequency in Hz.
pub fn frequency(&self) -> u32 {
field_shared!(self.regs, cntfrq).read()
}
/// Gets physical timer.
pub fn physical_timer(&mut self) -> Timer<'_> {
let frequency = self.frequency();
Timer::new(field!(self.regs, cntp), frequency)
}
/// Gets virtual timer.
pub fn virtual_timer(&mut self) -> Timer<'_> {
let frequency = self.frequency();
Timer::new(field!(self.regs, cntv), frequency)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sizes() {
assert_eq!(0x1000, core::mem::size_of::<CntControlBase>());
assert_eq!(0x1000, core::mem::size_of::<CntReadBase>());
assert_eq!(0x1000, core::mem::size_of::<CntCtlBase>());
assert_eq!(0x1000, core::mem::size_of::<CntBase>());
assert_eq!(0x1000, core::mem::size_of::<CntEl0Base>());
}
}