1use core::cell::RefCell;
3use core::future::poll_fn;
4use core::mem::MaybeUninit;
5use core::ops::ControlFlow;
6use core::slice;
7use core::sync::atomic::{compiler_fence, Ordering};
8use core::task::Poll;
9
10use cortex_m::peripheral::NVIC;
11use embassy_nrf::interrupt::Interrupt;
12use embassy_nrf::nvmc::{FLASH_SIZE, PAGE_SIZE};
13use embassy_nrf::pac::nvmc::vals::Wen;
14use embassy_nrf::peripherals::NVMC;
15use embassy_nrf::{pac, Peri};
16use embassy_sync::blocking_mutex::raw::RawMutex;
17use embassy_sync::blocking_mutex::Mutex;
18use embassy_sync::waitqueue::WakerRegistration;
19use embedded_storage::nor_flash::{ErrorType, NorFlashError, NorFlashErrorKind};
20
21use crate::{raw, MultiprotocolServiceLayer, RetVal};
22
23struct Timer0RawMutex;
26unsafe impl RawMutex for Timer0RawMutex {
27 const INIT: Self = Timer0RawMutex;
28 fn lock<R>(&self, f: impl FnOnce() -> R) -> R {
29 unsafe {
30 let nvic = &*NVIC::PTR;
31 nvic.icer[0].write(1 << Interrupt::TIMER0 as u8);
32 compiler_fence(Ordering::SeqCst);
33 let r = f();
34 compiler_fence(Ordering::SeqCst);
35 nvic.iser[0].write(1 << Interrupt::TIMER0 as u8);
36 r
37 }
38 }
39}
40
41#[derive(Debug, Copy, Clone, PartialEq, Eq)]
43#[cfg_attr(feature = "defmt", derive(defmt::Format))]
44pub enum FlashError {
45 Mpsl(crate::Error),
47 OutOfBounds,
49 Unaligned,
51}
52
53pub struct Flash<'d> {
77 _mpsl: &'d MultiprotocolServiceLayer<'d>,
78 _p: Peri<'d, NVMC>,
79}
80
81struct State {
83 inner: Mutex<Timer0RawMutex, RefCell<InnerState>>,
84}
85
86struct InnerState {
88 taken: bool,
89 operation: FlashOp,
90 slot_duration_us: u32,
91 waker: WakerRegistration,
92 result: Option<Result<(), FlashError>>,
93 timeslot_request: raw::mpsl_timeslot_request_t,
94 return_param: raw::mpsl_timeslot_signal_return_param_t,
95}
96
97enum FlashOp {
99 None,
100 Erase {
101 address: u32,
103 elapsed: u32,
105 to: u32,
107 },
108 Write {
109 dest: *mut u32,
111 src: *const u32,
113 words: u32,
114 },
115}
116
117unsafe impl Send for InnerState {}
121unsafe impl Sync for InnerState {}
122
123const TIMESLOT_FLASH_HFCLK_CFG: u8 = raw::MPSL_TIMESLOT_HFCLK_CFG_NO_GUARANTEE as u8;
124const TIMESLOT_FLASH_PRIORITY: u8 = raw::MPSL_TIMESLOT_PRIORITY_NORMAL as u8;
125
126const TIMESLOT_TIMEOUT_PRIORITY_NORMAL_US: u32 = 30000;
128const TIMESLOT_SLACK_US: u32 = 1000;
130
131const ERASE_PAGE_DURATION_US: u32 = 85_000;
133const WRITE_WORD_DURATION_US: u32 = 41;
134
135#[cfg(not(feature = "nrf52832"))]
136const ERASE_PARTIAL_PAGE_DURATION_MS: u32 = 10;
137#[cfg(not(feature = "nrf52832"))]
138const ERASE_PARTIAL_PAGE_DURATION_US: u32 = ERASE_PARTIAL_PAGE_DURATION_MS * 1000;
139
140const WORD_SIZE: u32 = 4;
141
142#[cfg(not(feature = "nrf52832"))]
144const TIMESLOT_LENGTH_ERASE_US: u32 = ERASE_PARTIAL_PAGE_DURATION_US;
145
146#[cfg(feature = "nrf52832")]
147const TIMESLOT_LENGTH_ERASE_US: u32 = ERASE_PAGE_DURATION_US;
148
149const TIMESLOT_LENGTH_WRITE_US: u32 = 7500;
150
151static STATE: State = State::new();
152
153impl<'d> Flash<'d> {
154 pub fn take(_mpsl: &'d MultiprotocolServiceLayer<'d>, _p: Peri<'d, NVMC>) -> Flash<'d> {
162 STATE.with_inner(|state| {
163 if state.taken {
164 panic!("nrf_mpsl::Flash::take() called multiple times.")
165 }
166 state.taken = true;
167 });
168
169 Self { _mpsl, _p }
170 }
171
172 pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), FlashError> {
176 if offset as usize >= FLASH_SIZE || offset as usize + bytes.len() > FLASH_SIZE {
177 return Err(FlashError::OutOfBounds);
178 }
179
180 let flash_data = unsafe { slice::from_raw_parts(offset as *const u8, bytes.len()) };
181 bytes.copy_from_slice(flash_data);
182 Ok(())
183 }
184
185 async fn do_op(&mut self, slot_duration_us: u32, op: FlashOp) -> Result<(), FlashError> {
193 poll_fn(|cx| {
195 STATE.with_inner(|state| {
196 state.waker.register(cx.waker());
197 if let FlashOp::None = state.operation {
198 Poll::Ready(())
199 } else {
200 Poll::Pending
201 }
202 })
203 })
204 .await;
205
206 let mut session_id: u8 = 0;
207 let ret =
208 unsafe { raw::mpsl_timeslot_session_open(Some(timeslot_session_callback), (&mut session_id) as *mut _) };
209 RetVal::from(ret).to_result()?;
210
211 let _drop = OnDrop::new(|| {
213 let _ = unsafe { raw::mpsl_timeslot_session_close(session_id) };
214 });
215
216 let request = STATE.with_inner(|state| {
218 state.result = None;
219 state.operation = op;
220 state.slot_duration_us = slot_duration_us;
221 state.timeslot_request.request_type = raw::MPSL_TIMESLOT_REQ_TYPE_EARLIEST as u8;
222 state.timeslot_request.params.earliest = raw::mpsl_timeslot_request_earliest_t {
223 hfclk: TIMESLOT_FLASH_HFCLK_CFG,
224 priority: TIMESLOT_FLASH_PRIORITY,
225 length_us: slot_duration_us + TIMESLOT_SLACK_US,
226 timeout_us: TIMESLOT_TIMEOUT_PRIORITY_NORMAL_US,
227 };
228 core::ptr::from_ref(&state.timeslot_request)
229 });
230
231 let ret = unsafe { raw::mpsl_timeslot_request(session_id, request) };
232 RetVal::from(ret).to_result()?;
233
234 poll_fn(|cx| {
236 STATE.with_inner(|state| {
237 state.waker.register(cx.waker());
238 match state.result.take() {
239 Some(result) => Poll::Ready(result),
240 None => Poll::Pending,
241 }
242 })
243 })
244 .await?;
245
246 _drop.defuse();
248
249 unsafe {
250 let ret = raw::mpsl_timeslot_session_close(session_id);
251 RetVal::from(ret).to_result()?;
252 }
253
254 Ok(())
255 }
256
257 pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), FlashError> {
262 if to < from || to as usize > FLASH_SIZE {
263 return Err(FlashError::OutOfBounds);
264 }
265 if from as usize % PAGE_SIZE != 0 || to as usize % PAGE_SIZE != 0 {
266 return Err(FlashError::Unaligned);
267 }
268
269 self.do_op(
270 TIMESLOT_LENGTH_ERASE_US,
271 FlashOp::Erase {
272 elapsed: 0,
273 address: from,
274 to,
275 },
276 )
277 .await?;
278 Ok(())
279 }
280
281 pub async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), FlashError> {
286 if offset as usize + data.len() > FLASH_SIZE {
287 return Err(FlashError::OutOfBounds);
288 }
289 if offset as usize % 4 != 0 || data.len() % 4 != 0 {
290 return Err(FlashError::Unaligned);
291 }
292
293 let src = data.as_ptr() as *const u32;
294 let dest = offset as *mut u32;
295 let words = data.len() as u32 / WORD_SIZE;
296 self.do_op(TIMESLOT_LENGTH_WRITE_US, FlashOp::Write { dest, words, src })
297 .await?;
298 Ok(())
299 }
300}
301
302unsafe extern "C" fn timeslot_session_callback(
303 session_id: u8,
304 signal: u32,
305) -> *mut raw::mpsl_timeslot_signal_return_param_t {
306 unsafe fn get_timeslot_time_us() -> u32 {
310 let p = pac::TIMER0;
311 p.tasks_capture(0).write_value(1);
312 p.cc(0).read()
313 }
314
315 match signal {
316 raw::MPSL_TIMESLOT_SIGNAL_START => STATE.with_inner(|state| {
317 match state
318 .operation
319 .perform(|| get_timeslot_time_us(), state.slot_duration_us)
320 {
321 ControlFlow::Continue(_) => {
322 state.return_param.callback_action = raw::MPSL_TIMESLOT_SIGNAL_ACTION_REQUEST as u8;
323 state.timeslot_request.params.earliest.priority = raw::MPSL_TIMESLOT_PRIORITY_NORMAL as u8;
324 state.timeslot_request.params.earliest.timeout_us = TIMESLOT_TIMEOUT_PRIORITY_NORMAL_US;
325 state.return_param.params.request.p_next = &mut state.timeslot_request;
326 }
327 ControlFlow::Break(_) => {
328 let p = pac::NVMC;
329 p.config().write(|w| w.set_wen(Wen::REN));
330 while !p.ready().read().ready() {}
331 state.result.replace(Ok(()));
332 state.return_param.callback_action = raw::MPSL_TIMESLOT_SIGNAL_ACTION_END as u8;
333 state.waker.wake();
334 }
335 }
336 &mut state.return_param as *mut _
337 }),
338 raw::MPSL_TIMESLOT_SIGNAL_SESSION_IDLE => core::ptr::null_mut(),
339
340 raw::MPSL_TIMESLOT_SIGNAL_SESSION_CLOSED => {
341 STATE.with_inner(|state| {
342 state.operation = FlashOp::None;
343 state.waker.wake();
344 });
345 core::ptr::null_mut()
346 }
347 raw::MPSL_TIMESLOT_SIGNAL_CANCELLED | raw::MPSL_TIMESLOT_SIGNAL_BLOCKED => {
348 STATE.with_inner(|state| {
349 state.timeslot_request.params.earliest.priority = raw::MPSL_TIMESLOT_PRIORITY_HIGH as u8;
350 state.timeslot_request.params.earliest.timeout_us = raw::MPSL_TIMESLOT_EARLIEST_TIMEOUT_MAX_US;
351 let ret = unsafe { raw::mpsl_timeslot_request(session_id, &state.timeslot_request) };
352 assert!(ret == 0);
353 });
354 core::ptr::null_mut()
355 }
356 raw::MPSL_TIMESLOT_SIGNAL_OVERSTAYED => {
357 panic!("Used too much of our timeslot");
358 }
359 _ => core::ptr::null_mut(),
360 }
361}
362
363impl State {
364 pub const fn new() -> Self {
365 Self {
366 inner: Mutex::new(RefCell::new(InnerState {
367 taken: false,
368 operation: FlashOp::None,
369 result: None,
370 slot_duration_us: 0,
371 waker: WakerRegistration::new(),
372 timeslot_request: raw::mpsl_timeslot_request_t {
373 request_type: raw::MPSL_TIMESLOT_REQ_TYPE_EARLIEST as u8,
374 params: raw::mpsl_timeslot_request_t__bindgen_ty_1 {
375 earliest: raw::mpsl_timeslot_request_earliest_t {
376 hfclk: TIMESLOT_FLASH_HFCLK_CFG,
377 priority: TIMESLOT_FLASH_PRIORITY,
378 length_us: 0,
379 timeout_us: TIMESLOT_TIMEOUT_PRIORITY_NORMAL_US,
380 },
381 },
382 },
383 return_param: raw::mpsl_timeslot_signal_return_param_t {
384 callback_action: 0,
385 params: raw::mpsl_timeslot_signal_return_param_t__bindgen_ty_1 {
386 request: raw::mpsl_timeslot_signal_return_param_t__bindgen_ty_1__bindgen_ty_1 {
387 p_next: core::ptr::null_mut(),
388 },
389 },
390 },
391 })),
392 }
393 }
394
395 fn with_inner<F: FnOnce(&mut InnerState) -> R, R>(&self, f: F) -> R {
396 self.inner.lock(|inner| {
397 let mut inner = inner.borrow_mut();
398 f(&mut inner)
399 })
400 }
401}
402
403impl FlashOp {
404 #[cfg(not(feature = "nrf52832"))]
405 fn erase<F: Fn() -> u32>(
406 get_time: F,
407 slot_duration_us: u32,
408 elapsed: &mut u32,
409 address: &mut u32,
410 to: u32,
411 ) -> core::ops::ControlFlow<()> {
412 let p = pac::NVMC;
413 loop {
414 p.config().write(|w| w.set_wen(Wen::EEN));
416 p.erasepagepartialcfg().write(|w| w.0 = ERASE_PARTIAL_PAGE_DURATION_MS);
417 while !p.ready().read().ready() {}
418
419 p.erasepagepartial().write_value(*address);
420 while !p.ready().read().ready() {}
421 p.config().write(|w| w.set_wen(Wen::REN));
422
423 *elapsed += ERASE_PARTIAL_PAGE_DURATION_US;
424 if *elapsed > ERASE_PAGE_DURATION_US {
425 *address += PAGE_SIZE as u32;
426 if *address >= to {
427 return ControlFlow::Break(());
428 }
429 }
430 if get_time() + ERASE_PARTIAL_PAGE_DURATION_US >= slot_duration_us {
431 return ControlFlow::Continue(());
432 }
433 }
434 }
435
436 #[cfg(feature = "nrf52832")]
438 fn erase<F: Fn() -> u32>(
439 _get_time: F,
440 _slot_duration_us: u32,
441 _elapsed: &mut u32,
442 address: &mut u32,
443 to: u32,
444 ) -> core::ops::ControlFlow<()> {
445 let p = pac::NVMC;
446 p.config().write(|w| w.set_wen(Wen::EEN));
447 while !p.ready().read().ready() {}
448 p.erasepage().write_value(*address);
449 while !p.ready().read().ready() {}
450 p.config().write(|w| w.set_wen(Wen::REN));
451 *address += PAGE_SIZE as u32;
452 if *address >= to {
453 ControlFlow::Break(())
454 } else {
455 ControlFlow::Continue(())
456 }
457 }
458
459 fn perform<F: Fn() -> u32>(&mut self, get_time: F, slot_duration_us: u32) -> core::ops::ControlFlow<()> {
460 match self {
461 Self::Erase { elapsed, address, to } => {
462 if *address >= *to {
464 return ControlFlow::Break(());
465 }
466 Self::erase(get_time, slot_duration_us, elapsed, address, *to)
467 }
468 Self::Write { dest, src, words } => {
469 let p = pac::NVMC;
470 let mut i = 0;
471 if *words > 0 {
473 loop {
474 p.config().write(|w| w.set_wen(Wen::WEN));
475 while !p.ready().read().ready() {}
476 unsafe {
477 let w = core::ptr::read_unaligned(src.add(i));
478 core::ptr::write_volatile(dest.add(i), w);
479 }
480 while !p.ready().read().ready() {}
481 i += 1;
482 if get_time() + WRITE_WORD_DURATION_US >= slot_duration_us || ((i as u32) >= *words) {
483 break;
484 }
485 }
486 }
487
488 unsafe {
489 *src = src.add(i);
490 *dest = dest.add(i);
491 *words -= i as u32;
492 }
493
494 if *words == 0 {
495 ControlFlow::Break(())
496 } else {
497 ControlFlow::Continue(())
498 }
499 }
500 FlashOp::None => ControlFlow::Break(()),
501 }
502 }
503}
504
505impl ErrorType for Flash<'_> {
506 type Error = FlashError;
507}
508
509impl embedded_storage_async::nor_flash::MultiwriteNorFlash for Flash<'_> {}
510
511impl NorFlashError for FlashError {
512 fn kind(&self) -> NorFlashErrorKind {
513 match self {
514 Self::Mpsl(_) => NorFlashErrorKind::Other,
515 Self::OutOfBounds => NorFlashErrorKind::OutOfBounds,
516 Self::Unaligned => NorFlashErrorKind::NotAligned,
517 }
518 }
519}
520
521impl embedded_storage::nor_flash::ReadNorFlash for Flash<'_> {
522 const READ_SIZE: usize = 1;
523 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
524 Self::read(self, offset, bytes)
525 }
526
527 fn capacity(&self) -> usize {
528 FLASH_SIZE
529 }
530}
531
532impl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_> {
533 const READ_SIZE: usize = 1;
534 async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
535 Self::read(self, offset, bytes)
536 }
537
538 fn capacity(&self) -> usize {
539 FLASH_SIZE
540 }
541}
542
543impl embedded_storage_async::nor_flash::NorFlash for Flash<'_> {
544 const WRITE_SIZE: usize = 4;
545 const ERASE_SIZE: usize = PAGE_SIZE;
546
547 async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
548 Self::erase(self, from, to).await
549 }
550
551 async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
552 Self::write(self, offset, bytes).await
553 }
554}
555
556impl From<crate::Error> for FlashError {
557 fn from(e: crate::Error) -> Self {
558 Self::Mpsl(e)
559 }
560}
561
562#[must_use = "to delay the drop handler invocation to the end of the scope"]
564pub(crate) struct OnDrop<F: FnOnce()> {
565 f: MaybeUninit<F>,
566}
567
568impl<F: FnOnce()> OnDrop<F> {
569 pub fn new(f: F) -> Self {
573 Self { f: MaybeUninit::new(f) }
574 }
575
576 pub fn defuse(self) {
578 core::mem::forget(self)
579 }
580}
581
582impl<F: FnOnce()> Drop for OnDrop<F> {
583 fn drop(&mut self) {
584 unsafe { self.f.as_ptr().read()() }
585 }
586}