use kernel::utilities::cells::OptionalCell;
use kernel::utilities::registers::interfaces::{Readable, Writeable};
use kernel::utilities::registers::{
register_bitfields, register_structs, ReadOnly, ReadWrite, WriteOnly,
};
use kernel::utilities::StaticRef;
register_structs! {
ClockRegisters {
(0x000 => tasks_hfclkstart: WriteOnly<u32, Control::Register>),
(0x004 => tasks_hfclkstop: WriteOnly<u32, Control::Register>),
(0x008 => tasks_lfclkstart: ReadWrite<u32, Control::Register>),
(0x00C => tasks_lfclkstop: WriteOnly<u32, Control::Register>),
(0x010 => tasks_cal: WriteOnly<u32, Control::Register>),
(0x014 => tasks_ctstart: WriteOnly<u32, Control::Register>),
(0x018 => tasks_ctstop: WriteOnly<u32, Control::Register>),
(0x01C => _reserved1),
(0x100 => events_hfclkstarted: ReadOnly<u32, Status::Register>),
(0x104 => events_lfclkstarted: ReadOnly<u32, Status::Register>),
(0x108 => _reserved2),
(0x10C => events_done: ReadOnly<u32, Status::Register>),
(0x110 => events_ctto: ReadOnly<u32, Status::Register>),
(0x114 => _reserved3),
(0x304 => intenset: ReadWrite<u32, Interrupt::Register>),
(0x308 => intenclr: ReadWrite<u32, Interrupt::Register>),
(0x30C => _reserved4),
(0x408 => hfclkrun: ReadOnly<u32, Status::Register>),
(0x40C => hfclkstat: ReadOnly<u32, HfClkStat::Register>),
(0x410 => _reserved5),
(0x414 => lfclkrun: ReadOnly<u32, Control::Register>),
(0x418 => lfclkstat: ReadWrite<u32, LfClkStat::Register>),
(0x41C => lfclksrccopy: ReadOnly<u32, LfClkSrcCopy::Register>),
(0x420 => _reserved6),
(0x518 => lfclksrc: ReadWrite<u32, LfClkSrc::Register>),
(0x51C => _reserved7),
(0x538 => ctiv: ReadWrite<u32, Ctiv::Register>),
(0x53C => _reserved8),
(0x55C => traceconfig: ReadWrite<u32, TraceConfig::Register>),
(0x560 => @END),
}
}
register_bitfields! [u32,
Control [
ENABLE OFFSET(0) NUMBITS(1)
],
Status [
READY OFFSET(0) NUMBITS(1)
],
Interrupt [
HFCLKSTARTED OFFSET(0) NUMBITS(1),
LFCLKSTARTED OFFSET(1) NUMBITS(1),
DONE OFFSET(3) NUMBITS(1),
CTTO OFFSET(4) NUMBITS(1)
],
HfClkStat [
SRC OFFSET(0) NUMBITS(1) [
RC = 0,
XTAL = 1
],
STATE OFFSET(16) NUMBITS(1) [
RUNNING = 1
]
],
LfClkStat [
SRC OFFSET(0) NUMBITS(2) [
RC = 0,
XTAL = 1,
SYNTH = 2
],
STATE OFFSET(16) NUMBITS(1) [
RUNNING = 1
]
],
LfClkSrcCopy [
SRC OFFSET(0) NUMBITS(2) [
RC = 0,
XTAL = 1,
SYNTH = 2
]
],
LfClkSrc [
SRC OFFSET(0) NUMBITS(2) [
RC = 0,
XTAL = 1,
SYNTH = 2
]
],
Ctiv [
CTIV OFFSET(0) NUMBITS(7) []
],
TraceConfig [
TracePortSpeed OFFSET(0) NUMBITS(2) [
THIRTYTWO = 0,
SIXTEEN = 1,
EIGHT = 2,
FOUR = 3
],
TraceMux OFFSET(16) NUMBITS(2) [
GPIO = 0,
SERIAL = 1,
PARALELL = 2
]
]
];
const CLOCK_BASE: StaticRef<ClockRegisters> =
unsafe { StaticRef::new(0x40000000 as *const ClockRegisters) };
pub enum InterruptField {
HFCLKSTARTED = 1 << 0,
LFCLKSTARTED = 1 << 1,
DONE = 1 << 3,
CTTO = 1 << 4,
}
pub enum LowClockSource {
RC = 0,
XTAL = 1,
SYNTH = 2,
MASK = 3,
}
pub enum HighClockSource {
RC = 0,
XTAL = 1,
}
pub struct Clock {
registers: StaticRef<ClockRegisters>,
client: OptionalCell<&'static dyn ClockClient>,
}
pub trait ClockClient {
fn event(&self);
}
impl Clock {
pub const fn new() -> Clock {
Clock {
registers: CLOCK_BASE,
client: OptionalCell::empty(),
}
}
pub fn set_client(&self, client: &'static dyn ClockClient) {
self.client.set(client);
}
pub fn interrupt_enable(&self, interrupt: InterruptField) {
match interrupt {
InterruptField::CTTO => self.registers.intenset.write(Interrupt::CTTO::SET),
InterruptField::DONE => self.registers.intenset.write(Interrupt::DONE::SET),
InterruptField::HFCLKSTARTED => {
self.registers.intenset.write(Interrupt::HFCLKSTARTED::SET)
}
InterruptField::LFCLKSTARTED => {
self.registers.intenset.write(Interrupt::LFCLKSTARTED::SET)
}
}
}
pub fn interrupt_disable(&self, interrupt: InterruptField) {
match interrupt {
InterruptField::CTTO => self.registers.intenset.write(Interrupt::CTTO::SET),
InterruptField::DONE => self.registers.intenset.write(Interrupt::DONE::SET),
InterruptField::HFCLKSTARTED => {
self.registers.intenset.write(Interrupt::HFCLKSTARTED::SET)
}
InterruptField::LFCLKSTARTED => {
self.registers.intenset.write(Interrupt::LFCLKSTARTED::SET)
}
}
}
pub fn high_start(&self) {
self.registers.tasks_hfclkstart.write(Control::ENABLE::SET);
}
pub fn high_stop(&self) {
self.registers.tasks_hfclkstop.write(Control::ENABLE::SET);
}
pub fn high_started(&self) -> bool {
self.registers
.events_hfclkstarted
.matches_all(Status::READY.val(1))
}
pub fn high_source(&self) -> HighClockSource {
match self.registers.hfclkstat.read(HfClkStat::SRC) {
0 => HighClockSource::RC,
_ => HighClockSource::XTAL,
}
}
pub fn high_running(&self) -> bool {
self.registers
.hfclkstat
.matches_all(HfClkStat::STATE::RUNNING)
}
pub fn low_start(&self) {
self.registers.tasks_lfclkstart.write(Control::ENABLE::SET);
}
pub fn low_stop(&self) {
self.registers.tasks_lfclkstop.write(Control::ENABLE::SET);
}
pub fn low_started(&self) -> bool {
self.registers
.events_lfclkstarted
.matches_all(Status::READY::SET)
}
pub fn low_source(&self) -> LowClockSource {
match self.registers.lfclkstat.read(LfClkStat::SRC) {
0b1 => LowClockSource::XTAL,
0b10 => LowClockSource::SYNTH,
_ => LowClockSource::RC,
}
}
pub fn low_running(&self) -> bool {
self.registers
.lfclkstat
.matches_all(LfClkStat::STATE::RUNNING)
}
pub fn low_set_source(&self, clock_source: LowClockSource) {
self.registers
.lfclksrc
.write(LfClkSrc::SRC.val(clock_source as u32));
}
}