use super::Error;
use crate::prelude::*;
pub const MAX_CALLBACKS: usize = 10;
#[derive(Copy, Clone, Debug)]
pub struct Locks {
cpu: usize,
apb: usize,
awake: usize,
pll_d2: usize,
}
static DFS_MUTEX: CriticalSectionSpinLockMutex<Locks> = CriticalSectionSpinLockMutex::new(Locks {
cpu: 0,
apb: 0,
awake: 0,
pll_d2: 0,
});
pub(super) struct DFS {
callbacks: [&'static dyn Fn(super::CPUSource, Hertz, Hertz, super::CPUSource, Hertz, Hertz);
MAX_CALLBACKS],
nr_callbacks: CriticalSectionSpinLockMutex<usize>,
}
impl DFS {
pub(crate) fn new() -> Self {
DFS {
callbacks: [&|_, _, _, _, _, _| {}; MAX_CALLBACKS],
nr_callbacks: CriticalSectionSpinLockMutex::new(0),
}
}
}
pub struct LockCPU {}
pub struct LockAPB {}
pub struct LockAwake {}
pub struct LockPllD2 {}
impl<'a> Drop for LockCPU {
fn drop(&mut self) {
unsafe {
super::CLOCK_CONTROL
.as_mut()
.unwrap()
.unlock_cpu_frequency();
}
}
}
impl<'a> Drop for LockAPB {
fn drop(&mut self) {
unsafe {
super::CLOCK_CONTROL
.as_mut()
.unwrap()
.unlock_apb_frequency();
}
}
}
impl<'a> Drop for LockAwake {
fn drop(&mut self) {
unsafe {
super::CLOCK_CONTROL.as_mut().unwrap().unlock_awake();
}
}
}
impl<'a> Drop for LockPllD2 {
fn drop(&mut self) {
unsafe {
super::CLOCK_CONTROL.as_mut().unwrap().unlock_plld2();
}
}
}
impl<'a> super::ClockControl {
fn do_callbacks(
&self,
cpu_source_before: super::CPUSource,
cpu_frequency_before: Hertz,
apb_frequency_before: Hertz,
cpu_source_after: super::CPUSource,
cpu_frequency_after: Hertz,
apb_frequency_after: Hertz,
) {
if cpu_source_after == cpu_source_before
&& cpu_frequency_after == cpu_frequency_before
&& apb_frequency_after == apb_frequency_before
{
return;
}
let (nr, callbacks) = (&self.dfs.nr_callbacks).lock(|nr| (*nr, self.dfs.callbacks));
for i in 0..nr {
callbacks[i](
cpu_source_before,
cpu_frequency_before,
apb_frequency_before,
cpu_source_after,
cpu_frequency_after,
apb_frequency_after,
);
}
}
pub(crate) fn lock_cpu_frequency(&'a mut self) -> LockCPU {
(&DFS_MUTEX).lock(|data| {
data.cpu += 1;
if data.cpu == 1 {
if data.apb == 0 || self.cpu_frequency_locked > self.cpu_frequency_apb_locked {
let cpu_source_before = self.cpu_source;
let cpu_frequency_before = self.cpu_frequency;
let apb_frequency_before = self.apb_frequency;
self.set_cpu_frequency_locked(data.pll_d2 > 0).unwrap();
self.do_callbacks(
cpu_source_before,
cpu_frequency_before,
apb_frequency_before,
self.cpu_source,
self.cpu_frequency,
self.apb_frequency,
);
}
}
});
LockCPU {}
}
fn unlock_cpu_frequency(&'a mut self) {
(&DFS_MUTEX).lock(|data| {
data.cpu -= 1;
if data.cpu == 0 {
let cpu_source_before = self.cpu_source;
let cpu_frequency_before = self.cpu_frequency;
let apb_frequency_before = self.apb_frequency;
if data.apb == 0 {
self.set_cpu_frequency_default(data.pll_d2 > 0).unwrap();
} else {
self.set_cpu_frequency_apb_locked(data.pll_d2 > 0).unwrap();
}
self.do_callbacks(
cpu_source_before,
cpu_frequency_before,
apb_frequency_before,
self.cpu_source,
self.cpu_frequency,
self.apb_frequency,
);
}
});
}
pub(crate) fn lock_apb_frequency(&'a mut self) -> LockAPB {
(&DFS_MUTEX).lock(|data| {
data.apb += 1;
if data.apb == 1 {
if data.cpu == 0 || self.cpu_frequency_apb_locked > self.cpu_frequency_locked {
let cpu_source_before = self.cpu_source;
let cpu_frequency_before = self.cpu_frequency;
let apb_frequency_before = self.apb_frequency;
self.set_cpu_frequency_apb_locked(data.pll_d2 > 0).unwrap();
self.do_callbacks(
cpu_source_before,
cpu_frequency_before,
apb_frequency_before,
self.cpu_source,
self.cpu_frequency,
self.apb_frequency,
);
}
}
});
LockAPB {}
}
fn unlock_apb_frequency(&'a mut self) {
(&DFS_MUTEX).lock(|data| {
data.apb -= 1;
if data.apb == 0 {
let cpu_source_before = self.cpu_source;
let cpu_frequency_before = self.cpu_frequency;
let apb_frequency_before = self.apb_frequency;
if data.cpu == 0 {
self.set_cpu_frequency_default(data.pll_d2 > 0).unwrap();
} else {
self.set_cpu_frequency_locked(data.pll_d2 > 0).unwrap();
}
self.do_callbacks(
cpu_source_before,
cpu_frequency_before,
apb_frequency_before,
self.cpu_source,
self.cpu_frequency,
self.apb_frequency,
);
}
});
}
pub(crate) fn lock_awake(&'a mut self) -> LockAwake {
(&DFS_MUTEX).lock(|data| {
data.awake += 1;
});
LockAwake {}
}
fn unlock_awake(&'a mut self) {
(&DFS_MUTEX).lock(|data| {
data.awake -= 1;
unimplemented!();
});
}
pub(crate) fn lock_plld2(&'a mut self) -> LockPllD2 {
(&DFS_MUTEX).lock(|data| {
data.pll_d2 += 1;
if data.pll_d2 == 1 && self.pll_frequency == super::FREQ_OFF {
let cpu_source_before = self.cpu_source;
let cpu_frequency_before = self.cpu_frequency;
let apb_frequency_before = self.apb_frequency;
self.pll_enable(false).unwrap();
self.do_callbacks(
cpu_source_before,
cpu_frequency_before,
apb_frequency_before,
self.cpu_source,
self.cpu_frequency,
self.apb_frequency,
);
}
});
LockPllD2 {}
}
fn unlock_plld2(&'a mut self) {
(&DFS_MUTEX).lock(|data| {
data.pll_d2 -= 1;
if data.pll_d2 == 0 && self.cpu_source() != super::CPUSource::PLL {
let cpu_source_before = self.cpu_source;
let cpu_frequency_before = self.cpu_frequency;
let apb_frequency_before = self.apb_frequency;
self.pll_disable();
self.do_callbacks(
cpu_source_before,
cpu_frequency_before,
apb_frequency_before,
self.cpu_source,
self.cpu_frequency,
self.apb_frequency,
);
}
});
}
pub(crate) fn add_callback<F>(&mut self, f: &'static F) -> Result<(), Error>
where
F: Fn(super::CPUSource, Hertz, Hertz, super::CPUSource, Hertz, Hertz),
{
let callbacks = &mut self.dfs.callbacks;
(&self.dfs.nr_callbacks).lock(|nr| {
if *nr >= MAX_CALLBACKS {
return Err(Error::TooManyCallbacks);
}
callbacks[*nr] = f;
*nr += 1;
Ok(())
})
}
pub fn get_lock_count(&self) -> Locks {
(&DFS_MUTEX).lock(|data| *data)
}
}