rtc: improve rtc device drivers

This commit is contained in:
2025-08-14 15:08:19 +03:00
parent 6552fa8059
commit 322cb0a958
7 changed files with 209 additions and 80 deletions
+1
View File
@@ -4,4 +4,5 @@
extern crate alloc;
mod pl011;
mod pl031;
mod pl061;
+66
View File
@@ -0,0 +1,66 @@
use alloc::sync::Arc;
use device_api::device::{Device, DeviceInitContext};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
use libk::{
error::Error,
fs::sysfs::{self, nodes::SysfsRtcNode},
};
use libk_mm::device::DeviceMemoryIo;
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
interfaces::Readable,
register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x00 => RTCDR: ReadOnly<u32>),
(0x04 => RTCMR: ReadWrite<u32>),
(0x08 => RTCLR: ReadWrite<u32>),
(0x0C => RTCCR: ReadWrite<u32>),
(0x10 => RTCIMSC: ReadWrite<u32>),
(0x14 => RTCRIS: ReadOnly<u32>),
(0x18 => RTCMIS: ReadOnly<u32>),
(0x1C => RTCICR: WriteOnly<u32>),
(0x20 => @END),
}
}
struct Pl031 {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
impl SysfsRtcNode for Pl031 {
fn read(&self) -> Result<u64, Error> {
let regs = self.regs.lock();
Ok(regs.RTCDR.get() as _)
}
}
impl Device for Pl031 {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
sysfs::nodes::add_rtc_node(self.clone());
Ok(())
}
fn display_name(&self) -> &str {
"ARM PL031 RTC"
}
}
device_tree_driver! {
compatible: ["arm,pl031"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let base = node.map_base(context, 0)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let rtc = Arc::new(Pl031 { regs: IrqSafeSpinlock::new(regs) });
Some(rtc)
}
}
}
+1
View File
@@ -5,6 +5,7 @@ use object::KObject;
use crate::vfs::NodeRef;
pub mod attribute;
pub mod nodes;
pub mod object;
static ROOT: OneTimeInit<NodeRef> = OneTimeInit::new();
+52
View File
@@ -0,0 +1,52 @@
use core::{
marker::PhantomData,
sync::atomic::{AtomicUsize, Ordering},
};
use alloc::{format, string::String, sync::Arc};
use libk_util::OneTimeInit;
use yggdrasil_abi::error::Error;
use crate::fs::sysfs::{
attribute::{StringAttribute, StringAttributeOps},
device,
object::KObject,
};
pub trait SysfsRtcNode: Send + Sync + 'static {
fn read(&self) -> Result<u64, Error>;
}
static RTC_NODES: AtomicUsize = AtomicUsize::new(0);
pub fn add_rtc_node<N: SysfsRtcNode>(node: Arc<N>) {
struct Time<N>(PhantomData<N>);
impl<N: SysfsRtcNode> StringAttributeOps for Time<N> {
type Data = Arc<N>;
const NAME: &'static str = "time";
fn read(state: &Self::Data) -> Result<String, Error> {
let time = state.read()?;
Ok(format!("{time}"))
}
}
static RTC_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
let rtc_object = RTC_OBJECT.or_init_with(|| {
let device_object = device().unwrap();
let rtc_object = KObject::new(());
device_object.add_object("rtc", rtc_object.clone()).ok();
rtc_object
});
let rtc_node = KObject::new(node);
rtc_node
.add_attribute(StringAttribute::from(Time(PhantomData)))
.ok();
let index = RTC_NODES.fetch_add(1, Ordering::AcqRel);
let name = format!("rtc{index}");
rtc_object.add_object(name, rtc_node).ok();
}
+20 -73
View File
@@ -1,18 +1,11 @@
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
device::Device,
interrupt::{InterruptHandler, Irq, IrqVector},
};
use device_api::device::Device;
use kernel_arch::{Architecture, ArchitectureImpl};
use kernel_arch_x86::{
intrinsics::{io_wait, IoPort, IoPortAccess},
ISA_IRQ_OFFSET,
};
use libk::{device::external_interrupt_controller, time};
use kernel_arch_x86::intrinsics::{io_wait, IoPort, IoPortAccess};
use libk::fs::sysfs::{self, nodes::SysfsRtcNode};
use libk_util::sync::IrqSafeSpinlock;
const NMI_DISABLE: u8 = 1 << 7;
const CMOS_REG_SEC: u8 = 0x00;
const CMOS_REG_MIN: u8 = 0x02;
const CMOS_REG_HOUR: u8 = 0x04;
@@ -20,13 +13,8 @@ const CMOS_REG_DAY: u8 = 0x07;
const CMOS_REG_MON: u8 = 0x08;
const CMOS_REG_YEAR: u8 = 0x09;
const CMOS_REG_STATUS_A: u8 = 0x0A;
const CMOS_REG_STATUS_B: u8 = 0x0B;
const CMOS_REG_STATUS_C: u8 = 0x0C;
const STATUS_A_UPDATE_IN_PROGRESS: u8 = 1 << 7;
// Refresh every 4 ticks (every 2s)
const REFRESH_INTERVAL: u32 = 4;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct DateTime {
seconds: u8,
@@ -40,14 +28,18 @@ struct DateTime {
struct Inner {
command: IoPort<u8>,
data: IoPort<u8>,
counter: u32,
last_timestamp: Option<DateTime>,
}
pub struct Rtc {
inner: IrqSafeSpinlock<Inner>,
}
fn bcd_to_dec(x: u8) -> u8 {
let a = (x >> 4) & 0xF;
let b = x & 0xF;
a * 10 + b
}
impl Inner {
fn read_date_time(&mut self) -> DateTime {
self.wait_for_update();
@@ -71,12 +63,12 @@ impl Inner {
}
fn try_read_time(&mut self) -> DateTime {
let seconds = self.read_reg(CMOS_REG_SEC);
let minutes = self.read_reg(CMOS_REG_MIN);
let hours = self.read_reg(CMOS_REG_HOUR);
let day_of_month = self.read_reg(CMOS_REG_DAY);
let month = self.read_reg(CMOS_REG_MON);
let year = self.read_reg(CMOS_REG_YEAR);
let seconds = bcd_to_dec(self.read_reg(CMOS_REG_SEC));
let minutes = bcd_to_dec(self.read_reg(CMOS_REG_MIN));
let hours = bcd_to_dec(self.read_reg(CMOS_REG_HOUR));
let day_of_month = bcd_to_dec(self.read_reg(CMOS_REG_DAY));
let month = bcd_to_dec(self.read_reg(CMOS_REG_MON));
let year = bcd_to_dec(self.read_reg(CMOS_REG_YEAR));
DateTime {
seconds,
minutes,
@@ -99,59 +91,16 @@ impl Inner {
}
value
}
fn write_reg(&mut self, reg: u8, value: u8) {
assert!(ArchitectureImpl::interrupt_mask());
self.command.write(reg);
for _ in 0..10 {
io_wait();
}
self.data.write(value);
for _ in 0..10 {
io_wait();
}
}
fn setup_irq(&mut self) {
let rate = 15; // freq = 2Hz
let old_a = self.read_reg(CMOS_REG_STATUS_A | NMI_DISABLE);
let old_b = self.read_reg(CMOS_REG_STATUS_B | NMI_DISABLE);
let new_a = (old_a & 0xF0) | rate;
let new_b = old_b | (1 << 6) | (1 << 2) | (1 << 1);
self.write_reg(CMOS_REG_STATUS_A | NMI_DISABLE, new_a);
self.write_reg(CMOS_REG_STATUS_B | NMI_DISABLE, new_b);
}
}
impl InterruptHandler for Rtc {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
let mut inner = self.inner.lock();
if inner.counter == 0 {
let time = inner.read_date_time();
inner.last_timestamp = Some(time);
time::set_real_seconds(time.to_seconds(), true);
}
inner.read_reg(CMOS_REG_STATUS_C);
inner.counter += 1;
if inner.counter == REFRESH_INTERVAL {
inner.counter = 0;
}
true
impl SysfsRtcNode for Rtc {
fn read(&self) -> Result<u64, Error> {
let time = self.inner.lock().read_date_time();
Ok(time.to_seconds())
}
}
impl Device for Rtc {
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
let irq = Irq::External(ISA_IRQ_OFFSET + 8);
let intc = external_interrupt_controller()?;
self.inner.lock().setup_irq();
intc.register_irq(irq, Default::default(), self.clone())?;
intc.enable_irq(irq)?;
Ok(())
}
fn display_name(&self) -> &str {
"x86 RTC"
}
@@ -163,15 +112,13 @@ impl Rtc {
inner: IrqSafeSpinlock::new(Inner {
command: IoPort::new(0x70),
data: IoPort::new(0x71),
counter: 0,
last_timestamp: None,
}),
}
}
pub fn setup() -> Result<Arc<Self>, Error> {
let this = Arc::new(Self::new());
unsafe { this.clone().init_irq() }?;
sysfs::nodes::add_rtc_node(this.clone());
Ok(this)
}
}