From 40f3f9119aeed6ad5cfa2a969f3c697511e7ce29 Mon Sep 17 00:00:00 2001 From: Andelf Date: Sat, 24 Aug 2024 15:38:19 +0800 Subject: [PATCH] feat(flash): add embedded-storage nor flash driver for xpi --- Cargo.toml | 1 + build.rs | 1 - src/flash/mod.rs | 290 ++++++++++++++++++++++++++ src/flash/romapi.rs | 492 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 5 files changed, 784 insertions(+), 1 deletion(-) create mode 100644 src/flash/mod.rs create mode 100644 src/flash/romapi.rs diff --git a/Cargo.toml b/Cargo.toml index 245e0f9..c1f8c0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ embedded-io-async = "0.6.1" embedded-hal-async = "1.0.0" chrono = { version = "0.4.38", default-features = false, optional = true } mcan = { version = "0.5.0", optional = true } +embedded-storage = "0.3.1" [build-dependencies] # hpm-metapac = { path = "../hpm-data/build/hpm-metapac", default-features = false, features = [ diff --git a/build.rs b/build.rs index 210851a..1d17a2d 100644 --- a/build.rs +++ b/build.rs @@ -161,7 +161,6 @@ fn main() { if let Some(r) = &p.registers { match r.kind { // Generate singletons per pin, not per port - "xpi" => {} "sysctl" => {} // For other peripherals, one singleton per peri _ => singletons.push(p.name.to_string()), diff --git a/src/flash/mod.rs b/src/flash/mod.rs new file mode 100644 index 0000000..40bcdce --- /dev/null +++ b/src/flash/mod.rs @@ -0,0 +1,290 @@ +//! XPI Flash memory (XPI Nor API) + +use core::marker::PhantomData; + +use embassy_hal_internal::{into_ref, Peripheral}; +use embedded_storage::nor_flash::NorFlashErrorKind; +use romapi::{ + xpi_nor_config_option_t, xpi_nor_config_t, xpi_nor_property_sector_size, xpi_nor_property_total_size, + xpi_xfer_channel_auto, +}; + +use crate::peripherals; + +#[allow(non_camel_case_types, non_snake_case, non_upper_case_globals, unused)] +mod romapi; + +const ROM_API_TABLE_ROOT: *const romapi::bootloader_api_table_t = 0x2001FF00 as *const romapi::bootloader_api_table_t; + +/// Flash page size. +pub const PAGE_SIZE: usize = 256; +/// Flash write size. +pub const WRITE_SIZE: usize = 1; +/// Flash read size. +pub const READ_SIZE: usize = 1; +/// Flash erase size. +pub const ERASE_SIZE: usize = 4096; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Error { + Fail, + InvalidArgument, + Timeout, + FlashSizeMismatch, + Unknown, +} + +impl Error { + fn chect_status(raw: u32) -> Result<(), Self> { + match raw { + 0 => Ok(()), + 1 => Err(Self::Fail), + 2 => Err(Self::InvalidArgument), + 3 => Err(Self::Timeout), + 4 => Err(Self::FlashSizeMismatch), + _ => Err(Self::Unknown), + } + } +} + +#[derive(Debug)] +pub struct Config { + pub header: u32, + pub option0: u32, + pub option1: u32, +} + +impl Config { + /// Load config from ROM data. + pub fn from_rom_data(xpi: impl Peripheral

) -> Option { + into_ref!(xpi); + let _ = xpi; + + const NOR_CFG_OPT_TAG: u32 = 0xfcf90_000; + + let nor_cfg_option_addr = (T::ADDR_OFFSET + 0x400) as *const u32; + + let header = unsafe { core::ptr::read_volatile(nor_cfg_option_addr) }; + if header & 0xfffff_000 != NOR_CFG_OPT_TAG { + return None; + } + + let option0 = unsafe { core::ptr::read_volatile(nor_cfg_option_addr.offset(1)) }; + let option1 = unsafe { core::ptr::read_volatile(nor_cfg_option_addr.offset(2)) }; + + Some(Self { + header, + option0, + option1, + }) + } +} + +// - MARK: Flash driver + +/// Flash driver. +pub struct Flash<'d, T: Instance, const FLASH_SIZE: usize> { + phantom: PhantomData<&'d mut T>, + sector_size: u32, + nor_config: xpi_nor_config_t, +} + +impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { + pub fn new(_periph: impl Peripheral

