From 0b14a865c6696191ebce557f53e8ea21f5f08b54 Mon Sep 17 00:00:00 2001 From: Shaibal Ghosh Date: Thu, 13 Feb 2025 21:04:02 +0530 Subject: [PATCH] Add flexspi support --- Cargo.toml | 18 +- examples/rt685s-evk/Cargo.toml | 2 + examples/rt685s-evk/memory.x | 23 +- .../src/bin/flexspistoragedevicedriver.rs | 242 +++ src/flexspistorage.rs | 1452 +++++++++++++++++ src/lib.rs | 4 + src/spinorstorage.rs | 133 ++ src/storage.rs | 126 ++ 8 files changed, 1988 insertions(+), 12 deletions(-) create mode 100644 examples/rt685s-evk/src/bin/flexspistoragedevicedriver.rs create mode 100644 src/flexspistorage.rs create mode 100644 src/spinorstorage.rs create mode 100644 src/storage.rs diff --git a/Cargo.toml b/Cargo.toml index 127b036a..01c13503 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,10 +25,7 @@ default = ["mimxrt685s", "rt"] ## [`rp-pac`](https://docs.rs/crates/rp-pac). This brings in the ## [`cortex-m-rt`](https://docs.rs/cortex-m-rt) crate, which adds ## startup code and minimal runtime initialization. -rt = [ - "mimxrt685s-pac?/rt", - "mimxrt633s-pac?/rt", -] +rt = ["mimxrt685s-pac?/rt", "mimxrt633s-pac?/rt"] ## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` ## debug-log messages and formatting in embassy drivers. @@ -87,13 +84,22 @@ embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = { version = "1.0" } embedded-hal-nb = { version = "1.0" } +mimxrt600-fcb = "0.1.0" document-features = "0.2.7" paste = "1.0" # PACs -mimxrt685s-pac = { version = "0.2.2", optional = true, features = ["rt", "critical-section", "defmt"] } -mimxrt633s-pac = { version = "0.2.0", optional = true, features = ["rt", "critical-section", "defmt"] } +mimxrt685s-pac = { version = "0.2.2", optional = true, features = [ + "rt", + "critical-section", + "defmt", +] } +mimxrt633s-pac = { version = "0.2.0", optional = true, features = [ + "rt", + "critical-section", + "defmt", +] } [dev-dependencies] embassy-executor = { git = "https://github.com/embassy-rs/embassy" } diff --git a/examples/rt685s-evk/Cargo.toml b/examples/rt685s-evk/Cargo.toml index c508991a..7384975a 100644 --- a/examples/rt685s-evk/Cargo.toml +++ b/examples/rt685s-evk/Cargo.toml @@ -40,6 +40,8 @@ embedded-hal-async = "1.0.0" futures = { version = "0.3.30", default-features = false, features = [ "async-await", ] } +embedded-storage = { version = "0.3" } +embedded-storage-async = { version = "0.4.1" } mimxrt600-fcb = "0.1.0" rand = { version = "0.8.5", default-features = false } diff --git a/examples/rt685s-evk/memory.x b/examples/rt685s-evk/memory.x index 5ea82fd7..e5669251 100644 --- a/examples/rt685s-evk/memory.x +++ b/examples/rt685s-evk/memory.x @@ -3,32 +3,43 @@ MEMORY { FCB : ORIGIN = 0x08000400, LENGTH = 512 BIV : ORIGIN = 0x08000600, LENGTH = 4 KEYSTORE : ORIGIN = 0x08000800, LENGTH = 2K - FLASH : ORIGIN = 0x08001000, LENGTH = 1M + FLASH : ORIGIN = 0x08001000, LENGTH = 1004K + FLEXSPIFLASH: ORIGIN = 0x080FC000, LENGTH = 10K RAM : ORIGIN = 0x20080000, LENGTH = 1536K + FLEXSPIRAM: ORIGIN = 0x0, LENGTH = 10K } - + +__flexspi_ram_start_addr__ = ORIGIN(FLEXSPIRAM); +__flexspi_flash_start_addr__ = ORIGIN(FLEXSPIFLASH); +__flexspi_flash_end_addr__ = ORIGIN(FLEXSPIFLASH) + LENGTH(FLEXSPIFLASH); + + SECTIONS { .otfad : { . = ALIGN(4); KEEP(* (.otfad)) . = ALIGN(4); } > OTFAD - + .fcb : { . = ALIGN(4); KEEP(* (.fcb)) . = ALIGN(4); } > FCB - + .biv : { . = ALIGN(4); KEEP(* (.biv)) . = ALIGN(4); } > BIV - + .keystore : { . = ALIGN(4); KEEP(* (.keystore)) . = ALIGN(4); } > KEYSTORE -} + + .flexspi_code : { + * (.flexspi_code) + } >FLEXSPIRAM AT>FLEXSPIFLASH +} \ No newline at end of file diff --git a/examples/rt685s-evk/src/bin/flexspistoragedevicedriver.rs b/examples/rt685s-evk/src/bin/flexspistoragedevicedriver.rs new file mode 100644 index 00000000..0a6c2e14 --- /dev/null +++ b/examples/rt685s-evk/src/bin/flexspistoragedevicedriver.rs @@ -0,0 +1,242 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_imxrt::flexspistorage::{ + AhbConfig, FlexSpiBusWidth, FlexSpiFlashPort, FlexSpiFlashPortDeviceInstance, FlexspiAhbBufferConfig, + FlexspiAhbWriteWaitUnit, FlexspiConfig, FlexspiCsIntervalCycleUnit, FlexspiDeviceConfig, FlexspiReadSampleClock, + FlexspiStorage, +}; +use embassy_imxrt::spinorstorage::SpiStorage; +use embassy_imxrt::storage::{ + BlockingNorStorageDriver, ConfigureCmdSeq, NorStorageCmd, NorStorageCmdMode, NorStorageCmdSeq, NorStorageCmdType, +}; +use embassy_time::Timer; +use embedded_storage::nor_flash::{NorFlash as BlockingNorFlash, ReadNorFlash as BlockingReadNorFlash}; +use {defmt_rtt as _, panic_probe as _}; + +static ADDR: u32 = 0x2F000; + +struct StorageDeviceDriver { + // Bus driver dependency + spi_nor_storage_bus: Option>, + flexspi_nor_storage_bus: Option>, +} + +impl StorageDeviceDriver { + pub fn new( + spidriver: Option>, + flexspidriver: Option>, + ) -> Result { + if let Some(spi) = spidriver { + return Ok(StorageDeviceDriver { + spi_nor_storage_bus: Some(spi), + flexspi_nor_storage_bus: None, + }); + }; + if let Some(flexspi) = flexspidriver { + return Ok(StorageDeviceDriver { + spi_nor_storage_bus: None, + flexspi_nor_storage_bus: Some(flexspi), + }); + } + + Err(()) + } + + pub fn init(&self) { + let bus_ref = self.flexspi_nor_storage_bus.as_ref().unwrap(); + let cmdarr = NorStorageCmdSeq { + fast_read: Some(NorStorageCmd { + cmd_lb: 0xEE, + cmd_ub: Some(0x11), + addr_width: Some(4), + mode: NorStorageCmdMode::DDR, + dummy: Some(20), + cmdtype: Some(NorStorageCmdType::Read), + }), + page_program: Some(NorStorageCmd { + cmd_lb: 0x12, + cmd_ub: Some(0xED), + addr_width: Some(4), + mode: NorStorageCmdMode::DDR, + dummy: None, + cmdtype: Some(NorStorageCmdType::Write), + }), + sector_erase: Some(NorStorageCmd { + cmd_lb: 0x21, + cmd_ub: Some(0xDE), + addr_width: Some(4), + mode: NorStorageCmdMode::DDR, + dummy: None, + cmdtype: None, + }), + write_enable: Some(NorStorageCmd { + cmd_lb: 0x06, + cmd_ub: Some(0xF9), + addr_width: None, + mode: NorStorageCmdMode::DDR, + dummy: None, + cmdtype: None, + }), + write_disable: None, + read_id: Some(NorStorageCmd { + cmd_lb: 0x9F, + cmd_ub: Some(0x60), + addr_width: Some(4), + mode: NorStorageCmdMode::DDR, + dummy: None, + cmdtype: Some(NorStorageCmdType::Read), + }), + poweup: None, + powerdonw: None, + read_status_reg: Some(NorStorageCmd { + cmd_lb: 0x05, + cmd_ub: Some(0xFA), + addr_width: Some(4), + mode: NorStorageCmdMode::DDR, + dummy: Some(4), + cmdtype: Some(NorStorageCmdType::Read), + }), + write_status_reg: None, + read_cfg_reg1: None, + write_cfg_reg1: None, + read_cfg_reg2: None, + write_cfg_reg2: None, + read_cfg_reg3: None, + write_cfg_reg3: None, + }; + + // Register the Cmd table with FlexSPI Storage + bus_ref.configure_cmd_seq(&cmdarr); + } + + pub fn read(&mut self, addr: u32, data: &mut [u8]) { + let bus_ref = self.flexspi_nor_storage_bus.as_mut().unwrap(); + // Read data from the storage device + bus_ref.read(addr as u32, data); + } + + pub fn write(&mut self, addr: u32, data: &[u8]) { + let bus_ref = self.flexspi_nor_storage_bus.as_mut().unwrap(); + // Write data to the storage device + bus_ref.write_enable(); + + bus_ref.erase(addr, addr + data.len() as u32); + + bus_ref.write_enable(); + + let data_size = data.len(); + for i in 0..data_size { + bus_ref.write(addr + i as u32, &data); + bus_ref.write_enable(); + } + } +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_imxrt::init(Default::default()); + + // Consider this is a storage service or file system service + // As per the design, this service is supposed to instantiate low level bus object and configure the bus driver + // and pass it to the storage device driver when creating it as a dependency injection + // Bus drivers - + // 1. FlexspiStorage + // 2. SpiStorage + + let mut read_data = [0_u8; 32]; + let mut write_data = [0_u8; 32]; + let flash_config = FlexspiDeviceConfig { + flexspi_root_clk: 48000000, + is_sck2_enabled: false, + // Flash size in this struct is in KB, so divide by 1KB + flash_size_kb: 0x10000, // 64 MB + cs_interval_unit: FlexspiCsIntervalCycleUnit::CsIntervalUnit1Cycle, + cs_interval: 2, + cs_hold_time: 3, + cs_setup_time: 3, + data_valid_time: 2, + columnspace: 0, + enable_word_address: false, + awr_seq_index: 1, + awr_seq_number: 0, + ard_seq_index: 0, + ard_seq_number: 0, + ahb_write_wait_unit: FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit2ahbCycle, + ahb_write_wait_interval: 0, + enable_write_mask: false, + }; + let ahb_buffer_config = FlexspiAhbBufferConfig { + priority: 0, + master_index: 0, + buffer_size: 256, + enable_prefetch: true, + }; + + let ahb_config = AhbConfig { + enable_ahb_write_ip_rx_fifo: false, + enable_ahb_write_ip_tx_fifo: false, + ahb_grant_timeout_cycle: 0xff, + ahb_bus_timeout_cycle: 0xffff, + resume_wait_cycle: 0x20, + buffer: [ahb_buffer_config; 8], + enable_clear_ahb_buffer_opt: false, + enable_read_address_opt: true, + enable_ahb_prefetch: true, + enable_ahb_bufferable: true, + enable_ahb_cachable: true, + }; + + let flexspi_config = FlexspiConfig { + rx_sample_clock: FlexspiReadSampleClock::FlexspiReadSampleClkLoopbackInternally, + enable_sck_free_running: false, + enable_combination: false, + enable_doze: false, // TODO - Check back after analyzing system low power mode requirements + enable_half_speed_access: false, + enable_sck_b_diff_opt: false, + enable_same_config_for_all: false, + seq_timeout_cycle: 0xFFFF, + ip_grant_timeout_cycle: 0xff, + tx_watermark: 0x08, + rx_watermark: 0x08, + ahb_config, + }; + + let mut flexspi_storage = FlexspiStorage::new_blocking( + p.FLEXSPI, // FlexSPI peripheral + Some(p.PIO1_11), // FlexSPI DATA 0 pin + Some(p.PIO1_12), + Some(p.PIO1_13), + Some(p.PIO1_14), + Some(p.PIO2_17), + Some(p.PIO2_18), + Some(p.PIO2_22), + Some(p.PIO2_23), + p.PIO1_29, + p.PIO2_19, + FlexSpiFlashPort::PortB, // FlexSPI port + FlexSpiBusWidth::Octal, // FlexSPI bus width + FlexSpiFlashPortDeviceInstance::DeviceInstance0, // FlexSPI device instance + ); + + flexspi_storage.configport.configure_flexspi(&flexspi_config); // Configure the Flexspi controller + + flexspi_storage + .configport + .configure_flexspi_device(&flash_config, &flexspi_config); // Configure the Flash device specific parameters like CS time, etc + + // Instanctiate the storage device driver and inject the bus driver dependency + let mut device_driver = StorageDeviceDriver::new(None, Some(flexspi_storage)).unwrap(); + device_driver.init(); + + // write data + device_driver.write(ADDR, &write_data); + + device_driver.read(ADDR, &mut read_data); + + loop { + Timer::after_millis(2000).await; + } +} diff --git a/src/flexspistorage.rs b/src/flexspistorage.rs new file mode 100644 index 00000000..ff34a894 --- /dev/null +++ b/src/flexspistorage.rs @@ -0,0 +1,1452 @@ +use cortex_m::asm; +use embassy_hal_internal::Peripheral; +use embedded_storage::nor_flash::{ + ErrorType, NorFlash as BlockingNorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash as BlockingReadNorFlash, +}; +use mimxrt600_fcb::flexspi_lut_seq; +use mimxrt600_fcb::FlexSpiLutOpcode::*; +use mimxrt600_fcb::FlexSpiNumPads::*; + +use crate::clocks::enable_and_reset; +// use crate::flexspi::errors::FlashError; +use crate::interrupt; +use crate::iopctl::IopctlPin as Pin; +use crate::peripherals; +use crate::storage::{BlockingNorStorageDriver, NorStorageCmdSeq}; + +#[repr(C)] +#[allow(non_snake_case)] +struct FlexSpi { + MCR0: u32, + /**< Module Control Register 0, offset: 0x0 */ + MCR1: u32, + /**< Module Control Register 1, offset: 0x4 */ + MCR2: u32, + /**< Module Control Register 2, offset: 0x8 */ + AHBCR: u32, + /**< AHB Bus Control Register, offset: 0xC */ + INTEN: u32, + /**< Interrupt Enable Register, offset: 0x10 */ + INTR: u32, + /**< Interrupt Register, offset: 0x14 */ + LUTKEY: u32, + /**< LUT Key Register, offset: 0x18 */ + LUTCR: u32, + /**< LUT Control Register, offset: 0x1C */ + AHBRXBUFCR0: [u32; 8], + /**< AHB RX Buffer 0 Control Register 0..AHB RX Buffer 7 Control Register 0, array offset: 0x20, array step: 0x4 */ + RESERVED_0: [u8; 32], + FLSHCR0: [u32; 4], + /**< Flash Control Register 0, array offset: 0x60, array step: 0x4 */ + FLSHCR1: [u32; 4], + /**< Flash Control Register 1, array offset: 0x70, array step: 0x4 */ + FLSHCR2: [u32; 4], + /**< Flash Control Register 2, array offset: 0x80, array step: 0x4 */ + RESERVED_1: [u8; 4], + FLSHCR4: u32, + /**< Flash Control Register 4, offset: 0x94 */ + RESERVED_2: [u8; 8], + pub IPCR0: u32, + /**< IP Control Register 0, offset: 0xA0 */ + IPCR1: u32, + /**< IP Control Register 1, offset: 0xA4 */ + RESERVED_3: [u8; 8], + IPCMD: u32, + /**< IP Command Register, offset: 0xB0 */ + DLPR: u32, + /**< Data Learn Pattern Register, offset: 0xB4 */ + IPRXFCR: u32, + /**< IP RX FIFO Control Register, offset: 0xB8 */ + IPTXFCR: u32, + /**< IP TX FIFO Control Register, offset: 0xBC */ + DLLCR: [u32; 2], + /**< DLL Control Register 0, array offset: 0xC0, array step: 0x4 */ + RESERVED_4: [u8; 24], + STS0: u32, + /**< Status Register 0, offset: 0xE0 */ + STS1: u32, + /**< Status Register 1, offset: 0xE4 */ + STS2: u32, + /**< Status Register 2, offset: 0xE8 */ + AHBSPNDSTS: u32, + /**< AHB Suspend Status Register, offset: 0xEC */ + IPRXFSTS: u32, + /**< IP RX FIFO Status Register, offset: 0xF0 */ + IPTXFSTS: u32, + /**< IP TX FIFO Status Register, offset: 0xF4 */ + RESERVED_5: [u8; 8], + RFDR: [u32; 32], + /**< IP RX FIFO Data Register 0..IP RX FIFO Data Register 31, array offset: 0x100, array step: 0x4 */ + TFDR: [u32; 32], + /**< IP TX FIFO Data Register 0..IP TX FIFO Data Register 31, array offset: 0x180, array step: 0x4 */ + LUT: [u32; 120], +} + +const LUT_NUM_REG_PER_SEQ: usize = 4; + +impl From for usize { + fn from(cmd: FlexSpiCmd) -> usize { + match cmd { + FlexSpiCmd::ReadId => 1, + FlexSpiCmd::WriteEnable => 3, + FlexSpiCmd::ReadStatusRegister => 4, + FlexSpiCmd::EraseSector => 6, + FlexSpiCmd::PageProgram => 12, + } + } +} + +#[derive(Clone, Copy, Debug)] +/// FlexSPI Port Enum. +pub enum FlexSpiFlashPort { + /// FlexSPI Port A + PortA, + /// FlexSPI Port B + PortB, +} + +#[derive(Clone, Copy, Debug)] +/// FlexSPI Flash Port Device Instance Enum. +pub enum FlexSpiFlashPortDeviceInstance { + /// Device Instance 0 + DeviceInstance0, + /// Device Instance 1 + DeviceInstance1, +} + +#[derive(Clone, Copy, Debug)] +/// FlexSPI Bus Width Enum. +pub enum FlexSpiBusWidth { + /// Single bit bus width + Single, + /// Dual bit bus width + Dual, + /// Quad bit bus width + Quad, + /// Octal bit bus width + Octal, +} +#[derive(Clone, Copy, Debug)] +/// FlexSPI Chip Select Interval unit Enum. +pub enum FlexspiCsIntervalCycleUnit { + /// CS interval unit is 1 cycle + CsIntervalUnit1Cycle, + /// CS interval unit is 256 cycle + CsIntervalUnit256Cycle, +} +#[derive(Clone, Copy, Debug)] +/// FlexSPI AHB Write Wait unit Enum. +pub enum FlexspiAhbWriteWaitUnit { + /// AWRWAIT unit is 2 ahb clock cycle + FlexspiAhbWriteWaitUnit2ahbCycle, + /// AWRWAIT unit is 8 ahb clock cycle. + FlexspiAhbWriteWaitUnit8ahbCycle, + /// AWRWAIT unit is 32 ahb clock cycle. + FlexspiAhbWriteWaitUnit32ahbCycle, + /// AWRWAIT unit is 128 ahb clock cycle. + FlexspiAhbWriteWaitUnit128ahbCycle, + /// AWRWAIT unit is 512 ahb clock cycle. + FlexspiAhbWriteWaitUnit512ahbCycle, + /// AWRWAIT unit is 2048 ahb clock cycle. + FlexspiAhbWriteWaitUnit2048ahbCycle, + /// AWRWAIT unit is 8192 ahb clock cycle. + FlexspiAhbWriteWaitUnit8192ahbCycle, + /// AWRWAIT unit is 32768 ahb clock cycle. + FlexspiAhbWriteWaitUnit32768ahbCycle, +} + +#[derive(Clone, Copy, Debug)] +/// FlexSPI Read Sample Clock Enum. +pub enum FlexspiReadSampleClock { + /// Dummy Read strobe generated by FlexSPI self.flexspi_ref and loopback internally + FlexspiReadSampleClkLoopbackInternally, + /// Dummy Read strobe generated by FlexSPI self.flexspi_ref and loopback from DQS pad + FlexspiReadSampleClkLoopbackFromDqsPad, + /// SCK output clock and loopback from SCK pad + FlexspiReadSampleClkLoopbackFromSckPad, + /// Flash provided Read strobe and input from DQS pad + FlexspiReadSampleClkExternalInputFromDqsPad, +} + +#[derive(Clone, Copy, Debug)] +/// FlexSPI AHB Buffer Configuration structure +pub struct FlexspiAhbBufferConfig { + /// This priority for AHB Master Read which this AHB RX Buffer is assigned. + pub priority: u8, + /// AHB Master ID the AHB RX Buffer is assigned. + pub master_index: u8, + /// AHB buffer size in byte. + pub buffer_size: u16, + /// AHB Read Prefetch Enable for current AHB RX Buffer corresponding Master, allows to prefetch data for AHB read access. + pub enable_prefetch: bool, +} + +#[derive(Clone, Copy, Debug)] +/// Flash Device configuration +pub struct FlexspiDeviceConfig { + /// FLEXSPI serial root clock + pub flexspi_root_clk: u32, + /// FLEXSPI use SCK2 + pub is_sck2_enabled: bool, + /// Flash size in KByte + pub flash_size_kb: u32, + /// CS interval unit, 1 or 256 cycle + pub cs_interval_unit: FlexspiCsIntervalCycleUnit, + /// CS line assert interval, multiply CS interval unit to get the CS line assert interval cycles + pub cs_interval: u16, + /// CS line hold time + pub cs_hold_time: u8, + /// CS line setup time + pub cs_setup_time: u8, + /// Data valid time for external device + pub data_valid_time: u8, + /// Column space size + pub columnspace: u8, + /// If enable word address + pub enable_word_address: bool, + /// Sequence ID for AHB write command + pub awr_seq_index: u8, + /// Sequence number for AHB write command + pub awr_seq_number: u8, + /// Sequence ID for AHB read command + pub ard_seq_index: u8, + /// Sequence number for AHB read command + pub ard_seq_number: u8, + /// AHB write wait unit + pub ahb_write_wait_unit: FlexspiAhbWriteWaitUnit, + /// AHB write wait interval, multiply AHB write interval unit to get the AHB write wait cycles + pub ahb_write_wait_interval: u16, + /// Enable/Disable FLEXSPI drive DQS pin as write mask + pub enable_write_mask: bool, +} + +#[derive(Clone, Copy, Debug)] +/// AHB configuration structure +pub struct AhbConfig { + /// Enable AHB bus write access to IP TX FIFO. + pub enable_ahb_write_ip_tx_fifo: bool, + /// Enable AHB bus write access to IP RX FIFO. + pub enable_ahb_write_ip_rx_fifo: bool, + /// Timeout wait cycle for AHB command grant, timeout after ahbGrantTimeoutCyle*1024 AHB clock cycles. + pub ahb_grant_timeout_cycle: u8, + /// Timeout wait cycle for AHB read/write access, timeout after ahbBusTimeoutCycle*1024 AHB clock cycles. + pub ahb_bus_timeout_cycle: u16, + /// Wait cycle for idle state before suspended command sequence resume, timeout after ahbBusTimeoutCycle AHB clock cycles. + pub resume_wait_cycle: u8, + /// AHB buffer size. + pub buffer: [FlexspiAhbBufferConfig; 8], + /// Enable/disable automatically clean AHB RX Buffer and TX Buffer when FLEXSPI returns STOP mode ACK. + pub enable_clear_ahb_buffer_opt: bool, + /// Enable/disable remove AHB read burst start address alignment limitation. when enable, there is no AHB read burst start address alignment limitation. + pub enable_read_address_opt: bool, + /// Enable/disable AHB read prefetch feature, when enabled, FLEXSPI will fetch more data than current AHB burst. + pub enable_ahb_prefetch: bool, + /// Enable/disable AHB bufferable write access support, when enabled, FLEXSPI return before waiting for command execution finished. + pub enable_ahb_bufferable: bool, + /// Enable AHB bus cachable read access support. + pub enable_ahb_cachable: bool, +} + +#[derive(Clone, Copy, Debug)] +/// FlexSPI configuration structure +pub struct FlexspiConfig { + /// Sample Clock source selection for Flash Reading. + pub rx_sample_clock: FlexspiReadSampleClock, + /// Enable/disable SCK output free-running. + pub enable_sck_free_running: bool, + /// Enable/disable combining PORT A and B Data Pins (SIOA[3:0] and SIOB[3:0]) to support Flash Octal mode. + pub enable_combination: bool, + /// Enable/disable doze mode support. + pub enable_doze: bool, + /// Enable/disable divide by 2 of the clock for half speed commands. + pub enable_half_speed_access: bool, + /// Enable/disable SCKB pad use as SCKA differential clock output, when enable, Port B flash access is not available. + pub enable_sck_b_diff_opt: bool, + /// Enable/disable same configuration for all connected devices when enabled, same configuration in FLASHA1CRx is applied to all. + pub enable_same_config_for_all: bool, + /// Timeout wait cycle for command sequence execution, timeout after ahbGrantTimeoutCyle*1024 serial root clock cycles. + pub seq_timeout_cycle: u16, + /// Timeout wait cycle for IP command grant, timeout after ipGrantTimeoutCycle*1024 AHB clock cycles. + pub ip_grant_timeout_cycle: u8, + /// FLEXSPI IP transmit watermark value. + pub tx_watermark: u8, + /// FLEXSPI receive watermark value. + pub rx_watermark: u8, + /// AHB configuration + pub ahb_config: AhbConfig, +} + +enum FlexSpiCmd { + WriteEnable, + ReadStatusRegister, + EraseSector, + ReadId, + PageProgram, +} + +mod sealed { + /// simply seal a trait + pub trait Sealed {} +} + +impl sealed::Sealed for T {} + +struct Info { + regs: &'static crate::pac::flexspi::RegisterBlock, +} + +trait SealedInstance { + fn info() -> Info; +} +/// Instance trait to be used for instanciating for FlexSPI HW instance +#[allow(private_bounds)] +pub trait Instance: SealedInstance + Peripheral

