// SPDX-License-Identifier: GPL-2.0 //! GSP Sequencer implementation for Pre-hopper GSP boot sequence. use core::{ array, mem::{ size_of, size_of_val, // }, }; use kernel::{ device, io::poll::read_poll_timeout, prelude::*, time::{ delay::fsleep, Delta, // }, transmute::FromBytes, types::ARef, // }; use crate::{ driver::Bar0, falcon::{ gsp::Gsp, sec2::Sec2, Falcon, // }, gsp::{ cmdq::{ Cmdq, MessageFromGsp, // }, fw, }, num::FromSafeCast, sbuffer::SBufferIter, }; /// GSP Sequencer information containing the command sequence and data. struct GspSequence { /// Current command index for error reporting. cmd_index: u32, /// Command data buffer containing the sequence of commands. cmd_data: KVec, } impl MessageFromGsp for GspSequence { const FUNCTION: fw::MsgFunction = fw::MsgFunction::GspRunCpuSequencer; type InitError = Error; type Message = fw::RunCpuSequencer; fn read( msg: &Self::Message, sbuffer: &mut SBufferIter>, ) -> Result { let cmd_data = sbuffer.flush_into_kvec(GFP_KERNEL)?; Ok(GspSequence { cmd_index: msg.cmd_index(), cmd_data, }) } } const CMD_SIZE: usize = size_of::(); /// GSP Sequencer Command types with payload data. /// Commands have an opcode and an opcode-dependent struct. #[allow(clippy::enum_variant_names)] pub(crate) enum GspSeqCmd { RegWrite(fw::RegWritePayload), RegModify(fw::RegModifyPayload), RegPoll(fw::RegPollPayload), DelayUs(fw::DelayUsPayload), RegStore(fw::RegStorePayload), CoreReset, CoreStart, CoreWaitForHalt, CoreResume, } impl GspSeqCmd { /// Creates a new `GspSeqCmd` from raw data returning the command and its size in bytes. pub(crate) fn new(data: &[u8], dev: &device::Device) -> Result<(Self, usize)> { let fw_cmd = fw::SequencerBufferCmd::from_bytes(data).ok_or(EINVAL)?; let opcode_size = core::mem::size_of::(); let (cmd, size) = match fw_cmd.opcode()? { fw::SeqBufOpcode::RegWrite => { let payload = fw_cmd.reg_write_payload()?; let size = opcode_size + size_of_val(&payload); (GspSeqCmd::RegWrite(payload), size) } fw::SeqBufOpcode::RegModify => { let payload = fw_cmd.reg_modify_payload()?; let size = opcode_size + size_of_val(&payload); (GspSeqCmd::RegModify(payload), size) } fw::SeqBufOpcode::RegPoll => { let payload = fw_cmd.reg_poll_payload()?; let size = opcode_size + size_of_val(&payload); (GspSeqCmd::RegPoll(payload), size) } fw::SeqBufOpcode::DelayUs => { let payload = fw_cmd.delay_us_payload()?; let size = opcode_size + size_of_val(&payload); (GspSeqCmd::DelayUs(payload), size) } fw::SeqBufOpcode::RegStore => { let payload = fw_cmd.reg_store_payload()?; let size = opcode_size + size_of_val(&payload); (GspSeqCmd::RegStore(payload), size) } fw::SeqBufOpcode::CoreReset => (GspSeqCmd::CoreReset, opcode_size), fw::SeqBufOpcode::CoreStart => (GspSeqCmd::CoreStart, opcode_size), fw::SeqBufOpcode::CoreWaitForHalt => (GspSeqCmd::CoreWaitForHalt, opcode_size), fw::SeqBufOpcode::CoreResume => (GspSeqCmd::CoreResume, opcode_size), }; if data.len() < size { dev_err!(dev, "Data is not enough for command"); return Err(EINVAL); } Ok((cmd, size)) } } /// GSP Sequencer for executing firmware commands during boot. pub(crate) struct GspSequencer<'a> { /// Sequencer information with command data. seq_info: GspSequence, /// `Bar0` for register access. bar: &'a Bar0, /// SEC2 falcon for core operations. sec2_falcon: &'a Falcon, /// GSP falcon for core operations. gsp_falcon: &'a Falcon, /// LibOS DMA handle address. libos_dma_handle: u64, /// Bootloader application version. bootloader_app_version: u32, /// Device for logging. dev: ARef, } /// Trait for running sequencer commands. pub(crate) trait GspSeqCmdRunner { fn run(&self, sequencer: &GspSequencer<'_>) -> Result; } impl GspSeqCmdRunner for fw::RegWritePayload { fn run(&self, sequencer: &GspSequencer<'_>) -> Result { let addr = usize::from_safe_cast(self.addr()); sequencer.bar.try_write32(self.val(), addr) } } impl GspSeqCmdRunner for fw::RegModifyPayload { fn run(&self, sequencer: &GspSequencer<'_>) -> Result { let addr = usize::from_safe_cast(self.addr()); sequencer.bar.try_read32(addr).and_then(|val| { sequencer .bar .try_write32((val & !self.mask()) | self.val(), addr) }) } } impl GspSeqCmdRunner for fw::RegPollPayload { fn run(&self, sequencer: &GspSequencer<'_>) -> Result { let addr = usize::from_safe_cast(self.addr()); // Default timeout to 4 seconds. let timeout_us = if self.timeout() == 0 { 4_000_000 } else { i64::from(self.timeout()) }; // First read. sequencer.bar.try_read32(addr)?; // Poll the requested register with requested timeout. read_poll_timeout( || sequencer.bar.try_read32(addr), |current| (current & self.mask()) == self.val(), Delta::ZERO, Delta::from_micros(timeout_us), ) .map(|_| ()) } } impl GspSeqCmdRunner for fw::DelayUsPayload { fn run(&self, _sequencer: &GspSequencer<'_>) -> Result { fsleep(Delta::from_micros(i64::from(self.val()))); Ok(()) } } impl GspSeqCmdRunner for fw::RegStorePayload { fn run(&self, sequencer: &GspSequencer<'_>) -> Result { let addr = usize::from_safe_cast(self.addr()); sequencer.bar.try_read32(addr).map(|_| ()) } } impl GspSeqCmdRunner for GspSeqCmd { fn run(&self, seq: &GspSequencer<'_>) -> Result { match self { GspSeqCmd::RegWrite(cmd) => cmd.run(seq), GspSeqCmd::RegModify(cmd) => cmd.run(seq), GspSeqCmd::RegPoll(cmd) => cmd.run(seq), GspSeqCmd::DelayUs(cmd) => cmd.run(seq), GspSeqCmd::RegStore(cmd) => cmd.run(seq), GspSeqCmd::CoreReset => { seq.gsp_falcon.reset(seq.bar)?; seq.gsp_falcon.dma_reset(seq.bar); Ok(()) } GspSeqCmd::CoreStart => { seq.gsp_falcon.start(seq.bar)?; Ok(()) } GspSeqCmd::CoreWaitForHalt => { seq.gsp_falcon.wait_till_halted(seq.bar)?; Ok(()) } GspSeqCmd::CoreResume => { // At this point, 'SEC2-RTOS' has been loaded into SEC2 by the sequencer // but neither SEC2-RTOS nor GSP-RM is running yet. This part of the // sequencer will start both. // Reset the GSP to prepare it for resuming. seq.gsp_falcon.reset(seq.bar)?; // Write the libOS DMA handle to GSP mailboxes. seq.gsp_falcon.write_mailboxes( seq.bar, Some(seq.libos_dma_handle as u32), Some((seq.libos_dma_handle >> 32) as u32), ); // Start the SEC2 falcon which will trigger GSP-RM to resume on the GSP. seq.sec2_falcon.start(seq.bar)?; // Poll until GSP-RM reload/resume has completed (up to 2 seconds). seq.gsp_falcon .check_reload_completed(seq.bar, Delta::from_secs(2))?; // Verify SEC2 completed successfully by checking its mailbox for errors. let mbox0 = seq.sec2_falcon.read_mailbox0(seq.bar); if mbox0 != 0 { dev_err!(seq.dev, "Sequencer: sec2 errors: {:?}\n", mbox0); return Err(EIO); } // Configure GSP with the bootloader version. seq.gsp_falcon .write_os_version(seq.bar, seq.bootloader_app_version); // Verify the GSP's RISC-V core is active indicating successful GSP boot. if !seq.gsp_falcon.is_riscv_active(seq.bar) { dev_err!(seq.dev, "Sequencer: RISC-V core is not active\n"); return Err(EIO); } Ok(()) } } } } /// Iterator over GSP sequencer commands. pub(crate) struct GspSeqIter<'a> { /// Command data buffer. cmd_data: &'a [u8], /// Current position in the buffer. current_offset: usize, /// Total number of commands to process. total_cmds: u32, /// Number of commands processed so far. cmds_processed: u32, /// Device for logging. dev: ARef, } impl<'a> Iterator for GspSeqIter<'a> { type Item = Result; fn next(&mut self) -> Option { // Stop if we've processed all commands or reached the end of data. if self.cmds_processed >= self.total_cmds || self.current_offset >= self.cmd_data.len() { return None; } // Check if we have enough data for opcode. if self.current_offset + core::mem::size_of::() > self.cmd_data.len() { return Some(Err(EIO)); } let offset = self.current_offset; // Handle command creation based on available data, // zero-pad if necessary (since last command may not be full size). let mut buffer = [0u8; CMD_SIZE]; let copy_len = if offset + CMD_SIZE <= self.cmd_data.len() { CMD_SIZE } else { self.cmd_data.len() - offset }; buffer[..copy_len].copy_from_slice(&self.cmd_data[offset..offset + copy_len]); let cmd_result = GspSeqCmd::new(&buffer, &self.dev); cmd_result.map_or_else( |_err| { dev_err!(self.dev, "Error parsing command at offset {}", offset); None }, |(cmd, size)| { self.current_offset += size; self.cmds_processed += 1; Some(Ok(cmd)) }, ) } } impl<'a> GspSequencer<'a> { fn iter(&self) -> GspSeqIter<'_> { let cmd_data = &self.seq_info.cmd_data[..]; GspSeqIter { cmd_data, current_offset: 0, total_cmds: self.seq_info.cmd_index, cmds_processed: 0, dev: self.dev.clone(), } } } /// Parameters for running the GSP sequencer. pub(crate) struct GspSequencerParams<'a> { /// Bootloader application version. pub(crate) bootloader_app_version: u32, /// LibOS DMA handle address. pub(crate) libos_dma_handle: u64, /// GSP falcon for core operations. pub(crate) gsp_falcon: &'a Falcon, /// SEC2 falcon for core operations. pub(crate) sec2_falcon: &'a Falcon, /// Device for logging. pub(crate) dev: ARef, /// BAR0 for register access. pub(crate) bar: &'a Bar0, } impl<'a> GspSequencer<'a> { pub(crate) fn run(cmdq: &mut Cmdq, params: GspSequencerParams<'a>) -> Result { let seq_info = loop { match cmdq.receive_msg::(Delta::from_secs(10)) { Ok(seq_info) => break seq_info, Err(ERANGE) => continue, Err(e) => return Err(e), } }; let sequencer = GspSequencer { seq_info, bar: params.bar, sec2_falcon: params.sec2_falcon, gsp_falcon: params.gsp_falcon, libos_dma_handle: params.libos_dma_handle, bootloader_app_version: params.bootloader_app_version, dev: params.dev, }; dev_dbg!(sequencer.dev, "Running CPU Sequencer commands"); for cmd_result in sequencer.iter() { match cmd_result { Ok(cmd) => cmd.run(&sequencer)?, Err(e) => { dev_err!( sequencer.dev, "Error running command at index {}", sequencer.seq_info.cmd_index ); return Err(e); } } } dev_dbg!( sequencer.dev, "CPU Sequencer commands completed successfully" ); Ok(()) } }