rp2040_flash/lib.rs
1#![no_std]
2
3pub mod flash {
4 use core::marker::PhantomData;
5 use rp2040_hal::rom_data;
6
7 #[repr(C)]
8 struct FlashFunctionPointers<'a> {
9 connect_internal_flash: unsafe extern "C" fn() -> (),
10 flash_exit_xip: unsafe extern "C" fn() -> (),
11 flash_range_erase: Option<
12 unsafe extern "C" fn(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> (),
13 >,
14 flash_range_program:
15 Option<unsafe extern "C" fn(addr: u32, data: *const u8, count: usize) -> ()>,
16 flash_flush_cache: unsafe extern "C" fn() -> (),
17 flash_enter_cmd_xip: unsafe extern "C" fn() -> (),
18 phantom: PhantomData<&'a ()>,
19 }
20
21 #[allow(unused)]
22 fn flash_function_pointers(erase: bool, write: bool) -> FlashFunctionPointers<'static> {
23 FlashFunctionPointers {
24 connect_internal_flash: rom_data::connect_internal_flash::ptr(),
25 flash_exit_xip: rom_data::flash_exit_xip::ptr(),
26 flash_range_erase: if erase {
27 Some(rom_data::flash_range_erase::ptr())
28 } else {
29 None
30 },
31 flash_range_program: if write {
32 Some(rom_data::flash_range_program::ptr())
33 } else {
34 None
35 },
36 flash_flush_cache: rom_data::flash_flush_cache::ptr(),
37 flash_enter_cmd_xip: rom_data::flash_enter_cmd_xip::ptr(),
38 phantom: PhantomData,
39 }
40 }
41
42 #[allow(unused)]
43 /// # Safety
44 ///
45 /// `boot2` must contain a valid 2nd stage boot loader which can be called to re-initialize XIP mode
46 unsafe fn flash_function_pointers_with_boot2(
47 erase: bool,
48 write: bool,
49 boot2: &[u32; 64],
50 ) -> FlashFunctionPointers {
51 let boot2_fn_ptr = (boot2 as *const u32 as *const u8).offset(1);
52 let boot2_fn: unsafe extern "C" fn() -> () = core::mem::transmute(boot2_fn_ptr);
53 FlashFunctionPointers {
54 connect_internal_flash: rom_data::connect_internal_flash::ptr(),
55 flash_exit_xip: rom_data::flash_exit_xip::ptr(),
56 flash_range_erase: if erase {
57 Some(rom_data::flash_range_erase::ptr())
58 } else {
59 None
60 },
61 flash_range_program: if write {
62 Some(rom_data::flash_range_program::ptr())
63 } else {
64 None
65 },
66 flash_flush_cache: rom_data::flash_flush_cache::ptr(),
67 flash_enter_cmd_xip: boot2_fn,
68 phantom: PhantomData,
69 }
70 }
71
72 /// Erase a flash range starting at `addr` with length `len`.
73 ///
74 /// `addr` and `len` must be multiples of 4096.
75 ///
76 /// `addr` is relative to the beginning of the flash area,
77 /// and must be smaller than 0x01000000.
78 ///
79 /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader
80 /// is used to re-initialize the XIP engine after flashing.
81 ///
82 /// # Safety
83 ///
84 /// Nothing must access flash while this is running.
85 /// Usually this means:
86 /// - interrupts must be disabled
87 /// - 2nd core must be running code from RAM or ROM with interrupts disabled
88 /// - DMA must not access flash memory
89 ///
90 /// `addr` and `len` parameters must be valid and are not checked.
91 pub unsafe fn flash_range_erase(addr: u32, len: u32, use_boot2: bool) {
92 assert!(addr < 0x1000000);
93 let mut boot2 = [0u32; 256 / 4];
94 let ptrs = if use_boot2 {
95 rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256);
96 flash_function_pointers_with_boot2(true, false, &boot2)
97 } else {
98 flash_function_pointers(true, false)
99 };
100 write_flash_inner(addr, len, None, &ptrs as *const FlashFunctionPointers);
101 }
102
103 /// Erase and rewrite a flash range starting at `addr` with data `data`.
104 ///
105 /// `addr` and `data.len()` must be multiples of 4096.
106 ///
107 /// `addr` is relative to the beginning of the flash area,
108 /// and must be smaller than 0x01000000.
109 ///
110 /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader
111 /// is used to re-initialize the XIP engine after flashing.
112 ///
113 /// # Safety
114 ///
115 /// Nothing must access flash while this is running.
116 /// Usually this means:
117 /// - interrupts must be disabled
118 /// - 2nd core must be running code from RAM or ROM with interrupts disabled
119 /// - DMA must not access flash memory
120 ///
121 /// `addr` and `len` parameters must be valid and are not checked.
122 pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8], use_boot2: bool) {
123 assert!(addr < 0x1000000);
124 let mut boot2 = [0u32; 256 / 4];
125 let ptrs = if use_boot2 {
126 rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256);
127 flash_function_pointers_with_boot2(true, true, &boot2)
128 } else {
129 flash_function_pointers(true, true)
130 };
131 write_flash_inner(
132 addr,
133 data.len() as u32,
134 Some(data),
135 &ptrs as *const FlashFunctionPointers,
136 );
137 }
138
139 /// Write a flash range starting at `addr` with data `data`.
140 ///
141 /// `addr` and `data.len()` must be multiples of 256.
142 ///
143 /// `addr` is relative to the beginning of the flash area,
144 /// and must be smaller than 0x01000000.
145 ///
146 /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader
147 /// is used to re-initialize the XIP engine after flashing.
148 ///
149 /// # Safety
150 ///
151 /// Nothing must access flash while this is running.
152 /// Usually this means:
153 /// - interrupts must be disabled
154 /// - 2nd core must be running code from RAM or ROM with interrupts disabled
155 /// - DMA must not access flash memory
156 ///
157 /// `addr` and `len` parameters must be valid and are not checked.
158 pub unsafe fn flash_range_program(addr: u32, data: &[u8], use_boot2: bool) {
159 assert!(addr < 0x1000000);
160 let mut boot2 = [0u32; 256 / 4];
161 let ptrs = if use_boot2 {
162 rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256);
163 flash_function_pointers_with_boot2(false, true, &boot2)
164 } else {
165 flash_function_pointers(false, true)
166 };
167 write_flash_inner(
168 addr,
169 data.len() as u32,
170 Some(data),
171 &ptrs as *const FlashFunctionPointers,
172 );
173 }
174
175 /// # Safety
176 ///
177 /// Nothing must access flash while this is running.
178 /// Usually this means:
179 /// - interrupts must be disabled
180 /// - 2nd core must be running code from RAM or ROM with interrupts disabled
181 /// - DMA must not access flash memory
182 ///
183 /// Length of data must be a multiple of 4096
184 /// addr must be aligned to 4096
185 #[inline(never)]
186 #[link_section = ".data.ram_func"]
187 unsafe fn write_flash_inner(
188 addr: u32,
189 len: u32,
190 data: Option<&[u8]>,
191 ptrs: *const FlashFunctionPointers,
192 ) {
193 /*
194 Should be equivalent to:
195 rom_data::connect_internal_flash();
196 rom_data::flash_exit_xip();
197 rom_data::flash_range_erase(addr, len, 1 << 31, 0); // if selected
198 rom_data::flash_range_program(addr, data as *const _, len); // if selected
199 rom_data::flash_flush_cache();
200 rom_data::flash_enter_cmd_xip();
201 */
202 core::arch::asm!(
203 "mov r8, r0",
204 "mov r9, r2",
205 "mov r10, r1",
206 "ldr r4, [{ptrs}, #0]",
207 "blx r4", // connect_internal_flash()
208
209 "ldr r4, [{ptrs}, #4]",
210 "blx r4", // flash_exit_xip()
211
212 "mov r0, r8", // r0 = addr
213 "mov r1, r10", // r1 = len
214 "movs r2, #1",
215 "lsls r2, r2, #31", // r2 = 1 << 31
216 "movs r3, #0", // r3 = 0
217 "ldr r4, [{ptrs}, #8]",
218 "cmp r4, #0",
219 "beq 1f",
220 "blx r4", // flash_range_erase(addr, len, 1 << 31, 0)
221 "1:",
222
223 "mov r0, r8", // r0 = addr
224 "mov r1, r9", // r0 = data
225 "mov r2, r10", // r2 = len
226 "ldr r4, [{ptrs}, #12]",
227 "cmp r4, #0",
228 "beq 1f",
229 "blx r4", // flash_range_program(addr, data, len);
230 "1:",
231
232 "ldr r4, [{ptrs}, #16]",
233 "blx r4", // flash_flush_cache();
234
235 "ldr r4, [{ptrs}, #20]",
236 "blx r4", // flash_enter_cmd_xip();
237 ptrs = in(reg) ptrs,
238 in("r0") addr,
239 in("r2") data.map(|d| d.as_ptr()).unwrap_or(core::ptr::null()),
240 in("r1") len,
241 out("r3") _,
242 out("r4") _,
243 // Registers r8-r10 are used to store values
244 // from r0-r2 in registers not clobbered by
245 // function calls.
246 // The values can't be passed in using r8-r10 directly
247 // due to https://github.com/rust-lang/rust/issues/99071
248 out("r8") _,
249 out("r9") _,
250 out("r10") _,
251 clobber_abi("C"),
252 );
253 }
254
255 #[repr(C)]
256 struct FlashCommand {
257 cmd_addr: *const u8,
258 cmd_addr_len: u32,
259 dummy_len: u32,
260 data: *mut u8,
261 data_len: u32,
262 }
263
264 /// Return SPI flash unique ID
265 ///
266 /// Not all SPI flashes implement this command, so check the JEDEC
267 /// ID before relying on it. The Winbond parts commonly seen on
268 /// RP2040 devboards (JEDEC=0xEF7015) support an 8-byte unique ID;
269 /// https://forums.raspberrypi.com/viewtopic.php?t=331949 suggests
270 /// that LCSC (Zetta) parts have a 16-byte unique ID (which is
271 /// *not* unique in just its first 8 bytes),
272 /// JEDEC=0xBA6015. Macronix and Spansion parts do not have a
273 /// unique ID.
274 ///
275 /// The returned bytes are relatively predictable and should be
276 /// salted and hashed before use if that is an issue (e.g. for MAC
277 /// addresses).
278 ///
279 /// # Safety
280 ///
281 /// Nothing must access flash while this is running.
282 /// Usually this means:
283 /// - interrupts must be disabled
284 /// - 2nd core must be running code from RAM or ROM with interrupts disabled
285 /// - DMA must not access flash memory
286 pub unsafe fn flash_unique_id(out: &mut [u8], use_boot2: bool) {
287 let mut boot2 = [0u32; 256 / 4];
288 let ptrs = if use_boot2 {
289 rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256);
290 flash_function_pointers_with_boot2(false, false, &boot2)
291 } else {
292 flash_function_pointers(false, false)
293 };
294 // 4B - read unique ID
295 let cmd = [0x4B];
296 read_flash(&cmd[..], 4, out, &ptrs as *const FlashFunctionPointers);
297 }
298
299 /// Return SPI flash JEDEC ID
300 ///
301 /// This is the three-byte manufacturer-and-model identifier
302 /// commonly used to check before using manufacturer-specific SPI
303 /// flash features, e.g. 0xEF7015 for Winbond W25Q16JV.
304 ///
305 /// # Safety
306 ///
307 /// Nothing must access flash while this is running.
308 /// Usually this means:
309 /// - interrupts must be disabled
310 /// - 2nd core must be running code from RAM or ROM with interrupts disabled
311 /// - DMA must not access flash memory
312 pub unsafe fn flash_jedec_id(use_boot2: bool) -> u32 {
313 let mut boot2 = [0u32; 256 / 4];
314 let ptrs = if use_boot2 {
315 rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256);
316 flash_function_pointers_with_boot2(false, false, &boot2)
317 } else {
318 flash_function_pointers(false, false)
319 };
320 let mut id = [0u8; 4];
321 // 9F - read JEDEC ID
322 let cmd = [0x9F];
323 read_flash(
324 &cmd[..],
325 0,
326 &mut id[1..4],
327 &ptrs as *const FlashFunctionPointers,
328 );
329 u32::from_be_bytes(id)
330 }
331
332 unsafe fn read_flash(
333 cmd_addr: &[u8],
334 dummy_len: u32,
335 out: &mut [u8],
336 ptrs: *const FlashFunctionPointers,
337 ) {
338 read_flash_inner(
339 FlashCommand {
340 cmd_addr: cmd_addr.as_ptr(),
341 cmd_addr_len: cmd_addr.len() as u32,
342 dummy_len,
343 data: out.as_mut_ptr(),
344 data_len: out.len() as u32,
345 },
346 ptrs,
347 );
348 }
349
350 /// Issue a generic SPI flash read command
351 ///
352 /// # Arguments
353 ///
354 /// * `cmd` - `FlashCommand` structure
355 /// * `ptrs` - Flash function pointers as per `write_flash_inner`
356 #[inline(never)]
357 #[link_section = ".data.ram_func"]
358 unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) {
359 core::arch::asm!(
360 // r6, r7 are LLVM-reserved and can't be marked as a clobber, so save/restore them manually
361 // (r6 is not actually used, but we need to push two words to maintain stack alignment)
362 "push {{r6, r7}}",
363
364 "mov r7, r0", // cmd
365 "mov r5, r1", // ptrs
366
367 "ldr r4, [r5, #0]",
368 "blx r4", // connect_internal_flash()
369
370 "ldr r4, [r5, #4]",
371 "blx r4", // flash_exit_xip()
372
373 "movs r4, #0x18",
374 "lsls r4, r4, #24", // 0x18000000, SSI, RP2040 datasheet 4.10.13
375
376 // Disable, write 0 to SSIENR
377 "movs r0, #0",
378 "str r0, [r4, #8]", // SSIENR
379
380 // Write ctrlr0
381 "movs r0, #0x3",
382 "lsls r0, r0, #8", // TMOD=0x300
383 "ldr r1, [r4, #0]", // CTRLR0
384 "orrs r1, r0",
385 "str r1, [r4, #0]",
386
387 // Write ctrlr1 with len-1
388 "ldr r0, [r7, #8]", // dummy_len
389 "ldr r1, [r7, #16]", // data_len
390 "add r0, r1",
391 "subs r0, #1",
392 "str r0, [r4, #0x04]", // CTRLR1
393
394 // Enable, write 1 to ssienr
395 "movs r0, #1",
396 "str r0, [r4, #8]", // SSIENR
397
398 // Write cmd/addr phase to DR
399 "mov r2, r4",
400 "adds r2, 0x60", // &DR
401 "ldr r0, [r7, #0]", // cmd_addr
402 "ldr r1, [r7, #4]", // cmd_addr_len
403 "10:",
404 "ldrb r3, [r0]",
405 "strb r3, [r2]", // DR
406 "adds r0, #1",
407 "subs r1, #1",
408 "bne 10b",
409
410 // Skip any dummy cycles
411 "ldr r1, [r7, #8]", // dummy_len
412 "cmp r1, #0",
413 "beq 9f",
414 "4:",
415 "ldr r3, [r4, #0x28]", // SR
416 "movs r2, #0x8",
417 "tst r3, r2", // SR.RFNE
418 "beq 4b",
419
420 "mov r2, r4",
421 "adds r2, 0x60", // &DR
422 "ldrb r3, [r2]", // DR
423 "subs r1, #1",
424 "bne 4b",
425
426 // Read RX fifo
427 "9:",
428 "ldr r0, [r7, #12]", // data
429 "ldr r1, [r7, #16]", // data_len
430
431 "2:",
432 "ldr r3, [r4, #0x28]", // SR
433 "movs r2, #0x8",
434 "tst r3, r2", // SR.RFNE
435 "beq 2b",
436
437 "mov r2, r4",
438 "adds r2, 0x60", // &DR
439 "ldrb r3, [r2]", // DR
440 "strb r3, [r0]",
441 "adds r0, #1",
442 "subs r1, #1",
443 "bne 2b",
444
445 // Disable, write 0 to ssienr
446 "movs r0, #0",
447 "str r0, [r4, #8]", // SSIENR
448
449 // Write 0 to CTRLR1 (returning to its default value)
450 //
451 // flash_enter_cmd_xip does NOT do this, and everything goes
452 // wrong unless we do it here
453 "str r0, [r4, #4]", // CTRLR1
454
455 "ldr r4, [r5, #20]",
456 "blx r4", // flash_enter_cmd_xip();
457
458 "pop {{r6, r7}}",
459
460 in("r0") &cmd as *const FlashCommand,
461 in("r1") ptrs,
462 out("r2") _,
463 out("r3") _,
464 out("r4") _,
465 out("r5") _,
466 clobber_abi("C"),
467 );
468 }
469}