diff options
| author | Priit Laes <[email protected]> | 2023-09-06 23:15:33 +0300 |
|---|---|---|
| committer | Priit Laes <[email protected]> | 2023-09-07 18:58:22 +0300 |
| commit | 9de08d56a087d946bb768ed164aed459b0a2ce3c (patch) | |
| tree | 094d5344f66f9b2ad724f728c21a4811645ce4ff | |
| parent | 26740bb3ef01b4965d3be92622f0f4220b6cb931 (diff) | |
nrf: spim: Anomaly 109 workaround for SPIM peripheral (#460)
This implements SPIM TX workaround suggested from section 3.8.1
from Anomaly 109 addendum.
In workaround case we first keep track of original maxcnt values,
then initiate "fake" transfer with zero-length maxcnt values.
Once the "fake" transfer is triggered, we handle it, fill in the
original maxcnt values and restart the transmission.
| -rw-r--r-- | embassy-nrf/src/spim.rs | 60 |
1 files changed, 56 insertions, 4 deletions
diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index d131a43dd..a468bc302 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs | |||
| @@ -68,6 +68,28 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 68 | let r = T::regs(); | 68 | let r = T::regs(); |
| 69 | let s = T::state(); | 69 | let s = T::state(); |
| 70 | 70 | ||
| 71 | #[cfg(feature = "nrf52832")] | ||
| 72 | // NRF32 Anomaly 109 workaround... NRF52832 | ||
| 73 | if r.intenset.read().started().is_enabled() && r.events_started.read().bits() != 0 { | ||
| 74 | // Handle the first "fake" transmission | ||
| 75 | r.events_started.reset(); | ||
| 76 | r.events_end.reset(); | ||
| 77 | |||
| 78 | // Update DMA registers with correct rx/tx buffer sizes | ||
| 79 | r.rxd | ||
| 80 | .maxcnt | ||
| 81 | .write(|w| unsafe { w.maxcnt().bits(s.rx.load(Ordering::Relaxed)) }); | ||
| 82 | r.txd | ||
| 83 | .maxcnt | ||
| 84 | .write(|w| unsafe { w.maxcnt().bits(s.tx.load(Ordering::Relaxed)) }); | ||
| 85 | |||
| 86 | // Disable interrupt for STARTED event... | ||
| 87 | r.intenclr.write(|w| w.started().clear()); | ||
| 88 | // ... and start actual, hopefully glitch-free transmission | ||
| 89 | r.tasks_start.write(|w| unsafe { w.bits(1) }); | ||
| 90 | return; | ||
| 91 | } | ||
| 92 | |||
| 71 | if r.events_end.read().bits() != 0 { | 93 | if r.events_end.read().bits() != 0 { |
| 72 | s.end_waker.wake(); | 94 | s.end_waker.wake(); |
| 73 | r.intenclr.write(|w| w.end().clear()); | 95 | r.intenclr.write(|w| w.end().clear()); |
| @@ -223,14 +245,33 @@ impl<'d, T: Instance> Spim<'d, T> { | |||
| 223 | let r = T::regs(); | 245 | let r = T::regs(); |
| 224 | 246 | ||
| 225 | // Set up the DMA write. | 247 | // Set up the DMA write. |
| 226 | let (ptr, len) = slice_ptr_parts(tx); | 248 | let (ptr, tx_len) = slice_ptr_parts(tx); |
| 227 | r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); | 249 | r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); |
| 228 | r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); | 250 | r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) }); |
| 229 | 251 | ||
| 230 | // Set up the DMA read. | 252 | // Set up the DMA read. |
| 231 | let (ptr, len) = slice_ptr_parts_mut(rx); | 253 | let (ptr, rx_len) = slice_ptr_parts_mut(rx); |
| 232 | r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); | 254 | r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); |
| 233 | r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); | 255 | r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) }); |
| 256 | |||
| 257 | // ANOMALY 109 workaround | ||
| 258 | #[cfg(feature = "nrf52832")] | ||
| 259 | { | ||
| 260 | let s = T::state(); | ||
| 261 | |||
| 262 | r.events_started.reset(); | ||
| 263 | |||
| 264 | // Set rx/tx buffer lengths to 0... | ||
| 265 | r.txd.maxcnt.reset(); | ||
| 266 | r.rxd.maxcnt.reset(); | ||
| 267 | |||
| 268 | // ...and keep track of original buffer lengths... | ||
| 269 | s.tx.store(tx_len as _, Ordering::Relaxed); | ||
| 270 | s.rx.store(rx_len as _, Ordering::Relaxed); | ||
| 271 | |||
| 272 | // ...signalling the start of the fake transfer. | ||
| 273 | r.intenset.write(|w| w.started().bit(true)); | ||
| 274 | } | ||
| 234 | 275 | ||
| 235 | // Reset and enable the event | 276 | // Reset and enable the event |
| 236 | r.events_end.reset(); | 277 | r.events_end.reset(); |
| @@ -386,18 +427,29 @@ impl<'d, T: Instance> Drop for Spim<'d, T> { | |||
| 386 | } | 427 | } |
| 387 | 428 | ||
| 388 | pub(crate) mod sealed { | 429 | pub(crate) mod sealed { |
| 430 | #[cfg(feature = "nrf52832")] | ||
| 431 | use core::sync::atomic::AtomicU8; | ||
| 432 | |||
| 389 | use embassy_sync::waitqueue::AtomicWaker; | 433 | use embassy_sync::waitqueue::AtomicWaker; |
| 390 | 434 | ||
| 391 | use super::*; | 435 | use super::*; |
| 392 | 436 | ||
| 393 | pub struct State { | 437 | pub struct State { |
| 394 | pub end_waker: AtomicWaker, | 438 | pub end_waker: AtomicWaker, |
| 439 | #[cfg(feature = "nrf52832")] | ||
| 440 | pub rx: AtomicU8, | ||
| 441 | #[cfg(feature = "nrf52832")] | ||
| 442 | pub tx: AtomicU8, | ||
| 395 | } | 443 | } |
| 396 | 444 | ||
| 397 | impl State { | 445 | impl State { |
| 398 | pub const fn new() -> Self { | 446 | pub const fn new() -> Self { |
| 399 | Self { | 447 | Self { |
| 400 | end_waker: AtomicWaker::new(), | 448 | end_waker: AtomicWaker::new(), |
| 449 | #[cfg(feature = "nrf52832")] | ||
| 450 | rx: AtomicU8::new(0), | ||
| 451 | #[cfg(feature = "nrf52832")] | ||
| 452 | tx: AtomicU8::new(0), | ||
| 401 | } | 453 | } |
| 402 | } | 454 | } |
| 403 | } | 455 | } |
