From 2b7b7a917d0fc5917cb968c52bd19d710675f540 Mon Sep 17 00:00:00 2001
From: Nils Fitinghoff <nils.fitinghoff@mobilaris.se>
Date: Fri, 22 Sep 2023 16:12:57 +0300
Subject: [PATCH] spim: Fix SPIM with polling executors

Co-authored-by: Priit Laes <plaes@plaes.org>
---
 embassy-nrf/src/spim.rs | 67 +++++++++++++++++++++++++++--------------
 1 file changed, 45 insertions(+), 22 deletions(-)

diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs
index 99a195a6e..a0d2c8eb1 100644
--- a/embassy-nrf/src/spim.rs
+++ b/embassy-nrf/src/spim.rs
@@ -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")]