use core::sync::atomic::{AtomicU8, Ordering};
use crate::pac;
struct RpSpinlockCs;
critical_section::set_impl!(RpSpinlockCs);
const LOCK_UNOWNED: u8 = 0;
static LOCK_OWNER: AtomicU8 = AtomicU8::new(LOCK_UNOWNED);
const LOCK_ALREADY_OWNED: u8 = 2;
unsafe impl critical_section::Impl for RpSpinlockCs {
unsafe fn acquire() -> u8 {
RpSpinlockCs::acquire()
}
unsafe fn release(token: u8) {
RpSpinlockCs::release(token);
}
}
impl RpSpinlockCs {
unsafe fn acquire() -> u8 {
let interrupts_active = cortex_m::register::primask::read().is_active();
let core = pac::SIO.cpuid().read() as u8 + 1;
if LOCK_OWNER.load(Ordering::Acquire) == core {
LOCK_ALREADY_OWNED
} else {
loop {
cortex_m::interrupt::disable();
core::sync::atomic::compiler_fence(Ordering::SeqCst);
if let Some(lock) = Spinlock31::try_claim() {
core::mem::forget(lock);
LOCK_OWNER.store(core, Ordering::Relaxed);
break;
}
if interrupts_active {
cortex_m::interrupt::enable();
}
}
interrupts_active as _
}
}
unsafe fn release(token: u8) {
if token != LOCK_ALREADY_OWNED {
LOCK_OWNER.store(LOCK_UNOWNED, Ordering::Relaxed);
core::sync::atomic::compiler_fence(Ordering::SeqCst);
Spinlock31::release();
if token != 0 {
cortex_m::interrupt::enable();
}
}
}
}
pub struct Spinlock<const N: usize>(core::marker::PhantomData<()>)
where
Spinlock<N>: SpinlockValid;
impl<const N: usize> Spinlock<N>
where
Spinlock<N>: SpinlockValid,
{
pub fn try_claim() -> Option<Self> {
let lock = pac::SIO.spinlock(N).read();
if lock > 0 {
Some(Self(core::marker::PhantomData))
} else {
None
}
}
pub unsafe fn release() {
pac::SIO.spinlock(N).write_value(1);
}
}
impl<const N: usize> Drop for Spinlock<N>
where
Spinlock<N>: SpinlockValid,
{
fn drop(&mut self) {
unsafe { Self::release() }
}
}
pub(crate) type Spinlock31 = Spinlock<31>;
pub trait SpinlockValid {}
impl SpinlockValid for Spinlock<31> {}