add spi2 master controller test. now it can get jedec id from flash #3 resolved Conflicts:
diff --git a/target/ast10x0/peripherals/scu/pinctrl.rs b/target/ast10x0/peripherals/scu/pinctrl.rs index d399716..9d90f54 100644 --- a/target/ast10x0/peripherals/scu/pinctrl.rs +++ b/target/ast10x0/peripherals/scu/pinctrl.rs
@@ -537,6 +537,10 @@ gen_pin_pairs!(SCU6B0, 0x6B0, 31); } +// GPIO +pub const PINCTRL_GPIOL2: &[PinctrlPin] = &[CLR_PIN_SCU418_26]; +pub const PINCTRL_GPIOL3: &[PinctrlPin] = &[CLR_PIN_SCU418_27]; + /// I2C1 pin group: SCL2/SDA2 mux selection on SCU414[30:31]. /// /// The SVD names these EnblSCL2FnPin/EnblSDA2FnPin, but they correspond to
diff --git a/target/ast10x0/peripherals/scu/routing.rs b/target/ast10x0/peripherals/scu/routing.rs index 0ed23f6..41a2868 100644 --- a/target/ast10x0/peripherals/scu/routing.rs +++ b/target/ast10x0/peripherals/scu/routing.rs
@@ -3,27 +3,92 @@ //! SCU routing and mux helpers for SPI monitor integration. +use core::ptr::{read_volatile, write_volatile}; + use super::registers::ScuRegisters; use super::types::{ Result, ScuExtMuxSelect, SpiMonitorInstance, SpiMonitorPassthrough, SpiMonitorSource, }; -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 type SpimGpioOriVal = [u32; 4]; +#[derive(Clone, Copy)] +pub struct SpimGpioOriVal { + clk_gpio_ori_val: [u32; 4], +} -macro_rules! modify_reg { - ($reg:expr, $bit:expr, $clear:expr) => {{ - let mut val: u32 = $reg.read().bits(); - if $clear { - val &= !(1 << $bit); - } else { - val |= 1 << $bit; - } - $reg.write(|w| unsafe { w.bits(val) }); - }}; +#[derive(Clone, Copy)] +struct SpimGpioInfo { + scu_reg_addr: usize, + scu_bit_mask: u32, + gpio_addr: usize, + gpio_bit_mask: u32, +} + +// Literal translation of g_ast1060_spim_clk_gpio[] in spi_aspeed.c. +const AST1060_SPIM_CLK_GPIO: [SpimGpioInfo; 4] = [ + SpimGpioInfo { + scu_reg_addr: 0x7e6e_2690, + scu_bit_mask: 1 << 7, + gpio_addr: 0x7e78_0000, + gpio_bit_mask: 1 << 7, + }, + SpimGpioInfo { + scu_reg_addr: 0x7e6e_2690, + scu_bit_mask: 1 << 21, + gpio_addr: 0x7e78_0000, + gpio_bit_mask: 1 << 21, + }, + SpimGpioInfo { + scu_reg_addr: 0x7e6e_2694, + scu_bit_mask: 1 << 3, + gpio_addr: 0x7e78_0020, + gpio_bit_mask: 1 << 3, + }, + SpimGpioInfo { + scu_reg_addr: 0x7e6e_2694, + scu_bit_mask: 1 << 17, + gpio_addr: 0x7e78_0020, + gpio_bit_mask: 1 << 17, + }, +]; + +// Literal translation of g_ast1060_spim_cs_gpio[] in spi_aspeed.c. +const AST1060_SPIM_CS_GPIO: [SpimGpioInfo; 4] = [ + SpimGpioInfo { + scu_reg_addr: 0x7e6e_2690, + scu_bit_mask: 1 << 1, + gpio_addr: 0x7e78_0000, + gpio_bit_mask: 1 << 6, + }, + SpimGpioInfo { + scu_reg_addr: 0x7e6e_2690, + scu_bit_mask: 1 << 20, + gpio_addr: 0x7e78_0000, + gpio_bit_mask: 1 << 20, + }, + SpimGpioInfo { + scu_reg_addr: 0x7e6e_2694, + scu_bit_mask: 1 << 2, + gpio_addr: 0x7e78_0020, + gpio_bit_mask: 1 << 2, + }, + SpimGpioInfo { + scu_reg_addr: 0x7e6e_2694, + scu_bit_mask: 1 << 16, + gpio_addr: 0x7e78_0020, + gpio_bit_mask: 1 << 16, + }, +]; + +fn ast1060_spim_op_idx(scu0f0: u32) -> Option<usize> { + if scu0f0 & 0x7 == 0 { + return None; + } + + match (scu0f0 & 0x7) - 1 { + 2 => Some(3), + 3 => Some(2), + _ => None, + } } impl ScuRegisters { @@ -94,120 +159,103 @@ pub fn spim_proprietary_pre_config(&self) -> Option<SpimGpioOriVal> { self.unlock_write_protection(); - let scu = self.regs(); - let gpio = unsafe { &*ast1060_pac::Gpio::ptr() }; + let scu0f0 = self.regs().scu0f0().read().bits(); + pw_log::debug!("SPIM pre-config: SCU0F0=0x{:08x}", scu0f0 as u32); + let op_idx = match ast1060_spim_op_idx(scu0f0) { + Some(idx) => idx, + None => return None, + }; + pw_log::debug!( + "SPIM pre-config: active SPIM index {}, op index {}", + ((scu0f0 & 0x7) - 1) as u32, + op_idx as u32 + ); - let scu0f0 = scu.scu0f0().read().bits(); - if scu0f0 & 0x7 == 0 { - return None; + let clk = AST1060_SPIM_CLK_GPIO[op_idx]; + let cs = AST1060_SPIM_CS_GPIO[op_idx]; + let mut clk_gpio_ori_val = [0u32; 4]; + + unsafe { + // Change the paired SPIM CLKOUT pin to GPIO mode. + let mut reg_val = read_volatile(clk.scu_reg_addr as *const u32); + reg_val &= !clk.scu_bit_mask; + write_volatile(clk.scu_reg_addr as *mut u32, reg_val); + + // Save its GPIO direction bit, then configure it as an input. + let clk_dir_addr = (clk.gpio_addr + 0x4) as *mut u32; + reg_val = read_volatile(clk_dir_addr); + clk_gpio_ori_val[op_idx] = reg_val & clk.gpio_bit_mask; + reg_val &= !clk.gpio_bit_mask; + write_volatile(clk_dir_addr, reg_val); + + // Drive the paired SPIM CSOUT GPIO high and configure it as output. + let cs_data_addr = cs.gpio_addr as *mut u32; + reg_val = read_volatile(cs_data_addr); + reg_val |= cs.gpio_bit_mask; + write_volatile(cs_data_addr, reg_val); + + let cs_dir_addr = (cs.gpio_addr + 0x4) as *mut u32; + reg_val = read_volatile(cs_dir_addr); + reg_val |= cs.gpio_bit_mask; + write_volatile(cs_dir_addr, reg_val); + + // Change the paired SPIM CSOUT pin to GPIO mode. + let cs_scu_addr = cs.scu_reg_addr as *mut u32; + reg_val = read_volatile(cs_scu_addr); + reg_val &= !cs.scu_bit_mask; + write_volatile(cs_scu_addr, reg_val); } - let spim_idx = (scu0f0 & 0x7) - 1; - if spim_idx > 3 { - return None; - } - - let mut gpio_ori_val = [0; 4]; - - for idx in 0..4 { - if idx as u32 == spim_idx { - continue; - } - - match idx { - 0 => { - modify_reg!(scu.scu690(), PIN_SPIM0_CLK_OUT_BIT, true); - gpio_ori_val[0] = gpio.gpio004().read().bits(); - modify_reg!(gpio.gpio004(), PIN_SPIM0_CLK_OUT_BIT, true); - } - 1 => { - modify_reg!(scu.scu690(), PIN_SPIM1_CLK_OUT_BIT, true); - gpio_ori_val[1] = gpio.gpio004().read().bits(); - modify_reg!(gpio.gpio004(), PIN_SPIM1_CLK_OUT_BIT, true); - } - 2 => { - modify_reg!(scu.scu694(), PIN_SPIM2_CLK_OUT_BIT, true); - gpio_ori_val[2] = gpio.gpio024().read().bits(); - modify_reg!(gpio.gpio024(), PIN_SPIM2_CLK_OUT_BIT, true); - } - 3 => { - modify_reg!(scu.scu694(), PIN_SPIM3_CLK_OUT_BIT, true); - gpio_ori_val[3] = gpio.gpio024().read().bits(); - modify_reg!(gpio.gpio024(), PIN_SPIM3_CLK_OUT_BIT, true); - } - _ => {} - } - } - - Some(gpio_ori_val) + pw_log::debug!( + "SPIM pre-config: op index {}, saved CLK direction mask 0x{:08x}", + op_idx as u32, + clk_gpio_ori_val[op_idx] as u32 + ); + Some(SpimGpioOriVal { + clk_gpio_ori_val, + }) } /// Restore AST1060 SPIM proprietary pin state after a transaction. pub fn spim_proprietary_post_config(&self, gpio_ori_val: SpimGpioOriVal) { self.unlock_write_protection(); - let scu = self.regs(); - let gpio = unsafe { &*ast1060_pac::Gpio::ptr() }; + let scu0f0 = self.regs().scu0f0().read().bits(); + let op_idx = match ast1060_spim_op_idx(scu0f0) { + Some(idx) => idx, + None => return, + }; + let clk = AST1060_SPIM_CLK_GPIO[op_idx]; + let cs = AST1060_SPIM_CS_GPIO[op_idx]; - let bits = scu.scu0f0().read().bits(); - if bits.trailing_zeros() >= 3 { - return; + pw_log::debug!( + "SPIM post-config: SCU0F0=0x{:08x}, op index {}, saved CLK direction mask 0x{:08x}", + scu0f0 as u32, + op_idx as u32, + gpio_ori_val.clk_gpio_ori_val[op_idx] as u32 + ); + + unsafe { + // Restore the paired CLKOUT GPIO direction bit. + let clk_dir_addr = (clk.gpio_addr + 0x4) as *mut u32; + let mut reg_val = read_volatile(clk_dir_addr); + reg_val &= !clk.gpio_bit_mask; + reg_val |= gpio_ori_val.clk_gpio_ori_val[op_idx]; + write_volatile(clk_dir_addr, reg_val); + + // Return paired CLKOUT and CSOUT pins to SPIM mode. + let clk_scu_addr = clk.scu_reg_addr as *mut u32; + reg_val = read_volatile(clk_scu_addr); + reg_val |= clk.scu_bit_mask; + write_volatile(clk_scu_addr, reg_val); + + let cs_scu_addr = cs.scu_reg_addr as *mut u32; + reg_val = read_volatile(cs_scu_addr); + reg_val |= cs.scu_bit_mask; + write_volatile(cs_scu_addr, reg_val); } - let spim_idx = (bits & 0x7) - 1; - if spim_idx > 3 { - return; - } - - for idx in 0..4 { - if idx as u32 == spim_idx { - continue; - } - - match idx { - 0 => { - let ori_val = gpio_ori_val[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, false); - } - 1 => { - let ori_val = gpio_ori_val[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, false); - } - 2 => { - let ori_val = gpio_ori_val[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, false); - } - 3 => { - let ori_val = gpio_ori_val[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, false); - } - _ => {} - } - } + pw_log::debug!("SPIM post-config: restore complete"); } /// Select the external mux signal for a SPI monitor instance. Uses SCU0F0[15:12].
diff --git a/target/ast10x0/tests/smc/read/target_spi2.rs b/target/ast10x0/tests/smc/read/target_spi2.rs new file mode 100644 index 0000000..9b9f56e --- /dev/null +++ b/target/ast10x0/tests/smc/read/target_spi2.rs
@@ -0,0 +1,242 @@ +// Licensed under the Apache-2.0 license +// SPDX-License-Identifier: Apache-2.0 + +//! AST10x0 SMC SPI2 read smoke test target. + +#![no_std] +#![no_main] +#[allow(unused_imports)] +use ast10x0_peripherals::scu::{ + pinctrl::{ + PINCTRL_GPIOL2, PINCTRL_GPIOL3, PINCTRL_SPI2_QUAD, PINCTRL_SPIM3_DEFAULT, + PINCTRL_SPIM4_DEFAULT, + }, + ScuRegisters, SpiMonitorInstance, SpiMonitorPassthrough, SpiMonitorSource, ScuExtMuxSelect, +}; +use ast10x0_peripherals::smc::{ + ChipSelect, FlashConfig, SmcConfig, SmcController, SmcError, SmcTopology, SpiTransaction, + SpiUninit, TransferMode, +}; +use console_backend::console_backend_write_all; +use core::ptr::{read_volatile, write_volatile}; +use target_common::{declare_target, TargetInterface}; +use {console_backend as _, entry as _}; + +#[path = "../target_debug.rs"] +mod target_debug; +use target_debug::{dump_smc_read, dump_smc_register}; + +const SPI_FLASH_CONFIG: FlashConfig = FlashConfig { + capacity_mb: 32, + page_size: 256, + sector_size: 4096, + block_size: 65536, + spi_clock_mhz: 25, +}; + +pub struct Target {} +/* +SCU418 = 0x7E6E_2418, clear bits 26, 27 +GPIO070 = 0x7E78_0070, set bits 26, 27 +GPIO074 = 0x7E78_0074, set bits 26, 27 +*/ +fn gpio_flash_power() { + // const SCU418: *mut u32 = 0x7E6E_2418 as *mut u32; + const GPIO_DATA: *mut u32 = 0x7E78_0070 as *mut u32; + const GPIO_DIR: *mut u32 = 0x7E78_0074 as *mut u32; + const GPIOL2_L3_MASK: u32 = (1 << 26) | (1 << 27); + + unsafe { + // Select GPIO function for GPIOL2/GPIOL3. + // write_volatile(SCU418, read_volatile(SCU418) & !GPIOL2_L3_MASK); + // Configure GPIOL2/GPIOL3 as outputs and drive them high. + write_volatile(GPIO_DIR, read_volatile(GPIO_DIR) | GPIOL2_L3_MASK); + write_volatile(GPIO_DATA, read_volatile(GPIO_DATA) | GPIOL2_L3_MASK); + } + + for _ in 0..1_000_000 { + core::hint::spin_loop(); + } +} + +// Temporary raw-MMIO implementation until GPIO and SGPIOM peripherals exist. +#[allow(dead_code)] +fn configure_spi2_external_mux(select_mux1: bool) { + const SCU41C: *mut u32 = 0x7E6E_241C as *mut u32; + const SGPIOM_PIN_MASK: u32 = 0xF << 8; + + const GPIO_E_H_DATA: *mut u32 = 0x7E78_0020 as *mut u32; + const GPIO_E_H_DIR: *mut u32 = 0x7E78_0024 as *mut u32; + const GPIO_E8: u32 = 1 << 8; + + const SGPIOM_DATA_A_D: *mut u32 = 0x7E78_0500 as *mut u32; + const SGPIOM_CONFIG: *mut u32 = 0x7E78_0554 as *mut u32; + const SGPIOM_WRITE_LATCH_A_D: *const u32 = 0x7E78_0570 as *const u32; + const SGPIOM_A_D_2: u32 = 1 << 2; + const SGPIOM_CONFIG_MASK: u32 = 1 | (0x1F << 6) | (0xFFFF << 16); + const SGPIOM_CONFIG_1MHZ_128_PINS: u32 = 1 | (16 << 6) | (24 << 16); + + unsafe { + // Select the SGPIOM physical pins and initialize the controller using + // the same 1 MHz / 128-pin settings as the Zephyr board configuration. + write_volatile(SCU41C, read_volatile(SCU41C) | SGPIOM_PIN_MASK); + let config = read_volatile(SGPIOM_CONFIG) & !SGPIOM_CONFIG_MASK; + write_volatile( + SGPIOM_CONFIG, + config | SGPIOM_CONFIG_1MHZ_128_PINS, + ); + + let mut gpio_data = read_volatile(GPIO_E_H_DATA); + let mut sgpio_data = read_volatile(SGPIOM_WRITE_LATCH_A_D); + if select_mux1 { + gpio_data |= GPIO_E8; + sgpio_data |= SGPIOM_A_D_2; + } else { + gpio_data &= !GPIO_E8; + sgpio_data &= !SGPIOM_A_D_2; + } + + write_volatile(GPIO_E_H_DATA, gpio_data); + write_volatile(GPIO_E_H_DIR, read_volatile(GPIO_E_H_DIR) | GPIO_E8); + write_volatile(SGPIOM_DATA_A_D, sgpio_data); + } + + // Match the overlay's ext-mux-sel-delay-us = <1000>. + for _ in 0..100_000 { + core::hint::spin_loop(); + } +} + +fn config_spi2_master_controller() -> Result<(), SmcError> { + let scu = unsafe { ScuRegisters::new_global_unlocked() }; + scu.apply_pinctrl_group(PINCTRL_SPIM3_DEFAULT); + scu.apply_pinctrl_group(PINCTRL_SPIM4_DEFAULT); + scu.apply_pinctrl_group(PINCTRL_SPI2_QUAD); + scu.apply_pinctrl_group(PINCTRL_GPIOL2); + scu.apply_pinctrl_group(PINCTRL_GPIOL3); + gpio_flash_power(); + //configure spi2 external mux through gpio pins + configure_spi2_external_mux(true); + + // Configure the mux for the SPI master controller path. + scu.set_spim_internal_master_route(SpiMonitorInstance::Spim2, SpiMonitorSource::Spi2); + scu.set_spim_passthrough(SpiMonitorInstance::Spim2, SpiMonitorPassthrough::Enabled); + scu.set_spim_ext_mux(SpiMonitorInstance::Spim2, ScuExtMuxSelect::Mux1); + pw_log::info!("SCU pinmux and SPIM routing configured for SPI2 monitoring"); + unsafe { + write_volatile(0x7E6E_20F0 as *mut u32, 0xfff0); + } + for _ in 0..1_000_000 { + core::hint::spin_loop(); + } + Ok(()) +} + +fn run_spi2_read_test() -> Result<(), SmcError> { + config_spi2_master_controller()?; + + let config = SmcConfig { + controller_id: SmcController::Spi2, + cs0: Some(SPI_FLASH_CONFIG), + cs1: None, + dma_enabled: true, + enable_interrupts: false, + topology: SmcTopology::NormalSpi { master_idx: 2 }, + }; + + pw_log::info!("=== AST10x0 SMC SPI2 read test ==="); + let spi = unsafe { SpiUninit::new(SmcController::Spi2, config)? }; + let mut spi = spi.init()?; + spi.spi_nor_read_init(ChipSelect::Cs0)?; + + if !spi.is_ready() { + return Err(SmcError::HardwareError); + } + + pw_log::info!("=== SPI2 controller register ==="); + dump_smc_register(0x7E64_0000, 16); + dump_smc_register(0x7E64_0080, 16); + pw_log::info!("=== SCU QSPI Mux routing register ==="); + dump_smc_register(0x7E6E_20F0, 2); + dump_smc_register(0x7E6E_2418, 2); + dump_smc_register(0x7e6e_2694, 2); + dump_smc_register(0x7E78_0020, 2); + dump_smc_register(0x7E78_0070, 2); + let mut jedec = [0u8; 3]; + SpiTransaction::transceive_user_with_spim( + &mut spi, + SpiMonitorInstance::Spim2, + ChipSelect::Cs0, + &[0x9f], + &[], + &mut jedec, + TransferMode::Mode111, + )?; + pw_log::info!( + "SPI2 CS0 JEDEC ID: {:02x} {:02x} {:02x}", + jedec[0] as u32, + jedec[1] as u32, + jedec[2] as u32 + ); + + if jedec[0] == 0xff { + pw_log::info!("SPI2 CS0 JEDEC manufacturer is 0xff; skipping read test"); + return Ok(()); + } + + pw_log::info!("=== SPI2 read ==="); + let mut buf = [0u8; 64]; + let n = SpiTransaction::read_with_spim( + &mut spi, + SpiMonitorInstance::Spim2, + ChipSelect::Cs0, + 0x0, + &mut buf, + )?; + if n != buf.len() { + return Err(SmcError::HardwareError); + } + dump_smc_read(&buf, buf.len() as u32); + + pw_log::info!("=== SPI2 DMA read @ 0x00000000 ==="); + let dma_buf = unsafe { core::slice::from_raw_parts_mut(0x41500 as *mut u8, 256) }; + let mut dma_txn = SpiTransaction::dma_read_with_spim( + &mut spi, + SpiMonitorInstance::Spim2, + ChipSelect::Cs0, + 0x0, + 0x41500usize, + dma_buf.len() as u32, + )?; + + loop { + match dma_txn.poll_dma_completion() { + core::task::Poll::Pending => {} + core::task::Poll::Ready(result) => { + result?; + break; + } + } + } + dump_smc_read(dma_buf, dma_buf.len() as u32); + + Ok(()) +} + +impl TargetInterface for Target { + const NAME: &'static str = "AST10x0 SMC SPI2 read Test"; + + fn main() -> ! { + let sentinel = if run_spi2_read_test().is_ok() { + b"TEST_RESULT:PASS\n" + } else { + b"TEST_RESULT:FAIL\n" + }; + let _ = console_backend_write_all(sentinel); + + #[expect(clippy::empty_loop)] + loop {} + } +} + +declare_target!(Target);