Path: blob/master/drivers/gpu/nova-core/gsp/sequencer.rs
38186 views
// SPDX-License-Identifier: GPL-2.012//! GSP Sequencer implementation for Pre-hopper GSP boot sequence.34use core::{5array,6mem::{7size_of,8size_of_val, //9},10};1112use kernel::{13device,14io::poll::read_poll_timeout,15prelude::*,16time::{17delay::fsleep,18Delta, //19},20transmute::FromBytes,21types::ARef, //22};2324use crate::{25driver::Bar0,26falcon::{27gsp::Gsp,28sec2::Sec2,29Falcon, //30},31gsp::{32cmdq::{33Cmdq,34MessageFromGsp, //35},36fw,37},38num::FromSafeCast,39sbuffer::SBufferIter,40};4142/// GSP Sequencer information containing the command sequence and data.43struct GspSequence {44/// Current command index for error reporting.45cmd_index: u32,46/// Command data buffer containing the sequence of commands.47cmd_data: KVec<u8>,48}4950impl MessageFromGsp for GspSequence {51const FUNCTION: fw::MsgFunction = fw::MsgFunction::GspRunCpuSequencer;52type InitError = Error;53type Message = fw::RunCpuSequencer;5455fn read(56msg: &Self::Message,57sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,58) -> Result<Self, Self::InitError> {59let cmd_data = sbuffer.flush_into_kvec(GFP_KERNEL)?;60Ok(GspSequence {61cmd_index: msg.cmd_index(),62cmd_data,63})64}65}6667const CMD_SIZE: usize = size_of::<fw::SequencerBufferCmd>();6869/// GSP Sequencer Command types with payload data.70/// Commands have an opcode and an opcode-dependent struct.71#[allow(clippy::enum_variant_names)]72pub(crate) enum GspSeqCmd {73RegWrite(fw::RegWritePayload),74RegModify(fw::RegModifyPayload),75RegPoll(fw::RegPollPayload),76DelayUs(fw::DelayUsPayload),77RegStore(fw::RegStorePayload),78CoreReset,79CoreStart,80CoreWaitForHalt,81CoreResume,82}8384impl GspSeqCmd {85/// Creates a new `GspSeqCmd` from raw data returning the command and its size in bytes.86pub(crate) fn new(data: &[u8], dev: &device::Device) -> Result<(Self, usize)> {87let fw_cmd = fw::SequencerBufferCmd::from_bytes(data).ok_or(EINVAL)?;88let opcode_size = core::mem::size_of::<u32>();8990let (cmd, size) = match fw_cmd.opcode()? {91fw::SeqBufOpcode::RegWrite => {92let payload = fw_cmd.reg_write_payload()?;93let size = opcode_size + size_of_val(&payload);94(GspSeqCmd::RegWrite(payload), size)95}96fw::SeqBufOpcode::RegModify => {97let payload = fw_cmd.reg_modify_payload()?;98let size = opcode_size + size_of_val(&payload);99(GspSeqCmd::RegModify(payload), size)100}101fw::SeqBufOpcode::RegPoll => {102let payload = fw_cmd.reg_poll_payload()?;103let size = opcode_size + size_of_val(&payload);104(GspSeqCmd::RegPoll(payload), size)105}106fw::SeqBufOpcode::DelayUs => {107let payload = fw_cmd.delay_us_payload()?;108let size = opcode_size + size_of_val(&payload);109(GspSeqCmd::DelayUs(payload), size)110}111fw::SeqBufOpcode::RegStore => {112let payload = fw_cmd.reg_store_payload()?;113let size = opcode_size + size_of_val(&payload);114(GspSeqCmd::RegStore(payload), size)115}116fw::SeqBufOpcode::CoreReset => (GspSeqCmd::CoreReset, opcode_size),117fw::SeqBufOpcode::CoreStart => (GspSeqCmd::CoreStart, opcode_size),118fw::SeqBufOpcode::CoreWaitForHalt => (GspSeqCmd::CoreWaitForHalt, opcode_size),119fw::SeqBufOpcode::CoreResume => (GspSeqCmd::CoreResume, opcode_size),120};121122if data.len() < size {123dev_err!(dev, "Data is not enough for command");124return Err(EINVAL);125}126127Ok((cmd, size))128}129}130131/// GSP Sequencer for executing firmware commands during boot.132pub(crate) struct GspSequencer<'a> {133/// Sequencer information with command data.134seq_info: GspSequence,135/// `Bar0` for register access.136bar: &'a Bar0,137/// SEC2 falcon for core operations.138sec2_falcon: &'a Falcon<Sec2>,139/// GSP falcon for core operations.140gsp_falcon: &'a Falcon<Gsp>,141/// LibOS DMA handle address.142libos_dma_handle: u64,143/// Bootloader application version.144bootloader_app_version: u32,145/// Device for logging.146dev: ARef<device::Device>,147}148149/// Trait for running sequencer commands.150pub(crate) trait GspSeqCmdRunner {151fn run(&self, sequencer: &GspSequencer<'_>) -> Result;152}153154impl GspSeqCmdRunner for fw::RegWritePayload {155fn run(&self, sequencer: &GspSequencer<'_>) -> Result {156let addr = usize::from_safe_cast(self.addr());157158sequencer.bar.try_write32(self.val(), addr)159}160}161162impl GspSeqCmdRunner for fw::RegModifyPayload {163fn run(&self, sequencer: &GspSequencer<'_>) -> Result {164let addr = usize::from_safe_cast(self.addr());165166sequencer.bar.try_read32(addr).and_then(|val| {167sequencer168.bar169.try_write32((val & !self.mask()) | self.val(), addr)170})171}172}173174impl GspSeqCmdRunner for fw::RegPollPayload {175fn run(&self, sequencer: &GspSequencer<'_>) -> Result {176let addr = usize::from_safe_cast(self.addr());177178// Default timeout to 4 seconds.179let timeout_us = if self.timeout() == 0 {1804_000_000181} else {182i64::from(self.timeout())183};184185// First read.186sequencer.bar.try_read32(addr)?;187188// Poll the requested register with requested timeout.189read_poll_timeout(190|| sequencer.bar.try_read32(addr),191|current| (current & self.mask()) == self.val(),192Delta::ZERO,193Delta::from_micros(timeout_us),194)195.map(|_| ())196}197}198199impl GspSeqCmdRunner for fw::DelayUsPayload {200fn run(&self, _sequencer: &GspSequencer<'_>) -> Result {201fsleep(Delta::from_micros(i64::from(self.val())));202Ok(())203}204}205206impl GspSeqCmdRunner for fw::RegStorePayload {207fn run(&self, sequencer: &GspSequencer<'_>) -> Result {208let addr = usize::from_safe_cast(self.addr());209210sequencer.bar.try_read32(addr).map(|_| ())211}212}213214impl GspSeqCmdRunner for GspSeqCmd {215fn run(&self, seq: &GspSequencer<'_>) -> Result {216match self {217GspSeqCmd::RegWrite(cmd) => cmd.run(seq),218GspSeqCmd::RegModify(cmd) => cmd.run(seq),219GspSeqCmd::RegPoll(cmd) => cmd.run(seq),220GspSeqCmd::DelayUs(cmd) => cmd.run(seq),221GspSeqCmd::RegStore(cmd) => cmd.run(seq),222GspSeqCmd::CoreReset => {223seq.gsp_falcon.reset(seq.bar)?;224seq.gsp_falcon.dma_reset(seq.bar);225Ok(())226}227GspSeqCmd::CoreStart => {228seq.gsp_falcon.start(seq.bar)?;229Ok(())230}231GspSeqCmd::CoreWaitForHalt => {232seq.gsp_falcon.wait_till_halted(seq.bar)?;233Ok(())234}235GspSeqCmd::CoreResume => {236// At this point, 'SEC2-RTOS' has been loaded into SEC2 by the sequencer237// but neither SEC2-RTOS nor GSP-RM is running yet. This part of the238// sequencer will start both.239240// Reset the GSP to prepare it for resuming.241seq.gsp_falcon.reset(seq.bar)?;242243// Write the libOS DMA handle to GSP mailboxes.244seq.gsp_falcon.write_mailboxes(245seq.bar,246Some(seq.libos_dma_handle as u32),247Some((seq.libos_dma_handle >> 32) as u32),248);249250// Start the SEC2 falcon which will trigger GSP-RM to resume on the GSP.251seq.sec2_falcon.start(seq.bar)?;252253// Poll until GSP-RM reload/resume has completed (up to 2 seconds).254seq.gsp_falcon255.check_reload_completed(seq.bar, Delta::from_secs(2))?;256257// Verify SEC2 completed successfully by checking its mailbox for errors.258let mbox0 = seq.sec2_falcon.read_mailbox0(seq.bar);259if mbox0 != 0 {260dev_err!(seq.dev, "Sequencer: sec2 errors: {:?}\n", mbox0);261return Err(EIO);262}263264// Configure GSP with the bootloader version.265seq.gsp_falcon266.write_os_version(seq.bar, seq.bootloader_app_version);267268// Verify the GSP's RISC-V core is active indicating successful GSP boot.269if !seq.gsp_falcon.is_riscv_active(seq.bar) {270dev_err!(seq.dev, "Sequencer: RISC-V core is not active\n");271return Err(EIO);272}273Ok(())274}275}276}277}278279/// Iterator over GSP sequencer commands.280pub(crate) struct GspSeqIter<'a> {281/// Command data buffer.282cmd_data: &'a [u8],283/// Current position in the buffer.284current_offset: usize,285/// Total number of commands to process.286total_cmds: u32,287/// Number of commands processed so far.288cmds_processed: u32,289/// Device for logging.290dev: ARef<device::Device>,291}292293impl<'a> Iterator for GspSeqIter<'a> {294type Item = Result<GspSeqCmd>;295296fn next(&mut self) -> Option<Self::Item> {297// Stop if we've processed all commands or reached the end of data.298if self.cmds_processed >= self.total_cmds || self.current_offset >= self.cmd_data.len() {299return None;300}301302// Check if we have enough data for opcode.303if self.current_offset + core::mem::size_of::<u32>() > self.cmd_data.len() {304return Some(Err(EIO));305}306307let offset = self.current_offset;308309// Handle command creation based on available data,310// zero-pad if necessary (since last command may not be full size).311let mut buffer = [0u8; CMD_SIZE];312let copy_len = if offset + CMD_SIZE <= self.cmd_data.len() {313CMD_SIZE314} else {315self.cmd_data.len() - offset316};317buffer[..copy_len].copy_from_slice(&self.cmd_data[offset..offset + copy_len]);318let cmd_result = GspSeqCmd::new(&buffer, &self.dev);319320cmd_result.map_or_else(321|_err| {322dev_err!(self.dev, "Error parsing command at offset {}", offset);323None324},325|(cmd, size)| {326self.current_offset += size;327self.cmds_processed += 1;328Some(Ok(cmd))329},330)331}332}333334impl<'a> GspSequencer<'a> {335fn iter(&self) -> GspSeqIter<'_> {336let cmd_data = &self.seq_info.cmd_data[..];337338GspSeqIter {339cmd_data,340current_offset: 0,341total_cmds: self.seq_info.cmd_index,342cmds_processed: 0,343dev: self.dev.clone(),344}345}346}347348/// Parameters for running the GSP sequencer.349pub(crate) struct GspSequencerParams<'a> {350/// Bootloader application version.351pub(crate) bootloader_app_version: u32,352/// LibOS DMA handle address.353pub(crate) libos_dma_handle: u64,354/// GSP falcon for core operations.355pub(crate) gsp_falcon: &'a Falcon<Gsp>,356/// SEC2 falcon for core operations.357pub(crate) sec2_falcon: &'a Falcon<Sec2>,358/// Device for logging.359pub(crate) dev: ARef<device::Device>,360/// BAR0 for register access.361pub(crate) bar: &'a Bar0,362}363364impl<'a> GspSequencer<'a> {365pub(crate) fn run(cmdq: &mut Cmdq, params: GspSequencerParams<'a>) -> Result {366let seq_info = loop {367match cmdq.receive_msg::<GspSequence>(Delta::from_secs(10)) {368Ok(seq_info) => break seq_info,369Err(ERANGE) => continue,370Err(e) => return Err(e),371}372};373374let sequencer = GspSequencer {375seq_info,376bar: params.bar,377sec2_falcon: params.sec2_falcon,378gsp_falcon: params.gsp_falcon,379libos_dma_handle: params.libos_dma_handle,380bootloader_app_version: params.bootloader_app_version,381dev: params.dev,382};383384dev_dbg!(sequencer.dev, "Running CPU Sequencer commands");385386for cmd_result in sequencer.iter() {387match cmd_result {388Ok(cmd) => cmd.run(&sequencer)?,389Err(e) => {390dev_err!(391sequencer.dev,392"Error running command at index {}",393sequencer.seq_info.cmd_index394);395return Err(e);396}397}398}399400dev_dbg!(401sequencer.dev,402"CPU Sequencer commands completed successfully"403);404Ok(())405}406}407408409