+ 'd, config: Config) -> Result { + let option: xpi_nor_config_option_t = xpi_nor_config_option_t { + header: config.header, + option0: config.option0, + option1: config.option1, + option2: 0, + }; + + let nor_config = rom_xpi_nor_auto_config(T::REGS.as_ptr() as *mut _, &option)?; + + let sector_size = + rom_xpi_nor_get_property(T::REGS.as_ptr() as *mut _, &nor_config, xpi_nor_property_sector_size)?; + let flash_size = + rom_xpi_nor_get_property(T::REGS.as_ptr() as *mut _, &nor_config, xpi_nor_property_total_size)?; + + // Due to HPMicro's dynamic flash config nature, end user must provide the correct flash size + if flash_size != FLASH_SIZE as u32 { + return Err(Error::FlashSizeMismatch); + } + + Ok(Self { + phantom: PhantomData, + sector_size, + nor_config, + }) + } + + /// Blocking read. + /// + /// The offset and buffer must be aligned. + /// + /// NOTE: `offset` is an offset from the flash start, NOT an absolute address. + pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + // FIXME: invalidate range instead of all + crate::rt::andes::l1c_dc_invalidate_all(); + let flash_data = + unsafe { core::slice::from_raw_parts((T::ADDR_OFFSET as u32 + offset) as *const u8, bytes.len()) }; + + bytes.copy_from_slice(flash_data); + Ok(()) + } + + /// Flash capacity. + pub fn capacity(&self) -> usize { + FLASH_SIZE + } + + /// Blocking erase. + /// + /// NOTE: `offset` is an offset from the flash start, NOT an absolute address. + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let xpi_nor_driver = unsafe { &*(*ROM_API_TABLE_ROOT).xpi_nor_driver_if }; + + let use_sector_erase = (from % self.sector_size == 0) && (to % self.sector_size == 0); + + if use_sector_erase { + let sectors = (to - from) / self.sector_size; + + for i in 0..sectors { + let sector = from + i * self.sector_size; + let ret = unsafe { + xpi_nor_driver.erase_sector.unwrap()( + T::REGS.as_ptr() as *mut _, + xpi_xfer_channel_auto, + &self.nor_config, + sector, + ) + }; + + Error::chect_status(ret)?; + } + } else { + let ret = unsafe { + xpi_nor_driver.erase.unwrap()( + T::REGS.as_ptr() as *mut _, + xpi_xfer_channel_auto, + &self.nor_config, + from, + to - from, + ) + }; + + Error::chect_status(ret)?; + } + + Ok(()) + } + + /// Blocking write. + /// + /// The offset and buffer must be aligned. + /// + /// NOTE: `offset` is an offset from the flash start, NOT an absolute address. + pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let xpi_nor_driver = unsafe { &*(*ROM_API_TABLE_ROOT).xpi_nor_driver_if }; + + let ret = unsafe { + xpi_nor_driver.program.unwrap()( + T::REGS.as_ptr() as *mut _, + xpi_xfer_channel_auto, + &mut self.nor_config, + bytes.as_ptr() as *const _, + offset, + bytes.len() as u32, + ) + }; + + Error::chect_status(ret) + } +} + +// - MARK: ROMAPI fn + +fn rom_xpi_nor_auto_config( + xpi_base: *mut u32, + cfg_option: &xpi_nor_config_option_t, +) -> Result { + let xpi_nor_driver = unsafe { &*(*ROM_API_TABLE_ROOT).xpi_nor_driver_if }; + + let mut nor_cfg = unsafe { core::mem::zeroed() }; + let ret = unsafe { xpi_nor_driver.auto_config.unwrap()(xpi_base, &mut nor_cfg, cfg_option) }; + + Error::chect_status(ret).map(|_| nor_cfg) +} + +fn rom_xpi_nor_get_property(xpi_base: *mut u32, nor_cfg: &xpi_nor_config_t, property_id: u32) -> Result { + let xpi_nor_driver = unsafe { &*(*ROM_API_TABLE_ROOT).xpi_nor_driver_if }; + + let mut value: u32 = 0; + let ret = unsafe { xpi_nor_driver.get_property.unwrap()(xpi_base, nor_cfg, property_id, &mut value) }; + + Error::chect_status(ret).map(|_| value) +} + +// - MARK: Traits + +trait SealedInstance { + const ADDR_OFFSET: u32; + + const REGS: crate::pac::xpi::Xpi; +} + +/// Flash instance. +#[allow(private_bounds)] +pub trait Instance: SealedInstance {} + +impl SealedInstance for peripherals::XPI0 { + const ADDR_OFFSET: u32 = 0x8000_0000; + const REGS: crate::pac::xpi::Xpi = crate::pac::XPI0; +} +impl Instance for peripherals::XPI0 {} + +#[cfg(peri_xpi1)] +impl SealedInstance for peripherals::XPI1 { + const ADDR_OFFSET: u32 = 0x9000_0000; + const REGS: crate::pac::xpi::Xpi = crate::pac::XPI1; +} +#[cfg(peri_xpi1)] +impl Instance for peripherals::XPI1 {} + +impl embedded_storage::nor_flash::NorFlashError for Error { + fn kind(&self) -> NorFlashErrorKind { + match *self { + Error::FlashSizeMismatch => NorFlashErrorKind::OutOfBounds, + _ => NorFlashErrorKind::Other, + } + } +} + +impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage::nor_flash::ErrorType for Flash<'d, T, FLASH_SIZE> { + type Error = Error; +} + +impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage::nor_flash::ReadNorFlash for Flash<'d, T, FLASH_SIZE> { + const READ_SIZE: usize = READ_SIZE; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.capacity() + } +} + +impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage::nor_flash::NorFlash for Flash<'d, T, FLASH_SIZE> { + const WRITE_SIZE: usize = WRITE_SIZE; + + const ERASE_SIZE: usize = ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.blocking_erase(from, to) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(offset, bytes) + } +} diff --git a/src/flash/romapi.rs b/src/flash/romapi.rs new file mode 100644 index 0000000..0e18a46 --- /dev/null +++ b/src/flash/romapi.rs @@ -0,0 +1,492 @@ +/* automatically generated by rust-bindgen 0.69.4 */ + +pub type hpm_stat_t = u32; +#[doc = "< Address range: [0, 7]"] +pub const otp_region0_mask: otp_region_t = 1; +#[doc = "< Address range: [8, 15]"] +pub const otp_region1_mask: otp_region_t = 2; +#[doc = "< Address range: [16, 127]"] +pub const otp_region2_mask: otp_region_t = 4; +#[doc = "< Address range: user defined"] +pub const otp_region3_mask: otp_region_t = 8; +#[doc = " Definitions\n/\n/**\n @brief OTP region definitions"] +pub type otp_region_t = ::core::ffi::c_uint; +pub const otp_no_lock: otp_lock_option_t = 0; +pub const otp_read_only: otp_lock_option_t = 1; +pub const otp_permanent_no_lock: otp_lock_option_t = 2; +pub const otp_disable_access: otp_lock_option_t = 3; +pub const otp_lock_option_max: otp_lock_option_t = 3; +#[doc = " @brief OTP lock options"] +pub type otp_lock_option_t = ::core::ffi::c_uint; +#[doc = " @brief XPI_Type definitions for\n @note For compatibility"] +pub type XPI_Type = u32; +#[doc = "< The address is based on the device connected to Channel A1"] +pub const xpi_xfer_channel_a1: xpi_xfer_channel_t = 0; +#[doc = "< The address is based on the device connected to Channel A2"] +pub const xpi_xfer_channel_a2: xpi_xfer_channel_t = 1; +#[doc = "< The address is based on the device connected to Channel B1"] +pub const xpi_xfer_channel_b1: xpi_xfer_channel_t = 2; +#[doc = "< The address is based on the device connected to Channel B2"] +pub const xpi_xfer_channel_b2: xpi_xfer_channel_t = 3; +#[doc = "< The channel is auto determined"] +pub const xpi_xfer_channel_auto: xpi_xfer_channel_t = 4; +#[doc = " @brief XPI Transfer Channel type definitions"] +pub type xpi_xfer_channel_t = ::core::ffi::c_uint; +#[doc = "< Port: Channel A1"] +pub const xpi_channel_a1: xpi_channel_t = 0; +#[doc = "< Port: Channel A2"] +pub const xpi_channel_a2: xpi_channel_t = 1; +#[doc = "< Port: Channel B1"] +pub const xpi_channel_b1: xpi_channel_t = 2; +#[doc = "< Port: Channel B2"] +pub const xpi_channel_b2: xpi_channel_t = 3; +#[doc = " @brief XPI Channel defitions"] +pub type xpi_channel_t = ::core::ffi::c_uint; + +#[doc = "< Total size in bytes"] +pub const xpi_nor_property_total_size: u32 = 0; +#[doc = "< Page size in bytes"] +pub const xpi_nor_property_page_size: u32 = 1; +#[doc = ", + pub deinit: ::core::option::Option, + pub read_from_shadow: ::core::option::Option u32>, + pub read_from_ip: ::core::option::Option u32>, + pub program: + ::core::option::Option hpm_stat_t>, + pub reload: ::core::option::Option hpm_stat_t>, + pub lock: ::core::option::Option hpm_stat_t>, + pub lock_shadow: + ::core::option::Option hpm_stat_t>, + pub set_configurable_region: + ::core::option::Option hpm_stat_t>, + pub write_shadow_register: ::core::option::Option hpm_stat_t>, +} +#[doc = " @brief XPI driver interface"] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct xpi_driver_interface_t { + pub version: u32, + pub get_default_config: ::core::option::Option hpm_stat_t>, + pub get_default_device_config: + ::core::option::Option hpm_stat_t>, + pub init: + ::core::option::Option hpm_stat_t>, + pub config_ahb_buffer: ::core::option::Option< + unsafe extern "C" fn(base: *mut XPI_Type, ahb_buf_cfg: *mut xpi_ahb_buffer_cfg_t) -> hpm_stat_t, + >, + pub config_device: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + dev_cfg: *mut xpi_device_config_t, + channel: xpi_channel_t, + ) -> hpm_stat_t, + >, + pub update_instr_table: ::core::option::Option< + unsafe extern "C" fn(base: *mut XPI_Type, inst_base: *const u32, seq_idx: u32, num: u32) -> hpm_stat_t, + >, + pub transfer_blocking: + ::core::option::Option hpm_stat_t>, + pub software_reset: ::core::option::Option, + pub is_idle: ::core::option::Option bool>, + pub update_dllcr: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + serial_root_clk_freq: u32, + data_valid_time: u32, + channel: xpi_channel_t, + dly_target: u32, + ), + >, + pub get_abs_apb_xfer_addr: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + channel: xpi_xfer_channel_t, + in_addr: u32, + out_addr: *mut u32, + ) -> hpm_stat_t, + >, +} +#[doc = " @brief XPI NOR driver interface"] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct xpi_nor_driver_interface_t { + pub version: u32, + pub get_config: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + nor_cfg: *mut xpi_nor_config_t, + cfg_option: *mut xpi_nor_config_option_t, + ) -> hpm_stat_t, + >, + pub init: ::core::option::Option< + unsafe extern "C" fn(base: *mut XPI_Type, nor_config: *mut xpi_nor_config_t) -> hpm_stat_t, + >, + pub enable_write: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + channel: xpi_xfer_channel_t, + nor_config: *const xpi_nor_config_t, + addr: u32, + ) -> hpm_stat_t, + >, + pub get_status: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + channel: xpi_xfer_channel_t, + nor_config: *const xpi_nor_config_t, + addr: u32, + out_status: *mut u16, + ) -> hpm_stat_t, + >, + pub wait_busy: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + channel: xpi_xfer_channel_t, + nor_config: *const xpi_nor_config_t, + addr: u32, + ) -> hpm_stat_t, + >, + pub erase: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + channel: xpi_xfer_channel_t, + nor_config: *const xpi_nor_config_t, + start: u32, + length: u32, + ) -> hpm_stat_t, + >, + pub erase_chip: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + channel: xpi_xfer_channel_t, + nor_config: *const xpi_nor_config_t, + ) -> hpm_stat_t, + >, + pub erase_sector: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + channel: xpi_xfer_channel_t, + nor_config: *const xpi_nor_config_t, + addr: u32, + ) -> hpm_stat_t, + >, + pub erase_block: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + channel: xpi_xfer_channel_t, + nor_config: *const xpi_nor_config_t, + addr: u32, + ) -> hpm_stat_t, + >, + pub program: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + channel: xpi_xfer_channel_t, + nor_config: *const xpi_nor_config_t, + src: *const u32, + dst_addr: u32, + length: u32, + ) -> hpm_stat_t, + >, + pub read: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + channel: xpi_xfer_channel_t, + nor_config: *const xpi_nor_config_t, + dst: *mut u32, + start: u32, + length: u32, + ) -> hpm_stat_t, + >, + pub page_program_nonblocking: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + channel: xpi_xfer_channel_t, + nor_config: *const xpi_nor_config_t, + src: *const u32, + dst_addr: u32, + length: u32, + ) -> hpm_stat_t, + >, + pub erase_sector_nonblocking: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + channel: xpi_xfer_channel_t, + nor_config: *const xpi_nor_config_t, + addr: u32, + ) -> hpm_stat_t, + >, + pub erase_block_nonblocking: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + channel: xpi_xfer_channel_t, + nor_config: *const xpi_nor_config_t, + addr: u32, + ) -> hpm_stat_t, + >, + pub erase_chip_nonblocking: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + channel: xpi_xfer_channel_t, + nor_config: *const xpi_nor_config_t, + ) -> hpm_stat_t, + >, + pub reserved0: [u32; 3usize], + pub auto_config: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + nor_cfg: *mut xpi_nor_config_t, + cfg_option: *const xpi_nor_config_option_t, + ) -> hpm_stat_t, + >, + pub get_property: ::core::option::Option< + unsafe extern "C" fn( + base: *mut XPI_Type, + nor_cfg: *const xpi_nor_config_t, + property_id: u32, + value: *mut u32, + ) -> hpm_stat_t, + >, +} +#[doc = " @brief Bootloader API table"] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct bootloader_api_table_t { + pub version: u32, + pub copyright: *const ::core::ffi::c_char, + pub run_bootloader: ::core::option::Option hpm_stat_t>, + pub otp_driver_if: *const otp_driver_interface_t, + pub xpi_driver_if: *const xpi_driver_interface_t, + pub xpi_nor_driver_if: *const xpi_nor_driver_interface_t, + pub xpi_ram_driver_if: *const ::core::ffi::c_void, + pub sdp_driver_if: *const ::core::ffi::c_void, + pub sec_boot_api_if: *const ::core::ffi::c_void, + _reserved: [u32; 2usize], + pub exip_api_if: *const ::core::ffi::c_void, +} diff --git a/src/lib.rs b/src/lib.rs index c2d62a7..f695e08 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,6 +42,7 @@ pub mod mode { // required peripherals pub mod dma; +pub mod flash; pub mod sysctl; // other peripherals