rtc: improve rtc device drivers
This commit is contained in:
@@ -4,4 +4,5 @@
|
||||
extern crate alloc;
|
||||
|
||||
mod pl011;
|
||||
mod pl031;
|
||||
mod pl061;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user