rp: Read flash unique id and jedec id
This commit is contained in:
parent
b0541c01be
commit
ab63f3832f
@ -187,6 +187,23 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
|
|||||||
crate::multicore::resume_core1();
|
crate::multicore::resume_core1();
|
||||||
Ok(())
|
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> {
|
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"),
|
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 {
|
mod sealed {
|
||||||
|
@ -24,6 +24,16 @@ async fn main(_spawner: Spawner) {
|
|||||||
Timer::after(Duration::from_millis(10)).await;
|
Timer::after(Duration::from_millis(10)).await;
|
||||||
|
|
||||||
let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH);
|
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);
|
erase_write_sector(&mut flash, 0x00);
|
||||||
|
|
||||||
multiwrite_bytes(&mut flash, ERASE_SIZE as u32);
|
multiwrite_bytes(&mut flash, ERASE_SIZE as u32);
|
||||||
|
Loading…
Reference in New Issue
Block a user