From 9de08d56a087d946bb768ed164aed459b0a2ce3c Mon Sep 17 00:00:00 2001 From: Priit Laes Date: Wed, 6 Sep 2023 23:15:33 +0300 Subject: [PATCH] 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. --- embassy-nrf/src/spim.rs | 60 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index d131a43d..a468bc30 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -68,6 +68,28 @@ impl interrupt::typelevel::Handler for InterruptHandl let r = T::regs(); let s = T::state(); + #[cfg(feature = "nrf52832")] + // NRF32 Anomaly 109 workaround... NRF52832 + if r.intenset.read().started().is_enabled() && r.events_started.read().bits() != 0 { + // Handle the first "fake" transmission + r.events_started.reset(); + r.events_end.reset(); + + // Update DMA registers with correct rx/tx buffer sizes + r.rxd + .maxcnt + .write(|w| unsafe { w.maxcnt().bits(s.rx.load(Ordering::Relaxed)) }); + r.txd + .maxcnt + .write(|w| unsafe { w.maxcnt().bits(s.tx.load(Ordering::Relaxed)) }); + + // Disable interrupt for STARTED event... + r.intenclr.write(|w| w.started().clear()); + // ... and start actual, hopefully glitch-free transmission + r.tasks_start.write(|w| unsafe { w.bits(1) }); + return; + } + if r.events_end.read().bits() != 0 { s.end_waker.wake(); r.intenclr.write(|w| w.end().clear()); @@ -223,14 +245,33 @@ impl<'d, T: Instance> Spim<'d, T> { let r = T::regs(); // Set up the DMA write. - let (ptr, len) = slice_ptr_parts(tx); + let (ptr, tx_len) = slice_ptr_parts(tx); r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); - r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) }); // Set up the DMA read. - let (ptr, len) = slice_ptr_parts_mut(rx); + let (ptr, rx_len) = slice_ptr_parts_mut(rx); r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); - r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) }); + + // ANOMALY 109 workaround + #[cfg(feature = "nrf52832")] + { + let s = T::state(); + + r.events_started.reset(); + + // Set rx/tx buffer lengths to 0... + r.txd.maxcnt.reset(); + r.rxd.maxcnt.reset(); + + // ...and keep track of original buffer lengths... + s.tx.store(tx_len as _, Ordering::Relaxed); + s.rx.store(rx_len as _, Ordering::Relaxed); + + // ...signalling the start of the fake transfer. + r.intenset.write(|w| w.started().bit(true)); + } // Reset and enable the event r.events_end.reset(); @@ -386,18 +427,29 @@ impl<'d, T: Instance> Drop for Spim<'d, T> { } pub(crate) mod sealed { + #[cfg(feature = "nrf52832")] + use core::sync::atomic::AtomicU8; + use embassy_sync::waitqueue::AtomicWaker; use super::*; pub struct State { pub end_waker: AtomicWaker, + #[cfg(feature = "nrf52832")] + pub rx: AtomicU8, + #[cfg(feature = "nrf52832")] + pub tx: AtomicU8, } impl State { pub const fn new() -> Self { Self { end_waker: AtomicWaker::new(), + #[cfg(feature = "nrf52832")] + rx: AtomicU8::new(0), + #[cfg(feature = "nrf52832")] + tx: AtomicU8::new(0), } } }