use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
use std::thread;
use std::time::Duration;
use error::{Error, ErrorKind};
use peripherals::consts::*;
use system::System;
use util::mem::RegisterMap;
static RESERVED: AtomicBool = ATOMIC_BOOL_INIT;
pub struct ClockManager {
registers: RegisterMap,
}
impl ClockManager {
pub fn new() -> Result<ClockManager, Error> {
if RESERVED.fetch_or(true, Ordering::SeqCst) {
return Err(Error::new(ErrorKind::Reserved));
}
Ok(ClockManager {
registers: unsafe { RegisterMap::map(
System::detect()?.peripheral_offset() + CM_PAGE_OFFSET,
PAGE_SIZE,
)}?,
})
}
pub fn set_source(&mut self, clock: Clock, source: ClockSource) {
assert!(!self.is_busy(clock));
let mut reg = self.registers.load(CM_CTL[clock.id()]);
reg &= !CM_CTL_SRC_MASK;
reg |= source.value() | CM_CTL_PASSWD;
self.registers.store(CM_CTL[clock.id()], reg);
}
pub fn enable(&mut self, clock: Clock) {
let mut reg = self.registers.load(CM_CTL[clock.id()]);
reg |= CM_CTL_ENAB | CM_CTL_PASSWD;
self.registers.store(CM_CTL[clock.id()], reg);
}
pub fn disable(&mut self, clock: Clock) {
let mut reg = self.registers.load(CM_CTL[clock.id()]);
reg &= !CM_CTL_ENAB;
reg |= CM_CTL_PASSWD;
self.registers.store(CM_CTL[clock.id()], reg);
thread::sleep(Duration::from_micros(110));
while self.is_busy(clock) {
thread::sleep(Duration::from_micros(1));
}
}
pub fn kill(&mut self, clock: Clock) {
let mut reg = self.registers.load(CM_CTL[clock.id()]);
reg |= CM_CTL_KILL | CM_CTL_PASSWD;
self.registers.store(CM_CTL[clock.id()], reg);
}
pub fn is_busy(&self, clock: Clock) -> bool {
let reg = self.registers.load(CM_CTL[clock.id()]);
reg & CM_CTL_BUSY != 0
}
pub fn enable_flip(&mut self, clock: Clock) {
let mut reg = self.registers.load(CM_CTL[clock.id()]);
reg |= CM_CTL_FLIP | CM_CTL_PASSWD;
self.registers.store(CM_CTL[clock.id()], reg);
}
pub fn disable_flip(&mut self, clock: Clock) {
let mut reg = self.registers.load(CM_CTL[clock.id()]);
reg &= !CM_CTL_FLIP;
reg |= CM_CTL_PASSWD;
self.registers.store(CM_CTL[clock.id()], reg);
}
pub fn set_mash(&mut self, clock: Clock, value: u32) {
assert!(!self.is_busy(clock));
let value = (value << CM_CTL_MASH_SHIFT) & CM_CTL_MASH_MASK;
let mut reg = self.registers.load(CM_CTL[clock.id()]);
reg &= !CM_CTL_MASH_MASK;
reg |= value | CM_CTL_PASSWD;
self.registers.store(CM_CTL[clock.id()], reg);
}
pub fn set_divider(&mut self, clock: Clock, int: u32, frac: u32) {
assert!(!self.is_busy(clock));
let int = (int << CM_DIV_DIVI_SHIFT) & CM_DIV_DIVI_MASK;
let frac = (frac << CM_DIV_DIVF_SHIFT) & CM_DIV_DIVF_MASK;
let reg = int | frac | CM_DIV_PASSWD;
self.registers.store(CM_DIV[clock.id()], reg);
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[allow(missing_docs)]
pub enum Clock {
Gp0,
Gp1,
Gp2,
Pwm,
}
impl Clock {
fn id(self) -> usize {
match self {
Clock::Gp0 => CM_CLOCK_GP0,
Clock::Gp1 => CM_CLOCK_GP1,
Clock::Gp2 => CM_CLOCK_GP2,
Clock::Pwm => CM_CLOCK_PWM,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum ClockSource {
Gnd,
Oscillator,
PllaPer,
PllcPer,
PlldPer,
HdmiAux,
}
impl ClockSource {
fn value(self) -> u32 {
match self {
ClockSource::Gnd => CM_CTL_SRC_GND,
ClockSource::Oscillator => CM_CTL_SRC_OSC,
ClockSource::PllaPer => CM_CTL_SRC_PLLA_PER,
ClockSource::PllcPer => CM_CTL_SRC_PLLC_PER,
ClockSource::PlldPer => CM_CTL_SRC_PLLD_PER,
ClockSource::HdmiAux => CM_CTL_SRC_HDMI_AUX,
}
}
}