use crate::{Timer, TimerInterface};
use bitflags::bitflags;
use safe_mmio::{
UniqueMmioPointer, field, field_shared,
fields::{ReadPure, ReadPureWrite},
};
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
pub struct CntCr(u32);
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
pub struct CntSr(u32);
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
pub struct CntId(u32);
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
pub struct CntAcr(u32);
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
pub struct Features(u8);
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
pub struct CntEl0Acr(u32);
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
pub struct TimerControl(u32);
bitflags! {
impl CntCr: u32 {
const SCEN = 1 << 2;
const HDBG = 1 << 1;
const EN = 1 << 0;
}
impl CntSr: u32 {
const HDBG = 1 << 1;
}
impl CntAcr: u32 {
const RWPT = 1 << 5;
const RWVT = 1 << 4;
const RVOFF = 1 << 3;
const RFRQ = 1 << 2;
const RVCT = 1 << 1;
const RPCT = 1 << 0;
}
impl Features: u8 {
const CNTEL0BASE = 1 << 2;
const VIRTUAL = 1 << 1;
const IMPLEMENTED = 1 << 0;
}
impl CntEl0Acr: u32 {
const EL0PTEN = 1 << 9;
const EL0VTEN = 1 << 8;
const EL0VCTEN = 1 << 1;
const EL0PCTEN = 1 << 0;
}
impl TimerControl: u32 {
const ISTATUS = 1 << 2;
const IMASK = 1 << 1;
const ENABLE = 1 << 0;
}
}
impl CntCr {
const FCREQ_MASK: u32 = 0x0000_03ff;
const FCREQ_SHIFT: u32 = 8;
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;
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;
pub fn scaling_implemented(&self) -> bool {
self.0 & Self::CNTSC_MASK == Self::CNTSC_IMPLEMENTED
}
}
#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
#[repr(C, align(4))]
pub struct CntControlBase {
cntcr: ReadPureWrite<CntCr>,
cntsr: ReadPure<CntSr>,
cntcv: ReadPureWrite<u64>,
cntscr: ReadPureWrite<u32>,
reserved_14: [u32; 2],
cntid: ReadPure<CntId>,
cntfid: [ReadPureWrite<u32>; 40],
impdef_0c0: [u32; 16],
reserved_100: [u32; 948],
counter_id: [ReadPure<u32>; 12],
}
#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
#[repr(C, align(4))]
pub struct CntReadBase {
cntcv: ReadPure<u64>,
reserved_8: [u32; 1010],
counter_id: [ReadPure<u32>; 12],
}
#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
#[repr(C, align(4))]
pub struct CntCtlBase {
cntfrq: ReadPureWrite<u32>,
cntnsar: ReadPureWrite<u32>,
cnttidr: ReadPure<u32>,
reserved_00c: [u32; 13],
cntacr: [ReadPureWrite<CntAcr>; 8],
reserved_060: [u32; 8],
cntvoff: [ReadPureWrite<u64>; 8],
reserved_0c0: [u32; 16],
impdef_100: [u32; 448],
reserved_800: [u32; 496],
impdef_fc0: [u32; 4],
counter_id: [ReadPure<u32>; 12],
}
#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
#[repr(C, align(4))]
pub struct TimerRegs {
cval: ReadPureWrite<u64>,
tval: ReadPureWrite<u32>,
ctl: ReadPureWrite<TimerControl>,
}
#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
#[repr(C, align(4))]
pub struct CntBase {
cntpct: ReadPure<u64>,
cntvct: ReadPure<u64>,
cntfrq: ReadPure<u32>,
cntel0acr: ReadPureWrite<CntEl0Acr>,
cntvoff: ReadPure<u64>,
cntp: TimerRegs,
cntv: TimerRegs,
reserved: [u32; 996],
counter_id: [ReadPure<u32>; 12],
}
#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
#[repr(C, align(4))]
pub struct CntEl0Base {
cntpct: ReadPure<u64>,
cntvct: ReadPure<u64>,
cntfrq: ReadPure<u32>,
reserved_014: [u32; 3],
cntp: TimerRegs,
cntv: TimerRegs,
reserved: [u32; 996],
counter_id: [ReadPure<u32>; 12],
}
pub struct GenericTimerControl<'a> {
regs: UniqueMmioPointer<'a, CntControlBase>,
}
impl<'a> GenericTimerControl<'a> {
pub fn new(regs: UniqueMmioPointer<'a, CntControlBase>) -> Self {
Self { regs }
}
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);
}
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);
}
pub fn frequency_index(&self) -> usize {
field_shared!(self.regs, cntsr).read().fcack()
}
pub fn count(&self) -> u64 {
field_shared!(self.regs, cntcv).read()
}
pub fn set_count(&mut self, count: u64) {
field!(self.regs, cntcv).write(count);
}
pub fn scaling_implemented(&self) -> bool {
field_shared!(self.regs, cntid).read().scaling_implemented()
}
pub fn scale(&self) -> u32 {
field_shared!(self.regs, cntscr).read()
}
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);
}
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);
}
pub fn base_frequency(&self) -> u32 {
field_shared!(self.regs, cntfid).get(0).unwrap().read()
}
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
}
}
pub fn set_frequency_mode(&mut self, index: usize, frequency: u32) {
field!(self.regs, cntfid)
.get(index)
.unwrap()
.write(frequency)
}
}
pub struct GenericTimerCtl<'a> {
regs: UniqueMmioPointer<'a, CntCtlBase>,
}
impl<'a> GenericTimerCtl<'a> {
pub fn new(regs: UniqueMmioPointer<'a, CntCtlBase>) -> Self {
Self { regs }
}
pub fn frequency(&self) -> u32 {
field_shared!(self.regs, cntfrq).read()
}
pub fn set_frequency(&mut self, frequency: u32) {
field!(self.regs, cntfrq).write(frequency);
}
pub fn non_secure_access(&self, index: usize) -> bool {
assert!(index < 8);
let cntnsar = field_shared!(self.regs, cntnsar).read();
cntnsar & (1 << index) != 0
}
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);
}
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)
}
pub fn access_control(&self, index: usize) -> CntAcr {
field_shared!(self.regs, cntacr).get(index).unwrap().read()
}
pub fn set_access_control(&mut self, index: usize, cntacr: CntAcr) {
field!(self.regs, cntacr).get(index).unwrap().write(cntacr);
}
pub fn virtual_offset(&self, index: usize) -> u64 {
field_shared!(self.regs, cntvoff).get(index).unwrap().read()
}
pub fn set_virtual_offset(&mut self, index: usize, offset: u64) {
field!(self.regs, cntvoff).get(index).unwrap().write(offset);
}
}
pub struct MmioTimer<'a> {
regs: UniqueMmioPointer<'a, TimerRegs>,
frequency: u32,
}
impl<'a> TimerInterface for MmioTimer<'a> {
fn enable(&mut self) {
let control = field_shared!(self.regs, ctl).read();
field!(self.regs, ctl).write(control | TimerControl::ENABLE);
}
fn timer_value(&self) -> u32 {
field_shared!(self.regs, tval).read()
}
fn frequency(&self) -> u32 {
self.frequency
}
}
pub struct GenericTimerCnt<'a> {
regs: UniqueMmioPointer<'a, CntBase>,
}
impl<'a> GenericTimerCnt<'a> {
pub fn new(regs: UniqueMmioPointer<'a, CntBase>) -> Self {
Self { regs }
}
pub fn physical_count(&self) -> u64 {
field_shared!(self.regs, cntpct).read()
}
pub fn virtual_count(&self) -> u64 {
field_shared!(self.regs, cntvct).read()
}
pub fn frequency(&self) -> u32 {
field_shared!(self.regs, cntfrq).read()
}
pub fn el0_access(&self) -> CntEl0Acr {
field_shared!(self.regs, cntel0acr).read()
}
pub fn set_el0_access(&mut self, value: CntEl0Acr) {
field!(self.regs, cntel0acr).write(value)
}
pub fn virtual_offset(&self) -> u64 {
field_shared!(self.regs, cntvoff).read()
}
pub fn physical_timer(&mut self) -> Timer<MmioTimer<'_>> {
let frequency = self.frequency();
Timer::new(MmioTimer {
regs: field!(self.regs, cntp),
frequency,
})
}
pub fn virtual_timer(&mut self) -> Timer<MmioTimer<'_>> {
let frequency = self.frequency();
Timer::new(MmioTimer {
regs: field!(self.regs, cntv),
frequency,
})
}
}
pub struct GenericTimerCntEl0<'a> {
regs: UniqueMmioPointer<'a, CntEl0Base>,
}
impl<'a> GenericTimerCntEl0<'a> {
pub fn new(regs: UniqueMmioPointer<'a, CntEl0Base>) -> Self {
Self { regs }
}
pub fn physical_count(&self) -> u64 {
field_shared!(self.regs, cntpct).read()
}
pub fn virtual_count(&self) -> u64 {
field_shared!(self.regs, cntvct).read()
}
pub fn frequency(&self) -> u32 {
field_shared!(self.regs, cntfrq).read()
}
pub fn physical_timer(&mut self) -> Timer<MmioTimer<'_>> {
let frequency = self.frequency();
Timer::new(MmioTimer {
regs: field!(self.regs, cntp),
frequency,
})
}
pub fn virtual_timer(&mut self) -> Timer<MmioTimer<'_>> {
let frequency = self.frequency();
Timer::new(MmioTimer {
regs: 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>());
}
}