embassy_rp/
flash.rs

1//! Flash driver.
2use core::future::Future;
3use core::marker::PhantomData;
4use core::pin::Pin;
5use core::task::{Context, Poll};
6
7use embassy_hal_internal::{Peri, PeripheralType};
8use embedded_storage::nor_flash::{
9    check_erase, check_read, check_write, ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind,
10    ReadNorFlash,
11};
12
13use crate::dma::{AnyChannel, Channel, Transfer};
14use crate::pac;
15use crate::peripherals::FLASH;
16
17/// Flash base address.
18pub const FLASH_BASE: *const u32 = 0x10000000 as _;
19
20/// Address for xip setup function set up by the 235x bootrom.
21#[cfg(feature = "_rp235x")]
22pub const BOOTRAM_BASE: *const u32 = 0x400e0000 as _;
23
24/// If running from RAM, we might have no boot2. Use bootrom `flash_enter_cmd_xip` instead.
25// TODO: when run-from-ram is set, completely skip the "pause cores and jumpp to RAM" dance.
26pub const USE_BOOT2: bool = !cfg!(feature = "run-from-ram") | cfg!(feature = "_rp235x");
27
28// **NOTE**:
29//
30// These limitations are currently enforced because of using the
31// RP2040 boot-rom flash functions, that are optimized for flash compatibility
32// rather than performance.
33/// Flash page size.
34pub const PAGE_SIZE: usize = 256;
35/// Flash write size.
36pub const WRITE_SIZE: usize = 1;
37/// Flash read size.
38pub const READ_SIZE: usize = 1;
39/// Flash erase size.
40pub const ERASE_SIZE: usize = 4096;
41/// Flash DMA read size.
42pub const ASYNC_READ_SIZE: usize = 4;
43
44/// Error type for NVMC operations.
45#[derive(Debug, Copy, Clone, PartialEq, Eq)]
46#[cfg_attr(feature = "defmt", derive(defmt::Format))]
47pub enum Error {
48    /// Operation using a location not in flash.
49    OutOfBounds,
50    /// Unaligned operation or using unaligned buffers.
51    Unaligned,
52    /// Accessed from the wrong core.
53    InvalidCore,
54    /// Other error
55    Other,
56}
57
58impl From<NorFlashErrorKind> for Error {
59    fn from(e: NorFlashErrorKind) -> Self {
60        match e {
61            NorFlashErrorKind::NotAligned => Self::Unaligned,
62            NorFlashErrorKind::OutOfBounds => Self::OutOfBounds,
63            _ => Self::Other,
64        }
65    }
66}
67
68impl NorFlashError for Error {
69    fn kind(&self) -> NorFlashErrorKind {
70        match self {
71            Self::OutOfBounds => NorFlashErrorKind::OutOfBounds,
72            Self::Unaligned => NorFlashErrorKind::NotAligned,
73            _ => NorFlashErrorKind::Other,
74        }
75    }
76}
77
78/// Future that waits for completion of a background read
79#[must_use = "futures do nothing unless you `.await` or poll them"]
80pub struct BackgroundRead<'a, 'd, T: Instance, const FLASH_SIZE: usize> {
81    flash: PhantomData<&'a mut Flash<'d, T, Async, FLASH_SIZE>>,
82    transfer: Transfer<'a, AnyChannel>,
83}
84
85impl<'a, 'd, T: Instance, const FLASH_SIZE: usize> Future for BackgroundRead<'a, 'd, T, FLASH_SIZE> {
86    type Output = ();
87    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
88        Pin::new(&mut self.transfer).poll(cx)
89    }
90}
91
92impl<'a, 'd, T: Instance, const FLASH_SIZE: usize> Drop for BackgroundRead<'a, 'd, T, FLASH_SIZE> {
93    fn drop(&mut self) {
94        if pac::XIP_CTRL.stream_ctr().read().0 == 0 {
95            return;
96        }
97        pac::XIP_CTRL
98            .stream_ctr()
99            .write_value(pac::xip_ctrl::regs::StreamCtr(0));
100        core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
101        // Errata RP2040-E8: Perform an uncached read to make sure there's not a transfer in
102        // flight that might effect an address written to start a new transfer.  This stalls
103        // until after any transfer is complete, so the address will not change anymore.
104        #[cfg(feature = "rp2040")]
105        const XIP_NOCACHE_NOALLOC_BASE: *const u32 = 0x13000000 as *const _;
106        #[cfg(feature = "_rp235x")]
107        const XIP_NOCACHE_NOALLOC_BASE: *const u32 = 0x14000000 as *const _;
108        unsafe {
109            core::ptr::read_volatile(XIP_NOCACHE_NOALLOC_BASE);
110        }
111        core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
112    }
113}
114
115/// Flash driver.
116pub struct Flash<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> {
117    dma: Option<Peri<'d, AnyChannel>>,
118    phantom: PhantomData<(&'d mut T, M)>,
119}
120
121impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SIZE> {
122    /// Blocking read.
123    ///
124    /// The offset and buffer must be aligned.
125    ///
126    /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
127    pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
128        trace!(
129            "Reading from 0x{:x} to 0x{:x}",
130            FLASH_BASE as u32 + offset,
131            FLASH_BASE as u32 + offset + bytes.len() as u32
132        );
133        check_read(self, offset, bytes.len())?;
134
135        let flash_data = unsafe { core::slice::from_raw_parts((FLASH_BASE as u32 + offset) as *const u8, bytes.len()) };
136
137        bytes.copy_from_slice(flash_data);
138        Ok(())
139    }
140
141    /// Flash capacity.
142    pub fn capacity(&self) -> usize {
143        FLASH_SIZE
144    }
145
146    /// Blocking erase.
147    ///
148    /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
149    pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
150        check_erase(self, from, to)?;
151
152        trace!(
153            "Erasing from 0x{:x} to 0x{:x}",
154            FLASH_BASE as u32 + from,
155            FLASH_BASE as u32 + to
156        );
157
158        let len = to - from;
159
160        unsafe { in_ram(|| ram_helpers::flash_range_erase(from, len))? };
161
162        Ok(())
163    }
164
165    /// Blocking write.
166    ///
167    /// The offset and buffer must be aligned.
168    ///
169    /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
170    pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
171        check_write(self, offset, bytes.len())?;
172
173        trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), FLASH_BASE as u32 + offset);
174
175        let end_offset = offset as usize + bytes.len();
176
177        let padded_offset = (offset as *const u8).align_offset(PAGE_SIZE);
178        let start_padding = core::cmp::min(padded_offset, bytes.len());
179
180        // Pad in the beginning
181        if start_padding > 0 {
182            let start = PAGE_SIZE - padded_offset;
183            let end = start + start_padding;
184
185            let mut pad_buf = [0xFF_u8; PAGE_SIZE];
186            pad_buf[start..end].copy_from_slice(&bytes[..start_padding]);
187
188            let unaligned_offset = offset as usize - start;
189
190            unsafe { in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf))? }
191        }
192
193        let remaining_len = bytes.len() - start_padding;
194        let end_padding = start_padding + PAGE_SIZE * (remaining_len / PAGE_SIZE);
195
196        // Write aligned slice of length in multiples of 256 bytes
197        // If the remaining bytes to be written is more than a full page.
198        if remaining_len >= PAGE_SIZE {
199            let mut aligned_offset = if start_padding > 0 {
200                offset as usize + padded_offset
201            } else {
202                offset as usize
203            };
204
205            if bytes.as_ptr() as usize >= 0x2000_0000 {
206                let aligned_data = &bytes[start_padding..end_padding];
207
208                unsafe { in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data))? }
209            } else {
210                for chunk in bytes[start_padding..end_padding].chunks_exact(PAGE_SIZE) {
211                    let mut ram_buf = [0xFF_u8; PAGE_SIZE];
212                    ram_buf.copy_from_slice(chunk);
213                    unsafe { in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf))? }
214                    aligned_offset += PAGE_SIZE;
215                }
216            }
217        }
218
219        // Pad in the end
220        let rem_offset = (end_offset as *const u8).align_offset(PAGE_SIZE);
221        let rem_padding = remaining_len % PAGE_SIZE;
222        if rem_padding > 0 {
223            let mut pad_buf = [0xFF_u8; PAGE_SIZE];
224            pad_buf[..rem_padding].copy_from_slice(&bytes[end_padding..]);
225
226            let unaligned_offset = end_offset - (PAGE_SIZE - rem_offset);
227
228            unsafe { in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf))? }
229        }
230
231        Ok(())
232    }
233
234    /// Read SPI flash unique ID
235    #[cfg(feature = "rp2040")]
236    pub fn blocking_unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> {
237        unsafe { in_ram(|| ram_helpers::flash_unique_id(uid))? };
238        Ok(())
239    }
240
241    /// Read SPI flash JEDEC ID
242    #[cfg(feature = "rp2040")]
243    pub fn blocking_jedec_id(&mut self) -> Result<u32, Error> {
244        let mut jedec = None;
245        unsafe {
246            in_ram(|| {
247                jedec.replace(ram_helpers::flash_jedec_id());
248            })?;
249        };
250        Ok(jedec.unwrap())
251    }
252}
253
254impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Blocking, FLASH_SIZE> {
255    /// Create a new flash driver in blocking mode.
256    pub fn new_blocking(_flash: Peri<'d, T>) -> Self {
257        Self {
258            dma: None,
259            phantom: PhantomData,
260        }
261    }
262}
263
264impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> {
265    /// Create a new flash driver in async mode.
266    pub fn new(_flash: Peri<'d, T>, dma: Peri<'d, impl Channel>) -> Self {
267        Self {
268            dma: Some(dma.into()),
269            phantom: PhantomData,
270        }
271    }
272
273    /// Start a background read operation.
274    ///
275    /// The offset and buffer must be aligned.
276    ///
277    /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
278    pub fn background_read<'a>(
279        &'a mut self,
280        offset: u32,
281        data: &'a mut [u32],
282    ) -> Result<BackgroundRead<'a, 'd, T, FLASH_SIZE>, Error> {
283        trace!(
284            "Reading in background from 0x{:x} to 0x{:x}",
285            FLASH_BASE as u32 + offset,
286            FLASH_BASE as u32 + offset + (data.len() * 4) as u32
287        );
288        // Can't use check_read because we need to enforce 4-byte alignment
289        let offset = offset as usize;
290        let length = data.len() * 4;
291        if length > self.capacity() || offset > self.capacity() - length {
292            return Err(Error::OutOfBounds);
293        }
294        if offset % 4 != 0 {
295            return Err(Error::Unaligned);
296        }
297
298        while !pac::XIP_CTRL.stat().read().fifo_empty() {
299            pac::XIP_CTRL.stream_fifo().read();
300        }
301
302        pac::XIP_CTRL
303            .stream_addr()
304            .write_value(pac::xip_ctrl::regs::StreamAddr(FLASH_BASE as u32 + offset as u32));
305        pac::XIP_CTRL
306            .stream_ctr()
307            .write_value(pac::xip_ctrl::regs::StreamCtr(data.len() as u32));
308
309        // Use the XIP AUX bus port, rather than the FIFO register access (e.x.
310        // pac::XIP_CTRL.stream_fifo().as_ptr()) to avoid DMA stalling on
311        // general XIP access.
312        #[cfg(feature = "rp2040")]
313        const XIP_AUX_BASE: *const u32 = 0x50400000 as *const _;
314        #[cfg(feature = "_rp235x")]
315        const XIP_AUX_BASE: *const u32 = 0x50500000 as *const _;
316        let transfer = unsafe {
317            crate::dma::read(
318                self.dma.as_mut().unwrap().reborrow(),
319                XIP_AUX_BASE,
320                data,
321                pac::dma::vals::TreqSel::XIP_STREAM,
322            )
323        };
324
325        Ok(BackgroundRead {
326            flash: PhantomData,
327            transfer,
328        })
329    }
330
331    /// Async read.
332    ///
333    /// The offset and buffer must be aligned.
334    ///
335    /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
336    pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
337        use core::mem::MaybeUninit;
338
339        // Checked early to simplify address validity checks
340        if bytes.len() % 4 != 0 {
341            return Err(Error::Unaligned);
342        }
343
344        // If the destination address is already aligned, then we can just DMA directly
345        if (bytes.as_ptr() as u32) % 4 == 0 {
346            // Safety: alignment and size have been checked for compatibility
347            let buf: &mut [u32] =
348                unsafe { core::slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut u32, bytes.len() / 4) };
349            self.background_read(offset, buf)?.await;
350            return Ok(());
351        }
352
353        // Destination address is unaligned, so use an intermediate buffer
354        const REALIGN_CHUNK: usize = PAGE_SIZE;
355        // Safety: MaybeUninit requires no initialization
356        let mut buf: [MaybeUninit<u32>; REALIGN_CHUNK / 4] = unsafe { MaybeUninit::uninit().assume_init() };
357        let mut chunk_offset: usize = 0;
358        while chunk_offset < bytes.len() {
359            let chunk_size = (bytes.len() - chunk_offset).min(REALIGN_CHUNK);
360            let buf = &mut buf[..(chunk_size / 4)];
361
362            // Safety: this is written to completely by DMA before any reads
363            let buf = unsafe { &mut *(buf as *mut [MaybeUninit<u32>] as *mut [u32]) };
364            self.background_read(offset + chunk_offset as u32, buf)?.await;
365
366            // Safety: [u8] has more relaxed alignment and size requirements than [u32], so this is just aliasing
367            let buf = unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const _, buf.len() * 4) };
368            bytes[chunk_offset..(chunk_offset + chunk_size)].copy_from_slice(&buf[..chunk_size]);
369
370            chunk_offset += chunk_size;
371        }
372
373        Ok(())
374    }
375}
376
377impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, M, FLASH_SIZE> {
378    type Error = Error;
379}
380
381impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, M, FLASH_SIZE> {
382    const READ_SIZE: usize = READ_SIZE;
383
384    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
385        self.blocking_read(offset, bytes)
386    }
387
388    fn capacity(&self) -> usize {
389        self.capacity()
390    }
391}
392
393impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, M, FLASH_SIZE> {}
394
395impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::MultiwriteNorFlash
396    for Flash<'d, T, Async, FLASH_SIZE>
397{
398}
399
400impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, M, FLASH_SIZE> {
401    const WRITE_SIZE: usize = WRITE_SIZE;
402
403    const ERASE_SIZE: usize = ERASE_SIZE;
404
405    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
406        self.blocking_erase(from, to)
407    }
408
409    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
410        self.blocking_write(offset, bytes)
411    }
412}
413
414impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::ReadNorFlash
415    for Flash<'d, T, Async, FLASH_SIZE>
416{
417    const READ_SIZE: usize = ASYNC_READ_SIZE;
418
419    async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
420        self.read(offset, bytes).await
421    }
422
423    fn capacity(&self) -> usize {
424        self.capacity()
425    }
426}
427
428impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::NorFlash
429    for Flash<'d, T, Async, FLASH_SIZE>
430{
431    const WRITE_SIZE: usize = WRITE_SIZE;
432
433    const ERASE_SIZE: usize = ERASE_SIZE;
434
435    async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
436        self.blocking_erase(from, to)
437    }
438
439    async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
440        self.blocking_write(offset, bytes)
441    }
442}
443
444#[allow(dead_code)]
445mod ram_helpers {
446    use super::*;
447    use crate::rom_data;
448
449    #[repr(C)]
450    struct FlashFunctionPointers<'a> {
451        connect_internal_flash: unsafe extern "C" fn() -> (),
452        flash_exit_xip: unsafe extern "C" fn() -> (),
453        flash_range_erase: Option<unsafe extern "C" fn(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> ()>,
454        flash_range_program: Option<unsafe extern "C" fn(addr: u32, data: *const u8, count: usize) -> ()>,
455        flash_flush_cache: unsafe extern "C" fn() -> (),
456        flash_enter_cmd_xip: unsafe extern "C" fn() -> (),
457        phantom: PhantomData<&'a ()>,
458    }
459
460    #[allow(unused)]
461    fn flash_function_pointers(erase: bool, write: bool) -> FlashFunctionPointers<'static> {
462        FlashFunctionPointers {
463            connect_internal_flash: rom_data::connect_internal_flash::ptr(),
464            flash_exit_xip: rom_data::flash_exit_xip::ptr(),
465            flash_range_erase: if erase {
466                Some(rom_data::flash_range_erase::ptr())
467            } else {
468                None
469            },
470            flash_range_program: if write {
471                Some(rom_data::flash_range_program::ptr())
472            } else {
473                None
474            },
475            flash_flush_cache: rom_data::flash_flush_cache::ptr(),
476            flash_enter_cmd_xip: rom_data::flash_enter_cmd_xip::ptr(),
477            phantom: PhantomData,
478        }
479    }
480
481    #[allow(unused)]
482    /// # Safety
483    ///
484    /// `boot2` must contain a valid 2nd stage boot loader which can be called to re-initialize XIP mode
485    unsafe fn flash_function_pointers_with_boot2(
486        erase: bool,
487        write: bool,
488        boot2: &[u32; 64],
489    ) -> FlashFunctionPointers<'_> {
490        let boot2_fn_ptr = (boot2 as *const u32 as *const u8).offset(1);
491        let boot2_fn: unsafe extern "C" fn() -> () = core::mem::transmute(boot2_fn_ptr);
492        FlashFunctionPointers {
493            connect_internal_flash: rom_data::connect_internal_flash::ptr(),
494            flash_exit_xip: rom_data::flash_exit_xip::ptr(),
495            flash_range_erase: if erase {
496                Some(rom_data::flash_range_erase::ptr())
497            } else {
498                None
499            },
500            flash_range_program: if write {
501                Some(rom_data::flash_range_program::ptr())
502            } else {
503                None
504            },
505            flash_flush_cache: rom_data::flash_flush_cache::ptr(),
506            flash_enter_cmd_xip: boot2_fn,
507            phantom: PhantomData,
508        }
509    }
510
511    /// Erase a flash range starting at `addr` with length `len`.
512    ///
513    /// `addr` and `len` must be multiples of 4096
514    ///
515    /// If `USE_BOOT2` is `true`, a copy of the 2nd stage boot loader
516    /// is used to re-initialize the XIP engine after flashing.
517    ///
518    /// # Safety
519    ///
520    /// Nothing must access flash while this is running.
521    /// Usually this means:
522    ///   - interrupts must be disabled
523    ///   - 2nd core must be running code from RAM or ROM with interrupts disabled
524    ///   - DMA must not access flash memory
525    ///
526    /// `addr` and `len` parameters must be valid and are not checked.
527    pub unsafe fn flash_range_erase(addr: u32, len: u32) {
528        let mut boot2 = [0u32; 256 / 4];
529        let ptrs = if USE_BOOT2 {
530            #[cfg(feature = "rp2040")]
531            rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);
532            #[cfg(feature = "_rp235x")]
533            core::ptr::copy_nonoverlapping(BOOTRAM_BASE as *const u8, boot2.as_mut_ptr() as *mut u8, 256);
534            flash_function_pointers_with_boot2(true, false, &boot2)
535        } else {
536            flash_function_pointers(true, false)
537        };
538
539        core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
540
541        write_flash_inner(addr, len, None, &ptrs as *const FlashFunctionPointers);
542    }
543
544    /// Erase and rewrite a flash range starting at `addr` with data `data`.
545    ///
546    /// `addr` and `data.len()` must be multiples of 4096
547    ///
548    /// If `USE_BOOT2` is `true`, a copy of the 2nd stage boot loader
549    /// is used to re-initialize the XIP engine after flashing.
550    ///
551    /// # Safety
552    ///
553    /// Nothing must access flash while this is running.
554    /// Usually this means:
555    ///   - interrupts must be disabled
556    ///   - 2nd core must be running code from RAM or ROM with interrupts disabled
557    ///   - DMA must not access flash memory
558    ///
559    /// `addr` and `len` parameters must be valid and are not checked.
560    pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8]) {
561        let mut boot2 = [0u32; 256 / 4];
562        let ptrs = if USE_BOOT2 {
563            #[cfg(feature = "rp2040")]
564            rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);
565            #[cfg(feature = "_rp235x")]
566            core::ptr::copy_nonoverlapping(BOOTRAM_BASE as *const u8, (boot2).as_mut_ptr() as *mut u8, 256);
567            flash_function_pointers_with_boot2(true, true, &boot2)
568        } else {
569            flash_function_pointers(true, true)
570        };
571
572        core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
573
574        write_flash_inner(
575            addr,
576            data.len() as u32,
577            Some(data),
578            &ptrs as *const FlashFunctionPointers,
579        );
580    }
581
582    /// Write a flash range starting at `addr` with data `data`.
583    ///
584    /// `addr` and `data.len()` must be multiples of 256
585    ///
586    /// If `USE_BOOT2` is `true`, a copy of the 2nd stage boot loader
587    /// is used to re-initialize the XIP engine after flashing.
588    ///
589    /// # Safety
590    ///
591    /// Nothing must access flash while this is running.
592    /// Usually this means:
593    ///   - interrupts must be disabled
594    ///   - 2nd core must be running code from RAM or ROM with interrupts disabled
595    ///   - DMA must not access flash memory
596    ///
597    /// `addr` and `len` parameters must be valid and are not checked.
598    pub unsafe fn flash_range_program(addr: u32, data: &[u8]) {
599        let mut boot2 = [0u32; 256 / 4];
600        let ptrs = if USE_BOOT2 {
601            #[cfg(feature = "rp2040")]
602            rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);
603            #[cfg(feature = "_rp235x")]
604            core::ptr::copy_nonoverlapping(BOOTRAM_BASE as *const u8, boot2.as_mut_ptr() as *mut u8, 256);
605            flash_function_pointers_with_boot2(false, true, &boot2)
606        } else {
607            flash_function_pointers(false, true)
608        };
609
610        core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
611
612        write_flash_inner(
613            addr,
614            data.len() as u32,
615            Some(data),
616            &ptrs as *const FlashFunctionPointers,
617        );
618    }
619
620    /// # Safety
621    ///
622    /// Nothing must access flash while this is running.
623    /// Usually this means:
624    ///   - interrupts must be disabled
625    ///   - 2nd core must be running code from RAM or ROM with interrupts disabled
626    ///   - DMA must not access flash memory
627    /// Length of data must be a multiple of 4096
628    /// addr must be aligned to 4096
629    #[inline(never)]
630    #[link_section = ".data.ram_func"]
631    #[cfg(feature = "rp2040")]
632    unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) {
633        #[cfg(target_arch = "arm")]
634        core::arch::asm!(
635            "mov r8, r0",
636            "mov r9, r2",
637            "mov r10, r1",
638            "ldr r4, [{ptrs}, #0]",
639            "blx r4", // connect_internal_flash()
640
641            "ldr r4, [{ptrs}, #4]",
642            "blx r4", // flash_exit_xip()
643
644            "mov r0, r8", // r0 = addr
645            "mov r1, r10", // r1 = len
646            "movs r2, #1",
647            "lsls r2, r2, #31", // r2 = 1 << 31
648            "movs r3, #0", // r3 = 0
649            "ldr r4, [{ptrs}, #8]",
650            "cmp r4, #0",
651            "beq 2f",
652            "blx r4", // flash_range_erase(addr, len, 1 << 31, 0)
653            "2:",
654
655            "mov r0, r8", // r0 = addr
656            "mov r1, r9", // r0 = data
657            "mov r2, r10", // r2 = len
658            "ldr r4, [{ptrs}, #12]",
659            "cmp r4, #0",
660            "beq 2f",
661            "blx r4", // flash_range_program(addr, data, len);
662            "2:",
663
664            "ldr r4, [{ptrs}, #16]",
665            "blx r4", // flash_flush_cache();
666
667            "ldr r4, [{ptrs}, #20]",
668            "blx r4", // flash_enter_cmd_xip();
669            ptrs = in(reg) ptrs,
670            // Registers r8-r15 are not allocated automatically,
671            // so assign them manually. We need to use them as
672            // otherwise there are not enough registers available.
673            in("r0") addr,
674            in("r2") data.map(|d| d.as_ptr()).unwrap_or(core::ptr::null()),
675            in("r1") len,
676            out("r3") _,
677            out("r4") _,
678            lateout("r8") _,
679            lateout("r9") _,
680            lateout("r10") _,
681            clobber_abi("C"),
682        );
683    }
684
685    /// # Safety
686    ///
687    /// Nothing must access flash while this is running.
688    /// Usually this means:
689    ///   - interrupts must be disabled
690    ///   - 2nd core must be running code from RAM or ROM with interrupts disabled
691    ///   - DMA must not access flash memory
692    /// Length of data must be a multiple of 4096
693    /// addr must be aligned to 4096
694    #[inline(never)]
695    #[link_section = ".data.ram_func"]
696    #[cfg(feature = "_rp235x")]
697    unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) {
698        let data = data.map(|d| d.as_ptr()).unwrap_or(core::ptr::null());
699        ((*ptrs).connect_internal_flash)();
700        ((*ptrs).flash_exit_xip)();
701        if (*ptrs).flash_range_erase.is_some() {
702            ((*ptrs).flash_range_erase.unwrap())(addr, len as usize, 1 << 31, 0);
703        }
704        if (*ptrs).flash_range_program.is_some() {
705            ((*ptrs).flash_range_program.unwrap())(addr, data as *const _, len as usize);
706        }
707        ((*ptrs).flash_flush_cache)();
708        ((*ptrs).flash_enter_cmd_xip)();
709    }
710
711    #[repr(C)]
712    struct FlashCommand {
713        cmd_addr: *const u8,
714        cmd_addr_len: u32,
715        dummy_len: u32,
716        data: *mut u8,
717        data_len: u32,
718    }
719
720    /// Return SPI flash unique ID
721    ///
722    /// Not all SPI flashes implement this command, so check the JEDEC
723    /// ID before relying on it. The Winbond parts commonly seen on
724    /// RP2040 devboards (JEDEC=0xEF7015) support an 8-byte unique ID;
725    /// https://forums.raspberrypi.com/viewtopic.php?t=331949 suggests
726    /// that LCSC (Zetta) parts have a 16-byte unique ID (which is
727    /// *not* unique in just its first 8 bytes),
728    /// JEDEC=0xBA6015. Macronix and Spansion parts do not have a
729    /// unique ID.
730    ///
731    /// The returned bytes are relatively predictable and should be
732    /// salted and hashed before use if that is an issue (e.g. for MAC
733    /// addresses).
734    ///
735    /// # Safety
736    ///
737    /// Nothing must access flash while this is running.
738    /// Usually this means:
739    ///   - interrupts must be disabled
740    ///   - 2nd core must be running code from RAM or ROM with interrupts disabled
741    ///   - DMA must not access flash memory
742    ///
743    /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT)
744    #[cfg(feature = "rp2040")]
745    pub unsafe fn flash_unique_id(out: &mut [u8]) {
746        let mut boot2 = [0u32; 256 / 4];
747        let ptrs = if USE_BOOT2 {
748            rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);
749            flash_function_pointers_with_boot2(false, false, &boot2)
750        } else {
751            flash_function_pointers(false, false)
752        };
753
754        // 4B - read unique ID
755        let cmd = [0x4B];
756        read_flash(&cmd[..], 4, out, &ptrs as *const FlashFunctionPointers);
757    }
758
759    /// Return SPI flash JEDEC ID
760    ///
761    /// This is the three-byte manufacturer-and-model identifier
762    /// commonly used to check before using manufacturer-specific SPI
763    /// flash features, e.g. 0xEF7015 for Winbond W25Q16JV.
764    ///
765    /// # Safety
766    ///
767    /// Nothing must access flash while this is running.
768    /// Usually this means:
769    ///   - interrupts must be disabled
770    ///   - 2nd core must be running code from RAM or ROM with interrupts disabled
771    ///   - DMA must not access flash memory
772    ///
773    /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT)
774    #[cfg(feature = "rp2040")]
775    pub unsafe fn flash_jedec_id() -> u32 {
776        let mut boot2 = [0u32; 256 / 4];
777        let ptrs = if USE_BOOT2 {
778            rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);
779            flash_function_pointers_with_boot2(false, false, &boot2)
780        } else {
781            flash_function_pointers(false, false)
782        };
783
784        let mut id = [0u8; 4];
785        // 9F - read JEDEC ID
786        let cmd = [0x9F];
787        read_flash(&cmd[..], 0, &mut id[1..4], &ptrs as *const FlashFunctionPointers);
788        u32::from_be_bytes(id)
789    }
790
791    #[cfg(feature = "rp2040")]
792    unsafe fn read_flash(cmd_addr: &[u8], dummy_len: u32, out: &mut [u8], ptrs: *const FlashFunctionPointers) {
793        read_flash_inner(
794            FlashCommand {
795                cmd_addr: cmd_addr.as_ptr(),
796                cmd_addr_len: cmd_addr.len() as u32,
797                dummy_len,
798                data: out.as_mut_ptr(),
799                data_len: out.len() as u32,
800            },
801            ptrs,
802        );
803    }
804
805    /// Issue a generic SPI flash read command
806    ///
807    /// # Arguments
808    ///
809    /// * `cmd` - `FlashCommand` structure
810    /// * `ptrs` - Flash function pointers as per `write_flash_inner`
811    ///
812    /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT)
813    #[inline(never)]
814    #[link_section = ".data.ram_func"]
815    #[cfg(feature = "rp2040")]
816    unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) {
817        #[cfg(target_arch = "arm")]
818        core::arch::asm!(
819            "mov r10, r0", // cmd
820            "mov r5, r1", // ptrs
821
822            "ldr r4, [r5, #0]",
823            "blx r4", // connect_internal_flash()
824
825            "ldr r4, [r5, #4]",
826            "blx r4", // flash_exit_xip()
827
828
829            "movs r4, #0x18",
830            "lsls r4, r4, #24", // 0x18000000, SSI, RP2040 datasheet 4.10.13
831
832            // Disable, write 0 to SSIENR
833            "movs r0, #0",
834            "str r0, [r4, #8]", // SSIENR
835
836            // Write ctrlr0
837            "movs r0, #0x3",
838            "lsls r0, r0, #8", // TMOD=0x300
839            "ldr r1, [r4, #0]", // CTRLR0
840            "orrs r1, r0",
841            "str r1, [r4, #0]",
842
843            // Write ctrlr1 with len-1
844            "mov r3, r10", // cmd
845            "ldr r0, [r3, #8]", // dummy_len
846            "ldr r1, [r3, #16]", // data_len
847            "add r0, r1",
848            "subs r0, #1",
849            "str r0, [r4, #0x04]", // CTRLR1
850
851            // Enable, write 1 to ssienr
852            "movs r0, #1",
853            "str r0, [r4, #8]", // SSIENR
854
855            // Write cmd/addr phase to DR
856            "mov r2, r4",
857            "adds r2, 0x60", // &DR
858            "ldr r0, [r3, #0]", // cmd_addr
859            "ldr r1, [r3, #4]", // cmd_addr_len
860            "3:",
861            "ldrb r3, [r0]",
862            "strb r3, [r2]", // DR
863            "adds r0, #1",
864            "subs r1, #1",
865            "bne 3b",
866
867            // Skip any dummy cycles
868            "mov r3, r10", // cmd
869            "ldr r1, [r3, #8]", // dummy_len
870            "cmp r1, #0",
871            "beq 9f",
872            "4:",
873            "ldr r3, [r4, #0x28]", // SR
874            "movs r2, #0x8",
875            "tst r3, r2", // SR.RFNE
876            "beq 4b",
877
878            "mov r2, r4",
879            "adds r2, 0x60", // &DR
880            "ldrb r3, [r2]", // DR
881            "subs r1, #1",
882            "bne 4b",
883
884            // Read RX fifo
885            "9:",
886            "mov r2, r10", // cmd
887            "ldr r0, [r2, #12]", // data
888            "ldr r1, [r2, #16]", // data_len
889
890            "2:",
891            "ldr r3, [r4, #0x28]", // SR
892            "movs r2, #0x8",
893            "tst r3, r2", // SR.RFNE
894            "beq 2b",
895
896            "mov r2, r4",
897            "adds r2, 0x60", // &DR
898            "ldrb r3, [r2]", // DR
899            "strb r3, [r0]",
900            "adds r0, #1",
901            "subs r1, #1",
902            "bne 2b",
903
904            // Disable, write 0 to ssienr
905            "movs r0, #0",
906            "str r0, [r4, #8]", // SSIENR
907
908            // Write 0 to CTRLR1 (returning to its default value)
909            //
910            // flash_enter_cmd_xip does NOT do this, and everything goes
911            // wrong unless we do it here
912            "str r0, [r4, #4]", // CTRLR1
913
914            "ldr r4, [r5, #20]",
915            "blx r4", // flash_enter_cmd_xip();
916
917            in("r0") &cmd as *const FlashCommand,
918            in("r1") ptrs,
919            out("r2") _,
920            out("r3") _,
921            out("r4") _,
922            out("r5") _,
923            // Registers r8-r10 are used to store values
924            // from r0-r2 in registers not clobbered by
925            // function calls.
926            // The values can't be passed in using r8-r10 directly
927            // due to https://github.com/rust-lang/rust/issues/99071
928            out("r10") _,
929            clobber_abi("C"),
930        );
931    }
932}
933
934/// Make sure to uphold the contract points with rp2040-flash.
935/// - interrupts must be disabled
936/// - DMA must not access flash memory
937pub(crate) unsafe fn in_ram(operation: impl FnOnce()) -> Result<(), Error> {
938    // Make sure we're running on CORE0
939    let core_id: u32 = pac::SIO.cpuid().read();
940    if core_id != 0 {
941        return Err(Error::InvalidCore);
942    }
943
944    // Make sure CORE1 is paused during the entire duration of the RAM function
945    crate::multicore::pause_core1();
946
947    critical_section::with(|_| {
948        // Wait for all DMA channels in flash to finish before ram operation
949        const SRAM_LOWER: u32 = 0x2000_0000;
950        for n in 0..crate::dma::CHANNEL_COUNT {
951            let ch = crate::pac::DMA.ch(n);
952            while ch.read_addr().read() < SRAM_LOWER && ch.ctrl_trig().read().busy() {}
953        }
954        // Wait for completion of any background reads
955        while pac::XIP_CTRL.stream_ctr().read().0 > 0 {}
956
957        // Run our flash operation in RAM
958        operation();
959    });
960
961    // Resume CORE1 execution
962    crate::multicore::resume_core1();
963    Ok(())
964}
965
966trait SealedInstance {}
967trait SealedMode {}
968
969/// Flash instance.
970#[allow(private_bounds)]
971pub trait Instance: SealedInstance + PeripheralType {}
972/// Flash mode.
973#[allow(private_bounds)]
974pub trait Mode: SealedMode {}
975
976impl SealedInstance for FLASH {}
977impl Instance for FLASH {}
978
979macro_rules! impl_mode {
980    ($name:ident) => {
981        impl SealedMode for $name {}
982        impl Mode for $name {}
983    };
984}
985
986/// Flash blocking mode.
987pub struct Blocking;
988/// Flash async mode.
989pub struct Async;
990
991impl_mode!(Blocking);
992impl_mode!(Async);