diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-05-26 22:18:40 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-05-26 22:18:40 +0000 |
| commit | 8b9e2efec25ae36041038b70f608fe55f5061a1f (patch) | |
| tree | fec29625eb74fb3de073643d96412d35b824d047 | |
| parent | ec185b2fd2ab34c24ed7e93b7e8c32ed6b346987 (diff) | |
| parent | d18a919ab9cfa1c07d339dd885d8268ab0abb7e6 (diff) | |
Merge pull request #3001 from embassy-rs/rp-spinlock-fix
rp: fix spinlocks staying locked after reset.
| -rw-r--r-- | embassy-rp/src/lib.rs | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 1c83e306d..507d42280 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs | |||
| @@ -352,12 +352,60 @@ pub fn init(config: config::Config) -> Peripherals { | |||
| 352 | peripherals | 352 | peripherals |
| 353 | } | 353 | } |
| 354 | 354 | ||
| 355 | #[cfg(feature = "rt")] | ||
| 356 | #[cortex_m_rt::pre_init] | ||
| 357 | unsafe fn pre_init() { | ||
| 358 | // SIO does not get reset when core0 is reset with either `scb::sys_reset()` or with SWD. | ||
| 359 | // Since we're using SIO spinlock 31 for the critical-section impl, this causes random | ||
| 360 | // hangs if we reset in the middle of a CS, because the next boot sees the spinlock | ||
| 361 | // as locked and waits forever. | ||
| 362 | // | ||
| 363 | // See https://github.com/embassy-rs/embassy/issues/1736 | ||
| 364 | // and https://github.com/rp-rs/rp-hal/issues/292 | ||
| 365 | // and https://matrix.to/#/!vhKMWjizPZBgKeknOo:matrix.org/$VfOkQgyf1PjmaXZbtycFzrCje1RorAXd8BQFHTl4d5M | ||
| 366 | // | ||
| 367 | // According to Raspberry Pi, this is considered Working As Intended, and not an errata, | ||
| 368 | // even though this behavior is different from every other ARM chip (sys_reset usually resets | ||
| 369 | // the *system* as its name implies, not just the current core). | ||
| 370 | // | ||
| 371 | // To fix this, reset SIO on boot. We must do this in pre_init because it's unsound to do it | ||
| 372 | // in `embassy_rp::init`, since the user could've acquired a CS by then. pre_init is guaranteed | ||
| 373 | // to run before any user code. | ||
| 374 | // | ||
| 375 | // A similar thing could happen with PROC1. It is unclear whether it's possible for PROC1 | ||
| 376 | // to stay unreset through a PROC0 reset, so we reset it anyway just in case. | ||
| 377 | // | ||
| 378 | // Important info from PSM logic (from Luke Wren in above Matrix thread) | ||
| 379 | // | ||
| 380 | // The logic is, each PSM stage is reset if either of the following is true: | ||
| 381 | // - The previous stage is in reset and FRCE_ON is false | ||
| 382 | // - FRCE_OFF is true | ||
| 383 | // | ||
| 384 | // The PSM order is SIO -> PROC0 -> PROC1. | ||
| 385 | // So, we have to force-on PROC0 to prevent it from getting reset when resetting SIO. | ||
| 386 | pac::PSM.frce_on().write_and_wait(|w| { | ||
| 387 | w.set_proc0(true); | ||
| 388 | }); | ||
| 389 | // Then reset SIO and PROC1. | ||
| 390 | pac::PSM.frce_off().write_and_wait(|w| { | ||
| 391 | w.set_sio(true); | ||
| 392 | w.set_proc1(true); | ||
| 393 | }); | ||
| 394 | // clear force_off first, force_on second. The other way around would reset PROC0. | ||
| 395 | pac::PSM.frce_off().write_and_wait(|_| {}); | ||
| 396 | pac::PSM.frce_on().write_and_wait(|_| {}); | ||
| 397 | } | ||
| 398 | |||
| 355 | /// Extension trait for PAC regs, adding atomic xor/bitset/bitclear writes. | 399 | /// Extension trait for PAC regs, adding atomic xor/bitset/bitclear writes. |
| 400 | #[allow(unused)] | ||
| 356 | trait RegExt<T: Copy> { | 401 | trait RegExt<T: Copy> { |
| 357 | #[allow(unused)] | 402 | #[allow(unused)] |
| 358 | fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R; | 403 | fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R; |
| 359 | fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R; | 404 | fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R; |
| 360 | fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R; | 405 | fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R; |
| 406 | fn write_and_wait<R>(&self, f: impl FnOnce(&mut T) -> R) -> R | ||
| 407 | where | ||
| 408 | T: PartialEq; | ||
| 361 | } | 409 | } |
| 362 | 410 | ||
| 363 | impl<T: Default + Copy, A: pac::common::Write> RegExt<T> for pac::common::Reg<T, A> { | 411 | 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, | |||
| 390 | } | 438 | } |
| 391 | res | 439 | res |
| 392 | } | 440 | } |
| 441 | |||
| 442 | fn write_and_wait<R>(&self, f: impl FnOnce(&mut T) -> R) -> R | ||
| 443 | where | ||
| 444 | T: PartialEq, | ||
| 445 | { | ||
| 446 | let mut val = Default::default(); | ||
| 447 | let res = f(&mut val); | ||
| 448 | unsafe { | ||
| 449 | self.as_ptr().write_volatile(val); | ||
| 450 | while self.as_ptr().read_volatile() != val {} | ||
| 451 | } | ||
| 452 | res | ||
| 453 | } | ||
| 393 | } | 454 | } |
