Merge pull request #1939 from nilfit/spim-anomaly-109

spim: Fix nRF52832 SPIM with polling executors
This commit is contained in:
Dario Nieuwenhuis 2023-09-23 11:14:16 +00:00 committed by GitHub
commit 1e80fd81f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 56 additions and 31 deletions

View File

@ -51,7 +51,7 @@ nrf52805 = ["nrf52805-pac", "_nrf52"]
nrf52810 = ["nrf52810-pac", "_nrf52"] nrf52810 = ["nrf52810-pac", "_nrf52"]
nrf52811 = ["nrf52811-pac", "_nrf52"] nrf52811 = ["nrf52811-pac", "_nrf52"]
nrf52820 = ["nrf52820-pac", "_nrf52"] nrf52820 = ["nrf52820-pac", "_nrf52"]
nrf52832 = ["nrf52832-pac", "_nrf52"] nrf52832 = ["nrf52832-pac", "_nrf52", "_nrf52832_anomaly_109"]
nrf52833 = ["nrf52833-pac", "_nrf52", "_gpio-p1"] nrf52833 = ["nrf52833-pac", "_nrf52", "_gpio-p1"]
nrf52840 = ["nrf52840-pac", "_nrf52", "_gpio-p1"] nrf52840 = ["nrf52840-pac", "_nrf52", "_gpio-p1"]
nrf5340-app-s = ["_nrf5340-app", "_s"] nrf5340-app-s = ["_nrf5340-app", "_s"]
@ -90,6 +90,9 @@ _ppi = []
_dppi = [] _dppi = []
_gpio-p1 = [] _gpio-p1 = []
# Errata workarounds
_nrf52832_anomaly_109 = []
[dependencies] [dependencies]
embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true } embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true }
embassy-sync = { version = "0.3.0", path = "../embassy-sync" } embassy-sync = { version = "0.3.0", path = "../embassy-sync" }

View File

@ -68,30 +68,14 @@ 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")] #[cfg(feature = "_nrf52832_anomaly_109")]
// NRF32 Anomaly 109 workaround... NRF52832 if r.events_started.read().bits() != 0 {
if r.intenset.read().started().is_enabled() && r.events_started.read().bits() != 0 { s.waker.wake();
// 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()); 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.waker.wake();
r.intenclr.write(|w| w.end().clear()); r.intenclr.write(|w| w.end().clear());
} }
} }
@ -222,8 +206,7 @@ impl<'d, T: Instance> Spim<'d, T> {
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(rx_len as _) }); r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) });
// ANOMALY 109 workaround #[cfg(feature = "_nrf52832_anomaly_109")]
#[cfg(feature = "nrf52832")]
{ {
let s = T::state(); let s = T::state();
@ -254,6 +237,9 @@ impl<'d, T: Instance> Spim<'d, T> {
fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
self.prepare(rx, tx)?; self.prepare(rx, tx)?;
#[cfg(feature = "_nrf52832_anomaly_109")]
while let Poll::Pending = self.nrf52832_dma_workaround_status() {}
// Wait for 'end' event. // Wait for 'end' event.
while T::regs().events_end.read().bits() == 0 {} while T::regs().events_end.read().bits() == 0 {}
@ -278,9 +264,19 @@ impl<'d, T: Instance> Spim<'d, T> {
async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
self.prepare(rx, tx)?; self.prepare(rx, tx)?;
#[cfg(feature = "_nrf52832_anomaly_109")]
poll_fn(|cx| {
let s = T::state();
s.waker.register(cx.waker());
self.nrf52832_dma_workaround_status()
})
.await;
// Wait for 'end' event. // Wait for 'end' event.
poll_fn(|cx| { poll_fn(|cx| {
T::state().end_waker.register(cx.waker()); T::state().waker.register(cx.waker());
if T::regs().events_end.read().bits() != 0 { if T::regs().events_end.read().bits() != 0 {
return Poll::Ready(()); return Poll::Ready(());
} }
@ -371,6 +367,32 @@ impl<'d, T: Instance> Spim<'d, T> {
pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> { pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> {
self.async_inner_from_ram(&mut [], data).await self.async_inner_from_ram(&mut [], data).await
} }
#[cfg(feature = "_nrf52832_anomaly_109")]
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> { impl<'d, T: Instance> Drop for Spim<'d, T> {
@ -395,7 +417,7 @@ impl<'d, T: Instance> Drop for Spim<'d, T> {
} }
pub(crate) mod sealed { pub(crate) mod sealed {
#[cfg(feature = "nrf52832")] #[cfg(feature = "_nrf52832_anomaly_109")]
use core::sync::atomic::AtomicU8; use core::sync::atomic::AtomicU8;
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
@ -403,20 +425,20 @@ pub(crate) mod sealed {
use super::*; use super::*;
pub struct State { pub struct State {
pub end_waker: AtomicWaker, pub waker: AtomicWaker,
#[cfg(feature = "nrf52832")] #[cfg(feature = "_nrf52832_anomaly_109")]
pub rx: AtomicU8, pub rx: AtomicU8,
#[cfg(feature = "nrf52832")] #[cfg(feature = "_nrf52832_anomaly_109")]
pub tx: AtomicU8, pub tx: AtomicU8,
} }
impl State { impl State {
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
end_waker: AtomicWaker::new(), waker: AtomicWaker::new(),
#[cfg(feature = "nrf52832")] #[cfg(feature = "_nrf52832_anomaly_109")]
rx: AtomicU8::new(0), rx: AtomicU8::new(0),
#[cfg(feature = "nrf52832")] #[cfg(feature = "_nrf52832_anomaly_109")]
tx: AtomicU8::new(0), tx: AtomicU8::new(0),
} }
} }