+ 'static + Send { + /// Interrupt for this SPI instance. + type Interrupt: interrupt::typelevel::Interrupt; +} + +impl SealedInstance for crate::peripherals::FLEXSPI { + fn info() -> Info { + Info { + regs: unsafe { &*crate::pac::Flexspi::ptr() }, + } + } +} + +impl Instance for crate::peripherals::FLEXSPI { + type Interrupt = crate::interrupt::typelevel::FLEXSPI; +} +/// Driver mode. +#[allow(private_bounds)] +pub trait Mode: sealed::Sealed {} + +/// Blocking mode. +pub struct Blocking; +impl Mode for Blocking {} + +/// Async mode. +pub struct Async; +impl Mode for Async {} + +/// Nor flash error object +#[derive(Debug)] +pub struct FlashStorageErrorOther; +impl ErrorType for FlexspiStorage { + type Error = FlashStorageErrorOther; +} + +impl NorFlashError for FlashStorageErrorOther { + fn kind(&self) -> embedded_storage::nor_flash::NorFlashErrorKind { + NorFlashErrorKind::Other + } +} + +#[allow(private_interfaces)] +/// FlexSPI Configuration Manager Port +pub struct FlexSpiConfigurationPort { + /// Bus Width + bus_width: FlexSpiBusWidth, + /// Flash Port + flash_port: FlexSpiFlashPort, + /// Device Instance + device_instance: FlexSpiFlashPortDeviceInstance, + /// FlexSPI HW Info Object + info: Info, +} + +/// FlexSPI instance +pub struct FlexspiStorage { + /// FlexSPI HW Info Object + info: Info, + /// RX FIFO watermark level + rx_watermark: u8, + /// TX FIFO Watermark Level + tx_watermark: u8, + /// Mode Phantom object + _mode: core::marker::PhantomData, + /// FlexSPI peripheral instance + flexspi_ref: &'static mut FlexSpi, + /// Flash Port + flash_port: FlexSpiFlashPort, + /// Device Instance + device_instance: FlexSpiFlashPortDeviceInstance, + /// FlexSPI Configuration Port + pub configport: FlexSpiConfigurationPort, +} + +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[allow(non_snake_case)] +/// FlexSPI self.flexspi_ref specific errors +/// This enum provides verbose error messages for FlexSPI self.flexspi_ref specific errors +pub enum FlexSpiError { + /// Flash command grant error + CmdGrantErr { + /// AHB read command error + AhbReadCmdErr: bool, + /// AHB write command error + AhbWriteCmdErr: bool, + /// IP command error + IpCmdErr: bool, + }, // INTR[AHBCMDGE] = 1 / INTR[IPCMDGE] = 1 + /// Flash command check error + CmdCheckErr { + /// AHB read command error + AhbReadCmdErr: bool, + /// AHB write command error + AhbWriteCmdErr: bool, + /// IP command error + IpCmdErr: bool, + }, // INTR[AHBCMDERR] = 1/ INTR[IPCMDERR] = 1 + /// Flash command execution error + CmdExecErr { + /// AHB read command error + AhbReadCmdErr: bool, + /// AHB write command error + AhbWriteCmdErr: bool, + /// IP command error + IpCmdErr: bool, + }, // INTR[AHBCMDERR] = 1/ INTR[SEQTIMEOUT] = 1/ INTR[IPCMDERR] = 1 + /// AHB bus timeout error + AhbBusTimeout { + /// AHB read command error + AhbReadCmdErr: bool, // INTR[AHBBUSTIMEO UT] = 1 + /// AHB write command error + AhbWriteCmdErr: bool, // INTR[AHBBUSTIMEO UT] = 1 + }, + /// Data learning failed + DataLearningFailed, // INTR[DATALEARNFAIL] = 1 +} + +impl FlexSpiError { + /// Get the description of the error + pub fn describe(&self) -> &str { + match self { + FlexSpiError::CmdGrantErr { + AhbReadCmdErr, + AhbWriteCmdErr, + IpCmdErr, + } => { + if *AhbReadCmdErr { + "AHB bus error response for Read Command. Command grant timeout" + } else if *AhbWriteCmdErr { + "AHB bus error response for Write Command. Command grant timeout" + } else if *IpCmdErr { + "IP command grant timeout. Command grant timeout" + } else { + "Unknown Flash command grant error" + } + } + FlexSpiError::CmdCheckErr { + AhbReadCmdErr, + AhbWriteCmdErr, + IpCmdErr, + } => { + if *AhbWriteCmdErr { + "Command is not executed when error detected in command check. Following are the possible reasons: + - AHB write command with JMP_ON_CS instruction used in the sequence + - There is unknown instruction opcode in the sequence. + - Instruction DUMMY_SDR/DUMMY_RWDS_SDR used in DDR sequence. + - Instruction DUMMY_DDR/DUMMY_RWDS_DDR used in SDR sequence." + } else if *AhbReadCmdErr { + "Command is not executed when error detected in command check. Following are the possible reasons: + - There is unknown instruction opcode in the sequence + - Instruction DUMMY_SDR/DUMMY_RWDS_SDR used in DDR sequence. + - Instruction DUMMY_DDR/DUMMY_RWDS_DDR used in SDR sequence." + } else if *IpCmdErr { + "Command is not executed when error detected in command check. Following are the possible reasons: + - IP command with JMP_ON_CS instruction used in the sequence + - There is unknown instruction opcode in the sequence. + - Instruction DUMMY_SDR/DUMMY_RWDS_SDRused in DDR sequence + - Instruction DUMMY_DDR/DUMMY_RWDS_DDR used in SDR sequence + - Flash boundary across" + } else { + "Unknown Flash command check error" + } + } + FlexSpiError::CmdExecErr { + AhbReadCmdErr, + AhbWriteCmdErr, + IpCmdErr, + } => { + if *AhbWriteCmdErr { + "There will be AHB bus error response except the following cases: + - AHB write command is triggered by flush (INCR burst ended with AHB_TX_BUF not empty) + - AHB bufferable write access and bufferable enabled (AHBCR[BUFFERABLEEN]=0x1) + Following are possible reasons for this error - + - Command timeout during execution" + } else if *AhbReadCmdErr { + "There will be AHB bus error response. Following are possible reasons for this error - + - Command timeout during execution" + } else if *IpCmdErr { + "Following are possible reasons for this error - + - Command timeout during execution" + } else { + "Unknown Flash command execution error" + } + } + FlexSpiError::AhbBusTimeout { + AhbReadCmdErr, + AhbWriteCmdErr, + } => { + if *AhbReadCmdErr || *AhbWriteCmdErr { + "There will be AHB bus error response. Following are possible reasons for this error - + - AHB bus timeout (no bus ready return)" + } else { + "Unknown AHB bus timeout error" + } + } + FlexSpiError::DataLearningFailed => "Data learning failed", + } + } +} + +impl BlockingReadNorFlash for FlexspiStorage { + const READ_SIZE: usize = 1; + #[no_mangle] + #[link_section = ".flexspi_code"] + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + let offset = 0x08000000 + offset; + let ptr = offset as *const u8; + unsafe { + let data = *ptr; + bytes[0] = data; + } + Ok(()) + } + fn capacity(&self) -> usize { + match self.flash_port { + FlexSpiFlashPort::PortA => match self.device_instance { + FlexSpiFlashPortDeviceInstance::DeviceInstance0 => { + self.info.regs.flsha1cr0().read().flshsz().bits() as usize + } + FlexSpiFlashPortDeviceInstance::DeviceInstance1 => { + self.info.regs.flsha2cr0().read().flshsz().bits() as usize + } + }, + FlexSpiFlashPort::PortB => match self.device_instance { + FlexSpiFlashPortDeviceInstance::DeviceInstance0 => { + self.info.regs.flshb1cr0().read().flshsz().bits() as usize + } + FlexSpiFlashPortDeviceInstance::DeviceInstance1 => { + self.info.regs.flshb2cr0().read().flshsz().bits() as usize + } + }, + } + } +} + +impl FlexspiStorage { + #[no_mangle] + #[link_section = ".flexspi_code"] + fn setup_write_transfer(&mut self) { + self.flexspi_ref.LUTKEY = 0x5AF05AF0; + self.flexspi_ref.LUTCR = 0x00000001; + + let cmd_idx: usize = FlexSpiCmd::PageProgram.into(); + self.flexspi_ref.FLSHCR2[2] &= !(0x1f << 8); + self.flexspi_ref.FLSHCR2[2] |= (cmd_idx as u32) << 8; + + self.flexspi_ref.LUT[cmd_idx * LUT_NUM_REG_PER_SEQ] = + flexspi_lut_seq(CMD_DDR, Octal, 0x12, CMD_DDR, Octal, 0xED); + self.flexspi_ref.LUT[cmd_idx * LUT_NUM_REG_PER_SEQ + 1] = + flexspi_lut_seq(RADDR_DDR, Octal, 0x20, WRITE_DDR, Octal, 0x04); + self.flexspi_ref.LUT[cmd_idx * LUT_NUM_REG_PER_SEQ + 2] = 0; + self.flexspi_ref.LUT[cmd_idx * LUT_NUM_REG_PER_SEQ + 3] = 0; + } +} + +impl BlockingNorFlash for FlexspiStorage { + const WRITE_SIZE: usize = 1; + const ERASE_SIZE: usize = 4096; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.setup_ip_transfer(FlexSpiCmd::EraseSector, Some(from), None, None); + self.execute_cmd(); + self.wait_for_cmd_completion(); + loop { + // Read Status Register + let status = self.read_status_reg().unwrap(); + // check if WIP is set or cleared + if status[0] & 0x1 == 0x0 { + break; + } + } + Ok(()) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + let addr = 0x08000000 + offset; + let ptr = addr as *mut u8; + self.setup_write_transfer(); + unsafe { + *ptr = bytes[0]; + } + + loop { + // Read Status Register + let status = self.read_status_reg().unwrap(); + // check if WIP is set or cleared + if status[0] & 0x1 == 0x0 { + break; + } + } + + Ok(()) + } +} + +impl crate::storage::BlockingNorStorageDriver for FlexspiStorage { + fn lock(&self) -> Result<(), Self::Error> { + // Lock the FlexSPI + Ok(()) + } + fn unlock(&self) -> Result<(), Self::Error> { + // Unlock the FlexSPI + Ok(()) + } + fn power_down(&self) -> Result<(), Self::Error> { + // Power down the FlexSPI + Ok(()) + } + fn power_up(&self) -> Result<(), Self::Error> { + // Power up the FlexSPI + Ok(()) + } + fn write_enable(&mut self) -> Result<(), Self::Error> { + self.setup_ip_transfer(FlexSpiCmd::WriteEnable, None, None, None); + self.execute_cmd(); + self.wait_for_cmd_completion(); + Ok(()) + } + fn write_disable(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + fn read_jedec_id(&self) -> Result<[u8; 3], Self::Error> { + Ok([0, 0, 0]) + } + fn read_status_reg(&mut self) -> Result<[u8; 4], Self::Error> { + // Read status register; + let mut data = [0x55; 4]; + + self.setup_ip_transfer(FlexSpiCmd::ReadStatusRegister, None, None, None); + self.execute_cmd(); + self.wait_for_cmd_completion(); + self.read_cmd_data(1, Some(&mut data)); + Ok(data) + } +} + +impl crate::storage::ConfigureCmdSeq for FlexspiStorage { + fn configure_cmd_seq(&self, cmd_seq: &NorStorageCmdSeq) {} +} + +impl FlexspiStorage { + fn setup_ip_transfer(&mut self, cmd: FlexSpiCmd, addr: Option, data: Option, size: Option) { + match addr { + Some(addr) => { + self.flexspi_ref.IPCR0 = addr; + } + + None => { + self.flexspi_ref.IPCR0 = 0; + } + } + // Clear the sequence ID + self.flexspi_ref.IPCR1 &= !(0x1F << 16); + + // Unlock the LUT + self.flexspi_ref.LUTKEY = 0x5AF05AF0; + self.flexspi_ref.LUTCR = 0x2; + + // Reset the sequence pointer + self.flexspi_ref.FLSHCR2[0] |= 0x1 << 31; + self.flexspi_ref.FLSHCR2[1] |= 0x1 << 31; + self.flexspi_ref.FLSHCR2[2] |= 0x1 << 31; + self.flexspi_ref.FLSHCR2[3] |= 0x1 << 31; + + match cmd { + FlexSpiCmd::ReadId => { + let cmd_idx: usize = cmd.into(); + self.flexspi_ref.IPCR1 |= (cmd_idx as u32) << 16; + } + FlexSpiCmd::WriteEnable => { + let cmd_idx: usize = cmd.into(); + self.flexspi_ref.IPCR1 |= (cmd_idx as u32) << 16; + } + FlexSpiCmd::ReadStatusRegister => { + let cmd_idx: usize = cmd.into(); + self.flexspi_ref.IPCR1 |= (cmd_idx as u32) << 16; + } + FlexSpiCmd::EraseSector => { + let cmd_idx: usize = cmd.into(); + self.flexspi_ref.IPCR1 |= (cmd_idx as u32) << 16; + } + _ => {} + } + + // Disable DMA for TX and RX + self.flexspi_ref.IPRXFCR &= !0x2; + self.flexspi_ref.IPTXFCR &= !0x2; + + // set watermark + //self.flexspi_ref.IPRXFCR &= !(0x3F << 2); + //self.flexspi_ref.IPRXFCR != (((0x8 / 8) - 1) << 2); // 8 bytes watermark + + // Reset RX and TX FIFO + self.flexspi_ref.IPRXFCR |= 0x1; + self.flexspi_ref.IPTXFCR |= 0x1; + } + + fn execute_cmd(&mut self) { + self.flexspi_ref.IPCMD |= 0x1; + } + + fn wait_for_cmd_completion(&mut self) { + #[allow(clippy::while_immutable_condition)] + while (self.flexspi_ref.INTR & 0x1 == 0) {} + } + + fn read_cmd_data(&mut self, size: u32, read_data: Option<&mut [u8]>) { + loop { + //info!("Waiting for filled data = {:02X}", controller.IPRXFSTS & 0xFF); + if ((self.flexspi_ref.IPRXFSTS & 0xFF) * 8) < size { + continue; + } + break; + } + + read_data.unwrap()[0] = self.flexspi_ref.RFDR[0] as u8; + } +} + +// ================================================================================// + +/// FlexSPI init API +pub fn init() { + // TODO - Need to find anything which is required as part of system init +} + +impl FlexSpiConfigurationPort { + /// Initialize FlexSPI + pub fn configure_flexspi(&mut self, config: &FlexspiConfig) { + let regs = self.info.regs; + + // Enable Clock and deassert Reset + enable_and_reset::(); + + let sysctl_reg = unsafe { &*crate::pac::Sysctl0::ptr() }; + sysctl_reg + .pdruncfg1_clr() + .write(|w| w.flexspi_sram_apd().set_bit().flexspi_sram_ppd().set_bit()); + + regs.mcr0().modify(|_, w| w.mdis().clear_bit()); + regs.mcr0().modify(|_, w| w.swreset().set_bit()); + while regs.mcr0().read().swreset().bit_is_set() {} + + //• Set MCR0[MDIS] to 0x1 (Make sure self.flexspi_ref is configured in module stop mode) + regs.mcr0().modify(|_, w| w.mdis().set_bit()); + + //• Configure module control registers: MCR0, MCR1, MCR2. (Don't change MCR0[MDIS]) + match config.rx_sample_clock { + FlexspiReadSampleClock::FlexspiReadSampleClkLoopbackInternally => { + regs.mcr0().modify(|_, w| w.rxclksrc().rxclksrc_0()); + } + FlexspiReadSampleClock::FlexspiReadSampleClkLoopbackFromDqsPad => { + regs.mcr0().modify(|_, w| w.rxclksrc().rxclksrc_1()); + } + FlexspiReadSampleClock::FlexspiReadSampleClkLoopbackFromSckPad => { + regs.mcr0().modify(|_, w| w.rxclksrc().rxclksrc_3()); + } + FlexspiReadSampleClock::FlexspiReadSampleClkExternalInputFromDqsPad => { + regs.mcr0().modify(|_, w| w.rxclksrc().rxclksrc_3()); + } + } + if config.enable_doze { + regs.mcr0().modify(|_, w| w.dozeen().set_bit()); + } else { + regs.mcr0().modify(|_, w| w.dozeen().clear_bit()); + } + //============================================================================================== + // These are only for debug purpose. So commenting out for now + // regs.mcr0() + // .modify(|_, w| unsafe { w.ipgrantwait().bits(config.ip_grant_timeout_cycle) }); + + // regs.mcr0() + // .write(|w| unsafe { w.ahbgrantwait().bits(config.ahb_config.ahb_grant_timeout_cycle) }); + //============================================================================================== + + if config.enable_sck_free_running { + regs.mcr0().modify(|_, w| w.sckfreerunen().set_bit()); + } else { + regs.mcr0().modify(|_, w| w.sckfreerunen().clear_bit()); + } + + if config.enable_half_speed_access { + regs.mcr0().modify(|_, w| w.hsen().set_bit()); + } else { + regs.mcr0().modify(|_, w| w.hsen().clear_bit()); + } + + regs.mcr1().modify(|_, w| unsafe { + w.ahbbuswait() + .bits(config.ahb_config.ahb_bus_timeout_cycle) + .seqwait() + .bits(config.seq_timeout_cycle) + }); + + if config.enable_same_config_for_all { + regs.mcr2().modify(|_, w| w.samedeviceen().set_bit()); + } else { + regs.mcr2().modify(|_, w| w.samedeviceen().clear_bit()); + } + + regs.mcr2() + .modify(|_, w| unsafe { w.resumewait().bits(config.ahb_config.resume_wait_cycle) }); + + if config.enable_sck_b_diff_opt { + regs.mcr2().write(|w| w.sckbdiffopt().set_bit()); + } else { + regs.mcr2().write(|w| w.sckbdiffopt().clear_bit()); + } + + if config.ahb_config.enable_clear_ahb_buffer_opt { + regs.mcr2().modify(|_, w| w.clrahbbufopt().set_bit()); + } else { + regs.mcr2().modify(|_, w| w.clrahbbufopt().clear_bit()); + } + + if config.ahb_config.enable_read_address_opt { + regs.ahbcr().modify(|_, w| w.readaddropt().set_bit()); + } else { + regs.ahbcr().modify(|_, w| w.readaddropt().clear_bit()); + } + + if config.ahb_config.enable_ahb_prefetch { + regs.ahbcr().modify(|_, w| w.prefetchen().set_bit()); + } else { + regs.ahbcr().modify(|_, w| w.prefetchen().clear_bit()); + } + + if config.ahb_config.enable_ahb_bufferable { + regs.ahbcr().modify(|_, w| w.bufferableen().set_bit()); + } else { + regs.ahbcr().modify(|_, w| w.bufferableen().clear_bit()); + } + + if config.ahb_config.enable_ahb_cachable { + regs.ahbcr().modify(|_, w| w.cachableen().set_bit()); + } else { + regs.ahbcr().modify(|_, w| w.cachableen().clear_bit()); + } + + regs.ahbrxbuf0cr0().modify(|_, w| unsafe { + w.mstrid() + .bits(0) + .prefetchen() + .set_bit() + .bufsz() + .bits(256) + .priority() + .bits(0) + }); + + regs.ahbrxbuf1cr0().modify(|_, w| unsafe { + w.mstrid() + .bits(0) + .prefetchen() + .set_bit() + .bufsz() + .bits(256) + .priority() + .bits(0) + }); + + regs.ahbrxbuf2cr0().modify(|_, w| unsafe { + w.mstrid() + .bits(0) + .prefetchen() + .set_bit() + .bufsz() + .bits(256) + .priority() + .bits(0) + }); + + regs.ahbrxbuf3cr0().modify(|_, w| unsafe { + w.mstrid() + .bits(0) + .prefetchen() + .set_bit() + .bufsz() + .bits(256) + .priority() + .bits(0) + }); + + regs.ahbrxbuf4cr0().modify(|_, w| unsafe { + w.mstrid() + .bits(0) + .prefetchen() + .set_bit() + .bufsz() + .bits(256) + .priority() + .bits(0) + }); + + regs.ahbrxbuf5cr0().modify(|_, w| unsafe { + w.mstrid() + .bits(0) + .prefetchen() + .set_bit() + .bufsz() + .bits(256) + .priority() + .bits(0) + }); + + regs.ahbrxbuf6cr0().modify(|_, w| unsafe { + w.mstrid() + .bits(0) + .prefetchen() + .set_bit() + .bufsz() + .bits(256) + .priority() + .bits(0) + }); + + regs.ahbrxbuf7cr0().modify(|_, w| unsafe { + w.mstrid() + .bits(0) + .prefetchen() + .set_bit() + .bufsz() + .bits(256) + .priority() + .bits(0) + }); + + // • Initialize Flash control registers (FLSHxCR0,FLSHxCR1,FLSHxCR2) + match self.flash_port { + FlexSpiFlashPort::PortA => match self.device_instance { + FlexSpiFlashPortDeviceInstance::DeviceInstance0 => { + regs.flsha1cr0().modify(|_, w| unsafe { w.flshsz().bits(0) }); + } + FlexSpiFlashPortDeviceInstance::DeviceInstance1 => { + regs.flsha2cr0().modify(|_, w| unsafe { w.flshsz().bits(0) }); + } + }, + FlexSpiFlashPort::PortB => match self.device_instance { + FlexSpiFlashPortDeviceInstance::DeviceInstance0 => { + regs.flshb1cr0().modify(|_, w| unsafe { w.flshsz().bits(0) }); + } + FlexSpiFlashPortDeviceInstance::DeviceInstance1 => { + regs.flshb2cr0().modify(|_, w| unsafe { w.flshsz().bits(0) }); + } + }, + } + + regs.iprxfcr().modify(|_, w| unsafe { w.rxwmrk().bits(0) }); + regs.iptxfcr().modify(|_, w| unsafe { w.txwmrk().bits(0) }); + } + + /// Configure the flash self.flexspi_ref based on the external flash device + pub fn configure_flexspi_device(&self, device_config: &FlexspiDeviceConfig, flexspi_config: &FlexspiConfig) { + let regs = self.info.regs; + let flash_size = device_config.flash_size_kb; + + while regs.sts0().read().arbidle().bit_is_clear() || regs.sts0().read().seqidle().bit_is_clear() {} + + let dll_val = Self::calc_dll_value(&device_config, &flexspi_config); + + if device_config.enable_write_mask { + regs.flshcr4().write(|w| w.wmopt1().wmopt1_1()); + } else { + regs.flshcr4().write(|w| w.wmopt1().wmopt1_0()); + } + + match self.flash_port { + FlexSpiFlashPort::PortA => { + regs.dllcr(0).modify(|_, w| unsafe { w.bits(dll_val) }); + if device_config.enable_write_mask { + regs.flshcr4().write(|w| w.wmena().wmena_1()); + } else { + regs.flshcr4().write(|w| w.wmena().wmena_0()); + } + match self.device_instance { + FlexSpiFlashPortDeviceInstance::DeviceInstance0 => { + regs.flsha1cr0().modify(|_, w| unsafe { w.flshsz().bits(flash_size) }); + regs.flshcr1a1().modify(|_, w| unsafe { + w.csinterval() + .bits(device_config.cs_interval) + .tcsh() + .bits(device_config.cs_hold_time) + .tcss() + .bits(device_config.cs_setup_time) + .cas() + .bits(device_config.columnspace) + .wa() + .bit(device_config.enable_word_address) + }); + match device_config.cs_interval_unit { + FlexspiCsIntervalCycleUnit::CsIntervalUnit256Cycle => { + regs.flshcr1a1().modify(|_, w| w.csintervalunit().csintervalunit_1()); + } + FlexspiCsIntervalCycleUnit::CsIntervalUnit1Cycle => { + regs.flshcr1a1().modify(|_, w| w.csintervalunit().csintervalunit_0()); + } + } + match device_config.ahb_write_wait_unit { + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit2ahbCycle => { + regs.flshcr2a1().modify(|_, w| w.awrwaitunit().awrwaitunit_0()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit8ahbCycle => { + regs.flshcr2a1().modify(|_, w| w.awrwaitunit().awrwaitunit_1()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit32ahbCycle => { + regs.flshcr2a1().modify(|_, w| w.awrwaitunit().awrwaitunit_2()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit128ahbCycle => { + regs.flshcr2a1().modify(|_, w| w.awrwaitunit().awrwaitunit_3()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit512ahbCycle => { + regs.flshcr2a1().modify(|_, w| w.awrwaitunit().awrwaitunit_4()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit2048ahbCycle => { + regs.flshcr2a1().modify(|_, w| w.awrwaitunit().awrwaitunit_5()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit8192ahbCycle => { + regs.flshcr2a1().modify(|_, w| w.awrwaitunit().awrwaitunit_6()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit32768ahbCycle => { + regs.flshcr2a1().modify(|_, w| w.awrwaitunit().awrwaitunit_7()); + } + } + + if device_config.ard_seq_number > 0 { + regs.flshcr2a1().modify(|_, w| unsafe { + w.ardseqnum() + .bits(device_config.ard_seq_number - 1) + .ardseqid() + .bits(device_config.ard_seq_index) + }); + } + } + + FlexSpiFlashPortDeviceInstance::DeviceInstance1 => { + regs.flsha2cr0().modify(|_, w| unsafe { w.flshsz().bits(flash_size) }); + regs.flshcr1a2().modify(|_, w| unsafe { + w.csinterval() + .bits(device_config.cs_interval) + .tcsh() + .bits(device_config.cs_hold_time) + .tcss() + .bits(device_config.cs_setup_time) + .cas() + .bits(device_config.columnspace) + .wa() + .bit(device_config.enable_word_address) + }); + match device_config.cs_interval_unit { + FlexspiCsIntervalCycleUnit::CsIntervalUnit256Cycle => { + regs.flshcr1a2().modify(|_, w| w.csintervalunit().csintervalunit_1()); + } + FlexspiCsIntervalCycleUnit::CsIntervalUnit1Cycle => { + regs.flshcr1a2().modify(|_, w| w.csintervalunit().csintervalunit_0()); + } + } + match device_config.ahb_write_wait_unit { + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit2ahbCycle => { + regs.flshcr2a2().modify(|_, w| w.awrwaitunit().awrwaitunit_0()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit8ahbCycle => { + regs.flshcr2a2().modify(|_, w| w.awrwaitunit().awrwaitunit_1()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit32ahbCycle => { + regs.flshcr2a2().modify(|_, w| w.awrwaitunit().awrwaitunit_2()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit128ahbCycle => { + regs.flshcr2a2().modify(|_, w| w.awrwaitunit().awrwaitunit_3()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit512ahbCycle => { + regs.flshcr2a2().modify(|_, w| w.awrwaitunit().awrwaitunit_4()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit2048ahbCycle => { + regs.flshcr2a2().modify(|_, w| w.awrwaitunit().awrwaitunit_5()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit8192ahbCycle => { + regs.flshcr2a2().modify(|_, w| w.awrwaitunit().awrwaitunit_6()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit32768ahbCycle => { + regs.flshcr2a2().modify(|_, w| w.awrwaitunit().awrwaitunit_7()); + } + } + if device_config.ard_seq_number > 0 { + regs.flshcr2a2().modify(|_, w| unsafe { + w.ardseqnum() + .bits(device_config.ard_seq_number - 1) + .ardseqid() + .bits(device_config.ard_seq_index) + }); + } + } + } + } + FlexSpiFlashPort::PortB => { + regs.dllcr(1).modify(|_, w| unsafe { w.bits(dll_val) }); + if device_config.enable_write_mask { + regs.flshcr4().write(|w| w.wmenb().wmenb_1()); + } else { + regs.flshcr4().write(|w| w.wmenb().wmenb_0()); + } + match self.device_instance { + FlexSpiFlashPortDeviceInstance::DeviceInstance0 => { + regs.flshb1cr0().modify(|_, w| unsafe { w.flshsz().bits(flash_size) }); + regs.flshcr1b1().modify(|_, w| unsafe { + w.csinterval() + .bits(device_config.cs_interval) + .tcsh() + .bits(device_config.cs_hold_time) + .tcss() + .bits(device_config.cs_setup_time) + .cas() + .bits(device_config.columnspace) + .wa() + .bit(device_config.enable_word_address) + }); + match device_config.cs_interval_unit { + FlexspiCsIntervalCycleUnit::CsIntervalUnit256Cycle => { + regs.flshcr1b1().modify(|_, w| w.csintervalunit().csintervalunit_1()); + } + FlexspiCsIntervalCycleUnit::CsIntervalUnit1Cycle => { + regs.flshcr1b1().modify(|_, w| w.csintervalunit().csintervalunit_0()); + } + } + match device_config.ahb_write_wait_unit { + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit2ahbCycle => { + regs.flshcr2b1().modify(|_, w| w.awrwaitunit().awrwaitunit_0()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit8ahbCycle => { + regs.flshcr2b1().modify(|_, w| w.awrwaitunit().awrwaitunit_1()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit32ahbCycle => { + regs.flshcr2b1().modify(|_, w| w.awrwaitunit().awrwaitunit_2()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit128ahbCycle => { + regs.flshcr2b1().modify(|_, w| w.awrwaitunit().awrwaitunit_3()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit512ahbCycle => { + regs.flshcr2b1().modify(|_, w| w.awrwaitunit().awrwaitunit_4()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit2048ahbCycle => { + regs.flshcr2b1().modify(|_, w| w.awrwaitunit().awrwaitunit_5()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit8192ahbCycle => { + regs.flshcr2b1().modify(|_, w| w.awrwaitunit().awrwaitunit_6()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit32768ahbCycle => { + regs.flshcr2b1().modify(|_, w| w.awrwaitunit().awrwaitunit_7()); + } + } + if device_config.ard_seq_number > 0 { + regs.flshcr2b1().modify(|_, w| unsafe { + w.ardseqnum() + .bits(device_config.ard_seq_number - 1) + .ardseqid() + .bits(device_config.ard_seq_index) + }); + } + } + FlexSpiFlashPortDeviceInstance::DeviceInstance1 => { + regs.flshb2cr0().modify(|_, w| unsafe { w.flshsz().bits(flash_size) }); + regs.flshcr1b2().modify(|_, w| unsafe { + w.csinterval() + .bits(device_config.cs_interval) + .tcsh() + .bits(device_config.cs_hold_time) + .tcss() + .bits(device_config.cs_setup_time) + .cas() + .bits(device_config.columnspace) + .wa() + .bit(device_config.enable_word_address) + }); + match device_config.cs_interval_unit { + FlexspiCsIntervalCycleUnit::CsIntervalUnit256Cycle => { + regs.flshcr1b2().modify(|_, w| w.csintervalunit().csintervalunit_1()); + } + FlexspiCsIntervalCycleUnit::CsIntervalUnit1Cycle => { + regs.flshcr1b2().modify(|_, w| w.csintervalunit().csintervalunit_0()); + } + } + match device_config.ahb_write_wait_unit { + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit2ahbCycle => { + regs.flshcr2b2().modify(|_, w| w.awrwaitunit().awrwaitunit_0()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit8ahbCycle => { + regs.flshcr2b2().modify(|_, w| w.awrwaitunit().awrwaitunit_1()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit32ahbCycle => { + regs.flshcr2b2().modify(|_, w| w.awrwaitunit().awrwaitunit_2()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit128ahbCycle => { + regs.flshcr2b2().modify(|_, w| w.awrwaitunit().awrwaitunit_3()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit512ahbCycle => { + regs.flshcr2b2().modify(|_, w| w.awrwaitunit().awrwaitunit_4()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit2048ahbCycle => { + regs.flshcr2b2().modify(|_, w| w.awrwaitunit().awrwaitunit_5()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit8192ahbCycle => { + regs.flshcr2b2().modify(|_, w| w.awrwaitunit().awrwaitunit_6()); + } + FlexspiAhbWriteWaitUnit::FlexspiAhbWriteWaitUnit32768ahbCycle => { + regs.flshcr2b2().modify(|_, w| w.awrwaitunit().awrwaitunit_7()); + } + } + if device_config.ard_seq_number > 0 { + regs.flshcr2b2().modify(|_, w| unsafe { + w.ardseqnum() + .bits(device_config.ard_seq_number - 1) + .ardseqid() + .bits(device_config.ard_seq_index) + }); + } + } + } + } + } + + // Enable the module + regs.mcr0().write(|w| w.mdis().clear_bit()); + + //Errata ERR011377 - need to delay at least 100 NOPs to ensure the DLL is locked. + match self.flash_port { + FlexSpiFlashPort::PortA => { + while regs.sts2().read().aslvlock().bit_is_clear() && regs.sts2().read().areflock().bit_is_clear() { + // Wait for DLL lock + } + + for i in 0..100 { + asm::nop(); + } + } + FlexSpiFlashPort::PortB => { + while regs.sts2().read().bslvlock().bit_is_clear() && regs.sts2().read().breflock().bit_is_clear() { + // Wait for DLL lock + } + + for i in 0..100 { + asm::nop(); + } + } + } + } + + fn calc_dll_value(device_config: &FlexspiDeviceConfig, flexspi_config: &FlexspiConfig) -> u32 { + let mut is_unified_config = true; + let mut flexspi_dll_value = 0u32; + let mut dll_value = 0u32; + let mut temp = 0u32; + + let rx_sample_clock = flexspi_config.rx_sample_clock; + match rx_sample_clock { + FlexspiReadSampleClock::FlexspiReadSampleClkLoopbackInternally => { + is_unified_config = true; + } + FlexspiReadSampleClock::FlexspiReadSampleClkLoopbackFromDqsPad => { + is_unified_config = true; + } + FlexspiReadSampleClock::FlexspiReadSampleClkLoopbackFromSckPad => { + is_unified_config = true; + } + FlexspiReadSampleClock::FlexspiReadSampleClkExternalInputFromDqsPad => { + is_unified_config = device_config.is_sck2_enabled; + } + } + + if is_unified_config { + flexspi_dll_value = 0x100; /* 1 fixed delay cells in DLL delay chain) */ + } else if (device_config.flexspi_root_clk >= 100000000) { + /* DLLEN = 1, SLVDLYTARGET = 0xF, */ + flexspi_dll_value = 0x1 | (0xF << 3); + } else { + temp = (device_config.data_valid_time) as u32 * 1000; /* Convert data valid time in ns to ps. */ + dll_value = temp / 75; + if (dll_value * 75 < temp) { + dll_value += 1; + } + flexspi_dll_value = 0x1 << 8 | (dll_value & 0x78) << 9; // TODO: remove hardcoding + } + flexspi_dll_value + } + + /// Enable or disable clock + pub fn enable_disable_clock(&self, op: bool) { + // Enable or disable clock + } + /// Enable or disable SRAM + pub fn enable_disable_sram(&self, op: bool) { + // Enable or disable SRAM + } + /// Reset FlexSPI + pub fn apply_clear_reset(&self, op: bool) { + // Reset FlexSPI + } + /// Enable Disable FlexSPI module + pub fn enable_disable_flexspi_module(&self, op: bool) {} +} + +impl FlexspiStorage { + #[allow(clippy::too_many_arguments)] + /// Create a new FlexSPI instance in blocking mode with RAM execution + pub fn new_blocking( + _inst: T, + data0: Option, + data1: Option, + data2: Option, + data3: Option, + data4: Option, + data5: Option, + data6: Option, + data7: Option, + clk: impl FlexSpiClkPin, + cs: impl FlexSpiCsPin, + port: FlexSpiFlashPort, + bus_width: FlexSpiBusWidth, + dev_instance: FlexSpiFlashPortDeviceInstance, + ) -> Self { + if let Some(data0) = data0 { + data0.config_pin(); + } + if let Some(data1) = data1 { + data1.config_pin(); + } + if let Some(data2) = data2 { + data2.config_pin(); + } + if let Some(data3) = data3 { + data3.config_pin(); + } + if let Some(data4) = data4 { + data4.config_pin(); + } + if let Some(data5) = data5 { + data5.config_pin(); + } + if let Some(data6) = data6 { + data6.config_pin(); + } + if let Some(data7) = data7 { + data7.config_pin(); + } + + cs.config_pin(); + clk.config_pin(); + + Self { + info: T::info(), + rx_watermark: 8, // 8 bytes + tx_watermark: 8, // 8 bytes + flexspi_ref: unsafe { (crate::pac::Flexspi::ptr() as *mut FlexSpi).as_mut().unwrap() }, + flash_port: port, + device_instance: dev_instance, + _mode: core::marker::PhantomData, + configport: FlexSpiConfigurationPort { + info: T::info(), + bus_width, + device_instance: dev_instance, + flash_port: port, + }, + } + } +} + +macro_rules! impl_data_pin { + ($peri:ident, $fn: ident, $invert: ident, $pull: ident) => { + impl FlexSpiDataPin for crate::peripherals::$peri { + fn config_pin(&self) { + self.set_function(crate::iopctl::Function::$fn) + .set_pull(crate::iopctl::Pull::None) + .set_slew_rate(crate::gpio::SlewRate::Slow) + .set_drive_strength(crate::gpio::DriveStrength::Normal) + .disable_analog_multiplex() + .set_drive_mode(crate::gpio::DriveMode::$pull) + .set_input_inverter(crate::gpio::Inverter::$invert); + } + } + }; +} + +macro_rules! impl_cs_pin { + ($peri:ident, $fn: ident) => { + impl FlexSpiCsPin for crate::peripherals::$peri { + fn config_pin(&self) { + self.set_function(crate::iopctl::Function::$fn) + .set_pull(crate::iopctl::Pull::None) + .set_slew_rate(crate::gpio::SlewRate::Standard) + .set_drive_strength(crate::gpio::DriveStrength::Normal) + .set_drive_mode(crate::gpio::DriveMode::PushPull) + .set_input_inverter(crate::gpio::Inverter::Disabled); + } + } + }; +} +macro_rules! impl_clk_pin { + ($peri:ident, $fn: ident) => { + impl FlexSpiClkPin for crate::peripherals::$peri { + fn config_pin(&self) { + self.set_function(crate::iopctl::Function::$fn) + .set_pull(crate::iopctl::Pull::None) + .enable_input_buffer() + .set_slew_rate(crate::gpio::SlewRate::Standard) + .set_drive_strength(crate::gpio::DriveStrength::Full) + .disable_analog_multiplex() + .set_drive_mode(crate::gpio::DriveMode::PushPull) + .set_input_inverter(crate::gpio::Inverter::Disabled); + } + } + }; +} + +/// FlexSPI Data Pins +pub trait FlexSpiDataPin: Pin + sealed::Sealed + crate::Peripheral { + /// Configure FlexSPI Data Pin + fn config_pin(&self); +} +/// FlexSPI CS Pin +pub trait FlexSpiCsPin: Pin + sealed::Sealed + crate::Peripheral { + /// Configure FlexSPI CS Pin + fn config_pin(&self); +} +/// FlexSPI Clock Pin +pub trait FlexSpiClkPin: Pin + sealed::Sealed + crate::Peripheral { + /// Configure FlexSPI Clock Pin + fn config_pin(&self); +} + +impl_data_pin!(PIO1_11, F6, Disabled, PushPull); // PortB-DATA0 +impl_data_pin!(PIO1_12, F6, Disabled, PushPull); // PortB-DATA1 +impl_data_pin!(PIO1_13, F6, Disabled, PushPull); // PortB-DATA2 +impl_data_pin!(PIO1_14, F6, Disabled, PushPull); // PortB-DATA3 +impl_data_pin!(PIO2_17, F6, Disabled, PushPull); // PortB-DATA4 +impl_data_pin!(PIO2_18, F6, Disabled, PushPull); // PortB-DATA5 +impl_data_pin!(PIO2_22, F6, Disabled, PushPull); // PortB-DATA6 +impl_data_pin!(PIO2_23, F6, Disabled, PushPull); // PortB-DATA7 +impl_cs_pin!(PIO2_19, F6); // PortB-CS0 +impl_cs_pin!(PIO2_21, F6); // PortB-CS1 +impl_clk_pin!(PIO1_29, F5); // PortB-SCLK + +impl_cs_pin!(PIO1_19, F1); // PortA-CS0 +impl_clk_pin!(PIO1_18, F1); // PortA-SCLK +impl_data_pin!(PIO1_20, F1, Disabled, PushPull); // PortA-DATA0 +impl_data_pin!(PIO1_21, F1, Disabled, PushPull); // PortA-DATA1 +impl_data_pin!(PIO1_22, F1, Disabled, PushPull); // PortA-DATA2 +impl_data_pin!(PIO1_23, F1, Disabled, PushPull); // PortA-DATA3 +impl_data_pin!(PIO1_24, F1, Disabled, PushPull); // PortA-DATA4 +impl_data_pin!(PIO1_25, F1, Disabled, PushPull); // PortA-DATA5 +impl_data_pin!(PIO1_26, F1, Disabled, PushPull); // PortA-DATA6 +impl_data_pin!(PIO1_27, F1, Disabled, PushPull); // PortA-DATA7 diff --git a/src/lib.rs b/src/lib.rs index f8aa22b3..826dfeb0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,12 +23,15 @@ pub mod crc; pub mod dma; pub mod flash; pub mod flexcomm; +pub mod flexspistorage; pub mod gpio; pub mod hashcrypt; pub mod i2c; pub mod iopctl; pub mod pwm; pub mod rng; +pub mod spinorstorage; +pub mod storage; /// Time driver for the iMX RT600 series. #[cfg(feature = "time-driver")] pub mod time_driver; @@ -154,6 +157,7 @@ pub fn init(config: config::Config) -> Peripherals { dma::init(); gpio::init(); timer::init(); + flexspistorage::init(); } peripherals diff --git a/src/spinorstorage.rs b/src/spinorstorage.rs new file mode 100644 index 00000000..82150fd8 --- /dev/null +++ b/src/spinorstorage.rs @@ -0,0 +1,133 @@ +use embassy_hal_internal::Peripheral; +use embedded_storage::nor_flash::{ + ErrorType, NorFlash as BlockingNorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash as BlockingReadNorFlash, +}; + +use crate::storage::{ConfigureCmdSeq, NorStorageCmdSeq}; + +/// Driver mode. +#[allow(private_bounds)] +mod sealed { + /// simply seal a trait + pub trait Sealed {} +} + +impl sealed::Sealed for T {} + +struct Info { + regs: &'static crate::pac::flexspi::RegisterBlock, +} + +trait SealedInstance { + fn info() -> Info; +} +pub trait Mode: sealed::Sealed {} + +/// Blocking mode. +pub struct Blocking; +impl Mode for Blocking {} + +/// Async mode. +pub struct Async; +impl Mode for Async {} + +pub trait Instance: SealedInstance + Peripheral

