Skip to content

Commit

Permalink
Merge pull request #2 from qwandor/features
Browse files Browse the repository at this point in the history
Add methods for match register and interrupts, and optional chrono support
  • Loading branch information
equation314 authored Jul 23, 2024
2 parents f3c67d0 + 802e9a0 commit fe52bc2
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 1 deletion.
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ jobs:
run: cargo fmt --all -- --check
- name: Clippy
run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default
- name: Build
- name: Build with no features
run: cargo build --target ${{ matrix.targets }} --no-default-features
- name: Build with all features
run: cargo build --target ${{ matrix.targets }} --all-features
- name: Unit test
if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }}
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## Unreleased

### New features

- Added methods for match register and interrupts.
- Added optional dependency on `chrono`.

## 0.2.0

### Breaking changes
Expand Down
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ keywords = ["arceos", "aarch64", "rtc", "pl031"]
categories = ["os", "hardware-support", "no-std"]

[dependencies]
chrono = { version = "0.4.38", default-features = false, optional = true }

[features]
chrono = ["dep:chrono"]
default = ["chrono"]
26 changes: 26 additions & 0 deletions src/chrono.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use crate::Rtc;
use chrono::{DateTime, TimeZone as _, Utc};
use core::num::TryFromIntError;

impl Rtc {
/// Returns the current time.
pub fn get_time(&self) -> DateTime<Utc> {
Utc.timestamp_opt(self.get_unix_timestamp().into(), 0)
.unwrap()
}

/// Sets the current time.
///
/// Returns an error if the given time is beyond the bounds supported by the RTC.
pub fn set_time(&mut self, time: DateTime<Utc>) -> Result<(), TryFromIntError> {
self.set_unix_timestamp(time.timestamp().try_into()?);
Ok(())
}

/// Sets the match register to the given time. When the RTC value matches this then an interrupt
/// will be generated (if it is enabled).
pub fn set_match(&mut self, match_time: DateTime<Utc>) -> Result<(), TryFromIntError> {
self.set_match_timestamp(match_time.timestamp().try_into()?);
Ok(())
}
}
85 changes: 85 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
//! System Real Time Clock (RTC) Drivers for aarch64 based on PL031.
#![cfg_attr(not(test), no_std)]
#![deny(
clippy::missing_safety_doc,
clippy::undocumented_unsafe_blocks,
unsafe_op_in_unsafe_fn
)]

#[cfg(feature = "chrono")]
mod chrono;

use core::ptr::{addr_of, addr_of_mut};

#[derive(Default)]
#[repr(C, align(4))]
struct Registers {
/// Data register
Expand Down Expand Up @@ -63,6 +72,52 @@ impl Rtc {
// of a PL031 device which is appropriately mapped.
unsafe { addr_of_mut!((*self.registers).lr).write_volatile(unix_time) }
}

/// Writes a match value. When the RTC value matches this then an interrupt
/// will be generated (if it is enabled).
pub fn set_match_timestamp(&mut self, match_timestamp: u32) {
// SAFETY: We know that self.registers points to the control registers
// of a PL031 device which is appropriately mapped.
unsafe { addr_of_mut!((*self.registers).mr).write_volatile(match_timestamp) }
}

/// Returns whether the match register matches the RTC value, whether or not
/// the interrupt is enabled.
pub fn matched(&self) -> bool {
// SAFETY: We know that self.registers points to the control registers
// of a PL031 device which is appropriately mapped.
let ris = unsafe { addr_of!((*self.registers).ris).read_volatile() };
(ris & 0x01) != 0
}

/// Returns whether there is currently an interrupt pending.
///
/// This should be true if and only if `matched` returns true and the
/// interrupt is masked.
pub fn interrupt_pending(&self) -> bool {
// SAFETY: We know that self.registers points to the control registers
// of a PL031 device which is appropriately mapped.
let ris = unsafe { addr_of!((*self.registers).mis).read_volatile() };
(ris & 0x01) != 0
}

/// Sets or clears the interrupt mask.
///
/// When the mask is true the interrupt is enabled; when it is false the
/// interrupt is disabled.
pub fn enable_interrupt(&mut self, mask: bool) {
let imsc = if mask { 0x01 } else { 0x00 };
// SAFETY: We know that self.registers points to the control registers
// of a PL031 device which is appropriately mapped.
unsafe { addr_of_mut!((*self.registers).imsc).write_volatile(imsc) }
}

/// Clears a pending interrupt, if any.
pub fn clear_interrupt(&mut self) {
// SAFETY: We know that self.registers points to the control registers
// of a PL031 device which is appropriately mapped.
unsafe { addr_of_mut!((*self.registers).icr).write_volatile(0x01) }
}
}

// SAFETY: `Rtc` just contains a pointer to device memory, which can be accessed from any context.
Expand All @@ -71,3 +126,33 @@ unsafe impl Send for Rtc {}
// SAFETY: An `&Rtc` only allows reading device registers, which can safety be done from multiple
// places at once.
unsafe impl Sync for Rtc {}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_get_timestamp() {
let mut registers = Registers {
dr: 12345678,
..Registers::default()
};

// SAFETY: The pointer is constructed from a reference so it must be valid
let rtc = unsafe { Rtc::new(&mut registers as *mut Registers as _) };

assert_eq!(rtc.get_unix_timestamp(), 12345678);
}

#[test]
fn test_set_timestamp() {
let mut registers = Registers::default();

// SAFETY: The pointer is constructed from a reference so it must be valid
let mut rtc = unsafe { Rtc::new(&mut registers as *mut Registers as _) };

rtc.set_unix_timestamp(424242);
drop(rtc);
assert_eq!(registers.lr, 424242);
}
}

0 comments on commit fe52bc2

Please sign in to comment.