Merge #1462
1462: rp: Read flash unique id and jedec id r=Dirbaio a=kalkyl Co-authored-by: kalkyl <henrik.alser@me.com>
This commit is contained in:
		| @@ -187,6 +187,23 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { | ||||
|         crate::multicore::resume_core1(); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Read SPI flash unique ID | ||||
|     pub fn unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> { | ||||
|         unsafe { self.in_ram(|| ram_helpers::flash_unique_id(uid, true))? }; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Read SPI flash JEDEC ID | ||||
|     pub fn jedec_id(&mut self) -> Result<u32, Error> { | ||||
|         let mut jedec = None; | ||||
|         unsafe { | ||||
|             self.in_ram(|| { | ||||
|                 jedec.replace(ram_helpers::flash_jedec_id(true)); | ||||
|             })?; | ||||
|         }; | ||||
|         Ok(jedec.unwrap()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> { | ||||
| @@ -457,6 +474,220 @@ mod ram_helpers { | ||||
|             clobber_abi("C"), | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     #[repr(C)] | ||||
|     struct FlashCommand { | ||||
|         cmd_addr: *const u8, | ||||
|         cmd_addr_len: u32, | ||||
|         dummy_len: u32, | ||||
|         data: *mut u8, | ||||
|         data_len: u32, | ||||
|     } | ||||
|  | ||||
|     /// Return SPI flash unique ID | ||||
|     /// | ||||
|     /// Not all SPI flashes implement this command, so check the JEDEC | ||||
|     /// ID before relying on it. The Winbond parts commonly seen on | ||||
|     /// RP2040 devboards (JEDEC=0xEF7015) support an 8-byte unique ID; | ||||
|     /// https://forums.raspberrypi.com/viewtopic.php?t=331949 suggests | ||||
|     /// that LCSC (Zetta) parts have a 16-byte unique ID (which is | ||||
|     /// *not* unique in just its first 8 bytes), | ||||
|     /// JEDEC=0xBA6015. Macronix and Spansion parts do not have a | ||||
|     /// unique ID. | ||||
|     /// | ||||
|     /// The returned bytes are relatively predictable and should be | ||||
|     /// salted and hashed before use if that is an issue (e.g. for MAC | ||||
|     /// addresses). | ||||
|     /// | ||||
|     /// # Safety | ||||
|     /// | ||||
|     /// Nothing must access flash while this is running. | ||||
|     /// Usually this means: | ||||
|     ///   - interrupts must be disabled | ||||
|     ///   - 2nd core must be running code from RAM or ROM with interrupts disabled | ||||
|     ///   - DMA must not access flash memory | ||||
|     /// | ||||
|     /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) | ||||
|     pub unsafe fn flash_unique_id(out: &mut [u8], use_boot2: bool) { | ||||
|         let mut boot2 = [0u32; 256 / 4]; | ||||
|         let ptrs = if use_boot2 { | ||||
|             rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); | ||||
|             flash_function_pointers_with_boot2(false, false, &boot2) | ||||
|         } else { | ||||
|             flash_function_pointers(false, false) | ||||
|         }; | ||||
|         // 4B - read unique ID | ||||
|         let cmd = [0x4B]; | ||||
|         read_flash(&cmd[..], 4, out, &ptrs as *const FlashFunctionPointers); | ||||
|     } | ||||
|  | ||||
|     /// Return SPI flash JEDEC ID | ||||
|     /// | ||||
|     /// This is the three-byte manufacturer-and-model identifier | ||||
|     /// commonly used to check before using manufacturer-specific SPI | ||||
|     /// flash features, e.g. 0xEF7015 for Winbond W25Q16JV. | ||||
|     /// | ||||
|     /// # Safety | ||||
|     /// | ||||
|     /// Nothing must access flash while this is running. | ||||
|     /// Usually this means: | ||||
|     ///   - interrupts must be disabled | ||||
|     ///   - 2nd core must be running code from RAM or ROM with interrupts disabled | ||||
|     ///   - DMA must not access flash memory | ||||
|     /// | ||||
|     /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) | ||||
|     pub unsafe fn flash_jedec_id(use_boot2: bool) -> u32 { | ||||
|         let mut boot2 = [0u32; 256 / 4]; | ||||
|         let ptrs = if use_boot2 { | ||||
|             rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); | ||||
|             flash_function_pointers_with_boot2(false, false, &boot2) | ||||
|         } else { | ||||
|             flash_function_pointers(false, false) | ||||
|         }; | ||||
|         let mut id = [0u8; 4]; | ||||
|         // 9F - read JEDEC ID | ||||
|         let cmd = [0x9F]; | ||||
|         read_flash(&cmd[..], 0, &mut id[1..4], &ptrs as *const FlashFunctionPointers); | ||||
|         u32::from_be_bytes(id) | ||||
|     } | ||||
|  | ||||
|     unsafe fn read_flash(cmd_addr: &[u8], dummy_len: u32, out: &mut [u8], ptrs: *const FlashFunctionPointers) { | ||||
|         read_flash_inner( | ||||
|             FlashCommand { | ||||
|                 cmd_addr: cmd_addr.as_ptr(), | ||||
|                 cmd_addr_len: cmd_addr.len() as u32, | ||||
|                 dummy_len, | ||||
|                 data: out.as_mut_ptr(), | ||||
|                 data_len: out.len() as u32, | ||||
|             }, | ||||
|             ptrs, | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /// Issue a generic SPI flash read command | ||||
|     /// | ||||
|     /// # Arguments | ||||
|     /// | ||||
|     /// * `cmd` - `FlashCommand` structure | ||||
|     /// * `ptrs` - Flash function pointers as per `write_flash_inner` | ||||
|     /// | ||||
|     /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) | ||||
|     #[inline(never)] | ||||
|     #[link_section = ".data.ram_func"] | ||||
|     unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) { | ||||
|         core::arch::asm!( | ||||
|             "mov r10, r0", // cmd | ||||
|             "mov r5, r1", // ptrs | ||||
|  | ||||
|             "ldr r4, [r5, #0]", | ||||
|             "blx r4", // connect_internal_flash() | ||||
|  | ||||
|             "ldr r4, [r5, #4]", | ||||
|             "blx r4", // flash_exit_xip() | ||||
|  | ||||
|             "mov r7, r10", // cmd | ||||
|  | ||||
|             "movs r4, #0x18", | ||||
|             "lsls r4, r4, #24", // 0x18000000, SSI, RP2040 datasheet 4.10.13 | ||||
|  | ||||
|             // Disable, write 0 to SSIENR | ||||
|             "movs r0, #0", | ||||
|             "str r0, [r4, #8]", // SSIENR | ||||
|  | ||||
|             // Write ctrlr0 | ||||
|             "movs r0, #0x3", | ||||
|             "lsls r0, r0, #8", // TMOD=0x300 | ||||
|             "ldr r1, [r4, #0]", // CTRLR0 | ||||
|             "orrs r1, r0", | ||||
|             "str r1, [r4, #0]", | ||||
|  | ||||
|             // Write ctrlr1 with len-1 | ||||
|             "ldr r0, [r7, #8]", // dummy_len | ||||
|             "ldr r1, [r7, #16]", // data_len | ||||
|             "add r0, r1", | ||||
|             "subs r0, #1", | ||||
|             "str r0, [r4, #0x04]", // CTRLR1 | ||||
|  | ||||
|             // Enable, write 1 to ssienr | ||||
|             "movs r0, #1", | ||||
|             "str r0, [r4, #8]", // SSIENR | ||||
|  | ||||
|             // Write cmd/addr phase to DR | ||||
|             "mov r2, r4", | ||||
|             "adds r2, 0x60", // &DR | ||||
|             "ldr r0, [r7, #0]", // cmd_addr | ||||
|             "ldr r1, [r7, #4]", // cmd_addr_len | ||||
|             "10:", | ||||
|             "ldrb r3, [r0]", | ||||
|             "strb r3, [r2]", // DR | ||||
|             "adds r0, #1", | ||||
|             "subs r1, #1", | ||||
|             "bne 10b", | ||||
|  | ||||
|             // Skip any dummy cycles | ||||
|             "ldr r1, [r7, #8]", // dummy_len | ||||
|             "cmp r1, #0", | ||||
|             "beq 9f", | ||||
|             "4:", | ||||
|             "ldr r3, [r4, #0x28]", // SR | ||||
|             "movs r2, #0x8", | ||||
|             "tst r3, r2", // SR.RFNE | ||||
|             "beq 4b", | ||||
|  | ||||
|             "mov r2, r4", | ||||
|             "adds r2, 0x60", // &DR | ||||
|             "ldrb r3, [r2]", // DR | ||||
|             "subs r1, #1", | ||||
|             "bne 4b", | ||||
|  | ||||
|             // Read RX fifo | ||||
|             "9:", | ||||
|             "ldr r0, [r7, #12]", // data | ||||
|             "ldr r1, [r7, #16]", // data_len | ||||
|  | ||||
|             "2:", | ||||
|             "ldr r3, [r4, #0x28]", // SR | ||||
|             "movs r2, #0x8", | ||||
|             "tst r3, r2", // SR.RFNE | ||||
|             "beq 2b", | ||||
|  | ||||
|             "mov r2, r4", | ||||
|             "adds r2, 0x60", // &DR | ||||
|             "ldrb r3, [r2]", // DR | ||||
|             "strb r3, [r0]", | ||||
|             "adds r0, #1", | ||||
|             "subs r1, #1", | ||||
|             "bne 2b", | ||||
|  | ||||
|             // Disable, write 0 to ssienr | ||||
|             "movs r0, #0", | ||||
|             "str r0, [r4, #8]", // SSIENR | ||||
|  | ||||
|             // Write 0 to CTRLR1 (returning to its default value) | ||||
|             // | ||||
|             // flash_enter_cmd_xip does NOT do this, and everything goes | ||||
|             // wrong unless we do it here | ||||
|             "str r0, [r4, #4]", // CTRLR1 | ||||
|  | ||||
|             "ldr r4, [r5, #20]", | ||||
|             "blx r4", // flash_enter_cmd_xip(); | ||||
|  | ||||
|             in("r0") &cmd as *const FlashCommand, | ||||
|             in("r1") ptrs, | ||||
|             out("r2") _, | ||||
|             out("r3") _, | ||||
|             out("r4") _, | ||||
|             // Registers r8-r10 are used to store values | ||||
|             // from r0-r2 in registers not clobbered by | ||||
|             // function calls. | ||||
|             // The values can't be passed in using r8-r10 directly | ||||
|             // due to https://github.com/rust-lang/rust/issues/99071 | ||||
|             out("r8") _, | ||||
|             out("r9") _, | ||||
|             out("r10") _, | ||||
|             clobber_abi("C"), | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| mod sealed { | ||||
|   | ||||
| @@ -24,6 +24,16 @@ async fn main(_spawner: Spawner) { | ||||
|     Timer::after(Duration::from_millis(10)).await; | ||||
|  | ||||
|     let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); | ||||
|  | ||||
|     // Get JEDEC id | ||||
|     let jedec = flash.jedec_id().unwrap(); | ||||
|     info!("jedec id: 0x{:x}", jedec); | ||||
|  | ||||
|     // Get unique id | ||||
|     let mut uid = [0; 8]; | ||||
|     flash.unique_id(&mut uid).unwrap(); | ||||
|     info!("unique id: {:?}", uid); | ||||
|  | ||||
|     erase_write_sector(&mut flash, 0x00); | ||||
|  | ||||
|     multiwrite_bytes(&mut flash, ERASE_SIZE as u32); | ||||
|   | ||||
| @@ -23,6 +23,15 @@ async fn main(_spawner: Spawner) { | ||||
|  | ||||
|     let mut flash = embassy_rp::flash::Flash::<_, { 2 * 1024 * 1024 }>::new(p.FLASH); | ||||
|  | ||||
|     // Get JEDEC id | ||||
|     let jedec = defmt::unwrap!(flash.jedec_id()); | ||||
|     info!("jedec id: 0x{:x}", jedec); | ||||
|  | ||||
|     // Get unique id | ||||
|     let mut uid = [0; 8]; | ||||
|     defmt::unwrap!(flash.unique_id(&mut uid)); | ||||
|     info!("unique id: {:?}", uid); | ||||
|  | ||||
|     let mut buf = [0u8; ERASE_SIZE]; | ||||
|     defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user