+ 'static + Send {} + +impl SealedInstance for crate::peripherals::FLEXSPI { + fn info() -> Info { + Info { + regs: unsafe { &*crate::pac::Flexspi::ptr() }, + } + } +} + +#[derive(Debug)] +pub struct FlashStorageErrorOther; +impl ErrorType for SpiStorage { + type Error = FlashStorageErrorOther; +} + +impl NorFlashError for FlashStorageErrorOther { + fn kind(&self) -> embedded_storage::nor_flash::NorFlashErrorKind { + NorFlashErrorKind::Other + } +} + +impl BlockingReadNorFlash for SpiStorage { + const READ_SIZE: usize = 1; + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + Ok(()) + } + fn capacity(&self) -> usize { + // Return the capacity of the flash + 0 + } +} + +impl BlockingNorFlash for SpiStorage { + const WRITE_SIZE: usize = 256; + const ERASE_SIZE: usize = 4096; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + // Erase data in blocking mode + panic!("Erase operation is not implemented for Data Port. Please use Command Port for erase operation"); + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + Ok(()) + } +} + +impl ConfigureCmdSeq for SpiStorage { + fn configure_cmd_seq(&self, cmd_seq: &NorStorageCmdSeq) { + // Configure the command sequence + } +} +pub struct SpiStorage { + info: Info, + phantom: core::marker::PhantomData, +} + +impl crate::storage::BlockingNorStorageDriver for SpiStorage { + fn lock(&self) -> Result<(), Self::Error> { + Ok(()) + } + + fn unlock(&self) -> Result<(), Self::Error> { + Ok(()) + } + + fn read_jedec_id(&self) -> Result<[u8; 3], Self::Error> { + Ok([0, 0, 0]) + } + + fn power_down(&self) -> Result<(), Self::Error> { + Ok(()) + } + + fn power_up(&self) -> Result<(), Self::Error> { + Ok(()) + } + + fn write_enable(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + + fn write_disable(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + + fn read_status_reg(&mut self) -> Result<[u8; 4], Self::Error> { + Ok([0, 0, 0, 0]) + } +} + +impl SpiStorage { + pub fn new_blocking(_spiinstance: T) -> Self { + let info = T::info(); + Self { + info, + phantom: core::marker::PhantomData, + } + } +} diff --git a/src/storage.rs b/src/storage.rs new file mode 100644 index 00000000..8e75fcd8 --- /dev/null +++ b/src/storage.rs @@ -0,0 +1,126 @@ +use embedded_storage::nor_flash::{NorFlash as BlockingNorFlash, ReadNorFlash as BlockingReadNorFlash}; +use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; + +/// Storage Mode +pub enum NorStorageCmdMode { + /// DDR mode for data transfer + DDR, + /// SDR mode for data transfer + SDR, +} +/// Storage Command Type +pub enum NorStorageCmdType { + /// Read transfer type + Read, + /// Write transfer type + Write, +} + +/// NOR Storage Command to be passed by NOR based storage device drivers +pub struct NorStorageCmd { + /// Nor Storage Command lower byte + pub cmd_lb: u8, + /// Nor Storage Command upper byte + pub cmd_ub: Option, + /// Address width in bytes + pub addr_width: Option, + /// DDR or SDR mode + pub mode: NorStorageCmdMode, + /// Number of Dummy clock cycles. Assuming max 256 dummy cycles beyond which its impractical + pub dummy: Option, + /// Command type - Reading data or writing data + pub cmdtype: Option, +} + +/// NOR Storage Command Array to be passed by NOR based storage device drivers +pub struct NorStorageCmdSeq { + /// Fast Read Sequence + pub fast_read: Option, + /// Page Program Sequence + pub page_program: Option, + /// Sector Erase Sequence + pub sector_erase: Option, + /// Write Enable Sequence + pub write_enable: Option, + /// Write Disable Sequence + pub write_disable: Option, + /// Read JEDEC Id Down Sequence + pub read_id: Option, + /// Power Up Sequence + pub poweup: Option, + /// Power Down Sequence + pub powerdonw: Option, + /// Read Status Register Sequence + pub read_status_reg: Option, + /// Write Status Register Sequence + pub write_status_reg: Option, + /// Read Config1 Register Sequence + pub read_cfg_reg1: Option, + /// Write Config1 Register Sequence + pub write_cfg_reg1: Option, + /// Read Config2 Register Sequence + pub read_cfg_reg2: Option, + /// Write Config2 Register Sequence + pub write_cfg_reg2: Option, + /// Read Config3 Register Sequence + pub read_cfg_reg3: Option, + /// Write Config3 Register Sequence + pub write_cfg_reg3: Option, +} + +/// NAND Storage Command Sequence to be passed by NAND based storage device drivers +pub struct NandStorageCmdSequence { + // TODO +} + +/// Config Storage Command sequences +pub trait ConfigureCmdSeq { + /// Configure the storage command sequences + fn configure_cmd_seq(&self, cmd_seq: &NorStorageCmdSeq); +} + +/// Blocking NOR Storage Driver +pub trait BlockingNorStorageDriver: BlockingNorFlash + BlockingReadNorFlash + ConfigureCmdSeq { + /// Lock the storage + fn lock(&self) -> Result<(), Self::Error>; + /// Unlock the storage + fn unlock(&self) -> Result<(), Self::Error>; + /// Read the JEDEC ID + fn read_jedec_id(&self) -> Result<[u8; 3], Self::Error>; + /// Power down the storage + fn power_down(&self) -> Result<(), Self::Error>; + /// Power up the storage + fn power_up(&self) -> Result<(), Self::Error>; + /// Write Enable + fn write_enable(&mut self) -> Result<(), Self::Error>; + /// Write Disable + fn write_disable(&mut self) -> Result<(), Self::Error>; + /// Read Status Register + fn read_status_reg(&mut self) -> Result<[u8; 4], Self::Error>; +} + +/// Async NOR Storage Driver +pub trait AsyncNorStorageDriver: AsyncNorFlash + AsyncReadNorFlash + ConfigureCmdSeq { + /// Lock the storage + async fn lock(&self) -> Result<(), Self::Error>; + /// Unlock the storage + async fn unlock(&self) -> Result<(), Self::Error>; + /// Read the JEDEC ID + async fn read_jedec_id(&self) -> Result<[u8; 3], Self::Error>; + /// Power down the storage + async fn power_down(&self) -> Result<(), Self::Error>; + /// Power up the storage + async fn power_up(&self) -> Result<(), Self::Error>; + /// Write Enable + async fn write_enable(&self) -> Result<(), Self::Error>; + /// Write Disable + async fn write_disable(&self) -> Result<(), Self::Error>; + /// Read Status Register + async fn read_status_reg(&mut self) -> Result<[u8; 4], Self::Error>; +} + +/// Blocking NAND storage driver +pub trait BlockingNandStorageDriver {} + +/// Async NAND storage driver +pub trait AsyncNandStorageDriver {}