diff options
| -rw-r--r-- | drivers/gpu/nova-core/gsp.rs | 1 | ||||
| -rw-r--r-- | drivers/gpu/nova-core/gsp/boot.rs | 15 | ||||
| -rw-r--r-- | drivers/gpu/nova-core/gsp/cmdq.rs | 1 | ||||
| -rw-r--r-- | drivers/gpu/nova-core/gsp/fw.rs | 1 | ||||
| -rw-r--r-- | drivers/gpu/nova-core/gsp/sequencer.rs | 236 | ||||
| -rw-r--r-- | drivers/gpu/nova-core/sbuffer.rs | 1 |
6 files changed, 252 insertions, 3 deletions
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs index e40354c47608..fb6f74797178 100644 --- a/drivers/gpu/nova-core/gsp.rs +++ b/drivers/gpu/nova-core/gsp.rs @@ -17,6 +17,7 @@ use kernel::{ pub(crate) mod cmdq; pub(crate) mod commands; mod fw; +mod sequencer; pub(crate) use fw::{ GspFwWprMeta, diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index eb0ee4f66f0c..d62bab07e861 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -33,6 +33,10 @@ use crate::{ gpu::Chipset, gsp::{ commands, + sequencer::{ + GspSequencer, + GspSequencerParams, // + }, GspFwWprMeta, // }, regs, @@ -221,6 +225,17 @@ impl super::Gsp { gsp_falcon.is_riscv_active(bar), ); + // Create and run the GSP sequencer. + let seq_params = GspSequencerParams { + bootloader_app_version: gsp_fw.bootloader.app_version, + libos_dma_handle: libos_handle, + gsp_falcon, + sec2_falcon, + dev: pdev.as_ref().into(), + bar, + }; + GspSequencer::run(&mut self.cmdq, seq_params)?; + Ok(()) } } diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index c0f3218f2980..6f946d14868a 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -645,7 +645,6 @@ impl Cmdq { /// - `EIO` if there was some inconsistency (e.g. message shorter than advertised) on the /// message queue. /// - `EINVAL` if the function of the message was unrecognized. - #[expect(unused)] pub(crate) fn receive_msg<M: MessageFromGsp>(&mut self, timeout: Delta) -> Result<M> where // This allows all error types, including `Infallible`, to be used for `M::InitError`. diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs index db3ef58b3ce7..4b569fc4c0b1 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs +++ b/drivers/gpu/nova-core/gsp/fw.rs @@ -621,7 +621,6 @@ unsafe impl AsBytes for SequencerBufferCmd {} #[repr(transparent)] pub(crate) struct RunCpuSequencer(r570_144::rpc_run_cpu_sequencer_v17_00); -#[expect(unused)] impl RunCpuSequencer { /// Returns the command index. pub(crate) fn cmd_index(&self) -> u32 { diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs new file mode 100644 index 000000000000..1f4f0a0d999b --- /dev/null +++ b/drivers/gpu/nova-core/gsp/sequencer.rs @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! GSP Sequencer implementation for Pre-hopper GSP boot sequence. + +use core::{ + array, + mem::size_of, // +}; + +use kernel::{ + device, + prelude::*, + time::Delta, + transmute::FromBytes, + types::ARef, // +}; + +use crate::{ + driver::Bar0, + falcon::{ + gsp::Gsp, + sec2::Sec2, + Falcon, // + }, + gsp::{ + cmdq::{ + Cmdq, + MessageFromGsp, // + }, + fw, + }, + 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<u8>, +} + +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<array::IntoIter<&[u8], 2>>, + ) -> Result<Self, Self::InitError> { + let cmd_data = sbuffer.flush_into_kvec(GFP_KERNEL)?; + Ok(GspSequence { + cmd_index: msg.cmd_index(), + cmd_data, + }) + } +} + +const CMD_SIZE: usize = size_of::<fw::SequencerBufferCmd>(); + +/// GSP Sequencer Command types with payload data. +/// Commands have an opcode and an opcode-dependent struct. +#[allow(dead_code)] +pub(crate) enum GspSeqCmd {} + +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::<u32>(); + + // NOTE: At this commit, NO opcodes exist yet, so just return error. + // Later commits will add match arms here. + Err(EINVAL) + } +} + +/// GSP Sequencer for executing firmware commands during boot. +#[expect(dead_code)] +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<Sec2>, + /// GSP falcon for core operations. + gsp_falcon: &'a Falcon<Gsp>, + /// LibOS DMA handle address. + libos_dma_handle: u64, + /// Bootloader application version. + bootloader_app_version: u32, + /// Device for logging. + dev: ARef<device::Device>, +} + +/// Trait for running sequencer commands. +pub(crate) trait GspSeqCmdRunner { + fn run(&self, sequencer: &GspSequencer<'_>) -> Result; +} + +impl GspSeqCmdRunner for GspSeqCmd { + fn run(&self, _seq: &GspSequencer<'_>) -> Result { + 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<device::Device>, +} + +impl<'a> Iterator for GspSeqIter<'a> { + type Item = Result<GspSeqCmd>; + + fn next(&mut self) -> Option<Self::Item> { + // 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::<u32>() > 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<Gsp>, + /// SEC2 falcon for core operations. + pub(crate) sec2_falcon: &'a Falcon<Sec2>, + /// Device for logging. + pub(crate) dev: ARef<device::Device>, + /// 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::<GspSequence>(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(()) + } +} diff --git a/drivers/gpu/nova-core/sbuffer.rs b/drivers/gpu/nova-core/sbuffer.rs index 7a5947b8be19..64758b7fae56 100644 --- a/drivers/gpu/nova-core/sbuffer.rs +++ b/drivers/gpu/nova-core/sbuffer.rs @@ -168,7 +168,6 @@ where /// Read all the remaining data into a [`KVec`]. /// /// `self` will be empty after this operation. - #[expect(unused)] pub(crate) fn flush_into_kvec(&mut self, flags: kernel::alloc::Flags) -> Result<KVec<u8>> { let mut buf = KVec::<u8>::new(); |
