diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2023-05-16 09:36:06 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-05-16 09:36:06 +0000 |
| commit | 56f2e0c9a02f6db9fe4e3a72a9b7fdd160a90aa8 (patch) | |
| tree | 08c779b0eb667841d33131a0d4f302d3a7b234a7 | |
| parent | b0541c01bed231e0e6355db83c87e3ec2b196e70 (diff) | |
| parent | b950d6d72bf92f1943f885ce700685fedf4b6cd9 (diff) | |
Merge #1462
1462: rp: Read flash unique id and jedec id r=Dirbaio a=kalkyl
Co-authored-by: kalkyl <[email protected]>
| -rw-r--r-- | embassy-rp/src/flash.rs | 231 | ||||
| -rw-r--r-- | examples/rp/src/bin/flash.rs | 10 | ||||
| -rw-r--r-- | tests/rp/src/bin/flash.rs | 9 |
3 files changed, 250 insertions, 0 deletions
diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 51c7af913..929bd028c 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs | |||
| @@ -187,6 +187,23 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { | |||
| 187 | crate::multicore::resume_core1(); | 187 | crate::multicore::resume_core1(); |
| 188 | Ok(()) | 188 | Ok(()) |
| 189 | } | 189 | } |
| 190 | |||
| 191 | /// Read SPI flash unique ID | ||
| 192 | pub fn unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> { | ||
| 193 | unsafe { self.in_ram(|| ram_helpers::flash_unique_id(uid, true))? }; | ||
| 194 | Ok(()) | ||
| 195 | } | ||
| 196 | |||
| 197 | /// Read SPI flash JEDEC ID | ||
| 198 | pub fn jedec_id(&mut self) -> Result<u32, Error> { | ||
| 199 | let mut jedec = None; | ||
| 200 | unsafe { | ||
| 201 | self.in_ram(|| { | ||
| 202 | jedec.replace(ram_helpers::flash_jedec_id(true)); | ||
| 203 | })?; | ||
| 204 | }; | ||
| 205 | Ok(jedec.unwrap()) | ||
| 206 | } | ||
| 190 | } | 207 | } |
| 191 | 208 | ||
| 192 | impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> { | 209 | impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> { |
| @@ -457,6 +474,220 @@ mod ram_helpers { | |||
| 457 | clobber_abi("C"), | 474 | clobber_abi("C"), |
| 458 | ); | 475 | ); |
| 459 | } | 476 | } |
| 477 | |||
| 478 | #[repr(C)] | ||
| 479 | struct FlashCommand { | ||
| 480 | cmd_addr: *const u8, | ||
| 481 | cmd_addr_len: u32, | ||
| 482 | dummy_len: u32, | ||
| 483 | data: *mut u8, | ||
| 484 | data_len: u32, | ||
| 485 | } | ||
| 486 | |||
| 487 | /// Return SPI flash unique ID | ||
| 488 | /// | ||
| 489 | /// Not all SPI flashes implement this command, so check the JEDEC | ||
| 490 | /// ID before relying on it. The Winbond parts commonly seen on | ||
| 491 | /// RP2040 devboards (JEDEC=0xEF7015) support an 8-byte unique ID; | ||
| 492 | /// https://forums.raspberrypi.com/viewtopic.php?t=331949 suggests | ||
| 493 | /// that LCSC (Zetta) parts have a 16-byte unique ID (which is | ||
| 494 | /// *not* unique in just its first 8 bytes), | ||
| 495 | /// JEDEC=0xBA6015. Macronix and Spansion parts do not have a | ||
| 496 | /// unique ID. | ||
| 497 | /// | ||
| 498 | /// The returned bytes are relatively predictable and should be | ||
| 499 | /// salted and hashed before use if that is an issue (e.g. for MAC | ||
| 500 | /// addresses). | ||
| 501 | /// | ||
| 502 | /// # Safety | ||
| 503 | /// | ||
| 504 | /// Nothing must access flash while this is running. | ||
| 505 | /// Usually this means: | ||
| 506 | /// - interrupts must be disabled | ||
| 507 | /// - 2nd core must be running code from RAM or ROM with interrupts disabled | ||
| 508 | /// - DMA must not access flash memory | ||
| 509 | /// | ||
| 510 | /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) | ||
| 511 | pub unsafe fn flash_unique_id(out: &mut [u8], use_boot2: bool) { | ||
| 512 | let mut boot2 = [0u32; 256 / 4]; | ||
| 513 | let ptrs = if use_boot2 { | ||
| 514 | rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); | ||
| 515 | flash_function_pointers_with_boot2(false, false, &boot2) | ||
| 516 | } else { | ||
| 517 | flash_function_pointers(false, false) | ||
| 518 | }; | ||
| 519 | // 4B - read unique ID | ||
| 520 | let cmd = [0x4B]; | ||
| 521 | read_flash(&cmd[..], 4, out, &ptrs as *const FlashFunctionPointers); | ||
| 522 | } | ||
| 523 | |||
| 524 | /// Return SPI flash JEDEC ID | ||
| 525 | /// | ||
| 526 | /// This is the three-byte manufacturer-and-model identifier | ||
| 527 | /// commonly used to check before using manufacturer-specific SPI | ||
| 528 | /// flash features, e.g. 0xEF7015 for Winbond W25Q16JV. | ||
| 529 | /// | ||
| 530 | /// # Safety | ||
| 531 | /// | ||
| 532 | /// Nothing must access flash while this is running. | ||
| 533 | /// Usually this means: | ||
| 534 | /// - interrupts must be disabled | ||
| 535 | /// - 2nd core must be running code from RAM or ROM with interrupts disabled | ||
| 536 | /// - DMA must not access flash memory | ||
| 537 | /// | ||
| 538 | /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) | ||
| 539 | pub unsafe fn flash_jedec_id(use_boot2: bool) -> u32 { | ||
| 540 | let mut boot2 = [0u32; 256 / 4]; | ||
| 541 | let ptrs = if use_boot2 { | ||
| 542 | rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); | ||
| 543 | flash_function_pointers_with_boot2(false, false, &boot2) | ||
| 544 | } else { | ||
| 545 | flash_function_pointers(false, false) | ||
| 546 | }; | ||
| 547 | let mut id = [0u8; 4]; | ||
| 548 | // 9F - read JEDEC ID | ||
| 549 | let cmd = [0x9F]; | ||
| 550 | read_flash(&cmd[..], 0, &mut id[1..4], &ptrs as *const FlashFunctionPointers); | ||
| 551 | u32::from_be_bytes(id) | ||
| 552 | } | ||
| 553 | |||
| 554 | unsafe fn read_flash(cmd_addr: &[u8], dummy_len: u32, out: &mut [u8], ptrs: *const FlashFunctionPointers) { | ||
| 555 | read_flash_inner( | ||
| 556 | FlashCommand { | ||
| 557 | cmd_addr: cmd_addr.as_ptr(), | ||
| 558 | cmd_addr_len: cmd_addr.len() as u32, | ||
| 559 | dummy_len, | ||
| 560 | data: out.as_mut_ptr(), | ||
| 561 | data_len: out.len() as u32, | ||
| 562 | }, | ||
| 563 | ptrs, | ||
| 564 | ); | ||
| 565 | } | ||
| 566 | |||
| 567 | /// Issue a generic SPI flash read command | ||
| 568 | /// | ||
| 569 | /// # Arguments | ||
| 570 | /// | ||
| 571 | /// * `cmd` - `FlashCommand` structure | ||
| 572 | /// * `ptrs` - Flash function pointers as per `write_flash_inner` | ||
| 573 | /// | ||
| 574 | /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) | ||
| 575 | #[inline(never)] | ||
| 576 | #[link_section = ".data.ram_func"] | ||
| 577 | unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) { | ||
| 578 | core::arch::asm!( | ||
| 579 | "mov r10, r0", // cmd | ||
| 580 | "mov r5, r1", // ptrs | ||
| 581 | |||
| 582 | "ldr r4, [r5, #0]", | ||
| 583 | "blx r4", // connect_internal_flash() | ||
| 584 | |||
| 585 | "ldr r4, [r5, #4]", | ||
| 586 | "blx r4", // flash_exit_xip() | ||
| 587 | |||
| 588 | "mov r7, r10", // cmd | ||
| 589 | |||
| 590 | "movs r4, #0x18", | ||
| 591 | "lsls r4, r4, #24", // 0x18000000, SSI, RP2040 datasheet 4.10.13 | ||
| 592 | |||
| 593 | // Disable, write 0 to SSIENR | ||
| 594 | "movs r0, #0", | ||
| 595 | "str r0, [r4, #8]", // SSIENR | ||
| 596 | |||
| 597 | // Write ctrlr0 | ||
| 598 | "movs r0, #0x3", | ||
| 599 | "lsls r0, r0, #8", // TMOD=0x300 | ||
| 600 | "ldr r1, [r4, #0]", // CTRLR0 | ||
| 601 | "orrs r1, r0", | ||
| 602 | "str r1, [r4, #0]", | ||
| 603 | |||
| 604 | // Write ctrlr1 with len-1 | ||
| 605 | "ldr r0, [r7, #8]", // dummy_len | ||
| 606 | "ldr r1, [r7, #16]", // data_len | ||
| 607 | "add r0, r1", | ||
| 608 | "subs r0, #1", | ||
| 609 | "str r0, [r4, #0x04]", // CTRLR1 | ||
| 610 | |||
| 611 | // Enable, write 1 to ssienr | ||
| 612 | "movs r0, #1", | ||
| 613 | "str r0, [r4, #8]", // SSIENR | ||
| 614 | |||
| 615 | // Write cmd/addr phase to DR | ||
| 616 | "mov r2, r4", | ||
| 617 | "adds r2, 0x60", // &DR | ||
| 618 | "ldr r0, [r7, #0]", // cmd_addr | ||
| 619 | "ldr r1, [r7, #4]", // cmd_addr_len | ||
| 620 | "10:", | ||
| 621 | "ldrb r3, [r0]", | ||
| 622 | "strb r3, [r2]", // DR | ||
| 623 | "adds r0, #1", | ||
| 624 | "subs r1, #1", | ||
| 625 | "bne 10b", | ||
| 626 | |||
| 627 | // Skip any dummy cycles | ||
| 628 | "ldr r1, [r7, #8]", // dummy_len | ||
| 629 | "cmp r1, #0", | ||
| 630 | "beq 9f", | ||
| 631 | "4:", | ||
| 632 | "ldr r3, [r4, #0x28]", // SR | ||
| 633 | "movs r2, #0x8", | ||
| 634 | "tst r3, r2", // SR.RFNE | ||
| 635 | "beq 4b", | ||
| 636 | |||
| 637 | "mov r2, r4", | ||
| 638 | "adds r2, 0x60", // &DR | ||
| 639 | "ldrb r3, [r2]", // DR | ||
| 640 | "subs r1, #1", | ||
| 641 | "bne 4b", | ||
| 642 | |||
| 643 | // Read RX fifo | ||
| 644 | "9:", | ||
| 645 | "ldr r0, [r7, #12]", // data | ||
| 646 | "ldr r1, [r7, #16]", // data_len | ||
| 647 | |||
| 648 | "2:", | ||
| 649 | "ldr r3, [r4, #0x28]", // SR | ||
| 650 | "movs r2, #0x8", | ||
| 651 | "tst r3, r2", // SR.RFNE | ||
| 652 | "beq 2b", | ||
| 653 | |||
| 654 | "mov r2, r4", | ||
| 655 | "adds r2, 0x60", // &DR | ||
| 656 | "ldrb r3, [r2]", // DR | ||
| 657 | "strb r3, [r0]", | ||
| 658 | "adds r0, #1", | ||
| 659 | "subs r1, #1", | ||
| 660 | "bne 2b", | ||
| 661 | |||
| 662 | // Disable, write 0 to ssienr | ||
| 663 | "movs r0, #0", | ||
| 664 | "str r0, [r4, #8]", // SSIENR | ||
| 665 | |||
| 666 | // Write 0 to CTRLR1 (returning to its default value) | ||
| 667 | // | ||
| 668 | // flash_enter_cmd_xip does NOT do this, and everything goes | ||
| 669 | // wrong unless we do it here | ||
| 670 | "str r0, [r4, #4]", // CTRLR1 | ||
| 671 | |||
| 672 | "ldr r4, [r5, #20]", | ||
| 673 | "blx r4", // flash_enter_cmd_xip(); | ||
| 674 | |||
| 675 | in("r0") &cmd as *const FlashCommand, | ||
| 676 | in("r1") ptrs, | ||
| 677 | out("r2") _, | ||
| 678 | out("r3") _, | ||
| 679 | out("r4") _, | ||
| 680 | // Registers r8-r10 are used to store values | ||
| 681 | // from r0-r2 in registers not clobbered by | ||
| 682 | // function calls. | ||
| 683 | // The values can't be passed in using r8-r10 directly | ||
| 684 | // due to https://github.com/rust-lang/rust/issues/99071 | ||
| 685 | out("r8") _, | ||
| 686 | out("r9") _, | ||
| 687 | out("r10") _, | ||
| 688 | clobber_abi("C"), | ||
| 689 | ); | ||
| 690 | } | ||
| 460 | } | 691 | } |
| 461 | 692 | ||
| 462 | mod sealed { | 693 | mod sealed { |
diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs index 8d6b379f4..19076150c 100644 --- a/examples/rp/src/bin/flash.rs +++ b/examples/rp/src/bin/flash.rs | |||
| @@ -24,6 +24,16 @@ async fn main(_spawner: Spawner) { | |||
| 24 | Timer::after(Duration::from_millis(10)).await; | 24 | Timer::after(Duration::from_millis(10)).await; |
| 25 | 25 | ||
| 26 | let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); | 26 | let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); |
| 27 | |||
| 28 | // Get JEDEC id | ||
| 29 | let jedec = flash.jedec_id().unwrap(); | ||
| 30 | info!("jedec id: 0x{:x}", jedec); | ||
| 31 | |||
| 32 | // Get unique id | ||
| 33 | let mut uid = [0; 8]; | ||
| 34 | flash.unique_id(&mut uid).unwrap(); | ||
| 35 | info!("unique id: {:?}", uid); | ||
| 36 | |||
| 27 | erase_write_sector(&mut flash, 0x00); | 37 | erase_write_sector(&mut flash, 0x00); |
| 28 | 38 | ||
| 29 | multiwrite_bytes(&mut flash, ERASE_SIZE as u32); | 39 | multiwrite_bytes(&mut flash, ERASE_SIZE as u32); |
diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs index 897e3804f..00bebe2b6 100644 --- a/tests/rp/src/bin/flash.rs +++ b/tests/rp/src/bin/flash.rs | |||
| @@ -23,6 +23,15 @@ async fn main(_spawner: Spawner) { | |||
| 23 | 23 | ||
| 24 | let mut flash = embassy_rp::flash::Flash::<_, { 2 * 1024 * 1024 }>::new(p.FLASH); | 24 | let mut flash = embassy_rp::flash::Flash::<_, { 2 * 1024 * 1024 }>::new(p.FLASH); |
| 25 | 25 | ||
| 26 | // Get JEDEC id | ||
| 27 | let jedec = defmt::unwrap!(flash.jedec_id()); | ||
| 28 | info!("jedec id: 0x{:x}", jedec); | ||
| 29 | |||
| 30 | // Get unique id | ||
| 31 | let mut uid = [0; 8]; | ||
| 32 | defmt::unwrap!(flash.unique_id(&mut uid)); | ||
| 33 | info!("unique id: {:?}", uid); | ||
| 34 | |||
| 26 | let mut buf = [0u8; ERASE_SIZE]; | 35 | let mut buf = [0u8; ERASE_SIZE]; |
| 27 | defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); | 36 | defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); |
| 28 | 37 | ||
