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:
		| @@ -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), | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user