nrf: spim: Anomaly 109 workaround for SPIM peripheral ()

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.
This commit is contained in:
Priit Laes 2023-09-06 23:15:33 +03:00
parent 26740bb3ef
commit 9de08d56a0

View file

@ -68,6 +68,28 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> 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),
}
}
}