1360: stm32/rcc: add i2s pll on some f4 micros r=Dirbaio a=xoviat Adds the i2s pll on some f4 micros. 1361: Executor: Replace unnecessary atomics in runqueue r=Dirbaio a=GrantM11235 Only the head pointer needs to be atomic. The `RunQueueItem` pointers are only loaded and stored, and never concurrently Co-authored-by: xoviat <xoviat@users.noreply.github.com> Co-authored-by: Grant Miller <GrantM11235@gmail.com>
This commit is contained in:
commit
1fdce6e52a
3 changed files with 100 additions and 10 deletions
|
@ -4,15 +4,16 @@ use core::ptr::NonNull;
|
||||||
use atomic_polyfill::{AtomicPtr, Ordering};
|
use atomic_polyfill::{AtomicPtr, Ordering};
|
||||||
|
|
||||||
use super::{TaskHeader, TaskRef};
|
use super::{TaskHeader, TaskRef};
|
||||||
|
use crate::raw::util::SyncUnsafeCell;
|
||||||
|
|
||||||
pub(crate) struct RunQueueItem {
|
pub(crate) struct RunQueueItem {
|
||||||
next: AtomicPtr<TaskHeader>,
|
next: SyncUnsafeCell<Option<TaskRef>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunQueueItem {
|
impl RunQueueItem {
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
next: AtomicPtr::new(ptr::null_mut()),
|
next: SyncUnsafeCell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +52,12 @@ impl RunQueue {
|
||||||
self.head
|
self.head
|
||||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
|
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
|
||||||
was_empty = prev.is_null();
|
was_empty = prev.is_null();
|
||||||
task.header().run_queue_item.next.store(prev, Ordering::Relaxed);
|
unsafe {
|
||||||
|
// safety: the pointer is either null or valid
|
||||||
|
let prev = NonNull::new(prev).map(|ptr| TaskRef::from_ptr(ptr.as_ptr()));
|
||||||
|
// safety: there are no concurrent accesses to `next`
|
||||||
|
task.header().run_queue_item.next.set(prev);
|
||||||
|
}
|
||||||
Some(task.as_ptr() as *mut _)
|
Some(task.as_ptr() as *mut _)
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
|
@ -64,18 +70,19 @@ impl RunQueue {
|
||||||
/// and will be processed by the *next* call to `dequeue_all`, *not* the current one.
|
/// and will be processed by the *next* call to `dequeue_all`, *not* the current one.
|
||||||
pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {
|
pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {
|
||||||
// Atomically empty the queue.
|
// Atomically empty the queue.
|
||||||
let mut ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel);
|
let ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel);
|
||||||
|
|
||||||
|
// safety: the pointer is either null or valid
|
||||||
|
let mut next = unsafe { NonNull::new(ptr).map(|ptr| TaskRef::from_ptr(ptr.as_ptr())) };
|
||||||
|
|
||||||
// Iterate the linked list of tasks that were previously in the queue.
|
// Iterate the linked list of tasks that were previously in the queue.
|
||||||
while let Some(task) = NonNull::new(ptr) {
|
while let Some(task) = next {
|
||||||
let task = unsafe { TaskRef::from_ptr(task.as_ptr()) };
|
|
||||||
// If the task re-enqueues itself, the `next` pointer will get overwritten.
|
// If the task re-enqueues itself, the `next` pointer will get overwritten.
|
||||||
// Therefore, first read the next pointer, and only then process the task.
|
// Therefore, first read the next pointer, and only then process the task.
|
||||||
let next = task.header().run_queue_item.next.load(Ordering::Relaxed);
|
// safety: there are no concurrent accesses to `next`
|
||||||
|
next = unsafe { task.header().run_queue_item.next.get() };
|
||||||
|
|
||||||
on_task(task);
|
on_task(task);
|
||||||
|
|
||||||
ptr = next
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,10 +29,66 @@ pub struct Config {
|
||||||
pub pclk1: Option<Hertz>,
|
pub pclk1: Option<Hertz>,
|
||||||
pub pclk2: Option<Hertz>,
|
pub pclk2: Option<Hertz>,
|
||||||
|
|
||||||
|
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
|
||||||
|
pub plli2s: Option<Hertz>,
|
||||||
|
|
||||||
pub pll48: bool,
|
pub pll48: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bool) -> PllResults {
|
#[cfg(stm32f410)]
|
||||||
|
unsafe fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not currently implemented, but will be in the future
|
||||||
|
#[cfg(any(stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))]
|
||||||
|
unsafe fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
|
||||||
|
unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option<u32>) -> Option<u32> {
|
||||||
|
let min_div = 2;
|
||||||
|
let max_div = 7;
|
||||||
|
let target = match plli2s {
|
||||||
|
Some(target) => target,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// We loop through the possible divider values to find the best configuration. Looping
|
||||||
|
// through all possible "N" values would result in more iterations.
|
||||||
|
let (n, outdiv, output, _error) = (min_div..=max_div)
|
||||||
|
.filter_map(|outdiv| {
|
||||||
|
let target_vco_out = match target.checked_mul(outdiv) {
|
||||||
|
Some(x) => x,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
let n = (target_vco_out + (vco_in >> 1)) / vco_in;
|
||||||
|
let vco_out = vco_in * n;
|
||||||
|
if !(100_000_000..=432_000_000).contains(&vco_out) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let output = vco_out / outdiv;
|
||||||
|
let error = (output as i32 - target as i32).unsigned_abs();
|
||||||
|
Some((n, outdiv, output, error))
|
||||||
|
})
|
||||||
|
.min_by_key(|(_, _, _, error)| *error)?;
|
||||||
|
|
||||||
|
RCC.plli2scfgr().modify(|w| {
|
||||||
|
w.set_plli2sn(n as u16);
|
||||||
|
w.set_plli2sr(outdiv as u8);
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn setup_pll(
|
||||||
|
pllsrcclk: u32,
|
||||||
|
use_hse: bool,
|
||||||
|
pllsysclk: Option<u32>,
|
||||||
|
plli2s: Option<u32>,
|
||||||
|
pll48clk: bool,
|
||||||
|
) -> PllResults {
|
||||||
use crate::pac::rcc::vals::{Pllp, Pllsrc};
|
use crate::pac::rcc::vals::{Pllp, Pllsrc};
|
||||||
|
|
||||||
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
|
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
|
||||||
|
@ -43,6 +99,7 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48
|
||||||
use_pll: false,
|
use_pll: false,
|
||||||
pllsysclk: None,
|
pllsysclk: None,
|
||||||
pll48clk: None,
|
pll48clk: None,
|
||||||
|
plli2sclk: None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Input divisor from PLL source clock, must result to frequency in
|
// Input divisor from PLL source clock, must result to frequency in
|
||||||
|
@ -101,6 +158,7 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48
|
||||||
use_pll: true,
|
use_pll: true,
|
||||||
pllsysclk: Some(real_pllsysclk),
|
pllsysclk: Some(real_pllsysclk),
|
||||||
pll48clk: if pll48clk { Some(real_pll48clk) } else { None },
|
pll48clk: if pll48clk { Some(real_pll48clk) } else { None },
|
||||||
|
plli2sclk: setup_i2s_pll(vco_in, plli2s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,6 +344,10 @@ pub(crate) unsafe fn init(config: Config) {
|
||||||
pllsrcclk,
|
pllsrcclk,
|
||||||
config.hse.is_some(),
|
config.hse.is_some(),
|
||||||
if sysclk_on_pll { Some(sysclk) } else { None },
|
if sysclk_on_pll { Some(sysclk) } else { None },
|
||||||
|
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
|
||||||
|
config.plli2s.map(|i2s| i2s.0),
|
||||||
|
#[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))]
|
||||||
|
None,
|
||||||
config.pll48,
|
config.pll48,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -376,6 +438,13 @@ pub(crate) unsafe fn init(config: Config) {
|
||||||
while !RCC.cr().read().pllrdy() {}
|
while !RCC.cr().read().pllrdy() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(stm32f410))]
|
||||||
|
if plls.plli2sclk.is_some() {
|
||||||
|
RCC.cr().modify(|w| w.set_plli2son(true));
|
||||||
|
|
||||||
|
while !RCC.cr().read().plli2srdy() {}
|
||||||
|
}
|
||||||
|
|
||||||
RCC.cfgr().modify(|w| {
|
RCC.cfgr().modify(|w| {
|
||||||
w.set_ppre2(Ppre(ppre2_bits));
|
w.set_ppre2(Ppre(ppre2_bits));
|
||||||
w.set_ppre1(Ppre(ppre1_bits));
|
w.set_ppre1(Ppre(ppre1_bits));
|
||||||
|
@ -409,6 +478,12 @@ pub(crate) unsafe fn init(config: Config) {
|
||||||
ahb3: Hertz(hclk),
|
ahb3: Hertz(hclk),
|
||||||
|
|
||||||
pll48: plls.pll48clk.map(Hertz),
|
pll48: plls.pll48clk.map(Hertz),
|
||||||
|
|
||||||
|
#[cfg(not(stm32f410))]
|
||||||
|
plli2s: plls.plli2sclk.map(Hertz),
|
||||||
|
|
||||||
|
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
||||||
|
pllsai: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,6 +491,8 @@ struct PllResults {
|
||||||
use_pll: bool,
|
use_pll: bool,
|
||||||
pllsysclk: Option<u32>,
|
pllsysclk: Option<u32>,
|
||||||
pll48clk: Option<u32>,
|
pll48clk: Option<u32>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
plli2sclk: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
mod max {
|
mod max {
|
||||||
|
|
|
@ -60,6 +60,12 @@ pub struct Clocks {
|
||||||
#[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))]
|
#[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))]
|
||||||
pub pll48: Option<Hertz>,
|
pub pll48: Option<Hertz>,
|
||||||
|
|
||||||
|
#[cfg(all(rcc_f4, not(stm32f410)))]
|
||||||
|
pub plli2s: Option<Hertz>,
|
||||||
|
|
||||||
|
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
||||||
|
pub pllsai: Option<Hertz>,
|
||||||
|
|
||||||
#[cfg(stm32f1)]
|
#[cfg(stm32f1)]
|
||||||
pub adc: Hertz,
|
pub adc: Hertz,
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue