spim: Fix SPIM with polling executors

Co-authored-by: Priit Laes <plaes@plaes.org>
This commit is contained in:
Nils Fitinghoff 2023-09-22 16:12:57 +03:00
parent f1488864eb
commit 2b7b7a917d

View File

@ -69,29 +69,13 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
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...
if r.events_started.read().bits() != 0 {
s.waker.wake();
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();
s.waker.wake();
r.intenclr.write(|w| w.end().clear());
}
}
@ -254,6 +238,9 @@ impl<'d, T: Instance> Spim<'d, T> {
fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
self.prepare(rx, tx)?;
#[cfg(feature = "nrf52832")]
while let Poll::Pending = self.nrf52832_dma_workaround_status() {}
// Wait for 'end' event.
while T::regs().events_end.read().bits() == 0 {}
@ -278,9 +265,19 @@ impl<'d, T: Instance> Spim<'d, T> {
async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
self.prepare(rx, tx)?;
#[cfg(feature = "nrf52832")]
poll_fn(|cx| {
let s = T::state();
s.waker.register(cx.waker());
self.nrf52832_dma_workaround_status()
})
.await;
// Wait for 'end' event.
poll_fn(|cx| {
T::state().end_waker.register(cx.waker());
T::state().waker.register(cx.waker());
if T::regs().events_end.read().bits() != 0 {
return Poll::Ready(());
}
@ -371,6 +368,32 @@ impl<'d, T: Instance> Spim<'d, T> {
pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> {
self.async_inner_from_ram(&mut [], data).await
}
#[cfg(feature = "nrf52832")]
fn nrf52832_dma_workaround_status(&mut self) -> Poll<()> {
let r = T::regs();
if r.events_started.read().bits() != 0 {
let s = T::state();
// 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)) });
r.intenset.write(|w| w.end().set());
// ... and start actual, hopefully glitch-free transmission
r.tasks_start.write(|w| unsafe { w.bits(1) });
return Poll::Ready(());
}
Poll::Pending
}
}
impl<'d, T: Instance> Drop for Spim<'d, T> {
@ -403,7 +426,7 @@ pub(crate) mod sealed {
use super::*;
pub struct State {
pub end_waker: AtomicWaker,
pub waker: AtomicWaker,
#[cfg(feature = "nrf52832")]
pub rx: AtomicU8,
#[cfg(feature = "nrf52832")]
@ -413,7 +436,7 @@ pub(crate) mod sealed {
impl State {
pub const fn new() -> Self {
Self {
end_waker: AtomicWaker::new(),
waker: AtomicWaker::new(),
#[cfg(feature = "nrf52832")]
rx: AtomicU8::new(0),
#[cfg(feature = "nrf52832")]