| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- // SPDX-License-Identifier: GPL-2.0
- //! GSP Sequencer implementation for Pre-hopper GSP boot sequence.
- use core::array;
- use kernel::{
- device,
- io::{
- poll::read_poll_timeout,
- Io, //
- },
- prelude::*,
- sync::aref::ARef,
- time::{
- delay::fsleep,
- Delta, //
- },
- transmute::FromBytes, //
- };
- 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<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(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::<u32>();
- 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\n");
- 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<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 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<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 {}\n", 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\n");
- 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 {}\n",
- sequencer.seq_info.cmd_index
- );
- return Err(e);
- }
- }
- }
- dev_dbg!(
- sequencer.dev,
- "CPU Sequencer commands completed successfully\n"
- );
- Ok(())
- }
- }
|