Complete the AST1060 SPI-monitor handoff from the RoT to the external BMC master and Host. Configure board-level GPIO: - BMC: mux select bit 0, active-low OE bit 1, reset bit 7 - Host: mux select bit 2, active-low OE bit 3, reset bit 6 Switch the shared SPIM1/2 BMC mux from RoT value 1 to BMC value 0.
diff --git a/target/ast10x0/board/src/spim_wiring.rs b/target/ast10x0/board/src/spim_wiring.rs index 1aa4fb1..a22224a 100644 --- a/target/ast10x0/board/src/spim_wiring.rs +++ b/target/ast10x0/board/src/spim_wiring.rs
@@ -110,18 +110,30 @@ scu.apply_pinctrl_group(group); } -/// Drive the external mux-select GPIO pair described by the AST1060 DTS. +/// Configure the external flash mux controls described by the board schematic. /// -/// SPIM1/2 use GPIO A-D pin 12 and SGPIOM pin 0. SPIM3/4 use GPIO E-H -/// pin 8 and SGPIOM pin 2. Both signals carry the same mux selection. +/// SPIM1/2 use GPIO A-D pin 12 plus SGPIOM select/OE/reset bits 0/1/7. +/// SPIM3/4 use GPIO E-H pin 8 plus SGPIOM select/OE/reset bits 2/3/6. pub fn apply_spim_external_mux(instance: SpiMonitorInstance, mux: ScuExtMuxSelect) { let high = matches!(mux, ScuExtMuxSelect::Mux1); - let (gpio_group, gpio_mask, sgpio_mask) = match instance { + let (gpio_group, gpio_mask, sgpio_select, sgpio_oe_n, sgpio_reset_n) = match instance { SpiMonitorInstance::Spim0 | SpiMonitorInstance::Spim1 => { - (ExternalMuxGpioGroup::Abcd, 1 << 12, 1 << 0) + ( + ExternalMuxGpioGroup::Abcd, + 1 << 12, + 1 << 0, + 1 << 1, + 1 << 7, + ) } SpiMonitorInstance::Spim2 | SpiMonitorInstance::Spim3 => { - (ExternalMuxGpioGroup::Efgh, 1 << 8, 1 << 2) + ( + ExternalMuxGpioGroup::Efgh, + 1 << 8, + 1 << 2, + 1 << 3, + 1 << 6, + ) } }; @@ -166,24 +178,38 @@ .bits(24) }); let latch = sgpio.gpio570().read().bits(); + let control_mask = sgpio_select | sgpio_oe_n | sgpio_reset_n; + let control_value = if high { sgpio_select } else { 0 } | sgpio_reset_n; sgpio .gpio500() - .write(|w| unsafe { w.bits(update_bit(latch, sgpio_mask, high)) }); + .write(|w| unsafe { w.bits((latch & !control_mask) | control_value) }); crate::delay_us(1_000); } -/// Read back the two board-level external mux-select outputs. +/// Read back the board-level mux selection, output enable, and flash reset. #[must_use] pub fn spim_external_mux_state( instance: SpiMonitorInstance, ) -> Option<ScuExtMuxSelect> { - let (gpio_group, gpio_mask, sgpio_mask) = match instance { + let (gpio_group, gpio_mask, sgpio_select, sgpio_oe_n, sgpio_reset_n) = match instance { SpiMonitorInstance::Spim0 | SpiMonitorInstance::Spim1 => { - (ExternalMuxGpioGroup::Abcd, 1 << 12, 1 << 0) + ( + ExternalMuxGpioGroup::Abcd, + 1 << 12, + 1 << 0, + 1 << 1, + 1 << 7, + ) } SpiMonitorInstance::Spim2 | SpiMonitorInstance::Spim3 => { - (ExternalMuxGpioGroup::Efgh, 1 << 8, 1 << 2) + ( + ExternalMuxGpioGroup::Efgh, + 1 << 8, + 1 << 2, + 1 << 3, + 1 << 6, + ) } }; let gpio = unsafe { &*device::Gpio::ptr() }; @@ -192,7 +218,13 @@ ExternalMuxGpioGroup::Efgh => gpio.gpio020().read().bits() & gpio_mask != 0, }; let sgpio = unsafe { &*device::Sgpiom::ptr() }; - let sgpio_high = sgpio.gpio570().read().bits() & sgpio_mask != 0; + let latch = sgpio.gpio570().read().bits(); + let sgpio_high = latch & sgpio_select != 0; + let mux_enabled = latch & sgpio_oe_n == 0; + let flash_reset_released = latch & sgpio_reset_n != 0; + if !mux_enabled || !flash_reset_released { + return None; + } if gpio_high != sgpio_high { None } else if gpio_high {
diff --git a/target/ast10x0/peripherals/scu/routing.rs b/target/ast10x0/peripherals/scu/routing.rs index b99d1bd..18a9a1f 100644 --- a/target/ast10x0/peripherals/scu/routing.rs +++ b/target/ast10x0/peripherals/scu/routing.rs
@@ -208,6 +208,40 @@ }); } + /// Route the external flash-reset input through one SPIPF. + /// + /// Clears the instance's reset signal and source-selection bits in + /// SCU0F0[19:16] and [23:20], and enables its reset output in [27:24]. + pub fn configure_spim_external_flash_reset(&self, instance: SpiMonitorInstance) { + self.unlock_write_protection(); + let instance_bit = match instance { + SpiMonitorInstance::Spim0 => 1 << 0, + SpiMonitorInstance::Spim1 => 1 << 1, + SpiMonitorInstance::Spim2 => 1 << 2, + SpiMonitorInstance::Spim3 => 1 << 3, + }; + let clear_mask = (instance_bit << 16) | (instance_bit << 20); + let output_enable = instance_bit << 24; + self.regs() + .scu0f0() + .modify(|r, w| unsafe { w.bits((r.bits() & !clear_mask) | output_enable) }); + } + + /// Check the external flash-reset source and output-enable configuration. + #[must_use] + pub fn is_spim_external_flash_reset_configured(&self, instance: SpiMonitorInstance) -> bool { + let instance_bit = match instance { + SpiMonitorInstance::Spim0 => 1 << 0, + SpiMonitorInstance::Spim1 => 1 << 1, + SpiMonitorInstance::Spim2 => 1 << 2, + SpiMonitorInstance::Spim3 => 1 << 3, + }; + let cleared_mask = (instance_bit << 16) | (instance_bit << 20); + let output_enable = instance_bit << 24; + let value = self.regs().scu0f0().read().bits(); + value & cleared_mask == 0 && value & output_enable == output_enable + } + /// Disable the internal pull-down on the monitor CS output pin. pub fn disable_spim_cs_internal_pull_down(&self, instance: SpiMonitorInstance) { self.unlock_write_protection();
diff --git a/target/ast10x0/tests/spimonitor/setup_all_spim.rs b/target/ast10x0/tests/spimonitor/setup_all_spim.rs index 820dac6..c94200e 100644 --- a/target/ast10x0/tests/spimonitor/setup_all_spim.rs +++ b/target/ast10x0/tests/spimonitor/setup_all_spim.rs
@@ -11,8 +11,12 @@ #[path = "test_common.rs"] mod test_common; -use ast10x0_board::{delay_us, enable_flash_power, set_bmc_resets}; -use ast10x0_peripherals::scu::{ScuRegisters, SpiMonitorInstance}; +use test_common::TestConfig; +use ast10x0_board::{ + apply_spim_external_mux, delay_us, enable_flash_power, set_bmc_resets, + spim_external_mux_state, +}; +use ast10x0_peripherals::scu::{ScuExtMuxSelect, ScuRegisters, SpiMonitorInstance}; use ast10x0_peripherals::spimonitor::{ MonitorPolicy, PrivilegeDirection, PrivilegeOp, SpiMonitorController, }; @@ -61,9 +65,6 @@ 0xb7, 0xe9, 0x32, 0x34, 0xd8, 0xdc, 0x02, 0x12, 0x3b, 0x3c, 0x70, 0xbb, 0xbc, 0x50, 0xeb, 0xec, 0xc2, ]; -const SPIM4_ALLOW_COMMANDS: [u8; 15] = [ - 0x01, 0x06, 0x04, 0x20, 0x21, 0xb7, 0xe9, 0x32, 0x34, 0xd8, 0xdc, 0x02, 0x12, 0x50, 0xc2, -]; fn log_buffer(log: &'static LogRam) -> &'static mut [u32] { // SAFETY: The caller assigns each static buffer to exactly one monitor. @@ -83,24 +84,10 @@ policy } -fn spim4_test_policy() -> MonitorPolicy { - let mut policy = MonitorPolicy::empty(); - policy.allow_commands[..SPIM4_ALLOW_COMMANDS.len()] - .copy_from_slice(&SPIM4_ALLOW_COMMANDS); - policy.allow_command_count = SPIM4_ALLOW_COMMANDS.len(); - let _ = policy.add_region( - 0, - WRITE_PROTECTED_LENGTH, - PrivilegeDirection::Write, - PrivilegeOp::Disable, - ); - policy -} - fn setup_all_spim() -> Result<(), test_common::TestError> { let scu = unsafe { ScuRegisters::new_global_unlocked() }; let policy = production_policy(); - let spim4_policy = spim4_test_policy(); + //let spim4_policy = spim4_test_policy(); pw_log::info!("=== GPIO flash power ==="); if !enable_flash_power(&scu) { @@ -123,6 +110,7 @@ &policy, )?; test_common::dump_policy(&spim1, WRITE_PROTECTED_LENGTH)?; + test_common::configure_passthrough::<Spim1Config>(&spim1)?; let _spim1 = test_common::lock_monitor(spim1)?; pw_log::info!("=== SPIM2 ==="); @@ -132,6 +120,7 @@ &policy, )?; test_common::dump_policy(&spim2, WRITE_PROTECTED_LENGTH)?; + test_common::configure_passthrough::<Spim2Config>(&spim2)?; let _spim2 = test_common::lock_monitor(spim2)?; pw_log::info!("=== SPIM3 ==="); @@ -141,24 +130,41 @@ &policy, )?; test_common::dump_policy(&spim3, WRITE_PROTECTED_LENGTH)?; + test_common::configure_passthrough::<Spim3Config>(&spim3)?; let _spim3 = test_common::lock_monitor(spim3)?; pw_log::info!("=== SPIM4 ==="); - pw_log::info!("SPIM4 test policy blocks all read commands"); test_common::configure_wiring::<Spim4Config>(&scu)?; let spim4 = test_common::initialize_monitor_with_policy::<Spim4Config>( log_buffer(&SPIM4_LOG), - &spim4_policy, + &policy, )?; test_common::dump_policy(&spim4, WRITE_PROTECTED_LENGTH)?; + test_common::configure_passthrough::<Spim4Config>(&spim4)?; let _spim4 = test_common::lock_monitor(spim4)?; - pw_log::info!("All SPIM1-4 monitors are configured and locked"); + if scu.route_control_raw() & 0x0fff_0000 != 0x0f00_0000 { + pw_log::info!( + "FAIL: final SPIPF reset routing: 0x{:08x}", + scu.route_control_raw() as u32 + ); + return Err(test_common::TestError::Check); + } + pw_log::info!("All SPIM1-4 paths are configured in passthrough mode and locked"); + pw_log::info!("SPI clock frequency must be configured to 25 MHz by the external master"); pw_log::info!( "SCU0F0 after SPIM setup: 0x{:08x}", scu.route_control_raw() as u32 ); + pw_log::info!("=== Hand BMC flash mux to external BMC master ==="); + apply_spim_external_mux(Spim1Config::INSTANCE, ScuExtMuxSelect::Mux0); + if spim_external_mux_state(Spim1Config::INSTANCE) != Some(ScuExtMuxSelect::Mux0) { + pw_log::info!("FAIL: SPIM1/2 external BMC mux handoff"); + return Err(test_common::TestError::Check); + } + pw_log::info!("PASS: SPIM1/2 mux switched from RoT=1 to BMC=0"); + pw_log::info!("Waiting 60 ms for flash routing to settle"); delay_us(60_000);
diff --git a/target/ast10x0/tests/spimonitor/test_common.rs b/target/ast10x0/tests/spimonitor/test_common.rs index 837930f..9286ca4 100644 --- a/target/ast10x0/tests/spimonitor/test_common.rs +++ b/target/ast10x0/tests/spimonitor/test_common.rs
@@ -8,7 +8,7 @@ use core::cell::UnsafeCell; use ast10x0_board::{ - apply_spim_external_mux, apply_spim_pinctrl, spim_external_mux_state, + apply_spim_external_mux, apply_spim_pinctrl, delay_us, spim_external_mux_state, }; use ast10x0_peripherals::scu::{ ScuError, ScuExtMuxSelect, ScuRegisters, SpiMonitorInstance, SpiMonitorPassthrough, @@ -139,6 +139,14 @@ "FAIL: software reset did not deassert" ); + let scu = unsafe { ScuRegisters::new_global_unlocked() }; + scu.configure_spim_external_flash_reset(C::INSTANCE); + test_check!( + scu.is_spim_external_flash_reset_configured(C::INSTANCE), + "FAIL: SPIPF external flash reset routing readback" + ); + delay_us(5_000); + let configured = monitor.apply_policy(policy)?; test_check!( configured.state() == MonitorState::Configured, @@ -192,6 +200,25 @@ } #[allow(dead_code)] +pub fn configure_passthrough<C: TestConfig>( + configured: &ConfiguredSpiMonitor, +) -> Result<(), TestError> { + configured.disable(); + configured.set_passthrough(PassthroughMode::Enabled); + + let scu = unsafe { ScuRegisters::new_global_unlocked() }; + scu.set_spim_passthrough(C::INSTANCE, SpiMonitorPassthrough::Enabled); + scu.set_spim_miso_multi_func(C::INSTANCE, false); + + test_check!( + configured.regs().read_ctrl() & 0x7 == 0x1, + "FAIL: SPIPF passthrough mode readback" + ); + pw_log::info!("PASS: SPIPF single-bit passthrough mode"); + Ok(()) +} + +#[allow(dead_code)] pub fn lock_monitor( configured: ConfiguredSpiMonitor, ) -> Result<ast10x0_peripherals::spimonitor::LockedSpiMonitor, TestError> {