diff --git a/Cargo.lock b/Cargo.lock index ea23f02..b6281fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,10 +65,12 @@ dependencies = [ "ast1060-pac", "cortex-m", "cortex-m-rt", + "critical-section", "embedded-hal 1.0.0", "embedded-hal 1.0.0-alpha.1", "embedded-io", "fugit", + "heapless", "hex-literal", "panic-halt", "paste", @@ -111,6 +113,12 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "clap" version = "4.5.40" @@ -165,6 +173,7 @@ checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" dependencies = [ "bare-metal", "bitfield", + "critical-section", "embedded-hal 0.2.7", "volatile-register", ] @@ -190,6 +199,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "embedded-hal" version = "0.2.7" @@ -247,12 +262,31 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -430,6 +464,12 @@ dependencies = [ "serde", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strsim" version = "0.11.1" diff --git a/Cargo.toml b/Cargo.toml index 0b117f2..049a88c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ test-hmac = [] test-hash = [] spi_dma = [] spi_dma_write = [] +spi_dma_irq = [] spi_monitor = [] [dependencies] @@ -33,9 +34,10 @@ embedded-io = "0.6.1" fugit = "0.3.7" proposed-traits = { git = "https://github.com/rusty1968/proposed_traits.git", package = "proposed-traits", rev = "85641310df5a5276c67f81621b104322cff0286c" } hex-literal = "0.4" +heapless = "0.8.0" paste = "1.0" - -cortex-m = { version = "0.7.5" } +critical-section = "1.2" +cortex-m = { version = "0.7.5", features = ["critical-section-single-core"] } cortex-m-rt = { version = "0.6.5", features = ["device"] } panic-halt = "1.0.0" diff --git a/src/common.rs b/src/common.rs index bf5909d..14d2037 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,5 +1,9 @@ // Licensed under the Apache-2.0 license +use crate::uart::UartController; +use core::ops::{Index, IndexMut}; +use embedded_io::Write; + pub struct DummyDelay; impl embedded_hal::delay::DelayNs for DummyDelay { @@ -10,6 +14,15 @@ impl embedded_hal::delay::DelayNs for DummyDelay { } } +pub fn fill_random(buf: &mut [u8], seed: &mut u32) { + for b in buf.iter_mut() { + *seed ^= *seed << 13; + *seed ^= *seed >> 17; + *seed ^= *seed << 5; + *b = (*seed & 0xFF) as u8; + } +} + #[repr(align(32))] pub struct DmaBuffer { pub buf: [u8; N], @@ -38,18 +51,18 @@ impl DmaBuffer { } #[must_use] - pub fn len(&self) -> usize { + pub const fn len(&self) -> usize { N } #[must_use] - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { N == 0 } #[must_use] - pub fn as_slice(&self) -> &[u8] { - &self.buf + pub fn as_slice(&self, start: usize, end: usize) -> &[u8] { + &self.buf[start..end] } pub fn as_mut_slice(&mut self, start: usize, end: usize) -> &mut [u8] { @@ -57,8 +70,6 @@ impl DmaBuffer { } } -use core::ops::{Index, IndexMut}; - impl Index for DmaBuffer { type Output = u8; fn index(&self, idx: usize) -> &Self::Output { @@ -71,3 +82,37 @@ impl IndexMut for DmaBuffer { &mut self.buf[idx] } } + +pub trait Logger { + fn debug(&mut self, msg: &str); + fn error(&mut self, msg: &str); +} + +// No-op implementation for production builds +pub struct NoOpLogger; +impl Logger for NoOpLogger { + fn debug(&mut self, _msg: &str) {} + fn error(&mut self, _msg: &str) {} +} + +// UART logger adapter (separate concern) +pub struct UartLogger<'a> { + uart: &'a mut UartController<'a>, +} + +impl<'a> UartLogger<'a> { + pub fn new(uart: &'a mut UartController<'a>) -> Self { + UartLogger { uart } + } +} + +impl<'a> Logger for UartLogger<'a> { + fn debug(&mut self, msg: &str) { + writeln!(self.uart, "{msg}").ok(); + write!(self.uart, "\r").ok(); + } + fn error(&mut self, msg: &str) { + writeln!(self.uart, "ERROR: {msg}").ok(); + write!(self.uart, "\r").ok(); + } +} diff --git a/src/main.rs b/src/main.rs index def120d..81a57ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,10 +18,10 @@ use aspeed_ddk::syscon::{ClockId, ResetId, SysCon}; use fugit::MillisDurationU32 as MilliSeconds; use aspeed_ddk::tests::functional::ecdsa_test::run_ecdsa_tests; -use aspeed_ddk::tests::functional::gpio_test; use aspeed_ddk::tests::functional::hash_test::run_hash_tests; use aspeed_ddk::tests::functional::hmac_test::run_hmac_tests; use aspeed_ddk::tests::functional::rsa_test::run_rsa_tests; +use aspeed_ddk::tests::functional::{gpio_test, spim_test}; use panic_halt as _; use proposed_traits::system_control::ResetControl; @@ -139,9 +139,10 @@ fn main() -> ! { let scu = peripherals.scu; let secure = peripherals.secure; - writeln!(uart_controller, "\r\nHello, world!!\r\n").unwrap(); + writeln!(uart_controller, "\r\n Hello, world!!\r\n").unwrap(); let delay = DummyDelay; + let mut syscon = SysCon::new(delay.clone(), scu); // Enable HACE (Hash and Crypto Engine) @@ -161,18 +162,40 @@ fn main() -> ! { let mut ecdsa = AspeedEcdsa::new(&secure, delay.clone()); run_ecdsa_tests(&mut uart_controller, &mut ecdsa); - let mut rsa = AspeedRsa::new(&secure, delay); - run_rsa_tests(&mut uart_controller, &mut rsa); - gpio_test::test_gpioa(&mut uart_controller); - test_wdt(&mut uart_controller); - - let test_spicontroller = false; + //let mut rsa = AspeedRsa::new(&secure, delay); + //run_rsa_tests(&mut uart_controller, &mut rsa); + //gpio_test::test_gpioa(&mut uart_controller); + //test_wdt(&mut uart_controller); + + writeln!(uart_controller, "\r\nTEST SPI IRQ starts!!\r\n").unwrap(); + let test_spicontroller = true; + #[cfg(feature = "spi_dma_irq")] + let test_irq = true; + #[cfg(not(feature = "spi_dma_irq"))] + let test_irq = false; if test_spicontroller { - spi::spitest::test_fmc(&mut uart_controller); - spi::spitest::test_spi(&mut uart_controller); + if test_irq { + writeln!(uart_controller, "\r\nTEST SPI IRQ!!\r\n").unwrap(); + + spi::spidmairqtest::test_fmc_dma_irq(&mut uart_controller); + spi::spidmairqtest::test_spi_dma_irq(&mut uart_controller); + } else { + spi::spitest::test_fmc(&mut uart_controller); + spi::spitest::test_spi(&mut uart_controller); + //gpio_test::test_gpio_flash_power(&mut uart_controller); + // spi::spitest::test_spi2(&mut uart_controller); + } + let mut delay1 = DummyDelay; + delay1.delay_ns(10_000_000); + writeln!(uart_controller, "\r\nTEST SPI IRQ DONE!!\r\n").unwrap(); + } + let spim_test = false; + if spim_test { + // use to release ast2600 + spim_test::test_spim0(&mut uart_controller); gpio_test::test_gpio_flash_power(&mut uart_controller); - spi::spitest::test_spi2(&mut uart_controller); + gpio_test::test_gpio_bmc_reset(&mut uart_controller); } // Initialize the peripherals here if needed loop { diff --git a/src/spi/consts.rs b/src/spi/consts.rs new file mode 100644 index 0000000..25b51d7 --- /dev/null +++ b/src/spi/consts.rs @@ -0,0 +1,56 @@ +// Licensed under the Apache-2.0 license + +// Constants (unchanged) +pub(crate) const SPI_CONF_CE0_ENABLE_WRITE_SHIFT: u32 = 16; + +pub(crate) const SPI_CTRL_FREQ_MASK: u32 = 0x0F00_0F00; +pub(crate) const SPI_CTRL_CEX_SPI_CMD_SHIFT: u32 = 16; +pub(crate) const SPI_CTRL_CEX_SPI_CMD_MASK: u32 = 0xff; +pub(crate) const SPI_CTRL_CEX_DUMMY_SHIFT: u32 = 6; +pub(crate) const SPI_CTRL_CEX_4BYTE_MODE_SET: u32 = 0x11; // bit0: 4byte mode, bit4: 4byte mode cmd + +pub(crate) const SPI_DMA_DELAY_SHIFT: u32 = 8; +pub(crate) const SPI_DMA_DELAY_MASK: u32 = 0xff; +pub(crate) const SPI_DMA_CLK_FREQ_SHIFT: u32 = 16; +pub(crate) const SPI_DMA_CLK_FREQ_MASK: u32 = 0xf; + +pub(crate) const SPI_DMA_GET_REQ_MAGIC: u32 = 0xaeed_0000; +pub(crate) const SPI_DMA_DISCARD_REQ_MAGIC: u32 = 0xdeea_0000; +pub(crate) const SPI_DMA_RAM_MAP_BASE: u32 = 0x8000_0000; +pub(crate) const SPI_DMA_FLASH_MAP_BASE: u32 = 0x6000_0000; + +pub(crate) const SPI_CALIB_LEN: usize = 0x400; + +#[cfg(feature = "spi_dma")] +pub(crate) const SPI_DMA_TRIGGER_LEN: u32 = 128; + +#[cfg(feature = "spi_dma")] +pub(crate) const SPI_DMA_WRITE: u32 = 1 << 1; + +pub(crate) const SPI_DMA_REQUEST: u32 = 1 << 31; +pub(crate) const SPI_DMA_GRANT: u32 = 1 << 30; +pub(crate) const SPI_DMA_CALIB_MODE: u32 = 1 << 3; +pub(crate) const SPI_DMA_CALC_CKSUM: u32 = 1 << 2; + +pub(crate) const SPI_DMA_ENABLE: u32 = 1 << 0; +pub(crate) const SPI_DMA_STATUS: u32 = 1 << 11; + +pub(crate) const ASPEED_MAX_CS: usize = 5; // Must be usize for array indexing + +pub(crate) const ASPEED_SPI_NORMAL_READ: u32 = 0x1; +pub(crate) const ASPEED_SPI_NORMAL_WRITE: u32 = 0x2; +pub(crate) const ASPEED_SPI_USER: u32 = 0x3; +pub(crate) const ASPEED_SPI_USER_INACTIVE: u32 = 0x4; + +pub(crate) const ASPEED_SPI_SZ_2M: u32 = 0x0020_0000; +pub(crate) const ASPEED_SPI_SZ_256M: u32 = 0x1000_0000; + +pub(crate) const HPLL_FREQ: u32 = 1_000_000_000; + +pub(crate) const SPI_DMA_TIMEOUT: u32 = 0x10000; + +// SPIM clock output pin bits +pub(crate) const PIN_SPIM0_CLK_OUT_BIT: u32 = 7; +pub(crate) const PIN_SPIM1_CLK_OUT_BIT: u32 = 21; +pub(crate) const PIN_SPIM2_CLK_OUT_BIT: u32 = 3; +pub(crate) const PIN_SPIM3_CLK_OUT_BIT: u32 = 17; diff --git a/src/spi/device.rs b/src/spi/device.rs index aebe83f..b4dc57e 100644 --- a/src/spi/device.rs +++ b/src/spi/device.rs @@ -1,40 +1,45 @@ // Licensed under the Apache-2.0 license +use crate::spimonitor::SpiMonitorNum; + use super::SpiBusWithCs; use super::SpiError; -use crate::spimonitor::{SpiMonitor, SpipfInstance}; use embedded_hal::spi::{ErrorType, Operation, SpiDevice}; #[derive(Debug)] -pub struct ChipSelectDevice<'a, B, SPIPF> +pub struct ChipSelectDevice<'a, B> where B: SpiBusWithCs, - SPIPF: SpipfInstance, { pub bus: &'a mut B, pub cs: usize, - pub spi_monitor: Option<&'a mut SpiMonitor>, + pub spim: Option, } -impl<'a, B, SPIPF> ErrorType for ChipSelectDevice<'a, B, SPIPF> +impl<'a, B> ErrorType for ChipSelectDevice<'a, B> where B: SpiBusWithCs, - SPIPF: SpipfInstance, { type Error = B::Error; } -impl<'a, B, SPIPF> SpiDevice for ChipSelectDevice<'a, B, SPIPF> +impl From for u32 { + #[inline] + fn from(v: SpiMonitorNum) -> u32 { + v as u32 + } +} + +impl<'a, B> SpiDevice for ChipSelectDevice<'a, B> where B: SpiBusWithCs, - SPIPF: SpipfInstance, { fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), SpiError> { self.bus.select_cs(self.cs)?; - if let Some(spim) = self.spi_monitor.as_mut() { + if let Some(spim) = self.spim { if self.bus.get_master_id() != 0 { - spim.spim_scu_ctrl_set(0x8, 0x8); - spim.spim_scu_ctrl_set(0x7, 1 + SPIPF::FILTER_ID as u32); + super::spim_scu_ctrl_set(0x8, 0x8); + super::spim_scu_ctrl_set(0x7, 1 + u32::from(spim)); } super::spim_proprietary_pre_config(); } @@ -48,14 +53,12 @@ where Operation::DelayNs(_) => todo!(), }; } - - super::spim_proprietary_post_config(); - if let Some(spim) = self.spi_monitor.as_mut() { + if let Some(_spim) = self.spim { + super::spim_proprietary_post_config(); if self.bus.get_master_id() != 0 { - spim.spim_scu_ctrl_clear(0xf); + super::spim_scu_ctrl_clear(0xf); } } - self.bus.deselect_cs(self.cs)?; Ok(()) } diff --git a/src/spi/error.rs b/src/spi/error.rs new file mode 100644 index 0000000..6fcd761 --- /dev/null +++ b/src/spi/error.rs @@ -0,0 +1,33 @@ +// Licensed under the Apache-2.0 license + +use embedded_hal::spi; + +#[derive(Debug)] +pub enum SpiError { + BusError, + DmaTimeout, + CsSelectFailed(usize), + LengthMismatch, + CapacityOutOfRange, + UnsupportedDevice(u8), + AddressNotAligned(u32), + InvalidCommand(u8), + Other(&'static str), +} + +/// Required by embedded-hal 1.0 +impl spi::Error for SpiError { + fn kind(&self) -> spi::ErrorKind { + match self { + SpiError::BusError + | SpiError::DmaTimeout + | SpiError::CsSelectFailed(_) + | SpiError::LengthMismatch + | SpiError::CapacityOutOfRange + | SpiError::UnsupportedDevice(_) + | SpiError::InvalidCommand(_) + | SpiError::AddressNotAligned(_) + | SpiError::Other(_) => spi::ErrorKind::Other, + } + } +} diff --git a/src/spi/fmccontroller.rs b/src/spi/fmccontroller.rs index 5e92e33..9b25a86 100644 --- a/src/spi/fmccontroller.rs +++ b/src/spi/fmccontroller.rs @@ -3,24 +3,25 @@ use super::{ aspeed_get_spi_freq_div, get_addr_buswidth, get_hclock_rate, get_mid_point_of_longest_one, spi_cal_dummy_cycle, spi_calibration_enable, spi_io_mode, spi_io_mode_user, spi_read_data, - spi_write_data, CtrlType, SpiBusWithCs, SpiConfig, SpiData, SpiError, Write, ASPEED_MAX_CS, - ASPEED_SPI_NORMAL_READ, ASPEED_SPI_NORMAL_WRITE, ASPEED_SPI_SZ_256M, ASPEED_SPI_SZ_2M, - ASPEED_SPI_USER, ASPEED_SPI_USER_INACTIVE, SPI_CALIB_LEN, SPI_CTRL_FREQ_MASK, - SPI_DMA_CALC_CKSUM, SPI_DMA_CALIB_MODE, SPI_DMA_DISCARD_REQ_MAGIC, SPI_DMA_ENABLE, - SPI_DMA_FLASH_MAP_BASE, SPI_DMA_GET_REQ_MAGIC, SPI_DMA_GRANT, SPI_DMA_RAM_MAP_BASE, - SPI_DMA_REQUEST, SPI_DMA_STATUS, SPI_DMA_TIMEOUT, + spi_write_data, CtrlType, SpiBusWithCs, SpiConfig, SpiData, SpiError, AddressWidth, }; +use super::consts::{ASPEED_MAX_CS,ASPEED_SPI_NORMAL_READ, ASPEED_SPI_NORMAL_WRITE, ASPEED_SPI_SZ_256M, + ASPEED_SPI_SZ_2M, ASPEED_SPI_USER, ASPEED_SPI_USER_INACTIVE, SPI_CALIB_LEN, + SPI_CTRL_FREQ_MASK, SPI_DMA_CALC_CKSUM, SPI_DMA_CALIB_MODE, SPI_DMA_DISCARD_REQ_MAGIC, + SPI_DMA_ENABLE, SPI_DMA_FLASH_MAP_BASE, SPI_DMA_GET_REQ_MAGIC, SPI_DMA_GRANT, + SPI_DMA_RAM_MAP_BASE, SPI_DMA_REQUEST, SPI_DMA_STATUS, SPI_DMA_TIMEOUT, + SPI_CTRL_CEX_4BYTE_MODE_SET, SPI_CTRL_CEX_DUMMY_SHIFT, SPI_CTRL_CEX_SPI_CMD_MASK, + SPI_CTRL_CEX_SPI_CMD_SHIFT, SPI_DMA_CLK_FREQ_SHIFT, SPI_DMA_CLK_FREQ_MASK, SPI_DMA_DELAY_MASK, + SPI_DMA_DELAY_SHIFT, SPI_CONF_CE0_ENABLE_WRITE_SHIFT}; + +use embedded_io::Write; #[cfg(feature = "spi_dma")] -use super::{SPI_DMA_TRIGGER_LEN, SPI_NOR_DATA_DIRECT_READ, SPI_NOR_DATA_DIRECT_WRITE}; +use super::consts::{SPI_DMA_TRIGGER_LEN}; use crate::dbg; -use crate::spi::{ - SPI_CONF_CE0_ENABLE_WRITE_SHIFT, SPI_CTRL_CEX_4BYTE_MODE_SET, SPI_CTRL_CEX_DUMMY_SHIFT, - SPI_CTRL_CEX_SPI_CMD_MASK, SPI_CTRL_CEX_SPI_CMD_SHIFT, SPI_DMA_CLK_FREQ_MASK, - SPI_DMA_CLK_FREQ_SHIFT, SPI_DMA_DELAY_MASK, SPI_DMA_DELAY_SHIFT, -}; -use crate::{common::DummyDelay, spi::norflash::SpiNorData, uart::UartController}; + +use crate::{common::DummyDelay, spi::norflash::SpiNorCommand, uart::UartController}; use embedded_hal::{ delay::DelayNs, spi::{ErrorType, SpiBus}, @@ -225,7 +226,7 @@ impl<'a> FmcController<'a> { } } - fn spi_nor_read_init(&mut self, cs: usize, op_info: &SpiNorData) { + fn spi_nor_read_init(&mut self, cs: usize, op_info: &SpiNorCommand) { dbg!( self, "spi_nor_read_init() cs:{} master_idx: {}", @@ -257,7 +258,7 @@ impl<'a> FmcController<'a> { ); cs_ctrlreg_w!(self, cs, read_cmd); - if op_info.addr_len == 4 { + if matches!(op_info.address.width, AddressWidth::FourByte) { self.regs.fmc004().modify(|r, w| unsafe { let current = r.bits(); w.bits(current | (SPI_CTRL_CEX_4BYTE_MODE_SET << cs)) @@ -266,7 +267,7 @@ impl<'a> FmcController<'a> { if matches!(self.spi_config.ctrl_type, CtrlType::HostSpi) { self.regs.fmc06c().modify(|r, w| unsafe { let mut current = r.bits(); - if op_info.addr_len == 4 { + if matches!(op_info.address.width, AddressWidth::FourByte){ current = (current & 0xffff_00ff) | (op_info.opcode << 8); } else { current = (current & 0xffff_ff00) | op_info.opcode; @@ -278,7 +279,7 @@ impl<'a> FmcController<'a> { self.timing_calibration(cs); } - fn spi_nor_write_init(&mut self, cs: usize, op_info: &SpiNorData) { + fn spi_nor_write_init(&mut self, cs: usize, op_info: &SpiNorCommand) { let io_mode = spi_io_mode(op_info.mode); let dummy = 0; let write_cmd = (io_mode @@ -480,7 +481,8 @@ impl<'a> FmcController<'a> { checksum } - fn spi_nor_transceive_user(&mut self, op_info: &mut SpiNorData) { + + fn spi_nor_transceive_user(&mut self, op_info: &mut SpiNorCommand) { let cs: usize = self.current_cs; let dummy = [0u8; 12]; let start_ptr = self.spi_data.decode_addr[cs].start as *mut u32; @@ -490,7 +492,7 @@ impl<'a> FmcController<'a> { u32::try_from(cs).unwrap(), self.spi_data.decode_addr[cs].start ); - + self.activate_user(); // Send command let cmd_mode = self.spi_data.cmd_mode[cs].user | super::spi_io_mode_user(u32::from(super::get_cmd_buswidth(op_info.mode as u32))); @@ -503,13 +505,16 @@ impl<'a> FmcController<'a> { | super::spi_io_mode_user(u32::from(super::get_addr_buswidth(op_info.mode as u32))); cs_ctrlreg_w!(self, cs, addr_mode); - let mut addr = op_info.addr; - if op_info.addr_len == 3 { - addr <<= 8; - } + let be = op_info.address.value.to_be_bytes(); - let addr_bytes = addr.to_be_bytes(); - unsafe { super::spi_write_data(start_ptr, &addr_bytes[..op_info.addr_len as usize]) }; + let bytes = match op_info.address.width { + AddressWidth::ThreeByte => &be[1..], // last 3 bytes (24-bit) + AddressWidth::FourByte => &be[..], // all 4 bytes + }; + + unsafe { + super::spi_write_data(start_ptr, bytes); + } // Dummy cycles let bus_width: u8 = super::get_addr_buswidth(op_info.mode as u32); @@ -524,61 +529,76 @@ impl<'a> FmcController<'a> { | spi_io_mode_user(u32::from(super::get_data_buswidth(op_info.mode as u32))); cs_ctrlreg_w!(self, cs, data_mode); - if op_info.data_direct == super::SPI_NOR_DATA_DIRECT_READ { - unsafe { spi_read_data(start_ptr, op_info.rx_buf) }; - } else { - unsafe { spi_write_data(start_ptr, op_info.tx_buf) }; + match op_info.data_direct { + super::DataDirection::Read => { + unsafe { spi_read_data(start_ptr, op_info.rx_buf) }; + } + super::DataDirection::Write => { + unsafe { spi_write_data(start_ptr, op_info.tx_buf) }; + } } + self.deactivate_user(); } // Helper wrappers would be defined for spi_write_data, spi_read_data, io_mode_user, etc. - pub fn spi_nor_transceive(&mut self, op_info: &mut SpiNorData) -> Result<(), SpiError> { + pub fn spi_nor_transceive(&mut self, op_info: &mut SpiNorCommand) -> Result<(), SpiError> { dbg!(self, "spi_nor_transceive()..."); #[cfg(feature = "spi_dma")] { dbg!(self, "spi dma enabled rx_len:{}", op_info.rx_buf.len()); - let addr_aligned = op_info.addr % 4 == 0; - - if op_info.data_direct == SPI_NOR_DATA_DIRECT_READ { - let buf_aligned = (op_info.rx_buf.as_ptr() as usize) % 4 == 0; - let use_dma = !self.spi_config.pure_spi_mode_only - && op_info.rx_buf.len() > SPI_DMA_TRIGGER_LEN as usize - && addr_aligned - && buf_aligned; - dbg!(self, "read dma"); - dbg!( - self, - "use_dma{} rx len: {}, addr_aligned: {}, buf_aligned: {}", - use_dma, - op_info.rx_buf.len(), - addr_aligned, - buf_aligned - ); - if use_dma { - return self.read_dma(op_info); - } else { - self.spi_nor_transceive_user(op_info); - } - } else if op_info.data_direct == SPI_NOR_DATA_DIRECT_WRITE { - dbg!(self, "write dma"); - #[cfg(feature = "spi_dma_write")] - { - let buf_aligned = (op_info.tx_buf.as_ptr() as usize) % 4 == 0; + let addr_aligned = op_info.address.value % 4 == 0; + + match op_info.data_direct { + super::DataDirection::Read => { + let buf_aligned = (op_info.rx_buf.as_ptr() as usize) % 4 == 0; let use_dma = !self.spi_config.pure_spi_mode_only - && op_info.tx_buf.len() > SPI_DMA_TRIGGER_LEN as usize + && op_info.rx_buf.len() > SPI_DMA_TRIGGER_LEN as usize && addr_aligned && buf_aligned; + dbg!(self, "read dma"); + dbg!( + self, + "use_dma{} rx len: {}, addr_aligned: {}, buf_aligned: {}", + use_dma, + op_info.rx_buf.len(), + addr_aligned, + buf_aligned + ); if use_dma { - return self.write_dma(op_info); + return self.read_dma(op_info); } else { self.spi_nor_transceive_user(op_info); } - } //spi dma write - #[cfg(not(feature = "spi_dma_write"))] - self.spi_nor_transceive_user(op_info); - } //write + } + super::DataDirection::Write => { + dbg!(self, "write dma"); + #[cfg(feature = "spi_dma_write")] + { + let buf_aligned = (op_info.tx_buf.as_ptr() as usize) % 4 == 0; + let use_dma = !self.spi_config.pure_spi_mode_only + && op_info.tx_buf.len() > SPI_DMA_TRIGGER_LEN as usize + && addr_aligned + && buf_aligned; + dbg!( + self, + "use_dma{} tx len: {}, addr_aligned: {}, buf_aligned: {}", + use_dma, + op_info.rx_buf.len(), + addr_aligned, + buf_aligned + ); + if use_dma { + return self.write_dma(op_info); + } else { + self.spi_nor_transceive_user(op_info); + } + } //spi dma write + #[cfg(not(feature = "spi_dma_write"))] + self.spi_nor_transceive_user(op_info); + } //write + } Ok(()) } // dma @@ -590,7 +610,8 @@ impl<'a> FmcController<'a> { } } - fn dma_disable(&mut self) { + pub fn dma_disable(&mut self) { + dbg!(self, "dma disable"); self.regs.fmc080().write(|w| unsafe { w.bits(0x0) }); self.regs @@ -598,48 +619,73 @@ impl<'a> FmcController<'a> { .write(|w| unsafe { w.bits(SPI_DMA_DISCARD_REQ_MAGIC) }); } - fn wait_for_dma_completion(&mut self, timeout: u32) -> Result<(), SpiError> { + pub fn wait_for_dma_completion(&mut self, timeout: u32) -> Result<(), SpiError> { let mut delay = DummyDelay {}; let mut to = timeout; //wait for_dma done - while !self.regs.fmc008().read().dmastatus().is_dma_finish() { - delay.delay_ns(500); - to -= 1; - - if to == 0 { - self.dma_disable(); - return Err(SpiError::DmaTimeout); + #[cfg(not(feature = "spi_dma_irq"))] + { + dbg!(self, "wait_for_dma_completion"); + while !self.regs.fmc008().read().dmastatus().is_dma_finish() { + delay.delay_ns(500); + to -= 1; + + if to == 0 { + self.dma_disable(); + return Err(SpiError::DmaTimeout); + } } + self.dma_disable(); } - self.dma_disable(); Ok(()) } - /* + fn dma_irq_disable(&mut self) { - // Enable the DMA interrupt bit (bit 3) + // disable the DMA interrupt bit (bit 3) + dbg!(self, "dma_irq_disable"); self.regs.fmc008().modify(|_, w| w.dmaintenbl().clear_bit()); } - + #[allow(dead_code)] fn dma_irq_enable(&mut self) { // Enable the DMA interrupt bit (bit 3) + dbg!(self, "dma_irq_enable"); self.regs.fmc008().modify(|_, w| w.dmaintenbl().set_bit()); } + #[allow(dead_code)] fn dbg_fmc_dma(&mut self) { + dbg!(self, "reg 0x14: {:14x}", self.regs.fmc014().read().bits()); dbg!(self, "reg 0x80: {:08x}", self.regs.fmc080().read().bits()); dbg!(self, "reg 0x84: {:08x}", self.regs.fmc084().read().bits()); dbg!(self, "reg 0x88: {:08x}", self.regs.fmc088().read().bits()); dbg!(self, "reg 0x8c: {:08x}", self.regs.fmc08c().read().bits()); } - */ - pub fn read_dma(&mut self, op: &mut SpiNorData) -> Result<(), SpiError> { + + pub fn handle_interrupt(&mut self) -> Result<(), SpiError> { + dbg!(self, "handle interrupt"); + if !self.regs.fmc008().read().dmastatus().is_dma_finish() { + return Err(SpiError::Other("irq error")); + } + /* disable IRQ */ + self.dma_irq_disable(); + + /* disable DMA */ + self.dma_disable(); + + // Set it to normal read again + let cs = self.current_cs; + cs_ctrlreg_w!(self, cs, self.spi_data.cmd_mode[cs].normal_read); + Ok(()) + } + + pub fn read_dma(&mut self, op: &mut SpiNorCommand) -> Result<(), SpiError> { let cs = self.current_cs; - dbg!(self, "##### read dma ####"); + dbg!(self, "##### fmc read dma ####"); dbg!(self, "device size: 0x{:08x} dv start: 0x{:08x}, read len: 0x{:08x}, rx_buf:0x{:08x} op addr: 0x{:08x}", self.spi_data.decode_addr[cs].len, self.spi_data.decode_addr[cs].start, op.rx_buf.len(), (op.rx_buf.as_ptr() as u32), - op.addr); + op.address.value); // Length check if op.rx_buf.len() > self.spi_data.decode_addr[cs].len.try_into().unwrap() { @@ -647,8 +693,8 @@ impl<'a> FmcController<'a> { } // Alignment check - if (op.addr % 4 != 0) || ((op.rx_buf.as_ptr() as u32) % 4 != 0) { - return Err(SpiError::AddressNotAligned(op.addr)); + if (op.address.value % 4 != 0) || ((op.rx_buf.as_ptr() as u32) % 4 != 0) { + return Err(SpiError::AddressNotAligned(op.address.value)); } // Construct control value let mut ctrl = self.spi_data.cmd_mode[cs].normal_read & SPI_CTRL_FREQ_MASK; @@ -672,7 +718,7 @@ impl<'a> FmcController<'a> { while self.regs.fmc080().read().bits() & SPI_DMA_GRANT != SPI_DMA_GRANT {} } - let flash_start = self.spi_data.decode_addr[cs].start + op.addr - SPI_DMA_FLASH_MAP_BASE; + let flash_start = self.spi_data.decode_addr[cs].start + op.address.value - SPI_DMA_FLASH_MAP_BASE; dbg!(self, "flash start: 0x{:08x}", flash_start); // DMA flash and RAM address self.regs.fmc084().write(|w| unsafe { w.bits(flash_start) }); @@ -687,26 +733,37 @@ impl<'a> FmcController<'a> { .write(|w| unsafe { w.bits(u32::try_from(read_length).unwrap()) }); // Enable IRQ - //self.dma_irq_enable(); + #[cfg(feature = "spi_dma_irq")] + { + self.dma_irq_enable(); + } + //self.dbg_fmc_dma(); // Start DMA + dbg!(self, "start dma"); self.regs.fmc080().modify(|_, w| { w.dmaenbl().enable_dma_operation(); w.dmadirection() .read_flash_move_from_flash_to_external_memory() }); - dbg!(self, "start wait for dma"); self.wait_for_dma_completion(SPI_DMA_TIMEOUT) } #[allow(dead_code)] - fn write_dma(&mut self, op: &mut SpiNorData) -> Result<(), SpiError> { + fn write_dma(&mut self, op: &mut SpiNorCommand) -> Result<(), SpiError> { let cs = self.current_cs; - dbg!(self, "##### write_dma ####"); + dbg!(self, "#####fmc write_dma ####"); + dbg!(self, "device size: 0x{:08x} dv start: 0x{:08x}, read len: 0x{:08x}, tx_buf:0x{:08x} op addr: 0x{:08x}", + self.spi_data.decode_addr[cs].len, + self.spi_data.decode_addr[cs].start, + op.tx_buf.len(), + (op.tx_buf.as_ptr() as u32), + op.address.value); + //self.dbg_fmc_dma(); // Check alignment and bounds - if op.addr % 4 != 0 || (op.tx_buf.as_ptr() as usize) % 4 != 0 { - return Err(SpiError::AddressNotAligned(op.addr)); + if op.address.value % 4 != 0 || (op.tx_buf.as_ptr() as usize) % 4 != 0 { + return Err(SpiError::AddressNotAligned(op.address.value)); } if op.tx_buf.len() > self.spi_data.decode_addr[cs].len.try_into().unwrap() { return Err(SpiError::Other("Write length exceeds decode region")); @@ -719,7 +776,13 @@ impl<'a> FmcController<'a> { ctrl_reg |= (op.opcode & SPI_CTRL_CEX_SPI_CMD_MASK) << SPI_CTRL_CEX_SPI_CMD_SHIFT; ctrl_reg |= (op.dummy_cycle / u32::from(8 / bus_width)) << SPI_CTRL_CEX_DUMMY_SHIFT; ctrl_reg |= ASPEED_SPI_NORMAL_WRITE; - + dbg!( + self, + "write opcode: {} , address.value/offset: {}", + op.opcode, + op.address.value + ); + cs_ctrlreg_w!(self, cs, ctrl_reg); // Write DMA control (REQ magic) @@ -732,7 +795,7 @@ impl<'a> FmcController<'a> { // Program addresses self.regs.fmc084().write(|w| unsafe { - w.bits(self.spi_data.decode_addr[cs].start + op.addr - SPI_DMA_FLASH_MAP_BASE) + w.bits(self.spi_data.decode_addr[cs].start + op.address.value - SPI_DMA_FLASH_MAP_BASE) }); self.regs.fmc088().write(|w| unsafe { w.bits(u32::try_from(op.tx_buf.as_ptr() as usize).unwrap() + SPI_DMA_RAM_MAP_BASE) @@ -740,36 +803,67 @@ impl<'a> FmcController<'a> { self.regs .fmc08c() .write(|w| unsafe { w.bits(u32::try_from(op.tx_buf.len()).unwrap() - 1) }); + //self.dbg_fmc_dma(); // Enable DMA IRQ if needed - // self.enable_dma_irq(); // implement if necessary + #[cfg(feature = "spi_dma_irq")] + { + self.dma_irq_enable(); + } + // Start DMA with write direction self.regs.fmc080().modify(|_, w| { w.dmaenbl().enable_dma_operation(); w.dmadirection() .write_flash_move_from_external_memory_to_flash() }); - self.wait_for_dma_completion(SPI_DMA_TIMEOUT) } + + fn activate_user(&mut self) { + let cs = self.current_cs; + let user_reg = self.spi_data.cmd_mode[cs].user; + cs_ctrlreg_w!(self, cs, user_reg | ASPEED_SPI_USER_INACTIVE); + cs_ctrlreg_w!(self, cs, user_reg); + dbg!(self, "activate cs:{}", u32::try_from(cs).unwrap()); + } + + fn deactivate_user(&mut self) { + let cs = self.current_cs; + let user_reg = self.spi_data.cmd_mode[cs].user; + + cs_ctrlreg_w!(self, cs, user_reg | ASPEED_SPI_USER_INACTIVE); + cs_ctrlreg_w!(self, cs, self.spi_data.cmd_mode[cs].normal_read); + dbg!(self, "deactivate cs:{}", u32::try_from(cs).unwrap()); + dbg!( + self, + "normal read:{:08x}", + self.spi_data.cmd_mode[cs].normal_read + ); + } } impl<'a> SpiBus for FmcController<'a> { // we only use mmap for all transaction fn read(&mut self, buffer: &mut [u8]) -> Result<(), SpiError> { let ahb_addr = self.spi_data.decode_addr[self.current_cs].start as usize as *const u32; + self.activate_user(); unsafe { spi_read_data(ahb_addr, buffer) }; + self.deactivate_user(); Ok(()) } fn write(&mut self, buffer: &[u8]) -> Result<(), SpiError> { let ahb_addr = self.spi_data.decode_addr[self.current_cs].start as usize as *mut u32; + self.activate_user(); unsafe { spi_write_data(ahb_addr, buffer) }; + self.deactivate_user(); Ok(()) } fn transfer(&mut self, rd_buffer: &mut [u8], wr_buffer: &[u8]) -> Result<(), SpiError> { let cs = self.current_cs; + self.activate_user(); if !wr_buffer.is_empty() { let ahb_addr = self.spi_data.decode_addr[cs].start as usize as *mut u32; unsafe { spi_write_data(ahb_addr, wr_buffer) }; @@ -780,57 +874,38 @@ impl<'a> SpiBus for FmcController<'a> { // Read RX buffer unsafe { super::spi_read_data(ahb_addr, rd_buffer) }; } + self.deactivate_user(); Ok(()) } fn transfer_in_place(&mut self, _buffer: &mut [u8]) -> Result<(), SpiError> { - todo!() + Err(SpiError::Other("transfer_in_place not supported")) } fn flush(&mut self) -> Result<(), SpiError> { - todo!() + Err(SpiError::Other("flush not supported")) } } impl<'a> SpiBusWithCs for FmcController<'a> { fn select_cs(&mut self, cs: usize) -> Result<(), SpiError> { - let user_reg = self.spi_data.cmd_mode[cs].user; - if cs > self.spi_config.max_cs { + if cs >= self.spi_config.max_cs { return Err(SpiError::CsSelectFailed(cs)); } self.current_cs = cs; - cs_ctrlreg_w!(self, cs, user_reg | ASPEED_SPI_USER_INACTIVE); - cs_ctrlreg_w!(self, cs, user_reg); - dbg!(self, "activate cs:{}", u32::try_from(cs).unwrap()); - Ok(()) - } - - fn deselect_cs(&mut self, cs: usize) -> Result<(), SpiError> { - let user_reg = self.spi_data.cmd_mode[cs].user; - if cs > self.spi_config.max_cs { - return Err(SpiError::CsSelectFailed(cs)); - } - cs_ctrlreg_w!(self, cs, user_reg | ASPEED_SPI_USER_INACTIVE); - cs_ctrlreg_w!(self, cs, self.spi_data.cmd_mode[cs].normal_read); - dbg!(self, "deactivate cs:{}", u32::try_from(cs).unwrap()); - dbg!( - self, - "normal read:{:08x}", - self.spi_data.cmd_mode[cs].normal_read - ); Ok(()) } - fn nor_transfer(&mut self, op_info: &mut SpiNorData) -> Result<(), SpiError> { + fn nor_transfer(&mut self, op_info: &mut SpiNorCommand) -> Result<(), SpiError> { let _ = self.spi_nor_transceive(op_info); Ok(()) } - fn nor_read_init(&mut self, cs: usize, op_info: &SpiNorData) { + fn nor_read_init(&mut self, cs: usize, op_info: &SpiNorCommand) { self.spi_nor_read_init(cs, op_info); } - fn nor_write_init(&mut self, cs: usize, op_info: &SpiNorData) { + fn nor_write_init(&mut self, cs: usize, op_info: &SpiNorCommand) { self.spi_nor_write_init(cs, op_info); } diff --git a/src/spi/mod.rs b/src/spi/mod.rs index c5730ce..6187b97 100644 --- a/src/spi/mod.rs +++ b/src/spi/mod.rs @@ -1,505 +1,31 @@ // Licensed under the Apache-2.0 license -use crate::{ - modify_reg, - spi::norflash::{Jesd216Mode, SpiNorData}, -}; -use ast1060_pac::Scu; -use embedded_hal::spi; -use embedded_hal::spi::ErrorType; -use embedded_hal::spi::SpiBus; -use embedded_io::Write; +//! Aspeed SPI HAL module pub mod device; pub mod fmccontroller; pub mod norflash; pub mod norflashblockdevice; pub mod spicontroller; +pub mod spidmairqtest; pub mod spitest; -#[derive(Debug)] +pub(crate) mod consts; -pub enum SpiError { - BusError, - DmaTimeout, - CsSelectFailed(usize), - LengthMismatch, - CapacityOutOfRange, - UnsupportedDevice(u8), - AddressNotAligned(u32), - InvalidCommand(u8), - Other(&'static str), -} +pub mod util; +pub mod error; +pub mod traits; +pub mod types; -/// Required by embedded-hal 1.0 -impl spi::Error for SpiError { - fn kind(&self) -> spi::ErrorKind { - match self { - SpiError::BusError - | SpiError::DmaTimeout - | SpiError::CsSelectFailed(_) - | SpiError::LengthMismatch - | SpiError::CapacityOutOfRange - | SpiError::UnsupportedDevice(_) - | SpiError::InvalidCommand(_) - | SpiError::AddressNotAligned(_) - | SpiError::Other(_) => spi::ErrorKind::Other, - } - } -} +pub mod spim; -pub trait SpiBusWithCs: SpiBus + ErrorType { - fn select_cs(&mut self, cs: usize) -> Result<(), SpiError>; - fn deselect_cs(&mut self, cs: usize) -> Result<(), SpiError>; - fn nor_transfer(&mut self, op_info: &mut SpiNorData) -> Result<(), SpiError>; - fn nor_read_init(&mut self, cs: usize, op_info: &SpiNorData); - fn nor_write_init(&mut self, cs: usize, op_info: &SpiNorData); +pub use error::SpiError; +pub use traits::SpiBusWithCs; +pub use types::{CommandMode, CtrlType, DataDirection, SpiConfig, SpiDecodeAddress, SpiData, + FlashAddress, AddressWidth}; +pub use norflash::{Jesd216Mode, SpiNorCommand, SpiNorDevice}; - fn get_device_info(&mut self, cs: usize) -> (u32, u32); - fn get_master_id(&mut self) -> u32; -} - -// Constants (unchanged) -const SPI_CONF_CE0_ENABLE_WRITE_SHIFT: u32 = 16; - -const SPI_CTRL_FREQ_MASK: u32 = 0x0F00_0F00; -const SPI_CTRL_CEX_SPI_CMD_SHIFT: u32 = 16; -const SPI_CTRL_CEX_SPI_CMD_MASK: u32 = 0xff; -const SPI_CTRL_CEX_DUMMY_SHIFT: u32 = 6; -const SPI_CTRL_CEX_4BYTE_MODE_SET: u32 = 0x11; // bit0: 4byte mode, bit4: 4byte mode cmd - -const SPI_DMA_DELAY_SHIFT: u32 = 8; -const SPI_DMA_DELAY_MASK: u32 = 0xff; -const SPI_DMA_CLK_FREQ_SHIFT: u32 = 16; -const SPI_DMA_CLK_FREQ_MASK: u32 = 0xf; - -const SPI_DMA_GET_REQ_MAGIC: u32 = 0xaeed_0000; -const SPI_DMA_DISCARD_REQ_MAGIC: u32 = 0xdeea_0000; -const SPI_DMA_RAM_MAP_BASE: u32 = 0x8000_0000; -const SPI_DMA_FLASH_MAP_BASE: u32 = 0x6000_0000; - -const SPI_CALIB_LEN: usize = 0x400; - -#[cfg(feature = "spi_dma")] -const SPI_DMA_TRIGGER_LEN: u32 = 128; -//const SPI_DMA_STS: u32 = 1 << 11; -//const SPI_DMA_IRQ_EN: u32 = 1 << 3; -#[cfg(feature = "spi_dma")] -const SPI_DMA_WRITE: u32 = 1 << 1; - -const SPI_DMA_REQUEST: u32 = 1 << 31; -const SPI_DMA_GRANT: u32 = 1 << 30; -const SPI_DMA_CALIB_MODE: u32 = 1 << 3; -const SPI_DMA_CALC_CKSUM: u32 = 1 << 2; - -const SPI_DMA_ENABLE: u32 = 1 << 0; -const SPI_DMA_STATUS: u32 = 1 << 11; - -const ASPEED_MAX_CS: usize = 5; // Must be usize for array indexing - -const ASPEED_SPI_NORMAL_READ: u32 = 0x1; -const ASPEED_SPI_NORMAL_WRITE: u32 = 0x2; -const ASPEED_SPI_USER: u32 = 0x3; -const ASPEED_SPI_USER_INACTIVE: u32 = 0x4; - -const ASPEED_SPI_SZ_2M: u32 = 0x0020_0000; -const ASPEED_SPI_SZ_256M: u32 = 0x1000_0000; - -const HPLL_FREQ: u32 = 1_000_000_000; -//const HCLK_DIV_SEL_MASK: u32 = 0b111 << 28; - -//const SPI_NOR_MAX_ID_LEN: u32 = 3; - -const SPI_DMA_TIMEOUT: u32 = 0x10000; -const SPI_NOR_DATA_DIRECT_READ: u32 = 0x0000_0001; -const SPI_NOR_DATA_DIRECT_WRITE: u32 = 0x0000_0002; - -#[derive(Clone, Copy)] -pub enum CtrlType { - BootSpi, - HostSpi, - NormalSpi, -} - -#[derive(Clone, Copy)] -pub struct CommandMode { - pub normal_read: u32, - pub normal_write: u32, - pub user: u32, -} - -#[derive(Default, Clone, Copy)] -pub struct SpiDecodeAddress { - pub start: u32, - pub len: u32, -} - -//Static spi controller configuration information -pub struct SpiConfig { - pub mmap_base: u32, - pub max_cs: usize, - pub write_block_size: u32, - pub ctrl_type: CtrlType, - pub timing_cali_start_off: u32, - pub master_idx: u32, - pub pure_spi_mode_only: bool, - pub frequency: u32, - pub timing_calibration_start_off: u32, - pub timing_calibration_disabled: bool, -} - -// Struct holding segment behavior as trait object -// controller state structure -pub struct SpiData { - pub decode_addr: [SpiDecodeAddress; ASPEED_MAX_CS], - pub cmd_mode: [CommandMode; ASPEED_MAX_CS], - pub hclk: u32, - pub spim_proprietary_pre_config: u32, -} - -impl Default for SpiData { - fn default() -> Self { - Self::new() - } -} - -impl SpiData { - #[must_use] - pub const fn new() -> Self { - const ZERO_ADDR: SpiDecodeAddress = SpiDecodeAddress { start: 0, len: 0 }; - const ZERO_CMD: CommandMode = CommandMode { - normal_read: 0, - normal_write: 0, - user: 0, - }; - - Self { - decode_addr: [ZERO_ADDR; ASPEED_MAX_CS], - cmd_mode: [ZERO_CMD; ASPEED_MAX_CS], - hclk: 0, - spim_proprietary_pre_config: 0, - } - } -} - -#[macro_export] -macro_rules! dbg { - ($self:expr, $($arg:tt)*) => {{ - if let Some(ref mut uart) = $self.dbg_uart { - writeln!(uart, $($arg)*).unwrap(); - write!(uart, "\r").unwrap(); - } - }}; -} - -#[inline] -fn hclk_div_reg_to_val(x: u32) -> u32 { - if x == 0 { - 2 - } else { - x + 1 - } -} - -#[must_use] -pub fn get_hclock_rate() -> u32 { - let scu_reg = unsafe { &*Scu::ptr() }; - let raw_div = scu_reg.scu314().read().hclkdivider_sel().bits(); - let clk_div = hclk_div_reg_to_val(u32::from(raw_div)); - - HPLL_FREQ / clk_div -} - -#[must_use] -pub fn spi_io_mode(mode: Jesd216Mode) -> u32 { - match mode { - //Jesd216Mode::Mode111 | Jesd216Mode::Mode111Fast => 0x0000_0000, - Jesd216Mode::Mode112 => 0x2000_0000, - Jesd216Mode::Mode122 => 0x3000_0000, - Jesd216Mode::Mode114 => 0x4000_0000, - Jesd216Mode::Mode144 => 0x5000_0000, - _ => 0, - } -} -#[must_use] -pub fn spi_io_mode_user(bus_width: u32) -> u32 { - match bus_width { - 4 => 0x4000_0000, - 2 => 0x2000_0000, - _ => 0x0000_0000, - } -} -#[must_use] -pub fn spi_cal_dummy_cycle(bus_width: u32, dummy_cycle: u32) -> u32 { - let dummy_byte = dummy_cycle / (8 / bus_width); - ((dummy_byte & 0x3) << 6) | (((dummy_byte & 0x4) >> 2) << 14) -} - -const fn get_cmd_buswidth(v: u32) -> u8 { - ((v & 0x0000_0F00) >> 8) as u8 -} -const fn get_addr_buswidth(v: u32) -> u8 { - ((v & 0x0000_00F0) >> 4) as u8 -} -const fn get_data_buswidth(v: u32) -> u8 { - (v & 0x0000_000F) as u8 -} - -/// Calculate the SPI frequency division setting based on bus clock and max frequency. -/// -/// # Arguments -/// * `bus_clk` - The bus clock frequency in Hz. -/// * `max_freq` - The maximum desired SPI frequency in Hz. -/// -/// # Returns -/// A 32-bit value encoding the frequency divider, -/// or 0 if no valid divider found. - -#[must_use] -pub fn aspeed_get_spi_freq_div(bus_clk: u32, max_freq: u32) -> u32 { - // Division mapping array matching C div_arr - let div_arr = [15, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 0]; - - for i in 0..0x0f { - for (j, div_val) in div_arr.iter().copied().enumerate() { - if i == 0 && j == 0 { - continue; - } - let divisor = j + 1 + (i * 16); - let freq = bus_clk / u32::try_from(divisor).unwrap(); - - if max_freq >= freq { - #[allow(clippy::cast_sign_loss)] - return ((i << 24) | ((div_val as u32) << 8) as usize) - .try_into() - .unwrap(); - } - } - } - // If not found, log error and return 0 (adjust logging as needed) - //log eprintln!("aspeed_get_spi_freq_div: cannot get correct frequency division."); - 0 -} - -/// Finds the midpoint of the longest consecutive sequence of 1's in a buffer. -/// -/// Returns the midpoint index if the longest run is at least length 4, -/// otherwise returns -1. -/// -/// # Arguments -/// * `buf` - slice of bytes (each should be 0 or 1). -#[must_use] -pub fn get_mid_point_of_longest_one(buf: &[u8]) -> i32 { - let mut start = 0; - let mut mid_point = 0; - let mut max_cnt = 0; - let mut cnt = 0; - - for (i, &val) in buf.iter().enumerate() { - if val == 1 { - cnt += 1; - } else { - cnt = 0; - start = i; - } - - if cnt > max_cnt { - max_cnt = cnt; - mid_point = start + (cnt / 2); - } - } - - if max_cnt < 4 { - -1 - } else { - i32::try_from(mid_point).unwrap() - } -} - -#[must_use] -pub fn spi_calibration_enable(buf: &[u8]) -> bool { - if buf.len() < 4 { - return false; - } - - let mut valid_count = 0; - - // Process 4 bytes at a time - for chunk in buf.chunks_exact(4) { - // Convert 4 bytes to u32 in little-endian order - let word = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); - - if word != 0 && word != 0xFFFF_FFFF { - valid_count += 1; - } - if valid_count > 100 { - return true; - } - } - - false -} - -#[allow(clippy::missing_safety_doc)] -pub unsafe fn spi_read_data(ahb_addr: *const u32, read_arr: &mut [u8]) { - let len = read_arr.len(); - let mut i = 0; - - // Read full u32 words - while i + 4 <= len { - let word = core::ptr::read_volatile(ahb_addr.add(i / 4)); - read_arr[i..i + 4].copy_from_slice(&word.to_le_bytes()); // adjust for BE if needed - i += 4; - } - - // Remaining bytes - while i < len { - read_arr[i] = core::ptr::read_volatile((ahb_addr.cast::()).add(i)); - i += 1; - } -} - -#[allow(clippy::missing_safety_doc)] -pub unsafe fn spi_write_data(ahb_addr: *mut u32, write_arr: &[u8]) { - if write_arr.is_empty() { - return; - } - - let len = write_arr.len(); - let mut i = 0; - - // Write in u32 words as long as possible - while i + 4 <= len { - let word = u32::from_le_bytes([ - write_arr[i], - write_arr[i + 1], - write_arr[i + 2], - write_arr[i + 3], - ]); - core::ptr::write_volatile(ahb_addr.add(i / 4), word); - i += 4; - } - - // Write remaining bytes (if any) - let ahb_addr_u8 = ahb_addr.cast::(); - while i < len { - core::ptr::write_volatile(ahb_addr_u8.add(i), write_arr[i]); - i += 1; - } -} -pub static mut GPIO_ORI_VAL: [u32; 4] = [0; 4]; -fn get_gpio_ori_val() -> [u32; 4] { - unsafe { GPIO_ORI_VAL } -} -const PIN_SPIM0_CLK_OUT_BIT: u32 = 7; -const PIN_SPIM1_CLK_OUT_BIT: u32 = 21; -const PIN_SPIM2_CLK_OUT_BIT: u32 = 3; -const PIN_SPIM3_CLK_OUT_BIT: u32 = 17; - -pub fn spim_proprietary_pre_config() { - let scu = unsafe { &*ast1060_pac::Scu::ptr() }; - let gpio = unsafe { &*ast1060_pac::Gpio::ptr() }; - - // If no SPIM in use, return - #[allow(clippy::verbose_bit_mask)] - if scu.scu0f0().read().bits() & 0x7 == 0 { - return; - } - - let spim_idx = (scu.scu0f0().read().bits() & 0x7) - 1; - if spim_idx > 3 { - return; - } - let clear = true; - for (idx, ori_val) in get_gpio_ori_val().iter_mut().enumerate() { - if u32::try_from(idx).unwrap() == spim_idx { - continue; - } - - match idx { - 0 => { - modify_reg!(scu.scu690(), PIN_SPIM0_CLK_OUT_BIT, clear); - *ori_val = gpio.gpio004().read().bits(); - modify_reg!(gpio.gpio004(), PIN_SPIM0_CLK_OUT_BIT, clear); - } - 1 => { - modify_reg!(scu.scu690(), PIN_SPIM1_CLK_OUT_BIT, clear); - *ori_val = gpio.gpio004().read().bits(); - modify_reg!(gpio.gpio004(), PIN_SPIM1_CLK_OUT_BIT, clear); - } - 2 => { - modify_reg!(scu.scu694(), PIN_SPIM2_CLK_OUT_BIT, clear); - *ori_val = gpio.gpio024().read().bits(); - modify_reg!(gpio.gpio024(), PIN_SPIM2_CLK_OUT_BIT, clear); - } - 3 => { - modify_reg!(scu.scu694(), PIN_SPIM3_CLK_OUT_BIT, clear); - *ori_val = gpio.gpio024().read().bits(); - modify_reg!(gpio.gpio024(), PIN_SPIM3_CLK_OUT_BIT, clear); - } - _ => (), - } - } -} - -pub fn spim_proprietary_post_config() { - let scu = unsafe { &*ast1060_pac::Scu::ptr() }; - let gpio = unsafe { &*ast1060_pac::Gpio::ptr() }; - - // If no SPIM in use, return - let bits = scu.scu0f0().read().bits(); - if bits.trailing_zeros() >= 3 { - return; - } - - let spim_idx = (scu.scu0f0().read().bits() & 0x7) - 1; - if spim_idx > 3 { - return; - } - let clear = false; - for (idx, ori_val) in get_gpio_ori_val().iter().copied().enumerate() { - if u32::try_from(idx).unwrap() == spim_idx { - continue; - } - - match idx { - 0 => { - gpio.gpio004().modify(|r, w| unsafe { - let mut current = r.bits(); - current &= !(1 << PIN_SPIM0_CLK_OUT_BIT); - current |= ori_val; - w.bits(current) - }); - modify_reg!(scu.scu690(), PIN_SPIM0_CLK_OUT_BIT, clear); - } - 1 => { - gpio.gpio004().modify(|r, w| unsafe { - let mut current = r.bits(); - current &= !(1 << PIN_SPIM1_CLK_OUT_BIT); - current |= ori_val; - w.bits(current) - }); - modify_reg!(gpio.gpio004(), PIN_SPIM1_CLK_OUT_BIT, clear); - } - 2 => { - gpio.gpio024().modify(|r, w| unsafe { - let mut current = r.bits(); - current &= !(1 << PIN_SPIM2_CLK_OUT_BIT); - current |= ori_val; - w.bits(current) - }); - modify_reg!(scu.scu694(), PIN_SPIM2_CLK_OUT_BIT, clear); - } - 3 => { - gpio.gpio024().modify(|r, w| unsafe { - let mut current = r.bits(); - current &= !(1 << PIN_SPIM3_CLK_OUT_BIT); - current |= ori_val; - w.bits(current) - }); - modify_reg!(scu.scu694(), PIN_SPIM3_CLK_OUT_BIT, clear); - } - _ => (), - } - } -} +pub use util::{spi_read_data, spi_write_data, aspeed_get_spi_freq_div, get_mid_point_of_longest_one, + spi_cal_dummy_cycle, spi_calibration_enable, get_hclock_rate, spi_io_mode, spi_io_mode_user, + get_addr_buswidth, get_cmd_buswidth, get_data_buswidth}; +pub use spim::{spim_proprietary_post_config, spim_proprietary_pre_config, spim_scu_ctrl_clear, spim_scu_ctrl_set}; diff --git a/src/spi/norflash.rs b/src/spi/norflash.rs index f953d0c..445c177 100644 --- a/src/spi/norflash.rs +++ b/src/spi/norflash.rs @@ -2,9 +2,8 @@ use super::device::ChipSelectDevice; use super::SpiBusWithCs; -use super::{norflash, SpiError, SPI_NOR_DATA_DIRECT_READ, SPI_NOR_DATA_DIRECT_WRITE}; +use super::{norflash, SpiError, DataDirection, FlashAddress, AddressWidth}; use crate::common::DummyDelay; -use crate::spimonitor::SpipfInstance; use embedded_hal::delay::DelayNs; /* Flash opcodes */ @@ -89,22 +88,21 @@ pub enum Jesd216Mode { Unknown = 0xFFF_FFFF, } -pub struct SpiNorData<'a> { +pub struct SpiNorCommand<'a> { pub mode: Jesd216Mode, pub opcode: u32, pub dummy_cycle: u32, - pub addr_len: u32, - pub addr: u32, + pub address: FlashAddress, pub data_len: u32, pub tx_buf: &'a [u8], pub rx_buf: &'a mut [u8], - pub data_direct: u32, + pub data_direct: DataDirection, } pub trait SpiNorDevice { type Error; - fn nor_read_init(&mut self, data: &SpiNorData) -> Result<(), Self::Error>; - fn nor_write_init(&mut self, data: &SpiNorData) -> Result<(), Self::Error>; + fn nor_read_init(&mut self, data: &SpiNorCommand) -> Result<(), Self::Error>; + fn nor_write_init(&mut self, data: &SpiNorCommand) -> Result<(), Self::Error>; fn nor_write_enable(&mut self) -> Result<(), Self::Error>; fn nor_write_disable(&mut self) -> Result<(), Self::Error>; fn nor_read_jedec_id(&mut self) -> Result<[u8; 3], Self::Error>; @@ -124,45 +122,42 @@ macro_rules! start_transfer { let _ = (|| -> Result<(), SpiError> { $this.bus.select_cs($this.cs)?; // SPIM config - if let Some(spim) = $this.spi_monitor.as_mut() { + if let Some(spim) = $this.spim { if $this.bus.get_master_id() != 0 { - spim.spim_scu_ctrl_set(0x8, 0x8); - spim.spim_scu_ctrl_set(0x7, 1 + SPIPF::FILTER_ID as u32); + super::spim_scu_ctrl_set(0x8, 0x8); + super::spim_scu_ctrl_set(0x7, 1 + u32::from(spim)); } super::spim_proprietary_pre_config(); } - $this.bus.nor_transfer($data)?; - $this.bus.deselect_cs($this.cs)?; //SPIM deconfig - super::spim_proprietary_post_config(); - if let Some(spim) = $this.spi_monitor.as_mut() { + if let Some(_spim) = $this.spim { + super::spim_proprietary_post_config(); if $this.bus.get_master_id() != 0 { - spim.spim_scu_ctrl_clear(0xf); + super::spim_scu_ctrl_clear(0xf); } } + Ok(()) })(); }}; } //TODO: add 4byte address mode support -impl<'a, B, SPIPF> SpiNorDevice for ChipSelectDevice<'a, B, SPIPF> +impl<'a, B> SpiNorDevice for ChipSelectDevice<'a, B> where B: SpiBusWithCs, - SPIPF: SpipfInstance, { type Error = B::Error; fn nor_write_enable(&mut self) -> Result<(), Self::Error> { - let mut nor_data = SpiNorData { + let mut nor_data = SpiNorCommand { mode: Jesd216Mode::Mode111, opcode: SPI_NOR_CMD_WREN, dummy_cycle: 0, - addr: 0, - addr_len: 0, + address: FlashAddress { value: 0, width: AddressWidth::ThreeByte }, data_len: 0, - data_direct: SPI_NOR_DATA_DIRECT_WRITE, + data_direct: DataDirection::Write, tx_buf: &[], rx_buf: &mut [], }; @@ -171,14 +166,13 @@ where } fn nor_write_disable(&mut self) -> Result<(), Self::Error> { - let mut nor_data = SpiNorData { + let mut nor_data = SpiNorCommand { mode: Jesd216Mode::Mode111, opcode: SPI_NOR_CMD_WRDI, dummy_cycle: 0, - addr: 0, - addr_len: 0, + address: FlashAddress { value: 0, width: AddressWidth::ThreeByte }, data_len: 0, - data_direct: SPI_NOR_DATA_DIRECT_WRITE, + data_direct: DataDirection::Write, tx_buf: &[], rx_buf: &mut [], }; @@ -188,16 +182,15 @@ where fn nor_read_jedec_id(&mut self) -> Result<[u8; 3], Self::Error> { let mut read_buf: [u8; 3] = [0, 0, 0]; - let mut nor_data = SpiNorData { + let mut nor_data = SpiNorCommand { mode: Jesd216Mode::Mode111, opcode: 0x9F, dummy_cycle: 0, - addr: 0, - addr_len: 0, + address: FlashAddress { value: 0, width: AddressWidth::ThreeByte }, data_len: 0, rx_buf: &mut read_buf, tx_buf: &[], - data_direct: SPI_NOR_DATA_DIRECT_READ, + data_direct: DataDirection::Read, }; start_transfer!(self, &mut nor_data); Ok([read_buf[0], read_buf[1], read_buf[2]]) @@ -206,16 +199,15 @@ where fn nor_sector_erase(&mut self, address: u32) -> Result<(), Self::Error> { self.nor_write_enable()?; if self.nor_sector_aligned(address) { - let mut nor_data = SpiNorData { + let mut nor_data = SpiNorCommand { mode: Jesd216Mode::Mode111, opcode: norflash::SPI_NOR_CMD_SE, dummy_cycle: 0, - addr: address, - addr_len: 3, + address: FlashAddress { value: address, width: AddressWidth::ThreeByte }, data_len: 0, tx_buf: &[], rx_buf: &mut [], - data_direct: SPI_NOR_DATA_DIRECT_WRITE, + data_direct: DataDirection::Write, }; start_transfer!(self, &mut nor_data); self.nor_wait_until_ready(); @@ -227,68 +219,61 @@ where fn nor_page_program(&mut self, address: u32, data: &[u8]) -> Result<(), Self::Error> { self.nor_write_enable()?; - let mut nor_data = SpiNorData { + let mut nor_data = SpiNorCommand { mode: Jesd216Mode::Mode111, opcode: norflash::SPI_NOR_CMD_PP, dummy_cycle: 0, - addr: address, - addr_len: 3, + address: FlashAddress { value: address, width: AddressWidth::ThreeByte }, data_len: u32::try_from(data.len()).unwrap(), tx_buf: data, rx_buf: &mut [], - data_direct: SPI_NOR_DATA_DIRECT_WRITE, + data_direct: DataDirection::Write, }; start_transfer!(self, &mut nor_data); - self.nor_wait_until_ready(); Ok(()) } fn nor_page_program_4b(&mut self, address: u32, data: &[u8]) -> Result<(), Self::Error> { self.nor_write_enable()?; - let mut nor_data = SpiNorData { + let mut nor_data = SpiNorCommand { mode: Jesd216Mode::Mode111, opcode: norflash::SPI_NOR_CMD_PP_4B, dummy_cycle: 0, - addr: address, - addr_len: 4, + address: FlashAddress { value: address, width: AddressWidth::FourByte }, data_len: u32::try_from(data.len()).unwrap(), tx_buf: data, rx_buf: &mut [], - data_direct: SPI_NOR_DATA_DIRECT_WRITE, + data_direct: DataDirection::Write, }; start_transfer!(self, &mut nor_data); - self.nor_wait_until_ready(); Ok(()) } fn nor_read_data(&mut self, address: u32, buf: &mut [u8]) -> Result<(), Self::Error> { - let mut nor_data = SpiNorData { + let mut nor_data = SpiNorCommand { mode: Jesd216Mode::Mode114, opcode: SPI_NOR_CMD_QREAD, dummy_cycle: 8, - addr: address, - addr_len: 3, - data_len: u32::try_from(buf.len()).unwrap(), // it is not in used. + address: FlashAddress { value: address, width: AddressWidth::ThreeByte }, + data_len: u32::try_from(buf.len()).unwrap(), tx_buf: &[], rx_buf: buf, - data_direct: SPI_NOR_DATA_DIRECT_READ, + data_direct: DataDirection::Read, }; start_transfer!(self, &mut nor_data); - Ok(()) } fn nor_read_fast_4b_data(&mut self, address: u32, buf: &mut [u8]) -> Result<(), Self::Error> { - let mut nor_data = SpiNorData { + let mut nor_data = SpiNorCommand { mode: Jesd216Mode::Mode111Fast, opcode: SPI_NOR_CMD_READ_FAST_4B, dummy_cycle: 8, - addr: address, - addr_len: 4, - data_len: u32::try_from(buf.len()).unwrap(), // it is not in used. + address: FlashAddress { value: address, width: AddressWidth::FourByte }, + data_len: u32::try_from(buf.len()).unwrap(), tx_buf: &[], rx_buf: buf, - data_direct: SPI_NOR_DATA_DIRECT_READ, + data_direct: DataDirection::Read, }; start_transfer!(self, &mut nor_data); @@ -296,60 +281,57 @@ where } fn nor_reset_enable(&mut self) -> Result<(), Self::Error> { - let mut nor_data = SpiNorData { + let mut nor_data = SpiNorCommand { mode: Jesd216Mode::Mode111, opcode: SPI_NOR_CMD_RESET_EN, dummy_cycle: 0, - addr: 0x0, - addr_len: 0x0, - data_len: 0x0, // it is not in used. + address: FlashAddress { value: 0x0, width: AddressWidth::ThreeByte }, + data_len: 0, tx_buf: &[], rx_buf: &mut [], - data_direct: SPI_NOR_DATA_DIRECT_WRITE, + data_direct: DataDirection::Write, }; start_transfer!(self, &mut nor_data); - Ok(()) } fn nor_reset(&mut self) -> Result<(), Self::Error> { - let mut nor_data = SpiNorData { + let mut nor_data = SpiNorCommand { mode: Jesd216Mode::Mode111, opcode: SPI_NOR_CMD_RESET_MEM, dummy_cycle: 0, - addr: 0x0, - addr_len: 0x0, - data_len: 0x0, // it is not in used. + address: FlashAddress { value: 0x0, width: AddressWidth::ThreeByte }, + data_len: 0x0, tx_buf: &[], rx_buf: &mut [], - data_direct: SPI_NOR_DATA_DIRECT_WRITE, + data_direct: DataDirection::Write, }; start_transfer!(self, &mut nor_data); Ok(()) } - fn nor_read_init(&mut self, nor_data: &SpiNorData) -> Result<(), Self::Error> { - if let Some(spim) = self.spi_monitor.as_mut() { + fn nor_read_init(&mut self, nor_data: &SpiNorCommand) -> Result<(), Self::Error> { + if let Some(spim) = self.spim { if self.bus.get_master_id() != 0 { - spim.spim_scu_ctrl_set(0x8, 0x8); - spim.spim_scu_ctrl_set(0x7, 1 + SPIPF::FILTER_ID as u32); + super::spim_scu_ctrl_set(0x8, 0x8); + super::spim_scu_ctrl_set(0x7, 1 + u32::from(spim)); } super::spim_proprietary_pre_config(); } self.bus.nor_read_init(self.cs, nor_data); - super::spim_proprietary_post_config(); - if let Some(spim) = self.spi_monitor.as_mut() { + if let Some(_spim) = self.spim { + super::spim_proprietary_post_config(); if self.bus.get_master_id() != 0 { - spim.spim_scu_ctrl_clear(0xf); + super::spim_scu_ctrl_clear(0xf); } } Ok(()) } - fn nor_write_init(&mut self, nor_data: &SpiNorData) -> Result<(), Self::Error> { + fn nor_write_init(&mut self, nor_data: &SpiNorCommand) -> Result<(), Self::Error> { self.bus.nor_write_init(self.cs, nor_data); Ok(()) } @@ -365,16 +347,15 @@ where let mut delay = DummyDelay {}; let mut buf: [u8; 1] = [0u8]; - let mut nor_data = SpiNorData { + let mut nor_data = SpiNorCommand { mode: Jesd216Mode::Mode111, opcode: SPI_NOR_CMD_RDSR, dummy_cycle: 0, - addr: 0, - addr_len: 0, - data_len: 1, // it is not in used. + address: FlashAddress { value: 0, width: AddressWidth::ThreeByte }, + data_len: 1, tx_buf: &[], rx_buf: &mut buf, - data_direct: SPI_NOR_DATA_DIRECT_READ, + data_direct: DataDirection::Read, }; loop { start_transfer!(self, &mut nor_data); diff --git a/src/spi/norflashblockdevice.rs b/src/spi/norflashblockdevice.rs index dd58266..e429fc0 100644 --- a/src/spi/norflashblockdevice.rs +++ b/src/spi/norflashblockdevice.rs @@ -172,12 +172,12 @@ where self.device .nor_page_program(u32::try_from(write_addr).unwrap(), chunk) }; - + self.device.nor_wait_until_ready(); + delay.delay_ns(2_000_000); if result.is_err() { return Err(BlockError::ProgramError); } offset += program_block; - delay.delay_ns(2_000_000); } Ok(()) diff --git a/src/spi/spicontroller.rs b/src/spi/spicontroller.rs index 2883040..230ed43 100644 --- a/src/spi/spicontroller.rs +++ b/src/spi/spicontroller.rs @@ -3,24 +3,25 @@ use super::{ aspeed_get_spi_freq_div, get_addr_buswidth, get_hclock_rate, get_mid_point_of_longest_one, spi_cal_dummy_cycle, spi_calibration_enable, spi_io_mode, spi_io_mode_user, spi_read_data, - spi_write_data, CtrlType, SpiBusWithCs, SpiConfig, SpiData, SpiError, Write, ASPEED_MAX_CS, - ASPEED_SPI_NORMAL_READ, ASPEED_SPI_NORMAL_WRITE, ASPEED_SPI_SZ_256M, ASPEED_SPI_SZ_2M, - ASPEED_SPI_USER, ASPEED_SPI_USER_INACTIVE, SPI_CALIB_LEN, SPI_CTRL_FREQ_MASK, - SPI_DMA_CALC_CKSUM, SPI_DMA_CALIB_MODE, SPI_DMA_DISCARD_REQ_MAGIC, SPI_DMA_ENABLE, - SPI_DMA_FLASH_MAP_BASE, SPI_DMA_GET_REQ_MAGIC, SPI_DMA_GRANT, SPI_DMA_RAM_MAP_BASE, - SPI_DMA_REQUEST, SPI_DMA_STATUS, SPI_DMA_TIMEOUT, + spi_write_data, CtrlType, SpiBusWithCs, SpiConfig, SpiData, SpiError, AddressWidth, }; +use super::consts::{ASPEED_MAX_CS,ASPEED_SPI_NORMAL_READ, ASPEED_SPI_NORMAL_WRITE, ASPEED_SPI_SZ_256M, + ASPEED_SPI_SZ_2M, ASPEED_SPI_USER, ASPEED_SPI_USER_INACTIVE, SPI_CALIB_LEN, + SPI_CTRL_FREQ_MASK, SPI_DMA_CALC_CKSUM, SPI_DMA_CALIB_MODE, SPI_DMA_DISCARD_REQ_MAGIC, + SPI_DMA_ENABLE, SPI_DMA_FLASH_MAP_BASE, SPI_DMA_GET_REQ_MAGIC, SPI_DMA_GRANT, + SPI_DMA_RAM_MAP_BASE, SPI_DMA_REQUEST, SPI_DMA_STATUS, SPI_DMA_TIMEOUT, + SPI_CTRL_CEX_4BYTE_MODE_SET, SPI_CTRL_CEX_DUMMY_SHIFT, SPI_CTRL_CEX_SPI_CMD_MASK, + SPI_CTRL_CEX_SPI_CMD_SHIFT, SPI_DMA_CLK_FREQ_SHIFT, SPI_DMA_CLK_FREQ_MASK, SPI_DMA_DELAY_MASK, + SPI_DMA_DELAY_SHIFT, SPI_CONF_CE0_ENABLE_WRITE_SHIFT}; + +use embedded_io::Write; + #[cfg(feature = "spi_dma")] -use super::{SPI_DMA_TRIGGER_LEN, SPI_NOR_DATA_DIRECT_READ, SPI_NOR_DATA_DIRECT_WRITE}; +use super::consts::{SPI_DMA_TRIGGER_LEN}; use crate::dbg; -use crate::spi::{ - SPI_CONF_CE0_ENABLE_WRITE_SHIFT, SPI_CTRL_CEX_4BYTE_MODE_SET, SPI_CTRL_CEX_DUMMY_SHIFT, - SPI_CTRL_CEX_SPI_CMD_MASK, SPI_CTRL_CEX_SPI_CMD_SHIFT, SPI_DMA_CLK_FREQ_MASK, - SPI_DMA_CLK_FREQ_SHIFT, SPI_DMA_DELAY_MASK, SPI_DMA_DELAY_SHIFT, -}; -use crate::{common::DummyDelay, spi::norflash::SpiNorData, uart::UartController}; +use crate::{common::DummyDelay, spi::norflash::SpiNorCommand, uart::UartController}; use embedded_hal::{ delay::DelayNs, @@ -230,7 +231,7 @@ impl<'a> SpiController<'a> { } } - fn spi_nor_read_init(&mut self, cs: usize, op_info: &SpiNorData) { + fn spi_nor_read_init(&mut self, cs: usize, op_info: &SpiNorCommand) { dbg!( self, "spi_nor_read_init() cs:{} master_idx: {}", @@ -262,7 +263,7 @@ impl<'a> SpiController<'a> { ); cs_ctrlreg_w!(self, cs, read_cmd); - if op_info.addr_len == 4 { + if matches!(op_info.address.width, AddressWidth::FourByte) { self.regs.spi004().modify(|r, w| unsafe { let current = r.bits(); w.bits(current | (SPI_CTRL_CEX_4BYTE_MODE_SET << cs)) @@ -271,7 +272,7 @@ impl<'a> SpiController<'a> { if matches!(self.spi_config.ctrl_type, CtrlType::HostSpi) { self.regs.spi06c().modify(|r, w| unsafe { let mut current = r.bits(); - if op_info.addr_len == 4 { + if matches!(op_info.address.width, AddressWidth::FourByte) { current = (current & 0xffff_00ff) | (op_info.opcode << 8); } else { current = (current & 0xffff_ff00) | op_info.opcode; @@ -283,7 +284,7 @@ impl<'a> SpiController<'a> { self.timing_calibration(cs); } - fn spi_nor_write_init(&mut self, cs: usize, op_info: &SpiNorData) { + fn spi_nor_write_init(&mut self, cs: usize, op_info: &SpiNorCommand) { let io_mode = spi_io_mode(op_info.mode); let dummy = 0; let write_cmd = (io_mode @@ -301,7 +302,7 @@ impl<'a> SpiController<'a> { self.regs.spi074().modify(|r, w| unsafe { let mut current = r.bits(); - if op_info.addr_len == 4 { + if matches!(op_info.address.width, AddressWidth::FourByte) { current = (current & 0xffff_00ff) | (op_info.opcode << 8); } else { current = (current & 0xffff_ff00) | op_info.opcode; @@ -510,7 +511,7 @@ impl<'a> SpiController<'a> { checksum } - fn spi_nor_transceive_user(&mut self, op_info: &mut SpiNorData) { + fn spi_nor_transceive_user(&mut self, op_info: &mut SpiNorCommand) { let cs: usize = self.current_cs; let dummy = [0u8; 12]; let start_ptr = self.spi_data.decode_addr[cs].start as *mut u32; @@ -520,7 +521,7 @@ impl<'a> SpiController<'a> { u32::try_from(cs).unwrap(), self.spi_data.decode_addr[cs].start ); - + self.activate_user(); // Send command let cmd_mode = self.spi_data.cmd_mode[cs].user | super::spi_io_mode_user(u32::from(super::get_cmd_buswidth(op_info.mode as u32))); @@ -532,7 +533,7 @@ impl<'a> SpiController<'a> { let addr_mode = self.spi_data.cmd_mode[cs].user | super::spi_io_mode_user(u32::from(super::get_addr_buswidth(op_info.mode as u32))); cs_ctrlreg_w!(self, cs, addr_mode); - + /* todo: let mut addr = op_info.addr; if op_info.addr_len == 3 { addr <<= 8; @@ -540,7 +541,19 @@ impl<'a> SpiController<'a> { //op_info.addr = sys_cpu_to_be32(op_info.addr); let addr_bytes = addr.to_be_bytes(); unsafe { super::spi_write_data(start_ptr, &addr_bytes[..op_info.addr_len as usize]) }; + */ + + let be = op_info.address.value.to_be_bytes(); + let bytes = match op_info.address.width { + AddressWidth::ThreeByte => &be[1..], // last 3 bytes (24-bit) + AddressWidth::FourByte => &be[..], // all 4 bytes + }; + + unsafe { + super::spi_write_data(start_ptr, bytes); + } + // Dummy cycles let bus_width: u8 = super::get_addr_buswidth(op_info.mode as u32); let dummy_len: u8 = (op_info.dummy_cycle / (8 / u32::from(bus_width))) @@ -554,16 +567,20 @@ impl<'a> SpiController<'a> { | spi_io_mode_user(u32::from(super::get_data_buswidth(op_info.mode as u32))); cs_ctrlreg_w!(self, cs, data_mode); - if op_info.data_direct == super::SPI_NOR_DATA_DIRECT_READ { - unsafe { spi_read_data(start_ptr, op_info.rx_buf) }; - } else { - unsafe { spi_write_data(start_ptr, op_info.tx_buf) }; + match op_info.data_direct { + super::DataDirection::Read => { + unsafe { spi_read_data(start_ptr, op_info.rx_buf) }; + } + super::DataDirection::Write => { + unsafe { spi_write_data(start_ptr, op_info.tx_buf) }; + } } + self.deactivate_user(); } // Helper wrappers would be defined for spi_write_data, spi_read_data, io_mode_user, etc. - pub fn spi_nor_transceive(&mut self, op_info: &mut SpiNorData) -> Result<(), SpiError> { + pub fn spi_nor_transceive(&mut self, op_info: &mut SpiNorCommand) -> Result<(), SpiError> { dbg!(self, "spi_nor_transceive()..."); #[cfg(feature = "spi_dma")] @@ -576,55 +593,58 @@ impl<'a> SpiController<'a> { op_info.tx_buf.as_ptr(), op_info.tx_buf.len() ); - let addr_aligned = op_info.addr % 4 == 0; - - if op_info.data_direct == SPI_NOR_DATA_DIRECT_READ { - let buf_aligned = (op_info.rx_buf.as_ptr() as usize) % 4 == 0; - let use_dma = !self.spi_config.pure_spi_mode_only - && op_info.rx_buf.len() > SPI_DMA_TRIGGER_LEN as usize - && addr_aligned - && buf_aligned; - dbg!(self, "read dma"); - dbg!( - self, - "use_dma{} rx len: {}, addr_aligned: {}, buf_aligned: {}", - use_dma, - op_info.rx_buf.len(), - addr_aligned, - buf_aligned - ); - if use_dma { - return self.read_dma(op_info); - } else { - self.spi_nor_transceive_user(op_info); - } - } else if op_info.data_direct == SPI_NOR_DATA_DIRECT_WRITE { - dbg!(self, "write dma"); + let addr_aligned = op_info.address.value % 4 == 0; - #[cfg(feature = "spi_dma_write")] - { - let buf_aligned = (op_info.tx_buf.as_ptr() as usize) % 4 == 0; + match op_info.data_direct { + super::DataDirection::Read =>{ + let buf_aligned = (op_info.rx_buf.as_ptr() as usize) % 4 == 0; let use_dma = !self.spi_config.pure_spi_mode_only - && op_info.tx_buf.len() > SPI_DMA_TRIGGER_LEN as usize + && op_info.rx_buf.len() > SPI_DMA_TRIGGER_LEN as usize && addr_aligned && buf_aligned; + dbg!(self, "read dma"); dbg!( self, - "use_dma{} tx len: {}, addr_aligned: {}, buf_aligned: {}", + "use_dma{} rx len: {}, addr_aligned: {}, buf_aligned: {}", use_dma, op_info.rx_buf.len(), addr_aligned, buf_aligned ); if use_dma { - return self.write_dma(op_info); + return self.read_dma(op_info); } else { self.spi_nor_transceive_user(op_info); } - } //spi dma write - #[cfg(not(feature = "spi_dma_write"))] - self.spi_nor_transceive_user(op_info); - } //write + } + super::DataDirection::Write => { + dbg!(self, "write dma"); + + #[cfg(feature = "spi_dma_write")] + { + let buf_aligned = (op_info.tx_buf.as_ptr() as usize) % 4 == 0; + let use_dma = !self.spi_config.pure_spi_mode_only + && op_info.tx_buf.len() > SPI_DMA_TRIGGER_LEN as usize + && addr_aligned + && buf_aligned; + dbg!( + self, + "use_dma{} tx len: {}, addr_aligned: {}, buf_aligned: {}", + use_dma, + op_info.rx_buf.len(), + addr_aligned, + buf_aligned + ); + if use_dma { + return self.write_dma(op_info); + } else { + self.spi_nor_transceive_user(op_info); + } + } //spi dma write + #[cfg(not(feature = "spi_dma_write"))] + self.spi_nor_transceive_user(op_info); + } //write + } Ok(()) } // dma @@ -648,38 +668,64 @@ impl<'a> SpiController<'a> { let mut delay = DummyDelay {}; let mut to = timeout; //wait for_dma done - while !self.regs.spi008().read().dmastatus().is_dma_finish() { - delay.delay_ns(500); - to -= 1; + #[cfg(not(feature = "spi_dma_irq"))] + { + while !self.regs.spi008().read().dmastatus().is_dma_finish() { + delay.delay_ns(500); + to -= 1; - if to == 0 { - self.dma_disable(); - return Err(SpiError::DmaTimeout); + if to == 0 { + self.dma_disable(); + return Err(SpiError::DmaTimeout); + } } + self.dma_disable(); } - self.dma_disable(); Ok(()) } - /* + fn dma_irq_disable(&mut self) { // Enable the DMA interrupt bit (bit 3) self.regs.spi008().modify(|_, w| w.dmaintenbl().clear_bit()); } - + #[allow(dead_code)] fn dma_irq_enable(&mut self) { // Enable the DMA interrupt bit (bit 3) self.regs.spi008().modify(|_, w| w.dmaintenbl().set_bit()); } - */ - pub fn read_dma(&mut self, op: &mut SpiNorData) -> Result<(), SpiError> { + #[allow(dead_code)] + fn dbg_spi_dma(&mut self) { + dbg!(self, "reg 0x80: {:08x}", self.regs.spi080().read().bits()); + dbg!(self, "reg 0x84: {:08x}", self.regs.spi084().read().bits()); + dbg!(self, "reg 0x88: {:08x}", self.regs.spi088().read().bits()); + dbg!(self, "reg 0x8c: {:08x}", self.regs.spi08c().read().bits()); + } + pub fn handle_interrupt(&mut self) -> Result<(), SpiError> { + dbg!(self, "spi handle_interrupt"); + if !self.regs.spi008().read().dmastatus().is_dma_finish() { + return Err(SpiError::Other("dma not finished")); + } + /* disable IRQ */ + self.dma_irq_disable(); + + /* disable DMA */ + self.dma_disable(); + + let cs = self.current_cs; + cs_ctrlreg_w!(self, cs, self.spi_data.cmd_mode[cs].normal_read); + Ok(()) + //spi_context_complete(ctx, dev, 0); + } + + pub fn read_dma(&mut self, op: &mut SpiNorCommand) -> Result<(), SpiError> { let cs = self.current_cs; - dbg!(self, "##### read dma ####"); + dbg!(self, "#####spi read dma ####"); dbg!(self, "device size: 0x{:08x} dv start: 0x{:08x}, read len: 0x{:08x}, rx_buf:0x{:08x} op addr: 0x{:08x}", self.spi_data.decode_addr[cs].len, self.spi_data.decode_addr[cs].start, op.rx_buf.len(), (op.rx_buf.as_ptr() as u32), - op.addr); + op.address.value); // Length check if op.rx_buf.len() > self.spi_data.decode_addr[cs].len.try_into().unwrap() { @@ -687,8 +733,8 @@ impl<'a> SpiController<'a> { } // Alignment check - if (op.addr % 4 != 0) || ((op.rx_buf.as_ptr() as u32) % 4 != 0) { - return Err(SpiError::AddressNotAligned(op.addr)); + if (op.address.value % 4 != 0) || ((op.rx_buf.as_ptr() as u32) % 4 != 0) { + return Err(SpiError::AddressNotAligned(op.address.value)); } dbg!(self, "set ctrl "); @@ -705,7 +751,6 @@ impl<'a> SpiController<'a> { // Write to CSx control cs_ctrlreg_w!(self, cs, ctrl); - self.regs .spi080() .write(|w| unsafe { w.bits(SPI_DMA_GET_REQ_MAGIC) }); @@ -714,7 +759,7 @@ impl<'a> SpiController<'a> { while self.regs.spi080().read().bits() & SPI_DMA_GRANT != SPI_DMA_GRANT {} } - let flash_start = self.spi_data.decode_addr[cs].start + op.addr - SPI_DMA_FLASH_MAP_BASE; + let flash_start = self.spi_data.decode_addr[cs].start + op.address.value - SPI_DMA_FLASH_MAP_BASE; dbg!(self, "flash start: 0x{:08x}", flash_start); // DMA flash and RAM address @@ -732,28 +777,35 @@ impl<'a> SpiController<'a> { .write(|w| unsafe { w.bits(u32::try_from(read_length).unwrap()) }); // Enable IRQ - //self.dma_irq_enable(); + #[cfg(feature = "spi_dma_irq")] + { + self.dma_irq_enable(); + } // Start DMA - // self.regs.spi080().write(|w| unsafe { w.bits(SPI_DMA_ENABLE) }); self.regs.spi080().modify(|_, w| { w.dmaenbl().enable_dma_operation(); w.dmadirection() .read_flash_move_from_flash_to_external_memory() }); - dbg!(self, "start wait for dma"); self.wait_for_dma_completion(SPI_DMA_TIMEOUT) } #[allow(dead_code)] - fn write_dma(&mut self, op: &mut SpiNorData) -> Result<(), SpiError> { + fn write_dma(&mut self, op: &mut SpiNorCommand) -> Result<(), SpiError> { let cs = self.current_cs; - dbg!(self, "##### write_dma ####"); - + dbg!(self, "#####spi write_dma ####"); + dbg!(self, "device size: 0x{:08x} dv start: 0x{:08x}, read len: 0x{:08x}, tx_buf:0x{:08x} op addr: 0x{:08x}", + self.spi_data.decode_addr[cs].len, + self.spi_data.decode_addr[cs].start, + op.tx_buf.len(), + (op.tx_buf.as_ptr() as u32), + op.address.value); + // Check alignment and bounds - if op.addr % 4 != 0 || (op.tx_buf.as_ptr() as usize) % 4 != 0 { - return Err(SpiError::AddressNotAligned(op.addr)); + if op.address.value % 4 != 0 || (op.tx_buf.as_ptr() as usize) % 4 != 0 { + return Err(SpiError::AddressNotAligned(op.address.value)); } if op.tx_buf.len() > self.spi_data.decode_addr[cs].len.try_into().unwrap() { return Err(SpiError::Other("Write length exceeds decode region")); @@ -768,9 +820,9 @@ impl<'a> SpiController<'a> { ctrl_reg |= ASPEED_SPI_NORMAL_WRITE; dbg!( self, - "write opcode: {} , addr/offset: {}", + "write opcode: {} , address.value/offset: {}", op.opcode, - op.addr + op.address.value ); cs_ctrlreg_w!(self, cs, ctrl_reg); @@ -784,7 +836,7 @@ impl<'a> SpiController<'a> { // Program addresses self.regs.spi084().write(|w| unsafe { - w.bits(self.spi_data.decode_addr[cs].start + op.addr - SPI_DMA_FLASH_MAP_BASE) + w.bits(self.spi_data.decode_addr[cs].start + op.address.value - SPI_DMA_FLASH_MAP_BASE) }); self.regs.spi088().write(|w| unsafe { w.bits(u32::try_from(op.tx_buf.as_ptr() as usize).unwrap() + SPI_DMA_RAM_MAP_BASE) @@ -794,7 +846,10 @@ impl<'a> SpiController<'a> { .write(|w| unsafe { w.bits(u32::try_from(op.tx_buf.len()).unwrap() - 1) }); // Enable DMA IRQ if needed - // self.enable_dma_irq(); // implement if necessary + #[cfg(feature = "spi_dma_irq")] + { + self.dma_irq_enable(); + } // Start DMA with write direction self.regs.spi080().modify(|_, w| { @@ -805,24 +860,51 @@ impl<'a> SpiController<'a> { self.wait_for_dma_completion(SPI_DMA_TIMEOUT) } + + fn activate_user(&mut self) { + let cs = self.current_cs; + let user_reg = self.spi_data.cmd_mode[cs].user; + cs_ctrlreg_w!(self, cs, user_reg | ASPEED_SPI_USER_INACTIVE); + cs_ctrlreg_w!(self, cs, user_reg); + dbg!(self, "activate cs:{}", u32::try_from(cs).unwrap()); + } + + fn deactivate_user(&mut self) { + let cs = self.current_cs; + let user_reg = self.spi_data.cmd_mode[cs].user; + + cs_ctrlreg_w!(self, cs, user_reg | ASPEED_SPI_USER_INACTIVE); + cs_ctrlreg_w!(self, cs, self.spi_data.cmd_mode[cs].normal_read); + dbg!(self, "deactivate cs:{}", u32::try_from(cs).unwrap()); + dbg!( + self, + "normal read:{:08x}", + self.spi_data.cmd_mode[cs].normal_read + ); + } } impl<'a> SpiBus for SpiController<'a> { // we only use mmap for all transaction fn read(&mut self, buffer: &mut [u8]) -> Result<(), SpiError> { let ahb_addr = self.spi_data.decode_addr[self.current_cs].start as usize as *const u32; + self.activate_user(); unsafe { spi_read_data(ahb_addr, buffer) }; + self.deactivate_user(); Ok(()) } fn write(&mut self, buffer: &[u8]) -> Result<(), SpiError> { let ahb_addr = self.spi_data.decode_addr[self.current_cs].start as usize as *mut u32; + self.activate_user(); unsafe { spi_write_data(ahb_addr, buffer) }; + self.deactivate_user(); Ok(()) } fn transfer(&mut self, rd_buffer: &mut [u8], wr_buffer: &[u8]) -> Result<(), SpiError> { let cs = self.current_cs; + self.activate_user(); if !wr_buffer.is_empty() { let ahb_addr = self.spi_data.decode_addr[cs].start as usize as *mut u32; unsafe { spi_write_data(ahb_addr, wr_buffer) }; @@ -833,6 +915,7 @@ impl<'a> SpiBus for SpiController<'a> { // Read RX buffer unsafe { super::spi_read_data(ahb_addr, rd_buffer) }; } + self.deactivate_user(); Ok(()) } @@ -842,53 +925,33 @@ impl<'a> SpiBus for SpiController<'a> { temp[..len].copy_from_slice(buffer); self.transfer(buffer, &temp[..len]) */ - todo!() + Err(SpiError::Other("transfer_in_place not supported")) } fn flush(&mut self) -> Result<(), SpiError> { - todo!() + Err(SpiError::Other("flush not supported")) } } impl<'a> SpiBusWithCs for SpiController<'a> { fn select_cs(&mut self, cs: usize) -> Result<(), SpiError> { - let user_reg = self.spi_data.cmd_mode[cs].user; - if cs > self.spi_config.max_cs { + if cs >= self.spi_config.max_cs { return Err(SpiError::CsSelectFailed(cs)); } self.current_cs = cs; - cs_ctrlreg_w!(self, cs, user_reg | ASPEED_SPI_USER_INACTIVE); - cs_ctrlreg_w!(self, cs, user_reg); - dbg!(self, "activate cs:{}", u32::try_from(cs).unwrap()); - Ok(()) - } - - fn deselect_cs(&mut self, cs: usize) -> Result<(), SpiError> { - let user_reg = self.spi_data.cmd_mode[cs].user; - if cs > self.spi_config.max_cs { - return Err(SpiError::CsSelectFailed(cs)); - } - cs_ctrlreg_w!(self, cs, user_reg | ASPEED_SPI_USER_INACTIVE); - cs_ctrlreg_w!(self, cs, self.spi_data.cmd_mode[cs].normal_read); - dbg!(self, "deactivate cs:{}", u32::try_from(cs).unwrap()); - dbg!( - self, - "normal read:{:08x}", - self.spi_data.cmd_mode[cs].normal_read - ); Ok(()) } - fn nor_transfer(&mut self, op_info: &mut SpiNorData) -> Result<(), SpiError> { + fn nor_transfer(&mut self, op_info: &mut SpiNorCommand) -> Result<(), SpiError> { let _ = self.spi_nor_transceive(op_info); Ok(()) } - fn nor_read_init(&mut self, cs: usize, op_info: &SpiNorData) { + fn nor_read_init(&mut self, cs: usize, op_info: &SpiNorCommand) { self.spi_nor_read_init(cs, op_info); } - fn nor_write_init(&mut self, cs: usize, op_info: &SpiNorData) { + fn nor_write_init(&mut self, cs: usize, op_info: &SpiNorCommand) { self.spi_nor_write_init(cs, op_info); } diff --git a/src/spi/spidmairqtest.rs b/src/spi/spidmairqtest.rs new file mode 100644 index 0000000..2c6685c --- /dev/null +++ b/src/spi/spidmairqtest.rs @@ -0,0 +1,514 @@ +// Licensed under the Apache-2.0 license + +//! spidmairqtest.rs - DMA irq read/write test harness using static buffers and chainable callbacks + +use super::fmccontroller::FmcController; +use crate::common::{self, DmaBuffer, DummyDelay}; +use crate::spi::device::ChipSelectDevice; +use crate::spi::norflash::{SpiNorCommand, SpiNorDevice}; +use crate::spi::spicontroller::SpiController; +use crate::spi::spitest::{self, DeviceId, FMC_CONFIG}; +use crate::spi::SpiData; +use crate::spimonitor::SpiMonitorNum; +use crate::uart::UartController; +use crate::{astdebug, pinctrl}; +use core::ptr; +use cortex_m::peripheral::NVIC; +use embedded_hal::delay::DelayNs; +use embedded_io::Write; +use heapless::Deque; + +static mut UART_PTR: Option<&'static mut UartController<'static>> = None; +static mut FMC_CONTROLLER: Option> = None; +static mut SPI_CONTROLLER: Option> = None; +//static mut SPI1_CONTROLLER: Option> = None; + +static mut FMC_DEVICE0: Option>> = None; +static mut FMC_DEV0_PTR: *mut ChipSelectDevice<'_, FmcController<'_>> = core::ptr::null_mut(); +static mut FMC_DEVICE1: Option>> = None; +static mut FMC_DEV1_PTR: *mut ChipSelectDevice<'_, FmcController<'_>> = core::ptr::null_mut(); + +static mut SPI_DEVICE0: Option>> = None; +static mut SPI_DEV0_PTR: *mut ChipSelectDevice<'_, SpiController<'_>> = core::ptr::null_mut(); + +static mut REQUST_ALLDONE: bool = true; +// DMA operation type selector +#[derive(Debug, Copy, Clone)] +pub enum DmaOp { + Read, + ReadFast, + Program, + ProgramFast, +} + +// DMA request struct with callback +#[derive(Debug)] +pub struct DmaRequest { + pub src_addr: usize, + pub dst_buf: &'static mut [u8], + pub len: usize, + pub op: DmaOp, + pub verify: bool, // for test + pub buf_idx: usize, // for test + pub on_complete: fn(bool, usize, &[u8]), +} + +// Configuration +const MAX_DMA_CHAIN: usize = 4; +const DMA_BUF_SIZE: usize = 256; +// Static state for current DMA and queue +// use as FIFO + +#[link_section = ".ram_nc"] +static mut READ_BUFFERS: [DmaBuffer; MAX_DMA_CHAIN] = + [const { DmaBuffer::new() }; MAX_DMA_CHAIN]; +#[link_section = ".ram_nc"] +static mut WRITE_BUFFERS: [DmaBuffer; MAX_DMA_CHAIN] = + [const { DmaBuffer::new() }; MAX_DMA_CHAIN]; + +static mut CURRENT_DMA: Option = None; +static mut DMA_QUEUE: Deque = Deque::new(); +static mut CURRENT_DEVID: DeviceId = DeviceId::FmcCs0Idx; + +#[no_mangle] +pub extern "C" fn fmc() { + unsafe { + let fmc = FMC_CONTROLLER.as_mut().unwrap(); + let uart = UART_PTR.as_mut().unwrap(); + let dev = match CURRENT_DEVID { + DeviceId::FmcCs0Idx => FMC_DEV0_PTR.as_mut().unwrap(), + DeviceId::FmcCs1Idx => FMC_DEV1_PTR.as_mut().unwrap(), + _ => todo!(), + }; + if let Err(e) = fmc.handle_interrupt() { + // test done!. irq error + writeln!(uart, "Failed: {e:?}").ok(); + } else { + writeln!(uart, "fmc()").ok(); + if let Some(req) = CURRENT_DMA.take() { + writeln!(uart, "completed").ok(); + if matches!(req.op, DmaOp::Program | DmaOp::ProgramFast) { + writeln!(uart, "wait").ok(); + dev.nor_wait_until_ready(); + } + + (req.on_complete)(req.verify, req.buf_idx, req.dst_buf); + } else { + writeln!(uart, "Error... no CURRENT fmc DMA").ok(); + } + start_next_dma(); + } + } +} + +#[no_mangle] +pub extern "C" fn spi() { + unsafe { + let spi = SPI_CONTROLLER.as_mut().unwrap(); + let uart = UART_PTR.as_mut().unwrap(); + + if let Err(e) = spi.handle_interrupt() { + // test done!. irq error + writeln!(uart, "Failed: {e:?}").ok(); + } else { + if let Some(req) = CURRENT_DMA.take() { + writeln!(uart, "completed").ok(); + (req.on_complete)(req.verify, req.buf_idx, req.dst_buf); + } else { + writeln!(uart, "Error... no CURRENT spi DMA").ok(); + } + + start_next_dma(); + } + } +} + +#[macro_export] +macro_rules! log_uart { + ($($arg:tt)*) => {{ + if let Some(uart) = $crate::spi::spidmairqtest::UART_PTR.as_mut() { + writeln!(uart, $($arg)*).ok(); + write!(uart, "\r").ok(); + } + }}; +} + +unsafe fn show_mmap_reg() { + let (_reg_base, mmap_addr, _cs_capacity) = spitest::device_info(CURRENT_DEVID); + + let uart = UART_PTR.as_mut().unwrap(); + unsafe { + log_uart!("[{:08x}]", mmap_addr); + } + astdebug::print_reg_u8(uart, mmap_addr, 0x400); +} +unsafe fn start_next_dma() { + if DMA_QUEUE.is_empty() { + REQUST_ALLDONE = true; + unsafe { + log_uart!("DMA queue is empty. All transfers are completed!!"); + } + show_mmap_reg(); + return; + } + + if let Some(req) = DMA_QUEUE.pop_front() { + CURRENT_DMA = Some(req); + match CURRENT_DEVID { + DeviceId::FmcCs0Idx | DeviceId::FmcCs1Idx => { + if let Err(e) = start_dma_fmc_transfer(CURRENT_DMA.as_mut().unwrap()) { + unsafe { + log_uart!("Failed to start DMA transfer: {:?}", e); + } + } + } + DeviceId::Spi0Cs0Idx + | DeviceId::Spi1Cs0Idx + | DeviceId::Spi1Cs1Idx + | DeviceId::Spi0Cs1Idx => { + if let Err(e) = start_dma_spi_transfer(CURRENT_DMA.as_mut().unwrap()) { + unsafe { + log_uart!("Failed to start DMA transfer: {:?}", e); + } + } + } + } + } +} + +pub fn on_complete_dma(verify: bool, idx: usize, _buf: &[u8]) { + if verify { + if verify_dma_buffer_match(idx) { + unsafe { + log_uart!("DMA test passed!!"); + } + } else { + unsafe { + log_uart!("DMA test failed!!"); + } + } + } //else if let Some(uart) = unsafe { UART_PTR.as_mut() } { + // astdebug::print_array_u8(uart, buf); + //} +} + +// Start DMA transfer using the device +fn start_dma_spi_transfer(req: &mut DmaRequest) -> Result<(), ()> { + unsafe { + log_uart!("spi start_dma_transfer"); + let dev = match CURRENT_DEVID { + DeviceId::Spi0Cs0Idx => SPI_DEV0_PTR.as_mut().unwrap(), + _ => todo!(), + }; + + let result = match req.op { + DmaOp::Read => dev.nor_read_data(u32::try_from(req.src_addr).unwrap(), req.dst_buf), + DmaOp::ReadFast => { + dev.nor_read_fast_4b_data(u32::try_from(req.src_addr).unwrap(), req.dst_buf) + } + DmaOp::Program => { + dev.nor_page_program(u32::try_from(req.src_addr).unwrap(), req.dst_buf) + } + DmaOp::ProgramFast => { + dev.nor_page_program_4b(u32::try_from(req.src_addr).unwrap(), req.dst_buf) + } + }; + + result.map_err(|e| { + log_uart!("start_dma_transfer failed at {:#x}: {:?}", req.src_addr, e); + }) + } +} + +// Start DMA transfer using the device +fn start_dma_fmc_transfer(req: &mut DmaRequest) -> Result<(), ()> { + unsafe { + log_uart!("fmc start_dma_transfer"); + let dev = match CURRENT_DEVID { + DeviceId::FmcCs0Idx => FMC_DEV0_PTR.as_mut().unwrap(), + DeviceId::FmcCs1Idx => FMC_DEV1_PTR.as_mut().unwrap(), + _ => todo!(), + }; + + let result = match req.op { + DmaOp::Read => dev.nor_read_data(u32::try_from(req.src_addr).unwrap(), req.dst_buf), + DmaOp::ReadFast => { + dev.nor_read_fast_4b_data(u32::try_from(req.src_addr).unwrap(), req.dst_buf) + } + DmaOp::Program => { + dev.nor_page_program(u32::try_from(req.src_addr).unwrap(), req.dst_buf) + } + DmaOp::ProgramFast => { + dev.nor_page_program_4b(u32::try_from(req.src_addr).unwrap(), req.dst_buf) + } + }; + + result.map_err(|e| { + log_uart!("start_dma_transfer failed at {:#x}: {:?}", req.src_addr, e); + }) + } +} + +#[must_use] +pub fn verify_dma_buffer_match(i: usize) -> bool { + unsafe { + let read = READ_BUFFERS[i].as_mut_slice(0, DMA_BUF_SIZE); + let write = WRITE_BUFFERS[i].as_mut_slice(0, DMA_BUF_SIZE); + + if read != write { + // Fast path failed. now scan for first mismatch for debug + for (j, (&r, &w)) in read.iter().zip(write.iter()).enumerate() { + if r != w { + log_uart!( + "Mismatch at buffer {}, index {}: read={:02x}, expected={:02x}", + i, + j, + r, + w + ); + break; + } + } + if let Some(uart) = UART_PTR.as_mut() { + astdebug::print_array_u8(uart, read); + astdebug::print_array_u8(uart, write); + } + return false; + } + } + true +} + +pub fn fill_dma_buffer(op_req: DmaOp, random: bool) { + let mut seed = 0xDEAD_FBEE; + + unsafe { + for i in 0..MAX_DMA_CHAIN { + let buf: &'static mut [u8] = match op_req { + DmaOp::Read | DmaOp::ReadFast => READ_BUFFERS[i].as_mut_slice(0, DMA_BUF_SIZE), + DmaOp::Program | DmaOp::ProgramFast => { + WRITE_BUFFERS[i].as_mut_slice(0, DMA_BUF_SIZE) + } + }; + + buf.fill(0x0); + + if random { + common::fill_random(buf, &mut seed); + } + } + } +} +// Example use +#[allow(clippy::missing_safety_doc)] +pub unsafe fn dma_irq_chain_test(start_addrs: &[u32], op_req: DmaOp, verify: bool) { + DMA_QUEUE.clear(); + REQUST_ALLDONE = false; + + for (i, &addr) in start_addrs.iter().enumerate() { + if i >= MAX_DMA_CHAIN { + unsafe { + log_uart!("Too many DMA addresses; max is {}", MAX_DMA_CHAIN); + } + break; + } + + // Select buffer based on operation type + let buf: &'static mut [u8] = match op_req { + DmaOp::Read | DmaOp::ReadFast => READ_BUFFERS[i].as_mut_slice(0, DMA_BUF_SIZE), + DmaOp::Program | DmaOp::ProgramFast => WRITE_BUFFERS[i].as_mut_slice(0, DMA_BUF_SIZE), + }; + let request = DmaRequest { + src_addr: addr as usize, + dst_buf: buf, + len: DMA_BUF_SIZE, + op: op_req, + buf_idx: i, + verify, + on_complete: on_complete_dma, + }; + DMA_QUEUE.push_back(request).unwrap(); + unsafe { + log_uart!("chaining {}", i); + } + } //for + start_next_dma(); +} + +pub fn test_fmc_dma_irq(uart: &mut UartController<'_>) { + let fmc_spi = unsafe { &*ast1060_pac::Fmc::ptr() }; + let mut delay = DummyDelay {}; + + pinctrl::Pinctrl::apply_pinctrl_group(pinctrl::PINCTRL_FMC_QUAD); + let fmc_data = SpiData::new(); + + unsafe { + // register interrupt + /* irq init */ + UART_PTR = Some(core::mem::transmute::< + &mut UartController<'_>, + &'static mut UartController<'static>, + >(uart)); + NVIC::unmask(ast1060_pac::Interrupt::fmc); + + FMC_CONTROLLER = Some(FmcController::new( + fmc_spi, + 0, + FMC_CONFIG, + fmc_data, + Some(UART_PTR.as_mut().unwrap()), + )); + + log_uart!("==== FMC DEV0 DMA read Test===="); + let controller = FMC_CONTROLLER.as_mut().unwrap(); + let _ = controller.init(); + + // You can now pass `fmc_ptr` into ChipSelectDevice or use it directly + let nor_read_data: SpiNorCommand<'_> = + spitest::nor_device_read_data(spitest::FMC_CS0_CAPACITY); + let nor_write_data = spitest::nor_device_write_data(spitest::FMC_CS0_CAPACITY); + + let flash_device0 = ChipSelectDevice { + bus: controller, + cs: 0, + spim: None, + }; + FMC_DEVICE0 = Some(flash_device0); + + let dev0 = FMC_DEVICE0.as_mut().unwrap(); + //FMC_DEV0_PTR = dev0 as *mut _; + FMC_DEV0_PTR = ptr::from_mut(dev0); + + // Wrap controller in a CS device (CS0) + let _ = dev0.nor_read_init(&nor_read_data); + let _ = dev0.nor_write_init(&nor_write_data); + let start_addrs = [0x0000_0000, 0x0000_0100, 0x0000_0200, 0x0000_0300]; + + CURRENT_DEVID = DeviceId::FmcCs0Idx; + fill_dma_buffer(DmaOp::Read, true); + delay.delay_ns(8_000_000); + dma_irq_chain_test(&start_addrs, DmaOp::Read, false); + + delay.delay_ns(8_000_000); + + log_uart!("==== FMC DEV1 DMA read & write Test===="); + let controller1 = FMC_CONTROLLER.as_mut().unwrap(); + let flash_device1 = ChipSelectDevice { + bus: controller1, // reuse same ref + cs: 1, + spim: None, + }; + + FMC_DEVICE1 = Some(flash_device1); + + let dev1 = FMC_DEVICE1.as_mut().unwrap(); + let _ = dev1.nor_read_init(&nor_read_data); + let _ = dev1.nor_write_init(&nor_write_data); + + FMC_DEV1_PTR = ptr::from_mut(dev1); + + //let start_addrs = [0x0000_0000, 0x0000_0100, 0x0000_0200, 0x0000_0300]; + //let start_addrs = [0x0000_0100]; + let read_only = false; + CURRENT_DEVID = DeviceId::FmcCs1Idx; + if read_only { + log_uart!("===FMC read only test!!!!==="); + fill_dma_buffer(DmaOp::Read, false); + dma_irq_chain_test(&start_addrs, DmaOp::Read, false); + } else { + log_uart!("===FMC write read verify test!!!!==="); + fill_dma_buffer(DmaOp::Program, true); + let _ = dev1.nor_sector_erase(0x0000_0000); + delay.delay_ns(8_000_000); + // NOTE: DMA write has an issue in AST2600-Errata-11 + // DMA write ends before finish transfering data + // work-around: add delay + dma_irq_chain_test(&start_addrs, DmaOp::Program, false); + delay.delay_ns(8_000_000); + if !REQUST_ALLDONE { + log_uart!("=ERROR: Programming race condition!!!!="); + } + dma_irq_chain_test(&start_addrs, DmaOp::Read, true); + } + } //unsafe + + delay.delay_ns(8_000_000); +} + +pub fn test_spi_dma_irq(uart: &mut UartController<'_>) { + let spi0 = unsafe { &*ast1060_pac::Spi::ptr() }; + //let base = core::ptr::from_ref(spi0) as usize; + let current_cs = 0; + let mut delay = DummyDelay {}; + + pinctrl::Pinctrl::apply_pinctrl_group(pinctrl::PINCTRL_SPIM0_QUAD_DEFAULT); + pinctrl::Pinctrl::apply_pinctrl_group(pinctrl::PINCTRL_SPI1_QUAD); + let scu_qspi_mux: &mut [u32] = + unsafe { core::slice::from_raw_parts_mut((spitest::SCU_BASE + 0xf0) as *mut u32, 4) }; + scu_qspi_mux[0] = 0x0000_fff0; + + let spi_data = SpiData::new(); + + unsafe { + // register interrupt + // irq init + UART_PTR = Some(core::mem::transmute::< + &mut UartController<'_>, + &'static mut UartController<'static>, + >(uart)); + NVIC::unmask(ast1060_pac::Interrupt::spi); + + SPI_CONTROLLER = Some(SpiController::new( + spi0, + current_cs, + spitest::SPI0_CONFIG, + spi_data, + Some(UART_PTR.as_mut().unwrap()), + )); + + let controller = SPI_CONTROLLER.as_mut().unwrap(); + let _ = controller.init(); + log_uart!("==== SPI0 DEV0 DMA read Test===="); + // You can now pass `fmc_ptr` into ChipSelectDevice or use it directly + let nor_read_data: SpiNorCommand<'_> = + spitest::nor_device_read_4b_data(spitest::SPI_CS0_CAPACITY); + let nor_write_data = spitest::nor_device_write_4b_data(spitest::SPI_CS0_CAPACITY); + + let flash_device0 = ChipSelectDevice { + bus: controller, + cs: 0, + spim: Some(SpiMonitorNum::SPIM0), + }; + + SPI_DEVICE0 = Some(flash_device0); + let dev0 = SPI_DEVICE0.as_mut().unwrap(); + SPI_DEV0_PTR = ptr::from_mut(dev0); + + // Wrap controller in a CS device (CS0) + let _ = dev0.nor_read_init(&nor_read_data); + let _ = dev0.nor_write_init(&nor_write_data); + + let start_addrs = [0x0000_0000, 0x0000_0100, 0x0000_0200, 0x0000_0300]; + //let start_addrs = [0x0000_0100]; + CURRENT_DEVID = DeviceId::Spi0Cs0Idx; + + let read_only = false; + if read_only { + fill_dma_buffer(DmaOp::ReadFast, false); + dma_irq_chain_test(&start_addrs, DmaOp::ReadFast, false); + } else { + fill_dma_buffer(DmaOp::Program, true); + let _ = dev0.nor_sector_erase(0x0000_0000); + delay.delay_ns(8_000_000); + dma_irq_chain_test(&start_addrs, DmaOp::ProgramFast, false); + delay.delay_ns(8_000_000); + if !REQUST_ALLDONE { + log_uart!("=ERROR: Programming race condition!!!!="); + } + dma_irq_chain_test(&start_addrs, DmaOp::ReadFast, true); + } + log_uart!("==== End SPI0 DEV0 DMA read Test===="); + } //unsafe + + scu_qspi_mux[0] = 0x0000_0000; +} diff --git a/src/spi/spim.rs b/src/spi/spim.rs new file mode 100644 index 0000000..ba54f5a --- /dev/null +++ b/src/spi/spim.rs @@ -0,0 +1,141 @@ +// Licensed under the Apache-2.0 license + +use crate::modify_reg; +use core::cell::Cell; +use critical_section::Mutex; + + +use super::consts::{PIN_SPIM0_CLK_OUT_BIT, PIN_SPIM1_CLK_OUT_BIT, PIN_SPIM2_CLK_OUT_BIT, PIN_SPIM3_CLK_OUT_BIT}; + +static GPIO_ORI_VAL: Mutex<[Cell; 4]> = + Mutex::new([const { Cell::new(0) }; 4]); + +pub fn get_gpio_ori_val() -> [u32; 4] { + critical_section::with(|crit| { + let v = GPIO_ORI_VAL.borrow(crit); + [v[0].get(), v[1].get(), v[2].get(), v[3].get()] + }) +} + +pub fn spim_scu_ctrl_set(mask: u32, val: u32) { + let scu = unsafe { &*ast1060_pac::Scu::ptr() }; + let mut reg_val = scu.scu0f0().read().bits(); + reg_val &= !mask; + reg_val |= val; + scu.scu0f0().write(|w| unsafe { w.bits(reg_val) }); +} + +pub fn spim_scu_ctrl_clear(clear_bits: u32) { + let scu = unsafe { &*ast1060_pac::Scu::ptr() }; + let mut reg_val = scu.scu0f0().read().bits(); + reg_val &= !clear_bits; + scu.scu0f0().write(|w| unsafe { w.bits(reg_val) }); +} + +pub fn spim_proprietary_pre_config() { + let scu = unsafe { &*ast1060_pac::Scu::ptr() }; + let gpio = unsafe { &*ast1060_pac::Gpio::ptr() }; + + // If no SPIM in use, return + #[allow(clippy::verbose_bit_mask)] + if scu.scu0f0().read().bits() & 0x7 == 0 { + return; + } + + let spim_idx = (scu.scu0f0().read().bits() & 0x7) - 1; + if spim_idx > 3 { + return; + } + let clear = true; + for (idx, ori_val) in get_gpio_ori_val().iter_mut().enumerate() { + if u32::try_from(idx).unwrap() == spim_idx { + continue; + } + + match idx { + 0 => { + modify_reg!(scu.scu690(), PIN_SPIM0_CLK_OUT_BIT, clear); + *ori_val = gpio.gpio004().read().bits(); + modify_reg!(gpio.gpio004(), PIN_SPIM0_CLK_OUT_BIT, clear); + } + 1 => { + modify_reg!(scu.scu690(), PIN_SPIM1_CLK_OUT_BIT, clear); + *ori_val = gpio.gpio004().read().bits(); + modify_reg!(gpio.gpio004(), PIN_SPIM1_CLK_OUT_BIT, clear); + } + 2 => { + modify_reg!(scu.scu694(), PIN_SPIM2_CLK_OUT_BIT, clear); + *ori_val = gpio.gpio024().read().bits(); + modify_reg!(gpio.gpio024(), PIN_SPIM2_CLK_OUT_BIT, clear); + } + 3 => { + modify_reg!(scu.scu694(), PIN_SPIM3_CLK_OUT_BIT, clear); + *ori_val = gpio.gpio024().read().bits(); + modify_reg!(gpio.gpio024(), PIN_SPIM3_CLK_OUT_BIT, clear); + } + _ => (), + } + } +} + +pub fn spim_proprietary_post_config() { + let scu = unsafe { &*ast1060_pac::Scu::ptr() }; + let gpio = unsafe { &*ast1060_pac::Gpio::ptr() }; + + // If no SPIM in use, return + let bits = scu.scu0f0().read().bits(); + if bits.trailing_zeros() >= 3 { + return; + } + + let spim_idx = (scu.scu0f0().read().bits() & 0x7) - 1; + if spim_idx > 3 { + return; + } + let clear = false; + for (idx, ori_val) in get_gpio_ori_val().iter().copied().enumerate() { + if u32::try_from(idx).unwrap() == spim_idx { + continue; + } + + match idx { + 0 => { + gpio.gpio004().modify(|r, w| unsafe { + let mut current = r.bits(); + current &= !(1 << PIN_SPIM0_CLK_OUT_BIT); + current |= ori_val; + w.bits(current) + }); + modify_reg!(scu.scu690(), PIN_SPIM0_CLK_OUT_BIT, clear); + } + 1 => { + gpio.gpio004().modify(|r, w| unsafe { + let mut current = r.bits(); + current &= !(1 << PIN_SPIM1_CLK_OUT_BIT); + current |= ori_val; + w.bits(current) + }); + modify_reg!(gpio.gpio004(), PIN_SPIM1_CLK_OUT_BIT, clear); + } + 2 => { + gpio.gpio024().modify(|r, w| unsafe { + let mut current = r.bits(); + current &= !(1 << PIN_SPIM2_CLK_OUT_BIT); + current |= ori_val; + w.bits(current) + }); + modify_reg!(scu.scu694(), PIN_SPIM2_CLK_OUT_BIT, clear); + } + 3 => { + gpio.gpio024().modify(|r, w| unsafe { + let mut current = r.bits(); + current &= !(1 << PIN_SPIM3_CLK_OUT_BIT); + current |= ori_val; + w.bits(current) + }); + modify_reg!(scu.scu694(), PIN_SPIM3_CLK_OUT_BIT, clear); + } + _ => (), + } + } +} diff --git a/src/spi/spitest.rs b/src/spi/spitest.rs index 7c94b72..28b8d30 100644 --- a/src/spi/spitest.rs +++ b/src/spi/spitest.rs @@ -1,26 +1,31 @@ // Licensed under the Apache-2.0 license +//! spitest.rs +//! - genric test for FMC, Spi0 and Spi1: get pid, read/write w/wo read dma, no write dma +//! - irq is not being handled in this test use super::device::ChipSelectDevice; use super::fmccontroller::FmcController; -use super::norflash::{ - Jesd216Mode, SpiNorData, SpiNorDevice, SPI_NOR_CMD_QREAD, SPI_NOR_CMD_READ_FAST_4B, -}; + use super::{ - norflash, CommandMode, CtrlType, SpiConfig, SpiData, SpiDecodeAddress, - SPI_NOR_DATA_DIRECT_READ, SPI_NOR_DATA_DIRECT_WRITE, + norflash, CommandMode, CtrlType, SpiConfig, SpiData, SpiDecodeAddress }; -use crate::common::{DmaBuffer, DummyDelay}; +use super::norflash::{ + Jesd216Mode, SpiNorCommand, SpiNorDevice, SPI_NOR_CMD_QREAD, SPI_NOR_CMD_READ_FAST_4B, + SPI_NOR_CMD_PP, SPI_NOR_CMD_PP_4B, +}; +use crate::common::{self, DmaBuffer, DummyDelay}; +use crate::spi::types::{AddressWidth, DataDirection, FlashAddress}; use crate::spi::norflashblockdevice; use crate::spi::norflashblockdevice::{BlockAddrUsize, NorFlashBlockDevice}; use crate::spi::spicontroller::SpiController; -use crate::spimonitor::{RegionInfo, SpiMonitor, SpimExtMuxSel}; +use crate::spimonitor::SpiMonitorNum; use crate::uart; use crate::uart::{Config, UartController}; use crate::{astdebug, pinctrl}; -use ast1060_pac::{Peripherals, Spipf, Spipf1, Spipf2, Spipf3}; +use ast1060_pac::Peripherals; use embedded_hal::delay::DelayNs; use embedded_hal::spi::SpiDevice; -use embedded_io::Write; +use embedded_io::Write as ioWrite; use proposed_traits::block_device::{BlockDevice, BlockRange}; pub const FMC_CTRL_BASE: usize = 0x7e62_0000; @@ -31,7 +36,7 @@ pub const SPI0_MMAP_BASE: usize = 0x9000_0000; pub const SPI1_CTRL_BASE: usize = 0x7e64_0000; pub const SPI1_MMAP_BASE: usize = 0xb000_0000; -const SCU_BASE: usize = 0x7E6E_2000; +pub const SCU_BASE: usize = 0x7E6E_2000; pub const CTRL_REG_SIZE: usize = 0xc4; pub const SPIPF1_BASE: usize = 0x7e79_1000; @@ -41,7 +46,7 @@ pub const SPIPF4_BASE: usize = 0x7e79_4000; pub const GPIO_BASE: usize = 0x7e78_0000; -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] #[deny(dead_code)] pub enum DeviceId { FmcCs0Idx, @@ -114,62 +119,58 @@ pub const SPI1_CONFIG: SpiConfig = SpiConfig { }; #[must_use] -pub fn nor_device_read_data<'a>(len: usize) -> SpiNorData<'a> { - SpiNorData { +pub fn nor_device_read_data<'a>(len: usize) -> SpiNorCommand<'a> { + SpiNorCommand { mode: Jesd216Mode::Mode114, opcode: SPI_NOR_CMD_QREAD, dummy_cycle: 8, - addr: 0, - addr_len: 3, + address: FlashAddress { value: 0x0, width: AddressWidth::ThreeByte }, data_len: u32::try_from(len).unwrap(), tx_buf: &[], rx_buf: &mut [], - data_direct: SPI_NOR_DATA_DIRECT_READ, + data_direct: DataDirection::Read, } } #[must_use] -pub fn nor_device_write_data<'a>(len: usize) -> SpiNorData<'a> { - SpiNorData { +pub fn nor_device_write_data<'a>(len: usize) -> SpiNorCommand<'a> { + SpiNorCommand { mode: Jesd216Mode::Mode111, opcode: norflash::SPI_NOR_CMD_PP, dummy_cycle: 0, - addr: 0, - addr_len: 3, + address: FlashAddress { value: 0x0, width: AddressWidth::ThreeByte }, data_len: u32::try_from(len).unwrap(), tx_buf: &[], rx_buf: &mut [], - data_direct: SPI_NOR_DATA_DIRECT_WRITE, + data_direct: DataDirection::Write, } } #[must_use] -pub fn nor_device_read_4b_data<'a>(len: usize) -> SpiNorData<'a> { - SpiNorData { +pub fn nor_device_read_4b_data<'a>(len: usize) -> SpiNorCommand<'a> { + SpiNorCommand { mode: Jesd216Mode::Mode111Fast, opcode: SPI_NOR_CMD_READ_FAST_4B, dummy_cycle: 8, - addr: 0, - addr_len: 4, + address: FlashAddress { value: 0x0, width: AddressWidth::FourByte }, data_len: u32::try_from(len).unwrap(), tx_buf: &[], rx_buf: &mut [], - data_direct: SPI_NOR_DATA_DIRECT_READ, + data_direct: DataDirection::Read, } } #[must_use] -pub fn nor_device_write_4b_data<'a>(len: usize) -> SpiNorData<'a> { - SpiNorData { +pub fn nor_device_write_4b_data<'a>(len: usize) -> SpiNorCommand<'a> { + SpiNorCommand { mode: Jesd216Mode::Mode111, opcode: norflash::SPI_NOR_CMD_PP_4B, dummy_cycle: 0, - addr: 0, - addr_len: 4, + address: FlashAddress { value: 0x0, width: AddressWidth::FourByte }, data_len: u32::try_from(len).unwrap(), tx_buf: &[], rx_buf: &mut [], - data_direct: SPI_NOR_DATA_DIRECT_WRITE, + data_direct: DataDirection::Write, } } @@ -255,6 +256,7 @@ pub fn test_cs, E>( let _ = dev.nor_page_program_4b(addr, wbuf); } } + dev.nor_wait_until_ready(); delay1.delay_ns(8_000_000); } // when data size is bigger than 128. use read dma @@ -304,6 +306,7 @@ pub fn test_cs, E>( // len > DMA_MIN_LENGTH { test_log!(uart, "Test FIFO read...buf len: 0x20"); let _ = dev.nor_read_data(addr, &mut rbuf[0..0x20]); + delay1.delay_ns(8_000_000); astdebug::print_array_u8(uart, &rbuf[0..0x20]); } } @@ -321,7 +324,7 @@ pub fn test_fmc(uart: &mut UartController<'_>) { let peripherals = unsafe { Peripherals::steal() }; let fmc_uart = peripherals.uart; let mut delay = DummyDelay {}; - let mut fmc_uart_controller = UartController::new(fmc_uart, &mut delay); + let fmc_uart_controller = UartController::new(fmc_uart, &mut delay); unsafe { fmc_uart_controller.init(&Config { baud_rate: 115_200, @@ -333,25 +336,22 @@ pub fn test_fmc(uart: &mut UartController<'_>) { } let mut controller = FmcController::new( - fmc_spi, - current_cs, - FMC_CONFIG, - fmc_data, - Some(&mut fmc_uart_controller), + fmc_spi, current_cs, FMC_CONFIG, fmc_data, //Some(&mut fmc_uart_controller), + None, ); test_log!(uart, "FMC controller init"); let _result = controller.init(); //astdebug::print_reg_u32(uart, FMC_CTRL_BASE, 0xb0); - let nor_read_data: SpiNorData<'_> = nor_device_read_data(FMC_CS0_CAPACITY); + let nor_read_data: SpiNorCommand<'_> = nor_device_read_data(FMC_CS0_CAPACITY); let nor_write_data = nor_device_write_data(FMC_CS0_CAPACITY); // Wrap controller in a CS device (CS0) - let mut flash_device0: ChipSelectDevice<'_, FmcController<'_>, Spipf> = ChipSelectDevice { + let mut flash_device0: ChipSelectDevice<'_, FmcController<'_>> = ChipSelectDevice { bus: &mut controller, cs: 0, - spi_monitor: None, + spim: None, }; test_read_jedec(uart, &mut flash_device0); let _ = flash_device0.nor_read_init(&nor_read_data); @@ -360,10 +360,10 @@ pub fn test_fmc(uart: &mut UartController<'_>) { //astdebug::print_reg_u32(uart, FMC_MMAP_BASE, 0x80); // Wrap controller in a CS device (CS1) - let mut flash_device1: ChipSelectDevice<'_, FmcController<'_>, Spipf> = ChipSelectDevice { + let mut flash_device1: ChipSelectDevice<'_, FmcController<'_>> = ChipSelectDevice { bus: &mut controller, cs: 1, - spi_monitor: None, + spim: None, }; test_read_jedec(uart, &mut flash_device1); let _ = flash_device1.nor_read_init(&nor_read_data); @@ -385,6 +385,7 @@ pub fn test_fmc(uart: &mut UartController<'_>) { TEST_DATA_SIZE, true, ); + test_log!(uart, "################# FMC test done ! ###############"); } @@ -431,16 +432,16 @@ pub fn test_spi(uart: &mut UartController<'_>) { let _result = spi_controller.init(); //astdebug::print_reg_u32(uart, SPI0_CTRL_BASE, 0xb0); - let mut spi_monitor0 = start_spim0(); // Wrap controller in a CS device (CS0) let mut flash_device = ChipSelectDevice { bus: &mut spi_controller, cs: 0, - spi_monitor: Some(&mut spi_monitor0), + spim: Some(SpiMonitorNum::SPIM0), }; - let nor_read_data: SpiNorData<'_> = nor_device_read_4b_data(SPI_CS0_CAPACITY); + let nor_read_data: SpiNorCommand<'_> = nor_device_read_4b_data(SPI_CS0_CAPACITY); let nor_write_data = nor_device_write_4b_data(SPI_CS0_CAPACITY); + let _ = flash_device.nor_read_init(&nor_read_data); let _ = flash_device.nor_write_init(&nor_write_data); @@ -538,7 +539,7 @@ pub fn test_block_device(blockdev: &mut NorFlashBlockDevice) let uart = peripherals.uart; let mut delay = DummyDelay {}; let mut uartc = UartController::new(uart, &mut delay); - let addr = 0x0; + let addr = 0x1000; unsafe { uartc.init(&Config { @@ -552,9 +553,11 @@ pub fn test_block_device(blockdev: &mut NorFlashBlockDevice) let testsize = 0x400; let wbuf: &mut [u8] = unsafe { SPI_NC_BUFFER[WRITE_IDX].as_mut_slice(0, testsize) }; - let rbuf: &mut [u8] = unsafe { SPI_NC_BUFFER[READ_IDX].as_mut_slice(0, testsize) }; + let mut seed = 0x179a_4e87; + common::fill_random(wbuf, &mut seed); + test_log!( uartc, "###########################page size: {} sector size: {} capacity:{:08X}", @@ -580,14 +583,10 @@ pub fn test_block_device(blockdev: &mut NorFlashBlockDevice) let mut delay = DummyDelay {}; test_log!(uartc, "########## start erase "); let _ = blockdev.erase(range); - - for (i, value) in wbuf.iter_mut().take(testsize).enumerate() { - *value = u8::try_from(i % 255).unwrap(); - } delay.delay_ns(2_000_000); test_log!( uartc, - "########## start block programming size: {:08x} ", + "########## start block programming size: 0x{:08x} ", testsize ); match blockdev.program(norflashblockdevice::BlockAddrUsize(addr), wbuf) { @@ -596,7 +595,7 @@ pub fn test_block_device(blockdev: &mut NorFlashBlockDevice) } let _ = blockdev.read(norflashblockdevice::BlockAddrUsize(addr), rbuf); - + delay.delay_ns(8_000_000); let result: bool; unsafe { result = core::slice::from_raw_parts(ptr_write, testsize) @@ -610,8 +609,8 @@ pub fn test_block_device(blockdev: &mut NorFlashBlockDevice) astdebug::print_array_u8(&mut uartc, wbuf); test_log!(uartc, "read buffer:"); astdebug::print_array_u8(&mut uartc, rbuf); - test_log!(uartc, "Mmap buffer: {:08x}", SPI0_MMAP_BASE + addr); - astdebug::print_reg_u8(&mut uartc, SPI0_MMAP_BASE + addr, testsize); + //test_log!(uartc, "Mmap buffer: {:08x}", SPI0_MMAP_BASE + addr); + //astdebug::print_reg_u8(&mut uartc, SPI0_MMAP_BASE + addr, testsize); } } @@ -640,7 +639,9 @@ pub fn test_spi2(uart: &mut UartController<'_>) { pinctrl::Pinctrl::apply_pinctrl_group(pinctrl::PINCTRL_SPI2_QUAD); test_log!(uart, "SPI1 set pinctrl"); - test_log!(uart, " SCU:: 0x{:08x}", SCU_BASE); + let scu_qspi_mux: &mut [u32] = + unsafe { core::slice::from_raw_parts_mut((SCU_BASE + 0xf0) as *mut u32, 4) }; + scu_qspi_mux[0] = 0x0000_fff0; let peripherals = unsafe { Peripherals::steal() }; let spi_uart = peripherals.uart; @@ -667,20 +668,18 @@ pub fn test_spi2(uart: &mut UartController<'_>) { let _result = spi_controller.init(); astdebug::print_reg_u32(uart, SPI1_CTRL_BASE, 0xb0); - let nor_read_data: SpiNorData<'_> = nor_device_read_4b_data(SPI_CS0_CAPACITY); - let nor_write_data = nor_device_read_4b_data(SPI_CS0_CAPACITY); - - if true { - let mut spi_monitor2 = start_spim2(); - // Wrap controller in a CS device (CS0) - let mut flash_device = ChipSelectDevice { - bus: &mut spi_controller, - cs: 0, - spi_monitor: Some(&mut spi_monitor2), - }; + let nor_read_data: SpiNorCommand<'_> = nor_device_read_4b_data(SPI_CS0_CAPACITY); + let nor_write_data = nor_device_write_4b_data(SPI_CS0_CAPACITY); - test_read_jedec(uart, &mut flash_device); + // Wrap controller in a CS device (CS0) + let mut flash_device = ChipSelectDevice { + bus: &mut spi_controller, + cs: 0, + spim: Some(SpiMonitorNum::SPIM2), + }; + test_read_jedec(uart, &mut flash_device); + if true { let mut delay1 = DummyDelay {}; if read_id { @@ -688,7 +687,7 @@ pub fn test_spi2(uart: &mut UartController<'_>) { let mut read_buf: [u8; 0x3] = [0u8; 3]; let write_buf: [u8; 1] = [0x9f]; let _ = flash_device.transfer(&mut read_buf, &write_buf); - delay1.delay_ns(2_000_000); + delay1.delay_ns(8_000_000); astdebug::print_array_u8(uart, &read_buf[..3]); } @@ -724,15 +723,14 @@ pub fn test_spi2(uart: &mut UartController<'_>) { true, ); } - { + if true { test_log!(uart, "####### SPI 2@1#######"); //NOTE: When SPI2 controller accesses the SPI flash through SPIM3/4 output pins by configuring SCU0F0[3:0], // only CS0 decoding address area can be used within this scenario. Thus, CS is fixed to 0. - let mut spi_monitor3 = start_spim3(); let mut flash_device2 = ChipSelectDevice { bus: &mut spi_controller, cs: 0, - spi_monitor: Some(&mut spi_monitor3), + spim: Some(SpiMonitorNum::SPIM3), }; let _ = flash_device2.nor_read_init(&nor_read_data); @@ -757,146 +755,3 @@ pub fn test_spi2(uart: &mut UartController<'_>) { } test_log!(uart, "################# SPI 2 TEST done ! ###############"); } - -#[must_use] -pub fn start_spim0() -> SpiMonitor { - let allow_cmds: [u8; 27] = [ - 0x03, 0x13, 0x0b, 0x0c, 0x6b, 0x6c, 0x01, 0x05, 0x35, 0x06, 0x04, 0x20, 0x21, 0x9f, 0x5a, - 0xb7, 0xe9, 0x32, 0x34, 0xd8, 0xdc, 0x02, 0x12, 0x15, 0x31, 0x3b, 0x3c, - ]; - - let read_blocked_regions = [RegionInfo { - /*pfm*/ - start: 0x0400_0000, - length: 0x0002_0000, - }]; - - let write_blocked_regions = [RegionInfo { - start: 0x0000_0000, - length: 0x0800_0000, - }]; - let mut spi_monitor0 = SpiMonitor::::new( - true, - SpimExtMuxSel::SpimExtMuxSel1, - &allow_cmds, - u8::try_from(allow_cmds.len()).unwrap(), - &read_blocked_regions, - u8::try_from(read_blocked_regions.len()).unwrap(), - &write_blocked_regions, - u8::try_from(write_blocked_regions.len()).unwrap(), - ); - spi_monitor0.spim_sw_rst(); - spi_monitor0.aspeed_spi_monitor_init(); - - //TODO: when do we disable the mux? - //spi_monitor0.spim_ext_mux_config(SpimExtMuxSel::SpimExtMuxSel0); - spi_monitor0 - // print spim pointer value -} - -#[must_use] -pub fn start_spim1() -> SpiMonitor { - let allow_cmds: [u8; 27] = [ - 0x03, 0x13, 0x0b, 0x0c, 0x6b, 0x6c, 0x01, 0x05, 0x35, 0x06, 0x04, 0x20, 0x21, 0x9f, 0x5a, - 0xb7, 0xe9, 0x32, 0x34, 0xd8, 0xdc, 0x02, 0x12, 0x15, 0x31, 0x3b, 0x3c, - ]; - - let write_blocked_regions = [RegionInfo { - start: 0x0000_0000, - length: 0x0800_0000, - }]; - let mut spi_monitor1 = SpiMonitor::::new( - true, - SpimExtMuxSel::SpimExtMuxSel1, - &allow_cmds, - u8::try_from(allow_cmds.len()).unwrap(), - &[], - 0, - &write_blocked_regions, - u8::try_from(write_blocked_regions.len()).unwrap(), - ); - spi_monitor1.spim_sw_rst(); - spi_monitor1.aspeed_spi_monitor_init(); - //spi_monitor1.spim_ext_mux_config(SpimExtMuxSel::SpimExtMuxSel0); - - spi_monitor1 -} - -#[must_use] -pub fn start_spim2() -> SpiMonitor { - let allow_cmds: [u8; 27] = [ - 0x03, 0x13, 0x0b, 0x0c, 0x6b, 0x6c, 0x01, 0x05, 0x35, 0x06, 0x04, 0x20, 0x21, 0x9f, 0x5a, - 0xb7, 0xe9, 0x32, 0x34, 0xd8, 0xdc, 0x02, 0x12, 0x15, 0x31, 0x3b, 0x3c, - ]; - - let write_blocked_regions = [RegionInfo { - start: 0x0000_0000, - length: 0x0800_0000, - }]; - let mut spi_monitor2 = SpiMonitor::::new( - true, - SpimExtMuxSel::SpimExtMuxSel1, - &allow_cmds, - u8::try_from(allow_cmds.len()).unwrap(), - &[], - 0, - &write_blocked_regions, - u8::try_from(write_blocked_regions.len()).unwrap(), - ); - spi_monitor2.spim_sw_rst(); - spi_monitor2.aspeed_spi_monitor_init(); - //spi_monitor2.spim_ext_mux_config(SpimExtMuxSel::SpimExtMuxSel0); - - spi_monitor2 -} - -#[must_use] -pub fn start_spim3() -> SpiMonitor { - let allow_cmds: [u8; 27] = [ - 0x03, 0x13, 0x0b, 0x0c, 0x6b, 0x6c, 0x01, 0x05, 0x35, 0x06, 0x04, 0x20, 0x21, 0x9f, 0x5a, - 0xb7, 0xe9, 0x32, 0x34, 0xd8, 0xdc, 0x02, 0x12, 0x15, 0x31, 0x3b, 0x3c, - ]; - let read_blocked_regions: [RegionInfo; 3] = [ - RegionInfo { - start: 0x0000_0000, - length: 0x0001_0000, - }, - RegionInfo { - start: 0x0027_4000, - length: 0x0000_4000, - }, - RegionInfo { - start: 0x01E0_0000, - length: 0x0008_0000, - }, - ]; - let write_blocked_regions: [RegionInfo; 3] = [ - RegionInfo { - start: 0x0000_0000, - length: 0x0001_0000, - }, - RegionInfo { - start: 0x013F_C000, - length: 0x0002_8000, - }, - RegionInfo { - start: 0x0FFF_8000, - length: 0x0000_8000, - }, - ]; - let mut spi_monitor3 = SpiMonitor::::new( - true, - SpimExtMuxSel::SpimExtMuxSel1, - &allow_cmds, - u8::try_from(allow_cmds.len()).unwrap(), - &read_blocked_regions, - u8::try_from(read_blocked_regions.len()).unwrap(), - &write_blocked_regions, - u8::try_from(write_blocked_regions.len()).unwrap(), - ); - spi_monitor3.spim_sw_rst(); - spi_monitor3.aspeed_spi_monitor_init(); - //spi_monitor3.spim_ext_mux_config(SpimExtMuxSel::SpimExtMuxSel0); - - spi_monitor3 -} diff --git a/src/spi/traits.rs b/src/spi/traits.rs new file mode 100644 index 0000000..9a10cfe --- /dev/null +++ b/src/spi/traits.rs @@ -0,0 +1,17 @@ +// Licensed under the Apache-2.0 license + +use embedded_hal::spi::{ErrorType, SpiBus}; + +use crate::spi::norflash::SpiNorCommand; + +use super::SpiError; + +pub trait SpiBusWithCs: SpiBus + ErrorType { + fn select_cs(&mut self, cs: usize) -> Result<(), SpiError>; + fn nor_transfer(&mut self, op_info: &mut SpiNorCommand) -> Result<(), SpiError>; + fn nor_read_init(&mut self, cs: usize, op_info: &SpiNorCommand); + fn nor_write_init(&mut self, cs: usize, op_info: &SpiNorCommand); + + fn get_device_info(&mut self, cs: usize) -> (u32, u32); + fn get_master_id(&mut self) -> u32; +} diff --git a/src/spi/types.rs b/src/spi/types.rs new file mode 100644 index 0000000..cd85097 --- /dev/null +++ b/src/spi/types.rs @@ -0,0 +1,88 @@ +// Licensed under the Apache-2.0 license + +use super::consts::ASPEED_MAX_CS; + +#[derive(Clone, Copy)] +pub enum DataDirection { + Read, + Write, + +} + +pub struct FlashAddress { + pub value: u32, + pub width: AddressWidth, +} + +pub enum AddressWidth { + ThreeByte, + FourByte, +} + +#[derive(Clone, Copy)] +pub enum CtrlType { + BootSpi, + HostSpi, + NormalSpi, +} + +#[derive(Clone, Copy)] +pub struct CommandMode { + pub normal_read: u32, + pub normal_write: u32, + pub user: u32, +} + +#[derive(Default, Clone, Copy)] +pub struct SpiDecodeAddress { + pub start: u32, + pub len: u32, +} + +/// Static SPI controller configuration information +pub struct SpiConfig { + pub mmap_base: u32, + pub max_cs: usize, + pub write_block_size: u32, + pub ctrl_type: CtrlType, + pub timing_cali_start_off: u32, + pub master_idx: u32, + pub pure_spi_mode_only: bool, + pub frequency: u32, + pub timing_calibration_start_off: u32, + pub timing_calibration_disabled: bool, +} + +/// Controller state structure +pub struct SpiData { + pub decode_addr: [SpiDecodeAddress; ASPEED_MAX_CS], + pub cmd_mode: [CommandMode; ASPEED_MAX_CS], + pub hclk: u32, + pub spim_proprietary_pre_config: u32, +} + +impl Default for SpiData { + fn default() -> Self { + Self::new() + } +} + +impl SpiData { + #[must_use] + pub const fn new() -> Self { + const ZERO_ADDR: SpiDecodeAddress = SpiDecodeAddress { start: 0, len: 0 }; + const ZERO_CMD: CommandMode = CommandMode { + normal_read: 0, + normal_write: 0, + user: 0, + }; + + Self { + decode_addr: [ZERO_ADDR; ASPEED_MAX_CS], + cmd_mode: [ZERO_CMD; ASPEED_MAX_CS], + hclk: 0, + spim_proprietary_pre_config: 0, + } + } +} + diff --git a/src/spi/util.rs b/src/spi/util.rs new file mode 100644 index 0000000..6f37d11 --- /dev/null +++ b/src/spi/util.rs @@ -0,0 +1,220 @@ +// Licensed under the Apache-2.0 license + +use crate::{ + spi::norflash::{Jesd216Mode}, +}; +use ast1060_pac::Scu; +use super::consts::HPLL_FREQ; + +#[macro_export] +macro_rules! dbg { + ($self:expr, $($arg:tt)*) => {{ + if let Some(ref mut uart) = $self.dbg_uart { + writeln!(uart, $($arg)*).unwrap(); + write!(uart, "\r").unwrap(); + } + }}; +} + +#[inline] +fn hclk_div_reg_to_val(x: u32) -> u32 { + if x == 0 { + 2 + } else { + x + 1 + } +} + +#[must_use] +pub fn get_hclock_rate() -> u32 { + let scu_reg = unsafe { &*Scu::ptr() }; + let raw_div = scu_reg.scu314().read().hclkdivider_sel().bits(); + let clk_div = hclk_div_reg_to_val(u32::from(raw_div)); + + HPLL_FREQ / clk_div +} + +#[must_use] +pub fn spi_io_mode(mode: Jesd216Mode) -> u32 { + match mode { + //Jesd216Mode::Mode111 | Jesd216Mode::Mode111Fast => 0x0000_0000, + Jesd216Mode::Mode112 => 0x2000_0000, + Jesd216Mode::Mode122 => 0x3000_0000, + Jesd216Mode::Mode114 => 0x4000_0000, + Jesd216Mode::Mode144 => 0x5000_0000, + _ => 0, + } +} +#[must_use] +pub fn spi_io_mode_user(bus_width: u32) -> u32 { + match bus_width { + 4 => 0x4000_0000, + 2 => 0x2000_0000, + _ => 0x0000_0000, + } +} +#[must_use] +pub fn spi_cal_dummy_cycle(bus_width: u32, dummy_cycle: u32) -> u32 { + if bus_width == 0 || bus_width > 8 { + return 0; + } + let bits_per_cycle = 8 / bus_width; + if bits_per_cycle == 0 { + return 0; + } + let dummy_byte = dummy_cycle / bits_per_cycle; + ((dummy_byte & 0x3) << 6) | (((dummy_byte & 0x4) >> 2) << 14) +} + +pub const fn get_cmd_buswidth(v: u32) -> u8 { + ((v & 0x0000_0F00) >> 8) as u8 +} +pub const fn get_addr_buswidth(v: u32) -> u8 { + ((v & 0x0000_00F0) >> 4) as u8 +} +pub const fn get_data_buswidth(v: u32) -> u8 { + (v & 0x0000_000F) as u8 +} + +/// Calculate the SPI frequency division setting based on bus clock and max frequency. +/// +/// # Arguments +/// * `bus_clk` - The bus clock frequency in Hz. +/// * `max_freq` - The maximum desired SPI frequency in Hz. +/// +/// # Returns +/// A 32-bit value encoding the frequency divider, +/// or 0 if no valid divider found. + +#[must_use] +pub fn aspeed_get_spi_freq_div(bus_clk: u32, max_freq: u32) -> u32 { + // Division mapping array matching C div_arr + let div_arr = [15, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 0]; + + for i in 0..0x0f { + for (j, div_val) in div_arr.iter().copied().enumerate() { + if i == 0 && j == 0 { + continue; + } + let divisor = j + 1 + (i * 16); + let freq = bus_clk / u32::try_from(divisor).unwrap(); + + if max_freq >= freq { + #[allow(clippy::cast_sign_loss)] + return ((i << 24) | ((div_val as u32) << 8) as usize) + .try_into() + .unwrap(); + } + } + } + // If not found, log error and return 0 (adjust logging as needed) + //log eprintln!("aspeed_get_spi_freq_div: cannot get correct frequency division."); + 0 +} + +/// Finds the midpoint of the longest consecutive sequence of 1's in a buffer. +/// +/// Returns the midpoint index if the longest run is at least length 4, +/// otherwise returns -1. +/// +/// # Arguments +/// * `buf` - slice of bytes (each should be 0 or 1). +#[must_use] +pub fn get_mid_point_of_longest_one(buf: &[u8]) -> i32 { + let mut start = 0; + let mut mid_point = 0; + let mut max_cnt = 0; + let mut cnt = 0; + + for (i, &val) in buf.iter().enumerate() { + if val == 1 { + cnt += 1; + } else { + cnt = 0; + start = i; + } + + if cnt > max_cnt { + max_cnt = cnt; + mid_point = start + (cnt / 2); + } + } + + if max_cnt < 4 { + -1 + } else { + i32::try_from(mid_point).unwrap() + } +} + +#[must_use] +pub fn spi_calibration_enable(buf: &[u8]) -> bool { + if buf.len() < 4 { + return false; + } + + let mut valid_count = 0; + + // Process 4 bytes at a time + for chunk in buf.chunks_exact(4) { + // Convert 4 bytes to u32 in little-endian order + let word = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); + + if word != 0 && word != 0xFFFF_FFFF { + valid_count += 1; + } + if valid_count > 100 { + return true; + } + } + + false +} + +#[allow(clippy::missing_safety_doc)] +pub unsafe fn spi_read_data(ahb_addr: *const u32, read_arr: &mut [u8]) { + let len = read_arr.len(); + let mut i = 0; + + // Read full u32 words + while i + 4 <= len { + let word = core::ptr::read_volatile(ahb_addr.add(i / 4)); + read_arr[i..i + 4].copy_from_slice(&word.to_le_bytes()); // adjust for BE if needed + i += 4; + } + + // Remaining bytes + while i < len { + read_arr[i] = core::ptr::read_volatile((ahb_addr.cast::()).add(i)); + i += 1; + } +} + +#[allow(clippy::missing_safety_doc)] +pub unsafe fn spi_write_data(ahb_addr: *mut u32, write_arr: &[u8]) { + if write_arr.is_empty() { + return; + } + + let len = write_arr.len(); + let mut i = 0; + + // Write in u32 words as long as possible + while i + 4 <= len { + let word = u32::from_le_bytes([ + write_arr[i], + write_arr[i + 1], + write_arr[i + 2], + write_arr[i + 3], + ]); + core::ptr::write_volatile(ahb_addr.add(i / 4), word); + i += 4; + } + + // Write remaining bytes (if any) + let ahb_addr_u8 = ahb_addr.cast::(); + while i < len { + core::ptr::write_volatile(ahb_addr_u8.add(i), write_arr[i]); + i += 1; + } +} diff --git a/src/spimonitor.rs b/src/spimonitor/hardware.rs similarity index 86% rename from src/spimonitor.rs rename to src/spimonitor/hardware.rs index 693606d..d6dc78f 100644 --- a/src/spimonitor.rs +++ b/src/spimonitor/hardware.rs @@ -2,126 +2,13 @@ use ast1060_pac::Scu; use core::cmp::min; -use core::fmt; -use core::marker::PhantomData; -//use core::ops::bit; -//use embedded_hal::delay::DelayNs; - -#[derive(Debug)] -#[repr(u8)] -pub enum SpiMonitorNum { - SPIM0 = 0, - SPIM1 = 1, - SPIM2 = 2, - SPIM3 = 3, -} - -//abstracts register base access for different instances -pub trait SpipfInstance { - fn ptr() -> *const ast1060_pac::spipf::RegisterBlock; - const FILTER_ID: SpiMonitorNum; -} - -macro_rules! macro_spif { - ($Spipfx: ident, $x: path) => { - impl SpipfInstance for ast1060_pac::$Spipfx { - fn ptr() -> *const ast1060_pac::spipf::RegisterBlock { - ast1060_pac::$Spipfx::ptr() - } - const FILTER_ID: SpiMonitorNum = $x; - } - }; -} -macro_spif!(Spipf, SpiMonitorNum::SPIM0); -macro_spif!(Spipf1, SpiMonitorNum::SPIM1); -macro_spif!(Spipf2, SpiMonitorNum::SPIM2); -macro_spif!(Spipf3, SpiMonitorNum::SPIM3); - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(u8)] -pub enum SpimSpiMaster { - SPI1 = 0, - SPI2 = 1, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum SpimPassthroughMode { - SinglePassthrough = 0, - MultiPassthrough = 1, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(u8)] -pub enum SpimExtMuxSel { - SpimExtMuxSel0 = 0, - SpimExtMuxSel1 = 1, -} -impl SpimExtMuxSel { - #[must_use] - pub fn to_bool(self) -> bool { - self as u8 != 0 - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(u8)] -pub enum SpimBlockMode { - SpimDeassertCsEearly = 0, - SpimBlockExtraClk = 1, -} -//address privilege table control -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(u8)] -pub enum AddrPrivRWSel { - AddrPrivReadSel, - AddrPrivWriteSel, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(u8)] -pub enum AddrPriOp { - FlagAddrPrivEnable, - FlagAddrPrivDisable, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(u8)] -pub enum SpiMonitorError { - CommandNotFound(u8), - NoAllowCmdSlotAvail(u32), - InvalidCmdSlotIndex(u32), - AllowCmdSlotLocked(u32), - AllowCmdSlotInvalid(u32), - AddressInvalid(u32), - LengthInvalid(u32), - AddrTblRegsLocked(u32), -} -//Allow command table information -pub const SPIM_CMD_TABLE_NUM: usize = 32; -pub const MAX_CMD_INDEX: usize = 31; -pub const BLOCK_REGION_NUM: usize = 32; -//generic type -pub struct SpiMonitor { - pub spi_monitor: &'static ast1060_pac::spipf::RegisterBlock, - pub scu: &'static ast1060_pac::scu::RegisterBlock, - pub extra_clk_en: bool, - pub force_rel_flash_rst: bool, - pub ext_mux_sel: SpimExtMuxSel, - pub allow_cmd_list: [u8; SPIM_CMD_TABLE_NUM], - pub allow_cmd_num: u8, - pub read_blocked_regions: [RegionInfo; BLOCK_REGION_NUM], - pub read_blocked_region_num: u8, - pub write_blocked_regions: [RegionInfo; BLOCK_REGION_NUM], - pub write_blocked_region_num: u8, - _marker: PhantomData, -} - -impl fmt::Debug for SpiMonitor { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("SpiMonitor") - } -} +use crate::spimonitor::{ + AddrPriOp, AddrPrivRWSel, RegionInfo, SpiMonitor, SpiMonitorError, SpiMonitorNum, + SpimBlockMode, SpimExtMuxSel, SpimPassthroughMode, SpimSpiMaster, SpipfInstance, + BLOCK_REGION_NUM, MAX_CMD_INDEX, SPIM_CMD_TABLE_NUM, +}; +use core::marker::PhantomData; //Address table selection majic value pub const SEL_READ_TBL_MAJIC: u32 = 0x52 << 24; @@ -208,12 +95,6 @@ pub struct CmdTableInfo { cmd_table_val: u32, } -#[derive(Debug, Clone, Copy)] -pub struct RegionInfo { - pub start: u32, - pub length: u32, -} - //#[derive(Debug, Clone, Copy)] //pub struct GpioInfo { @@ -245,7 +126,7 @@ pub const fn cmd_table_value( | cmd } -pub fn spim_get_cmd_table_val(cmd: u8) -> Result { +fn spim_get_cmd_table_val(cmd: u8) -> Result { for entry in CMDS_ARRAY { if entry.cmd == cmd { return Ok(entry.cmd_table_val); @@ -482,25 +363,13 @@ impl SpiMonitor { _marker: PhantomData, } } - pub fn spim_scu_ctrl_set(&mut self, mask: u32, val: u32) { - let mut reg_val = self.scu.scu0f0().read().bits(); - reg_val &= !mask; - reg_val |= val; - self.scu.scu0f0().write(|w| unsafe { w.bits(reg_val) }); - } - - pub fn spim_scu_ctrl_clear(&mut self, clear_bits: u32) { - let mut reg_val = self.scu.scu0f0().read().bits(); - reg_val &= !clear_bits; - self.scu.scu0f0().write(|w| unsafe { w.bits(reg_val) }); - } //SPI_M1: GPIOA6, SCU610[6], SPI master CS output //SPI_M2: GPIOC4, SCU610[20] //SPI_M3: GPIOE2, SCU614[2] (dummy, cannot be disabled) //SPI_M4: GPIOG0, SCU614[16] //disable chip select internal pull down - pub fn spim_disable_cs_internal_pd(&mut self) { + pub(crate) fn spim_disable_cs_internal_pd(&mut self) { //SPIPF::FILTER_ID match SPIPF::FILTER_ID { SpiMonitorNum::SPIM0 => { @@ -522,7 +391,8 @@ impl SpiMonitor { } } //Enable MISO - pub fn spim_miso_multi_func_adjust(&mut self, enable: bool) { + #[allow(dead_code)] + pub(crate) fn spim_miso_multi_func_adjust(&mut self, enable: bool) { match SPIPF::FILTER_ID { SpiMonitorNum::SPIM0 => { self.scu @@ -548,7 +418,7 @@ impl SpiMonitor { } //enable/disable pass through - pub fn spim_scu_passthrough_mode(&mut self, passthrough_en: bool) { + pub(crate) fn spim_scu_passthrough_mode(&mut self, passthrough_en: bool) { self.scu.scu0f0().modify(|_, w| match SPIPF::FILTER_ID { SpiMonitorNum::SPIM0 => w.enbl_passthrough_of_spipf1().bit(passthrough_en), SpiMonitorNum::SPIM1 => w.enbl_passthrough_of_spipf2().bit(passthrough_en), @@ -558,7 +428,7 @@ impl SpiMonitor { } //set passthrough mode - pub fn spim_passthrough_mode_set(&mut self, mode: SpimPassthroughMode) { + pub(crate) fn spim_passthrough_mode_set(&mut self, mode: SpimPassthroughMode) { match mode { SpimPassthroughMode::SinglePassthrough => { self.spi_monitor @@ -578,7 +448,7 @@ impl SpiMonitor { //Internal SPI master selection //Select internal SPI master connection - pub fn spim_spi_ctrl_detour_enable(&mut self, spi_master: SpimSpiMaster, enable: bool) { + pub(crate) fn spim_spi_ctrl_detour_enable(&mut self, spi_master: SpimSpiMaster, enable: bool) { if enable { self.scu.scu0f0().modify(|_, w| unsafe { w.select_int_spimaster_connection() @@ -592,11 +462,18 @@ impl SpiMonitor { .scu0f0() .modify(|_, w| w.int_spimaster_sel().bit(bit_val)); } else { - self.spim_scu_ctrl_clear(0xF); + //self.spim_scu_ctrl_clear(0xF); + let mut reg_val = self.scu.scu0f0().read().bits(); + reg_val &= !0xF; + self.scu.scu0f0().write(|w| unsafe { w.bits(reg_val) }); } } - pub fn spim_passthrough_config(&mut self, passthrough_en: bool, mode: SpimPassthroughMode) { + pub(crate) fn spim_passthrough_config( + &mut self, + passthrough_en: bool, + mode: SpimPassthroughMode, + ) { // self.spim_scu_passthrough_enable(passthrough_en); if passthrough_en { self.spim_passthrough_mode_set(mode); @@ -610,7 +487,7 @@ impl SpiMonitor { } } //External Mux Selection Signal - pub fn spim_ext_mux_config(&mut self, mux_sel: SpimExtMuxSel) { + pub(crate) fn spim_ext_mux_config(&mut self, mux_sel: SpimExtMuxSel) { assert!(mux_sel as u32 <= SpimExtMuxSel::SpimExtMuxSel1 as u32); let bit_val = mux_sel.to_bool(); match SPIPF::FILTER_ID { @@ -639,7 +516,7 @@ impl SpiMonitor { // //Block Mode: Block a command by one extra CLK //Block a command by deasserting CS early - pub fn spim_block_mode_config(&mut self, block_mode: SpimBlockMode) { + pub(crate) fn spim_block_mode_config(&mut self, block_mode: SpimBlockMode) { if block_mode == SpimBlockMode::SpimBlockExtraClk { self.spi_monitor .spipf000() @@ -651,7 +528,11 @@ impl SpiMonitor { } } - pub fn spim_write_fixed_loc_in_allow_cmd_table(&mut self, cmd: u8, reg_val: u32) -> usize { + pub(crate) fn spim_write_fixed_loc_in_allow_cmd_table( + &mut self, + cmd: u8, + reg_val: u32, + ) -> usize { match cmd { CMD_EN4B => { self.spi_monitor @@ -676,7 +557,7 @@ impl SpiMonitor { } //init allow commands table registers //0, 1 and 31 are reserved for the particular commands - pub fn spim_allow_cmd_table_init(&mut self, cmd_list: &[u8], cmd_num: u8, flag: u32) { + pub(crate) fn spim_allow_cmd_table_init(&mut self, cmd_list: &[u8], cmd_num: u8, flag: u32) { let mut index = 1; let list_size = min(cmd_num as usize, cmd_list.len()); for &cmd in cmd_list.iter().take(list_size) { @@ -707,7 +588,8 @@ impl SpiMonitor { } } - pub fn spim_get_empty_allow_cmd_slot(&mut self) -> Result { + #[allow(dead_code)] + pub(crate) fn spim_get_empty_allow_cmd_slot(&mut self) -> Result { for index in 2..SPIM_CMD_TABLE_NUM { let reg_val = self.spi_monitor.spipfwt(index).read().bits(); if reg_val == 0 { @@ -719,7 +601,7 @@ impl SpiMonitor { )) } - pub fn spim_get_allow_cmd_slot( + pub(crate) fn spim_get_allow_cmd_slot( &mut self, cmd: u8, start_offset: u32, @@ -738,7 +620,12 @@ impl SpiMonitor { Err(SpiMonitorError::CommandNotFound(cmd)) } - pub fn spim_add_new_command(&mut self, cmd: u8, flag: u32) -> Result { + #[allow(dead_code)] + pub(crate) fn spim_add_new_command( + &mut self, + cmd: u8, + flag: u32, + ) -> Result { // Retrieve the command table value let Ok(mut reg_val) = spim_get_cmd_table_val(cmd) else { return Err(SpiMonitorError::CommandNotFound(cmd)); @@ -772,7 +659,12 @@ impl SpiMonitor { // If the command doesn't exist in allow command table, an // empty slot will be found and the command info will be // filled into. - pub fn spim_add_allow_command(&mut self, cmd: u8, flag: u32) -> Result { + #[allow(dead_code)] + pub(crate) fn spim_add_allow_command( + &mut self, + cmd: u8, + flag: u32, + ) -> Result { //check if the command is already in allow command Table let mut offset: u32 = 0; @@ -808,7 +700,8 @@ impl SpiMonitor { } //All command table slots where command is equal to "cmd", valid and not locked //will be removed - pub fn spim_remove_allow_command(&mut self, cmd: u8) -> Result { + #[allow(dead_code)] + pub(crate) fn spim_remove_allow_command(&mut self, cmd: u8) -> Result { //check if the command is already in allow command Table let mut offset: u32 = 0; let mut count: u32 = 0; @@ -847,7 +740,7 @@ impl SpiMonitor { //- All command table slot which command is equal to "cmd" // parameter will be locked. // - pub fn spim_lock_allow_command_table( + pub(crate) fn spim_lock_allow_command_table( &mut self, cmd: u8, flag: u32, @@ -888,7 +781,7 @@ impl SpiMonitor { Ok(count) } } - pub fn spim_is_pri_regs_locked(&mut self, rw_select: AddrPrivRWSel) -> bool { + pub(crate) fn spim_is_pri_regs_locked(&mut self, rw_select: AddrPrivRWSel) -> bool { match rw_select { AddrPrivRWSel::AddrPrivWriteSel => { if self.spi_monitor.spipf07c().read().wr_dis_of_spipfwa().bit() { @@ -903,7 +796,7 @@ impl SpiMonitor { } false } - pub fn spim_addr_priv_access_enable(&mut self, priv_sel: AddrPrivRWSel) { + pub(crate) fn spim_addr_priv_access_enable(&mut self, priv_sel: AddrPrivRWSel) { let mut reg_val = self.spi_monitor.spipf000().read().bits(); //mask out the upper 8 bits reg_val &= 0x00FF_FFFF; @@ -918,7 +811,8 @@ impl SpiMonitor { } //get aligned length - pub fn spim_get_adjusted_addr_len(&mut self, addr: u32, len: u32) -> (u32, u32) { + #[allow(clippy::unused_self)] + pub(crate) fn spim_get_adjusted_addr_len(&mut self, addr: u32, len: u32) -> (u32, u32) { if len == 0 { return (addr, 0); } @@ -937,12 +831,13 @@ impl SpiMonitor { //Each bit defines permission of one 16KB block //Calculate numbers of 16KB blocks //Start address may cross two different 16KB blocks - pub fn spim_get_total_block_num(&mut self, addr: u32, len: u32) -> u32 { + #[allow(dead_code)] + pub(crate) fn spim_get_total_block_num(&mut self, addr: u32, len: u32) -> u32 { let (_aligned_addr, adjusted_len) = self.spim_get_adjusted_addr_len(addr, len); adjusted_len / ACCESS_BLOCK_UNIT } - pub fn spim_address_privilege_config( + pub(crate) fn spim_address_privilege_config( &mut self, rw_select: AddrPrivRWSel, priv_op: AddrPriOp, @@ -1009,7 +904,7 @@ impl SpiMonitor { Ok(total_blocks) } //lock all SPIPFWA/RA for writing - pub fn spim_lock_rw_priv_table(&mut self, rw_select: AddrPrivRWSel) { + pub(crate) fn spim_lock_rw_priv_table(&mut self, rw_select: AddrPrivRWSel) { if rw_select == AddrPrivRWSel::AddrPrivWriteSel { self.spi_monitor .spipf07c() @@ -1022,7 +917,7 @@ impl SpiMonitor { } } // - pub fn spim_lock_common(&mut self) { + pub(crate) fn spim_lock_common(&mut self) { self.spim_lock_rw_priv_table(AddrPrivRWSel::AddrPrivReadSel); self.spim_lock_rw_priv_table(AddrPrivRWSel::AddrPrivWriteSel); let _ = self.spim_lock_allow_command_table(0, FLAG_CMD_TABLE_LOCK_ALL); @@ -1045,7 +940,8 @@ impl SpiMonitor { .bit(true) }); } - pub fn spim_set_read_blocked_regions( + #[allow(dead_code)] + pub(crate) fn spim_set_read_blocked_regions( &mut self, read_blocked_regions: &[RegionInfo], read_blocked_region_num: u8, @@ -1055,7 +951,8 @@ impl SpiMonitor { self.read_blocked_region_num = read_blocked_region_num; } - pub fn spim_set_write_blocked_regions( + #[allow(dead_code)] + pub(crate) fn spim_set_write_blocked_regions( &mut self, write_blocked_regions: &[RegionInfo], write_blocked_region_num: u8, @@ -1066,17 +963,19 @@ impl SpiMonitor { } // supported command list - pub fn spim_set_cmd_table(&mut self, allow_cmd_list: &[u8], allow_cmd_num: u8) { + #[allow(dead_code)] + pub(crate) fn spim_set_cmd_table(&mut self, allow_cmd_list: &[u8], allow_cmd_num: u8) { self.allow_cmd_list[..allow_cmd_num as usize] .copy_from_slice(&allow_cmd_list[..allow_cmd_num as usize]); self.allow_cmd_num = allow_cmd_num; } - - pub fn spim_dump_read_blocked_regions(&mut self) {} - pub fn spim_dump_write_blocked_regions(&mut self) {} + #[allow(clippy::unused_self, dead_code)] + pub(crate) fn spim_dump_read_blocked_regions(&mut self) {} + #[allow(clippy::unused_self, dead_code)] + pub(crate) fn spim_dump_write_blocked_regions(&mut self) {} //Block read and write to regions - pub fn spim_rw_perm_init(&mut self) { + pub(crate) fn spim_rw_perm_init(&mut self) { //Enable previliege control for 256MB area let _ = self.spim_address_privilege_config( AddrPrivRWSel::AddrPrivReadSel, @@ -1110,7 +1009,7 @@ impl SpiMonitor { } } //Enable/disable SPI monitor from SCU - pub fn spim_scu_monitor_config(&mut self, enable: bool) { + pub(crate) fn spim_scu_monitor_config(&mut self, enable: bool) { match SPIPF::FILTER_ID { SpiMonitorNum::SPIM0 => { self.scu @@ -1135,20 +1034,20 @@ impl SpiMonitor { } } //Enable/disable SPI monitor/filter function - pub fn spim_ctrl_monitor_config(&mut self, enable: bool) { + pub(crate) fn spim_ctrl_monitor_config(&mut self, enable: bool) { self.spi_monitor .spipf000() .modify(|_, w| w.enbl_filter_fn().bit(enable)); } - pub fn spim_monitor_enable(&mut self, enable: bool) { + pub(crate) fn spim_enable(&mut self, enable: bool) { self.spim_ctrl_monitor_config(enable); // self.spim_miso_multi_func_adjust(enable); self.spim_passthrough_config(enable, SpimPassthroughMode::SinglePassthrough); } //Use SCU0F0 to enable flash rst - pub fn spim_release_flash_rst(&mut self) { + pub(crate) fn spim_release_flash_rst(&mut self) { //SCU0F0[23:20]: Reset source selection //SCU0F0[27:24]: Enable reset signal output match SPIPF::FILTER_ID { @@ -1214,7 +1113,7 @@ impl SpiMonitor { } //Enable push pull mode - pub fn spim_push_pull_mode_config(&mut self) { + pub(crate) fn spim_push_pull_mode_config(&mut self) { self.spi_monitor .spipf004() .modify(|_, w| w.enbl_push_pull_mode().bit(true)); @@ -1225,7 +1124,7 @@ impl SpiMonitor { self.spim_scu_monitor_config(true); } - pub fn spim_irq_enable(&mut self) { + pub(crate) fn spim_irq_enable(&mut self) { self.spi_monitor.spipf004().modify(|_, w| { w.enbl_intof_cmd_block() .bit(true) @@ -1235,8 +1134,9 @@ impl SpiMonitor { .bit(true) }); } - pub fn spim_abnormal_log_init(&mut self) {} - pub fn spim_sw_rst(&mut self) { + #[allow(clippy::unused_self, dead_code)] + pub(crate) fn spim_abnormal_log_init(&mut self) {} + pub(crate) fn spim_sw_rst(&mut self) { self.spi_monitor .spipf000() .modify(|_, w| w.sweng_rst().bit(true)); @@ -1252,7 +1152,7 @@ impl SpiMonitor { //SCU410, SCU414[27:0], SCU4B4 must keep at value 0x0 //SPIM0 pin ctrl //SCU410/4B0/690[13:0], rstin: SCU414/4B4/694[24] - pub fn spim_enbl_spim0_pin_ctrl(&mut self) { + pub(crate) fn spim_enbl_spim0_pin_ctrl(&mut self) { // Clear SCU4B0[13:0] let mut reg_val = self.scu.scu4b0().read().bits(); reg_val &= !0x3FFF; @@ -1269,7 +1169,7 @@ impl SpiMonitor { //SCU410, SCU41C[7:0], SCU4B4, 4BC[24:0] must keep at value 0x0 //SPIM1 pin ctrl //SCU410/4B0/690[27:14], rstin:41C/4BC/69C[9] - pub fn spim_enbl_spim1_pin_ctrl(&mut self) { + pub(crate) fn spim_enbl_spim1_pin_ctrl(&mut self) { // Clear SCU4B0[14:27] let mut reg_val = self.scu.scu4b0().read().bits(); reg_val &= !(0x3FFF << 14); @@ -1292,7 +1192,7 @@ impl SpiMonitor { //SPIM2 pin ctrl //SCU410/4B0/690[31:28], SCU414/4B4/694[9:0] //rstin SCU414/4B4/694[25] - pub fn spim_enbl_spim2_pin_ctrl(&mut self) { + pub(crate) fn spim_enbl_spim2_pin_ctrl(&mut self) { //Clear SCU4B0[31:28] let mut reg_val = self.scu.scu4b0().read().bits(); reg_val &= !(0xF << 28); @@ -1312,7 +1212,7 @@ impl SpiMonitor { } //SCU410, SCU41C[7:0], SCU4B4, 4BC[24:0] must keep at value 0x0 //SCU414/4B4/694[23:10]:SPIM3 pin ctrl rstin,41C/4BC/69C[11] - pub fn spim_enbl_spim3_pin_ctrl(&mut self) { + pub(crate) fn spim_enbl_spim3_pin_ctrl(&mut self) { //Set SCU694[23:10] let mut reg_val = self.scu.scu694().read().bits(); reg_val |= 0x3FFF << 10; @@ -1325,7 +1225,7 @@ impl SpiMonitor { .modify(|_, w| w.enbl_qspimonitor4rstin_fn_pin().bit(true)); } //SCU410,SCU414[27:0],SCU41C[7:0],SCU4B4,4BC[24:0], must be kept as 0x0 - pub fn spim_pin_ctrl_config(&mut self) { + pub(crate) fn spim_pin_ctrl_config(&mut self) { match SPIPF::FILTER_ID { SpiMonitorNum::SPIM0 => { self.spim_enbl_spim0_pin_ctrl(); @@ -1341,7 +1241,7 @@ impl SpiMonitor { } } } - pub fn aspeed_spi_monitor_init(&mut self) { + pub(crate) fn aspeed_spi_monitor_init(&mut self) { let allow_cmd_list = self.allow_cmd_list; let allow_cmd_num = self.allow_cmd_num; @@ -1359,7 +1259,7 @@ impl SpiMonitor { } self.spim_allow_cmd_table_init(&allow_cmd_list, allow_cmd_num, 0); self.spim_rw_perm_init(); - self.spim_monitor_enable(true); + self.spim_enable(true); //log info init //self.spim_abnormal_log_init(); diff --git a/src/spimonitor/mod.rs b/src/spimonitor/mod.rs new file mode 100644 index 0000000..fe5a5a8 --- /dev/null +++ b/src/spimonitor/mod.rs @@ -0,0 +1,256 @@ +// Licensed under the Apache-2.0 license + +use core::fmt; +use core::marker::PhantomData; +pub mod hardware; + +#[derive(Debug, Copy, Clone)] +#[repr(u8)] +pub enum SpiMonitorNum { + SPIM0 = 0, + SPIM1 = 1, + SPIM2 = 2, + SPIM3 = 3, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum SpimSpiMaster { + SPI1 = 0, + SPI2 = 1, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum SpimPassthroughMode { + SinglePassthrough = 0, + MultiPassthrough = 1, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum SpimExtMuxSel { + SpimExtMuxSel0 = 0, + SpimExtMuxSel1 = 1, +} +impl SpimExtMuxSel { + #[must_use] + pub fn to_bool(self) -> bool { + self as u8 != 0 + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum SpimBlockMode { + SpimDeassertCsEearly = 0, + SpimBlockExtraClk = 1, +} + +//address privilege table control +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum AddrPrivRWSel { + AddrPrivReadSel, + AddrPrivWriteSel, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum AddrPriOp { + FlagAddrPrivEnable, + FlagAddrPrivDisable, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum SpiMonitorError { + CommandNotFound(u8), + NoAllowCmdSlotAvail(u32), + InvalidCmdSlotIndex(u32), + AllowCmdSlotLocked(u32), + AllowCmdSlotInvalid(u32), + AddressInvalid(u32), + LengthInvalid(u32), + AddrTblRegsLocked(u32), +} + +//abstracts register base access for different instances +pub trait SpipfInstance { + fn ptr() -> *const ast1060_pac::spipf::RegisterBlock; + const FILTER_ID: SpiMonitorNum; +} + +macro_rules! macro_spif { + ($Spipfx: ident, $x: path) => { + impl SpipfInstance for ast1060_pac::$Spipfx { + fn ptr() -> *const ast1060_pac::spipf::RegisterBlock { + ast1060_pac::$Spipfx::ptr() + } + const FILTER_ID: SpiMonitorNum = $x; + } + }; +} +macro_spif!(Spipf, SpiMonitorNum::SPIM0); +macro_spif!(Spipf1, SpiMonitorNum::SPIM1); +macro_spif!(Spipf2, SpiMonitorNum::SPIM2); +macro_spif!(Spipf3, SpiMonitorNum::SPIM3); + +#[derive(Debug, Clone, Copy)] +pub struct RegionInfo { + pub start: u32, + pub length: u32, +} + +//Allow command table information +pub const SPIM_CMD_TABLE_NUM: usize = 32; +pub const MAX_CMD_INDEX: usize = 31; +pub const BLOCK_REGION_NUM: usize = 32; +//generic type +pub struct SpiMonitor { + pub spi_monitor: &'static ast1060_pac::spipf::RegisterBlock, + pub scu: &'static ast1060_pac::scu::RegisterBlock, + pub extra_clk_en: bool, + pub force_rel_flash_rst: bool, + pub ext_mux_sel: SpimExtMuxSel, + pub allow_cmd_list: [u8; SPIM_CMD_TABLE_NUM], + pub allow_cmd_num: u8, + pub read_blocked_regions: [RegionInfo; BLOCK_REGION_NUM], + pub read_blocked_region_num: u8, + pub write_blocked_regions: [RegionInfo; BLOCK_REGION_NUM], + pub write_blocked_region_num: u8, + _marker: PhantomData, +} + +impl fmt::Debug for SpiMonitor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("SpiMonitor") + } +} + +// public traits for spimonitor +// Keep these traits tiny and object-safe (no generics, no Self returns). +pub trait SpiMonitorInit { + fn init(&mut self); + fn sw_reset(&mut self); + fn ext_mux_config(&mut self, mux_sel: SpimExtMuxSel); +} + +pub trait SpiMonitorOps { + fn enable(&mut self); + fn diable(&mut self); + fn passthrough_config(&mut self, passthrough_en: bool, mode: SpimPassthroughMode); + fn spi_ctrl_detour_enable(&mut self, spi_master: SpimSpiMaster, enable: bool); + fn block_mode_config(&mut self, block_mode: SpimBlockMode); + fn lock_common(&mut self); +} + +pub trait PrivilegeCtrl { + fn addr_priv_enable(&mut self, rw: AddrPrivRWSel); + fn address_privilege_config(&mut self, rw: AddrPrivRWSel, op: AddrPriOp, addr: u32, len: u32); + fn lock_rw_privilege_table(&mut self, rw: AddrPrivRWSel); +} + +pub trait AllowCmdCtrl { + fn get_cmd_table_val(&mut self, cmd: u8) -> Result; + fn set_cmd_table(&mut self, cmd_list: &[u8], cmd_num: u8); + fn init_allow_cmd_table(&mut self, cmd_list: &[u8], cmd_num: u8, flags: u32); + fn first_empty_slot(&mut self) -> Result; + fn find_allow_cmd_slot(&mut self, cmd: u8, start_offset: u32) -> Result; + fn add_allow_cmd(&mut self, cmd: u8, flags: u32) -> Result; + fn remove_allow_cmd(&mut self, cmd: u8) -> Result; + fn lock_allow_cmd(&mut self, cmd: u8, flags: u32) -> Result; +} + +// calling the functions within the same crate, so use inline +impl SpiMonitorInit for SpiMonitor { + #[inline] + fn init(&mut self) { + self.aspeed_spi_monitor_init(); + } + #[inline] + fn sw_reset(&mut self) { + self.spim_sw_rst(); + } + #[inline] + fn ext_mux_config(&mut self, mux_sel: SpimExtMuxSel) { + self.spim_ext_mux_config(mux_sel); + } +} + +impl SpiMonitorOps for SpiMonitor { + #[inline] + fn passthrough_config(&mut self, passthrough_en: bool, mode: SpimPassthroughMode) { + self.spim_passthrough_config(passthrough_en, mode); + } + #[inline] + fn spi_ctrl_detour_enable(&mut self, spi_master: SpimSpiMaster, enable: bool) { + self.spim_spi_ctrl_detour_enable(spi_master, enable); + } + #[inline] + fn block_mode_config(&mut self, block_mode: SpimBlockMode) { + self.spim_block_mode_config(block_mode); + } + #[inline] + fn enable(&mut self) { + self.spim_enable(true); + } + #[inline] + fn diable(&mut self) { + self.spim_enable(false); + } + #[inline] + fn lock_common(&mut self) { + self.spim_lock_common(); + } +} +/* +impl AllowCmdCtrl for SpiMonitor { + #[inline] + fn get_cmd_table_val(&mut self, cmd: u8) -> Result { + self.spim_get_cmd_table_val(cmd) + } + #[inline] + fn set_cmd_table(&mut self, list: &[u8], num: u8) { + self.spim_set_cmd_table(list, num) + } + #[inline] + fn init_allow_cmd_table(&mut self, list: &[u8], num: u8, flags: u32) { + self.spim_allow_cmd_table_init(list, num, flags) + } + #[inline] + fn first_empty_slot(&mut self) -> Result { + self.spim_get_empty_allow_cmd_slot() + } + #[inline] + fn find_allow_cmd_slot(&mut self, cmd: u8, start: u32) -> Result { + self.spim_get_allow_cmd_slot(cmd, start) + } + #[inline] + fn add_allow_cmd(&mut self, cmd: u8, flags: u32) -> Result { + self.spim_add_allow_command(cmd, flags) + } + #[inline] + fn remove_allow_cmd(&mut self, cmd: u8) -> Result { + self.spim_remove_allow_command(cmd) + } + #[inline] + fn lock_allow_cmd(&mut self, cmd: u8, flags: u32) -> Result { + self.spim_lock_allow_command_table(cmd, flags) + } +} + +impl PrivilegeCtrl for SpiMonitor { + #[inline] + fn addr_priv_enable(&mut self, rw: AddrPrivRWSel) { + self.spim_addr_priv_access_enable(rw); + } + #[inline] + fn address_privilege_config(&mut self, rw: AddrPrivRWSel, op: AddrPriOp, addr: u32, len: u32) { + self.spim_address_privilege_config(rw, op, addr, len); + } + #[inline] + fn lock_rw_privilege_table(&mut self, rw: AddrPrivRWSel) { + self.spim_lock_rw_priv_table(rw); + } +} +*/ diff --git a/src/tests/functional/mod.rs b/src/tests/functional/mod.rs index 3206b64..170efd8 100644 --- a/src/tests/functional/mod.rs +++ b/src/tests/functional/mod.rs @@ -6,3 +6,4 @@ pub mod hash_test; pub mod hmac_test; pub mod rsa_test; pub mod rsa_test_vec; +pub mod spim_test; diff --git a/src/tests/functional/spim_test.rs b/src/tests/functional/spim_test.rs new file mode 100644 index 0000000..815368c --- /dev/null +++ b/src/tests/functional/spim_test.rs @@ -0,0 +1,167 @@ +// Licensed under the Apache-2.0 license + +use ast1060_pac::{Spipf, Spipf1, Spipf2, Spipf3}; + +use crate::astdebug; +use crate::spimonitor::{RegionInfo, SpiMonitor, SpiMonitorInit, SpimExtMuxSel}; +use crate::uart::UartController; +use embedded_io::Write; + +pub const SPIM1_BASE: usize = 0x7e79_1000; +pub const SPIM2_BASE: usize = 0x7e79_2000; +pub const SPIM3_BASE: usize = 0x7e79_3000; +pub const SPIM4_BASE: usize = 0x7e79_4000; + +//follow DTS configuration examples +pub fn test_spim0(uart: &mut UartController<'_>) { + uart.write_all(b"\r\n####### SPIM0 setup #######\r\n") + .unwrap(); + let allow_cmds: [u8; 27] = [ + 0x03, 0x13, 0x0b, 0x0c, 0x6b, 0x6c, 0x01, 0x05, 0x35, 0x06, 0x04, 0x20, 0x21, 0x9f, 0x5a, + 0xb7, 0xe9, 0x32, 0x34, 0xd8, 0xdc, 0x02, 0x12, 0x15, 0x31, 0x3b, 0x3c, + ]; + + let read_blocked_regions = [RegionInfo { + /*pfm*/ + start: 0x0300_0000, + length: 0x0004_0000, + }]; + + let write_blocked_regions = [RegionInfo { + start: 0x0000_0000, + length: 0x0020_0000, + }]; + let mut spi_monitor0 = SpiMonitor::::new( + true, + SpimExtMuxSel::SpimExtMuxSel1, + &allow_cmds, + u8::try_from(allow_cmds.len()).unwrap(), + &read_blocked_regions, + u8::try_from(read_blocked_regions.len()).unwrap(), + &write_blocked_regions, + u8::try_from(write_blocked_regions.len()).unwrap(), + ); + spi_monitor0.sw_reset(); + spi_monitor0.init(); + spi_monitor0.ext_mux_config(SpimExtMuxSel::SpimExtMuxSel0); + if false { + astdebug::print_reg_u32(uart, SPIM1_BASE, 0x80); + astdebug::print_reg_u32(uart, SPIM1_BASE + 0x70, 0x80); + astdebug::print_reg_u32(uart, SPIM1_BASE + 0x100, 0x80); + astdebug::print_reg_u32(uart, SPIM1_BASE + 0x300, 0x100); + astdebug::print_reg_u32(uart, SPIM1_BASE + 0x400, 0x80); + } + + let scu_qspi_mux: &mut [u32] = + unsafe { core::slice::from_raw_parts_mut((SPIM1_BASE) as *mut u32, 4) }; + scu_qspi_mux[0] = 0x5200_0004; + if false { + astdebug::print_reg_u32(uart, SPIM1_BASE, 0x80); + astdebug::print_reg_u32(uart, SPIM1_BASE + 0x70, 0x80); + astdebug::print_reg_u32(uart, SPIM1_BASE + 0x100, 0x80); + astdebug::print_reg_u32(uart, SPIM1_BASE + 0x200, 0x100); + astdebug::print_reg_u32(uart, SPIM1_BASE + 0x300, 0x100); + } + + // print spim pointer value +} + +pub fn test_spim1() { + let allow_cmds: [u8; 27] = [ + 0x03, 0x13, 0x0b, 0x0c, 0x6b, 0x6c, 0x01, 0x05, 0x35, 0x06, 0x04, 0x20, 0x21, 0x9f, 0x5a, + 0xb7, 0xe9, 0x32, 0x34, 0xd8, 0xdc, 0x02, 0x12, 0x15, 0x31, 0x3b, 0x3c, + ]; + + let write_blocked_regions = [RegionInfo { + start: 0x0000_0000, + length: 0x0800_0000, + }]; + let mut spi_monitor1 = SpiMonitor::::new( + true, + SpimExtMuxSel::SpimExtMuxSel1, + &allow_cmds, + u8::try_from(allow_cmds.len()).unwrap(), + &[], + 0, + &write_blocked_regions, + u8::try_from(write_blocked_regions.len()).unwrap(), + ); + spi_monitor1.sw_reset(); + spi_monitor1.init(); + spi_monitor1.ext_mux_config(SpimExtMuxSel::SpimExtMuxSel0); +} + +pub fn test_spim2() { + let allow_cmds: [u8; 27] = [ + 0x03, 0x13, 0x0b, 0x0c, 0x6b, 0x6c, 0x01, 0x05, 0x35, 0x06, 0x04, 0x20, 0x21, 0x9f, 0x5a, + 0xb7, 0xe9, 0x32, 0x34, 0xd8, 0xdc, 0x02, 0x12, 0x15, 0x31, 0x3b, 0x3c, + ]; + + let write_blocked_regions = [RegionInfo { + start: 0x0000_0000, + length: 0x0800_0000, + }]; + let mut spi_monitor2 = SpiMonitor::::new( + true, + SpimExtMuxSel::SpimExtMuxSel1, + &allow_cmds, + u8::try_from(allow_cmds.len()).unwrap(), + &[], + 0, + &write_blocked_regions, + u8::try_from(write_blocked_regions.len()).unwrap(), + ); + spi_monitor2.sw_reset(); + spi_monitor2.init(); + spi_monitor2.ext_mux_config(SpimExtMuxSel::SpimExtMuxSel0); +} + +pub fn test_spim3(uart: &mut UartController<'_>) { + uart.write_all(b"\r\n####### SPIM3 setup #######\r\n") + .unwrap(); + let allow_cmds: [u8; 27] = [ + 0x03, 0x13, 0x0b, 0x0c, 0x6b, 0x6c, 0x01, 0x05, 0x35, 0x06, 0x04, 0x20, 0x21, 0x9f, 0x5a, + 0xb7, 0xe9, 0x32, 0x34, 0xd8, 0xdc, 0x02, 0x12, 0x15, 0x31, 0x3b, 0x3c, + ]; + let read_blocked_regions: [RegionInfo; 3] = [ + RegionInfo { + start: 0x0000_0000, + length: 0x0001_0000, + }, + RegionInfo { + start: 0x0027_4000, + length: 0x0000_4000, + }, + RegionInfo { + start: 0x01E0_0000, + length: 0x0008_0000, + }, + ]; + let write_blocked_regions: [RegionInfo; 3] = [ + RegionInfo { + start: 0x0000_0000, + length: 0x0001_0000, + }, + RegionInfo { + start: 0x013F_C000, + length: 0x0002_8000, + }, + RegionInfo { + start: 0x0FFF_8000, + length: 0x0000_8000, + }, + ]; + let mut spi_monitor3 = SpiMonitor::::new( + true, + SpimExtMuxSel::SpimExtMuxSel1, + &allow_cmds, + u8::try_from(allow_cmds.len()).unwrap(), + &read_blocked_regions, + u8::try_from(read_blocked_regions.len()).unwrap(), + &write_blocked_regions, + u8::try_from(write_blocked_regions.len()).unwrap(), + ); + spi_monitor3.sw_reset(); + spi_monitor3.init(); + spi_monitor3.ext_mux_config(SpimExtMuxSel::SpimExtMuxSel0); +}