Merge pull request #3001 from embassy-rs/rp-spinlock-fix
rp: fix spinlocks staying locked after reset.
This commit is contained in:
commit
8b9e2efec2
1 changed files with 61 additions and 0 deletions
|
@ -352,12 +352,60 @@ pub fn init(config: config::Config) -> Peripherals {
|
||||||
peripherals
|
peripherals
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rt")]
|
||||||
|
#[cortex_m_rt::pre_init]
|
||||||
|
unsafe fn pre_init() {
|
||||||
|
// SIO does not get reset when core0 is reset with either `scb::sys_reset()` or with SWD.
|
||||||
|
// Since we're using SIO spinlock 31 for the critical-section impl, this causes random
|
||||||
|
// hangs if we reset in the middle of a CS, because the next boot sees the spinlock
|
||||||
|
// as locked and waits forever.
|
||||||
|
//
|
||||||
|
// See https://github.com/embassy-rs/embassy/issues/1736
|
||||||
|
// and https://github.com/rp-rs/rp-hal/issues/292
|
||||||
|
// and https://matrix.to/#/!vhKMWjizPZBgKeknOo:matrix.org/$VfOkQgyf1PjmaXZbtycFzrCje1RorAXd8BQFHTl4d5M
|
||||||
|
//
|
||||||
|
// According to Raspberry Pi, this is considered Working As Intended, and not an errata,
|
||||||
|
// even though this behavior is different from every other ARM chip (sys_reset usually resets
|
||||||
|
// the *system* as its name implies, not just the current core).
|
||||||
|
//
|
||||||
|
// To fix this, reset SIO on boot. We must do this in pre_init because it's unsound to do it
|
||||||
|
// in `embassy_rp::init`, since the user could've acquired a CS by then. pre_init is guaranteed
|
||||||
|
// to run before any user code.
|
||||||
|
//
|
||||||
|
// A similar thing could happen with PROC1. It is unclear whether it's possible for PROC1
|
||||||
|
// to stay unreset through a PROC0 reset, so we reset it anyway just in case.
|
||||||
|
//
|
||||||
|
// Important info from PSM logic (from Luke Wren in above Matrix thread)
|
||||||
|
//
|
||||||
|
// The logic is, each PSM stage is reset if either of the following is true:
|
||||||
|
// - The previous stage is in reset and FRCE_ON is false
|
||||||
|
// - FRCE_OFF is true
|
||||||
|
//
|
||||||
|
// The PSM order is SIO -> PROC0 -> PROC1.
|
||||||
|
// So, we have to force-on PROC0 to prevent it from getting reset when resetting SIO.
|
||||||
|
pac::PSM.frce_on().write_and_wait(|w| {
|
||||||
|
w.set_proc0(true);
|
||||||
|
});
|
||||||
|
// Then reset SIO and PROC1.
|
||||||
|
pac::PSM.frce_off().write_and_wait(|w| {
|
||||||
|
w.set_sio(true);
|
||||||
|
w.set_proc1(true);
|
||||||
|
});
|
||||||
|
// clear force_off first, force_on second. The other way around would reset PROC0.
|
||||||
|
pac::PSM.frce_off().write_and_wait(|_| {});
|
||||||
|
pac::PSM.frce_on().write_and_wait(|_| {});
|
||||||
|
}
|
||||||
|
|
||||||
/// Extension trait for PAC regs, adding atomic xor/bitset/bitclear writes.
|
/// Extension trait for PAC regs, adding atomic xor/bitset/bitclear writes.
|
||||||
|
#[allow(unused)]
|
||||||
trait RegExt<T: Copy> {
|
trait RegExt<T: Copy> {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
|
fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
|
||||||
fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
|
fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
|
||||||
fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
|
fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
|
||||||
|
fn write_and_wait<R>(&self, f: impl FnOnce(&mut T) -> R) -> R
|
||||||
|
where
|
||||||
|
T: PartialEq;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Default + Copy, A: pac::common::Write> RegExt<T> for pac::common::Reg<T, A> {
|
impl<T: Default + Copy, A: pac::common::Write> RegExt<T> for pac::common::Reg<T, A> {
|
||||||
|
@ -390,4 +438,17 @@ impl<T: Default + Copy, A: pac::common::Write> RegExt<T> for pac::common::Reg<T,
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_and_wait<R>(&self, f: impl FnOnce(&mut T) -> R) -> R
|
||||||
|
where
|
||||||
|
T: PartialEq,
|
||||||
|
{
|
||||||
|
let mut val = Default::default();
|
||||||
|
let res = f(&mut val);
|
||||||
|
unsafe {
|
||||||
|
self.as_ptr().write_volatile(val);
|
||||||
|
while self.as_ptr().read_volatile() != val {}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue