Implement system register based timers
Add sysregs module that implements system register based timer drivers.
Signed-off-by: Imre Kis <imre.kis@arm.com>
Change-Id: I66c659a42896492a2806a33318b0a344a4983d1d
diff --git a/Cargo.lock b/Cargo.lock
index 063a650..9c33e54 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6,18 +6,57 @@
name = "arm-generic-timer"
version = "0.2.0"
dependencies = [
+ "arm-sysregs",
"bitflags",
"safe-mmio",
"zerocopy",
]
[[package]]
+name = "arm-sysregs"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d026e843848da615748d0d35dbdaa9b86f2421a11a32b13b93a761aaea73c20f"
+dependencies = [
+ "bitflags",
+ "num_enum",
+ "paste",
+]
+
+[[package]]
name = "bitflags"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
[[package]]
+name = "num_enum"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
+dependencies = [
+ "num_enum_derive",
+ "rustversion",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
name = "proc-macro2"
version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -36,6 +75,12 @@
]
[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
name = "safe-mmio"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 548774e..78ce852 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,20 @@
rust-version = "1.85"
[dependencies]
+arm-sysregs = "0.2.6"
bitflags = "2.11.0"
safe-mmio = "0.3.0"
zerocopy = "0.8"
+
+[dev-dependencies]
+arm-sysregs = { version = "0.2.6", features = ["fakes"] }
+
+[features]
+el1 = ["arm-sysregs/el1"]
+el2 = ["el1", "arm-sysregs/el2"]
+fakes = ["arm-sysregs/fakes"]
+
+[package.metadata.docs.rs]
+default-target = "aarch64-unknown-none"
+features = ["el1", "el2"]
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/README.md b/README.md
index 9f495f1..70ef091 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,10 @@
# Arm Generic Timer driver
-Driver implementation for the memory mapped Generic Timer peripheral of the Arm A-profile
-architecture. The implementation is based on the following sections of the
-[Arm Architecture Reference Manual for A-profile architecture](https://developer.arm.com/documentation/ddi0487/la/).
+Driver implementation for the memory mapped and system register based Generic Timer peripheral of
+the Arm A-profile architecture. The implementation is based on the following sections of the
+[Arm Architecture Reference Manual for A-profile architecture](https://developer.arm.com/documentation/ddi0487/maa/).
+* D24.10 Generic Timer registers
* I2.2.3 Counter module control and status register summary
* I2.3 Memory-mapped timer components
* I5.6 Generic Timer memory-mapped registers overview
@@ -11,13 +12,28 @@
## Implemented features
-* Register descriptions and drivers for the following frames:
- * `CNTControlBase`
- * `CNTCTLBase`
- * `CNTReadBase`
- * `CNTBaseN`
- * `CNTEL0BaseN`
-* Blocking and interrupt based timer wait functions.
+* Memory mapped generic timer
+ * Register descriptions and drivers for the following frames:
+ * `CNTControlBase`
+ * `CNTCTLBase`
+ * `CNTReadBase`
+ * `CNTBaseN`
+ * `CNTEL0BaseN`
+* System register based generic timer
+ * Physical Secure Timer
+ * Hypervisor Physical Timer
+ * Secure EL2 Physical Timer
+ * EL2 Virtual Timer
+ * Secure EL2 Virtual Timer
+ * Physical Timer
+ * Virtual Timer
+* Generic delay timer logic
+
+## Feature flags
+
+- `el1`: Enables system register based timers which relies on EL1 system registers.
+- `el2`: Enables system register based timers which relies on EL2 system registers.
+- `fakes`: Accesses fake system registers rather than the real ones, for running tests on the host.
## License
diff --git a/src/lib.rs b/src/lib.rs
index ed33b83..340037f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -10,6 +10,11 @@
///
/// See I5.6 Generic Timer memory-mapped registers overview.
pub mod memory_mapped;
+/// System register based timer driver implementations.
+///
+/// See D24.10 Generic Timer registers.
+#[cfg(any(test, feature = "fakes", target_arch = "aarch64"))]
+pub mod sysreg;
use core::{hint::spin_loop, time::Duration};
diff --git a/src/sysreg.rs b/src/sysreg.rs
new file mode 100644
index 0000000..ad3638d
--- /dev/null
+++ b/src/sysreg.rs
@@ -0,0 +1,167 @@
+// SPDX-FileCopyrightText: Copyright The arm-generic-timer Contributors.
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+use crate::TimerInterface;
+#[cfg(feature = "el2")]
+use arm_sysregs::{
+ CnthpCtlEl2, CnthpsCtlEl2, CnthvCtlEl2, CnthvsCtlEl2, read_cnthp_ctl_el2, read_cnthp_tval_el2,
+ read_cnthps_ctl_el2, read_cnthps_tval_el2, read_cnthv_ctl_el2, read_cnthv_tval_el2,
+ read_cnthvs_ctl_el2, read_cnthvs_tval_el2, write_cnthp_ctl_el2, write_cnthps_ctl_el2,
+ write_cnthv_ctl_el2, write_cnthvs_ctl_el2,
+};
+use arm_sysregs::{
+ CntpCtlEl0, CntvCtlEl0, read_cntfrq_el0, read_cntp_ctl_el0, read_cntp_tval_el0,
+ read_cntv_ctl_el0, read_cntv_tval_el0, write_cntp_ctl_el0, write_cntv_ctl_el0,
+};
+#[cfg(feature = "el1")]
+use arm_sysregs::{CntpsCtlEl1, read_cntps_ctl_el1, read_cntps_tval_el1, write_cntps_ctl_el1};
+
+/// Physical Secure Timer
+///
+/// Uses `CNTPS_*` system registers.
+#[cfg(feature = "el1")]
+pub struct PhysicalSecureTimer;
+
+#[cfg(feature = "el1")]
+impl TimerInterface for PhysicalSecureTimer {
+ fn enable(&mut self) {
+ let control = read_cntps_ctl_el1();
+ write_cntps_ctl_el1(control | CntpsCtlEl1::ENABLE);
+ }
+
+ fn frequency(&self) -> u32 {
+ read_cntfrq_el0().clockfreq()
+ }
+
+ fn timer_value(&self) -> u32 {
+ read_cntps_tval_el1().timervalue()
+ }
+}
+
+/// Hypervisor Physical Timer
+///
+/// Uses `CNTHP_*` system registers.
+#[cfg(feature = "el2")]
+pub struct HypervisorPhysicalTimer;
+
+#[cfg(feature = "el2")]
+impl TimerInterface for HypervisorPhysicalTimer {
+ fn enable(&mut self) {
+ let control = read_cnthp_ctl_el2();
+ write_cnthp_ctl_el2(control | CnthpCtlEl2::ENABLE);
+ }
+
+ fn frequency(&self) -> u32 {
+ read_cntfrq_el0().clockfreq()
+ }
+
+ fn timer_value(&self) -> u32 {
+ read_cnthp_tval_el2().timervalue()
+ }
+}
+
+/// Secure EL2 Physical Timer
+///
+/// Uses `CNTHPS_*` system registers.
+#[cfg(feature = "el2")]
+pub struct SecureEl2PhysicalTimer;
+
+#[cfg(feature = "el2")]
+impl TimerInterface for SecureEl2PhysicalTimer {
+ fn enable(&mut self) {
+ let control = read_cnthps_ctl_el2();
+ write_cnthps_ctl_el2(control | CnthpsCtlEl2::ENABLE);
+ }
+
+ fn frequency(&self) -> u32 {
+ read_cntfrq_el0().clockfreq()
+ }
+
+ fn timer_value(&self) -> u32 {
+ read_cnthps_tval_el2().timervalue()
+ }
+}
+
+/// EL2 Virtual Timer
+///
+/// Uses `CNTHV_*` system registers
+#[cfg(feature = "el2")]
+pub struct El2VirtualTimer;
+
+#[cfg(feature = "el2")]
+impl TimerInterface for El2VirtualTimer {
+ fn enable(&mut self) {
+ let control = read_cnthv_ctl_el2();
+ write_cnthv_ctl_el2(control | CnthvCtlEl2::ENABLE);
+ }
+
+ fn frequency(&self) -> u32 {
+ read_cntfrq_el0().clockfreq()
+ }
+
+ fn timer_value(&self) -> u32 {
+ read_cnthv_tval_el2().timervalue()
+ }
+}
+
+/// Secure EL2 Virtual Timer
+///
+/// Uses `CNTHVS_*` system registers
+#[cfg(feature = "el2")]
+pub struct SecureEl2VirtualTimer;
+
+#[cfg(feature = "el2")]
+impl TimerInterface for SecureEl2VirtualTimer {
+ fn enable(&mut self) {
+ let control = read_cnthvs_ctl_el2();
+ write_cnthvs_ctl_el2(control | CnthvsCtlEl2::ENABLE);
+ }
+
+ fn frequency(&self) -> u32 {
+ read_cntfrq_el0().clockfreq()
+ }
+
+ fn timer_value(&self) -> u32 {
+ read_cnthvs_tval_el2().timervalue()
+ }
+}
+
+/// Physical Timer
+///
+/// Uses `CNTP_*` system registers.
+pub struct PhysicalTimer;
+
+impl TimerInterface for PhysicalTimer {
+ fn enable(&mut self) {
+ let control = read_cntp_ctl_el0();
+ write_cntp_ctl_el0(control | CntpCtlEl0::ENABLE);
+ }
+
+ fn frequency(&self) -> u32 {
+ read_cntfrq_el0().clockfreq()
+ }
+
+ fn timer_value(&self) -> u32 {
+ read_cntp_tval_el0().timervalue()
+ }
+}
+
+/// Virtual Timer
+///
+/// Uses `CNTV_*` system registers.
+pub struct VirtualTimer;
+
+impl TimerInterface for VirtualTimer {
+ fn enable(&mut self) {
+ let control = read_cntv_ctl_el0();
+ write_cntv_ctl_el0(control | CntvCtlEl0::ENABLE);
+ }
+
+ fn frequency(&self) -> u32 {
+ read_cntfrq_el0().clockfreq()
+ }
+
+ fn timer_value(&self) -> u32 {
+ read_cntv_tval_el0().timervalue()
+ }
+}