diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..531ddd1
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,55 @@
+name: CI
+
+on: [push, pull_request]
+
+jobs:
+ ci:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ rust-toolchain: [nightly]
+ targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat]
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@nightly
+ with:
+ toolchain: ${{ matrix.rust-toolchain }}
+ components: rust-src, clippy, rustfmt
+ targets: ${{ matrix.targets }}
+ - name: Check rust version
+ run: rustc --version --verbose
+ - name: Check code format
+ run: cargo fmt --all -- --check
+ - name: Clippy
+ run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default
+ - name: Build
+ run: cargo build --target ${{ matrix.targets }} --all-features
+ - name: Unit test
+ if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }}
+ run: cargo test --target ${{ matrix.targets }} -- --nocapture
+
+ doc:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ permissions:
+ contents: write
+ env:
+ default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }}
+ RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@nightly
+ - name: Build docs
+ continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }}
+ run: |
+ cargo doc --no-deps --all-features
+ printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html
+ - name: Deploy to Github Pages
+ if: ${{ github.ref == env.default-branch }}
+ uses: JamesIves/github-pages-deploy-action@v4
+ with:
+ single-commit: true
+ branch: gh-pages
+ folder: target/doc
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ff78c42
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.vscode
+.DS_Store
+Cargo.lock
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..6235f4a
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "arm_pl031"
+version = "0.1.0"
+edition = "2021"
+authors = ["Keyang Hu "]
+description = "System Real Time Clock (RTC) Drivers for aarch64 based on PL031."
+license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0"
+homepage = "https://github.com/arceos-org/arceos"
+repository = "https://github.com/arceos-org/arceos/tree/main/crates/arm_pl031"
+documentation = "https://docs.rs/arm_pl031"
+keywords = ["arceos", "aarch64", "rtc"]
+categories = ["os", "hardware-support", "no-std"]
+
+[dependencies]
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e573f1e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,35 @@
+# arm_pl031
+
+[](https://crates.io/crates/arm_pl031)
+
+System Real Time Clock (RTC) Drivers for aarch64 based on PL031.
+
+## Examples
+
+```rust
+use arm_pl031::Rtc;
+
+let epoch_time = Rtc::new(0x901_0000).get_unix_timestamp();
+```
+
+`base_addr` needs to be the device virtual address available for mmio, which can be obtained from the device tree, for example:
+
+```
+/ {
+ interrupt-parent = <0x8002>;
+ model = "linux,dummy-virt";
+ #size-cells = <0x02>;
+ #address-cells = <0x02>;
+ compatible = "linux,dummy-virt";
+
+ pl031@9010000 {
+ clock-names = "apb_pclk";
+ clocks = <0x8000>;
+ interrupts = <0x00 0x02 0x04>;
+ reg = <0x00 0x9010000 0x00 0x1000>;
+ compatible = "arm,pl031\0arm,primecell";
+ };
+
+ ...
+}
+```
\ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..7eed437
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,41 @@
+//! System Real Time Clock (RTC) Drivers for aarch64 based on PL031.
+
+#![cfg_attr(not(test), no_std)]
+
+const RTC_DR: usize = 0x00; //Data Register
+const RTC_LR: usize = 0x08; //Load Register
+
+/// The System Real Time Clock structure for aarch64 based on PL031.
+pub struct Rtc {
+ base_address: usize,
+}
+
+impl Rtc {
+ unsafe fn read(&self, reg: usize) -> u32 {
+ core::ptr::read_volatile((self.base_address + reg) as *const u32)
+ }
+
+ unsafe fn write(&self, reg: usize, value: u32) {
+ core::ptr::write_volatile((self.base_address + reg) as *mut u32, value);
+ }
+}
+
+impl Rtc {
+ /// Construct a new PL031 RTC structure.
+ ///
+ /// `base_addr` represents the device address
+ /// (which can be obtained from the device tree).
+ pub fn new(base_address: usize) -> Self {
+ Rtc { base_address }
+ }
+
+ /// Returns the current time in seconds since UNIX epoch.
+ pub fn get_unix_timestamp(&self) -> u64 {
+ unsafe { self.read(RTC_DR) as u64 }
+ }
+
+ /// Sets the current time in seconds since UNIX epoch.
+ pub fn set_unix_timestamp(&self, unix_time: u64) {
+ unsafe { self.write(RTC_LR, unix_time as u32) }
+ }
+}