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.
This commit is contained in:
parent
26740bb3ef
commit
9de08d56a0
@ -68,6 +68,28 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
|||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
let s = T::state();
|
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 {
|
if r.events_end.read().bits() != 0 {
|
||||||
s.end_waker.wake();
|
s.end_waker.wake();
|
||||||
r.intenclr.write(|w| w.end().clear());
|
r.intenclr.write(|w| w.end().clear());
|
||||||
@ -223,14 +245,33 @@ impl<'d, T: Instance> Spim<'d, T> {
|
|||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
// Set up the DMA write.
|
// 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.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.
|
// 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.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
|
// Reset and enable the event
|
||||||
r.events_end.reset();
|
r.events_end.reset();
|
||||||
@ -386,18 +427,29 @@ impl<'d, T: Instance> Drop for Spim<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
|
#[cfg(feature = "nrf52832")]
|
||||||
|
use core::sync::atomic::AtomicU8;
|
||||||
|
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub end_waker: AtomicWaker,
|
pub end_waker: AtomicWaker,
|
||||||
|
#[cfg(feature = "nrf52832")]
|
||||||
|
pub rx: AtomicU8,
|
||||||
|
#[cfg(feature = "nrf52832")]
|
||||||
|
pub tx: AtomicU8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
end_waker: AtomicWaker::new(),
|
end_waker: AtomicWaker::new(),
|
||||||
|
#[cfg(feature = "nrf52832")]
|
||||||
|
rx: AtomicU8::new(0),
|
||||||
|
#[cfg(feature = "nrf52832")]
|
||||||
|
tx: AtomicU8::new(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user