File Index
Files and directories
Sign in
FILE

sequencer.rs

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/nova-core/gsp/sequencer.rs RUST
  1// SPDX-License-Identifier: GPL-2.0
  2
  3//! GSP Sequencer implementation for Pre-hopper GSP boot sequence.
  4
  5use core::{
  6    array,
  7    mem::{
  8        size_of,
  9        size_of_val, //
 10    },
 11};
 12
 13use kernel::{
 14    device,
 15    io::poll::read_poll_timeout,
 16    prelude::*,
 17    time::{
 18        delay::fsleep,
 19        Delta, //
 20    },
 21    transmute::FromBytes,
 22    types::ARef, //
 23};
 24
 25use crate::{
 26    driver::Bar0,
 27    falcon::{
 28        gsp::Gsp,
 29        sec2::Sec2,
 30        Falcon, //
 31    },
 32    gsp::{
 33        cmdq::{
 34            Cmdq,
 35            MessageFromGsp, //
 36        },
 37        fw,
 38    },
 39    num::FromSafeCast,
 40    sbuffer::SBufferIter,
 41};
 42
 43/// GSP Sequencer information containing the command sequence and data.
 44struct GspSequence {
 45    /// Current command index for error reporting.
 46    cmd_index: u32,
 47    /// Command data buffer containing the sequence of commands.
 48    cmd_data: KVec<u8>,
 49}
 50
 51impl MessageFromGsp for GspSequence {
 52    const FUNCTION: fw::MsgFunction = fw::MsgFunction::GspRunCpuSequencer;
 53    type InitError = Error;
 54    type Message = fw::RunCpuSequencer;
 55
 56    fn read(
 57        msg: &Self::Message,
 58        sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
 59    ) -> Result<Self, Self::InitError> {
 60        let cmd_data = sbuffer.flush_into_kvec(GFP_KERNEL)?;
 61        Ok(GspSequence {
 62            cmd_index: msg.cmd_index(),
 63            cmd_data,
 64        })
 65    }
 66}
 67
 68const CMD_SIZE: usize = size_of::<fw::SequencerBufferCmd>();
 69
 70/// GSP Sequencer Command types with payload data.
 71/// Commands have an opcode and an opcode-dependent struct.
 72#[allow(clippy::enum_variant_names)]
 73pub(crate) enum GspSeqCmd {
 74    RegWrite(fw::RegWritePayload),
 75    RegModify(fw::RegModifyPayload),
 76    RegPoll(fw::RegPollPayload),
 77    DelayUs(fw::DelayUsPayload),
 78    RegStore(fw::RegStorePayload),
 79    CoreReset,
 80    CoreStart,
 81    CoreWaitForHalt,
 82    CoreResume,
 83}
 84
 85impl GspSeqCmd {
 86    /// Creates a new `GspSeqCmd` from raw data returning the command and its size in bytes.
 87    pub(crate) fn new(data: &[u8], dev: &device::Device) -> Result<(Self, usize)> {
 88        let fw_cmd = fw::SequencerBufferCmd::from_bytes(data).ok_or(EINVAL)?;
 89        let opcode_size = core::mem::size_of::<u32>();
 90
 91        let (cmd, size) = match fw_cmd.opcode()? {
 92            fw::SeqBufOpcode::RegWrite => {
 93                let payload = fw_cmd.reg_write_payload()?;
 94                let size = opcode_size + size_of_val(&payload);
 95                (GspSeqCmd::RegWrite(payload), size)
 96            }
 97            fw::SeqBufOpcode::RegModify => {
 98                let payload = fw_cmd.reg_modify_payload()?;
 99                let size = opcode_size + size_of_val(&payload);
100                (GspSeqCmd::RegModify(payload), size)
101            }
102            fw::SeqBufOpcode::RegPoll => {
103                let payload = fw_cmd.reg_poll_payload()?;
104                let size = opcode_size + size_of_val(&payload);
105                (GspSeqCmd::RegPoll(payload), size)
106            }
107            fw::SeqBufOpcode::DelayUs => {
108                let payload = fw_cmd.delay_us_payload()?;
109                let size = opcode_size + size_of_val(&payload);
110                (GspSeqCmd::DelayUs(payload), size)
111            }
112            fw::SeqBufOpcode::RegStore => {
113                let payload = fw_cmd.reg_store_payload()?;
114                let size = opcode_size + size_of_val(&payload);
115                (GspSeqCmd::RegStore(payload), size)
116            }
117            fw::SeqBufOpcode::CoreReset => (GspSeqCmd::CoreReset, opcode_size),
118            fw::SeqBufOpcode::CoreStart => (GspSeqCmd::CoreStart, opcode_size),
119            fw::SeqBufOpcode::CoreWaitForHalt => (GspSeqCmd::CoreWaitForHalt, opcode_size),
120            fw::SeqBufOpcode::CoreResume => (GspSeqCmd::CoreResume, opcode_size),
121        };
122
123        if data.len() < size {
124            dev_err!(dev, "Data is not enough for command");
125            return Err(EINVAL);
126        }
127
128        Ok((cmd, size))
129    }
130}
131
132/// GSP Sequencer for executing firmware commands during boot.
133pub(crate) struct GspSequencer<'a> {
134    /// Sequencer information with command data.
135    seq_info: GspSequence,
136    /// `Bar0` for register access.
137    bar: &'a Bar0,
138    /// SEC2 falcon for core operations.
139    sec2_falcon: &'a Falcon<Sec2>,
140    /// GSP falcon for core operations.
141    gsp_falcon: &'a Falcon<Gsp>,
142    /// LibOS DMA handle address.
143    libos_dma_handle: u64,
144    /// Bootloader application version.
145    bootloader_app_version: u32,
146    /// Device for logging.
147    dev: ARef<device::Device>,
148}
149
150/// Trait for running sequencer commands.
151pub(crate) trait GspSeqCmdRunner {
152    fn run(&self, sequencer: &GspSequencer<'_>) -> Result;
153}
154
155impl GspSeqCmdRunner for fw::RegWritePayload {
156    fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
157        let addr = usize::from_safe_cast(self.addr());
158
159        sequencer.bar.try_write32(self.val(), addr)
160    }
161}
162
163impl GspSeqCmdRunner for fw::RegModifyPayload {
164    fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
165        let addr = usize::from_safe_cast(self.addr());
166
167        sequencer.bar.try_read32(addr).and_then(|val| {
168            sequencer
169                .bar
170                .try_write32((val & !self.mask()) | self.val(), addr)
171        })
172    }
173}
174
175impl GspSeqCmdRunner for fw::RegPollPayload {
176    fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
177        let addr = usize::from_safe_cast(self.addr());
178
179        // Default timeout to 4 seconds.
180        let timeout_us = if self.timeout() == 0 {
181            4_000_000
182        } else {
183            i64::from(self.timeout())
184        };
185
186        // First read.
187        sequencer.bar.try_read32(addr)?;
188
189        // Poll the requested register with requested timeout.
190        read_poll_timeout(
191            || sequencer.bar.try_read32(addr),
192            |current| (current & self.mask()) == self.val(),
193            Delta::ZERO,
194            Delta::from_micros(timeout_us),
195        )
196        .map(|_| ())
197    }
198}
199
200impl GspSeqCmdRunner for fw::DelayUsPayload {
201    fn run(&self, _sequencer: &GspSequencer<'_>) -> Result {
202        fsleep(Delta::from_micros(i64::from(self.val())));
203        Ok(())
204    }
205}
206
207impl GspSeqCmdRunner for fw::RegStorePayload {
208    fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
209        let addr = usize::from_safe_cast(self.addr());
210
211        sequencer.bar.try_read32(addr).map(|_| ())
212    }
213}
214
215impl GspSeqCmdRunner for GspSeqCmd {
216    fn run(&self, seq: &GspSequencer<'_>) -> Result {
217        match self {
218            GspSeqCmd::RegWrite(cmd) => cmd.run(seq),
219            GspSeqCmd::RegModify(cmd) => cmd.run(seq),
220            GspSeqCmd::RegPoll(cmd) => cmd.run(seq),
221            GspSeqCmd::DelayUs(cmd) => cmd.run(seq),
222            GspSeqCmd::RegStore(cmd) => cmd.run(seq),
223            GspSeqCmd::CoreReset => {
224                seq.gsp_falcon.reset(seq.bar)?;
225                seq.gsp_falcon.dma_reset(seq.bar);
226                Ok(())
227            }
228            GspSeqCmd::CoreStart => {
229                seq.gsp_falcon.start(seq.bar)?;
230                Ok(())
231            }
232            GspSeqCmd::CoreWaitForHalt => {
233                seq.gsp_falcon.wait_till_halted(seq.bar)?;
234                Ok(())
235            }
236            GspSeqCmd::CoreResume => {
237                // At this point, 'SEC2-RTOS' has been loaded into SEC2 by the sequencer
238                // but neither SEC2-RTOS nor GSP-RM is running yet. This part of the
239                // sequencer will start both.
240
241                // Reset the GSP to prepare it for resuming.
242                seq.gsp_falcon.reset(seq.bar)?;
243
244                // Write the libOS DMA handle to GSP mailboxes.
245                seq.gsp_falcon.write_mailboxes(
246                    seq.bar,
247                    Some(seq.libos_dma_handle as u32),
248                    Some((seq.libos_dma_handle >> 32) as u32),
249                );
250
251                // Start the SEC2 falcon which will trigger GSP-RM to resume on the GSP.
252                seq.sec2_falcon.start(seq.bar)?;
253
254                // Poll until GSP-RM reload/resume has completed (up to 2 seconds).
255                seq.gsp_falcon
256                    .check_reload_completed(seq.bar, Delta::from_secs(2))?;
257
258                // Verify SEC2 completed successfully by checking its mailbox for errors.
259                let mbox0 = seq.sec2_falcon.read_mailbox0(seq.bar);
260                if mbox0 != 0 {
261                    dev_err!(seq.dev, "Sequencer: sec2 errors: {:?}\n", mbox0);
262                    return Err(EIO);
263                }
264
265                // Configure GSP with the bootloader version.
266                seq.gsp_falcon
267                    .write_os_version(seq.bar, seq.bootloader_app_version);
268
269                // Verify the GSP's RISC-V core is active indicating successful GSP boot.
270                if !seq.gsp_falcon.is_riscv_active(seq.bar) {
271                    dev_err!(seq.dev, "Sequencer: RISC-V core is not active\n");
272                    return Err(EIO);
273                }
274                Ok(())
275            }
276        }
277    }
278}
279
280/// Iterator over GSP sequencer commands.
281pub(crate) struct GspSeqIter<'a> {
282    /// Command data buffer.
283    cmd_data: &'a [u8],
284    /// Current position in the buffer.
285    current_offset: usize,
286    /// Total number of commands to process.
287    total_cmds: u32,
288    /// Number of commands processed so far.
289    cmds_processed: u32,
290    /// Device for logging.
291    dev: ARef<device::Device>,
292}
293
294impl<'a> Iterator for GspSeqIter<'a> {
295    type Item = Result<GspSeqCmd>;
296
297    fn next(&mut self) -> Option<Self::Item> {
298        // Stop if we've processed all commands or reached the end of data.
299        if self.cmds_processed >= self.total_cmds || self.current_offset >= self.cmd_data.len() {
300            return None;
301        }
302
303        // Check if we have enough data for opcode.
304        if self.current_offset + core::mem::size_of::<u32>() > self.cmd_data.len() {
305            return Some(Err(EIO));
306        }
307
308        let offset = self.current_offset;
309
310        // Handle command creation based on available data,
311        // zero-pad if necessary (since last command may not be full size).
312        let mut buffer = [0u8; CMD_SIZE];
313        let copy_len = if offset + CMD_SIZE <= self.cmd_data.len() {
314            CMD_SIZE
315        } else {
316            self.cmd_data.len() - offset
317        };
318        buffer[..copy_len].copy_from_slice(&self.cmd_data[offset..offset + copy_len]);
319        let cmd_result = GspSeqCmd::new(&buffer, &self.dev);
320
321        cmd_result.map_or_else(
322            |_err| {
323                dev_err!(self.dev, "Error parsing command at offset {}", offset);
324                None
325            },
326            |(cmd, size)| {
327                self.current_offset += size;
328                self.cmds_processed += 1;
329                Some(Ok(cmd))
330            },
331        )
332    }
333}
334
335impl<'a> GspSequencer<'a> {
336    fn iter(&self) -> GspSeqIter<'_> {
337        let cmd_data = &self.seq_info.cmd_data[..];
338
339        GspSeqIter {
340            cmd_data,
341            current_offset: 0,
342            total_cmds: self.seq_info.cmd_index,
343            cmds_processed: 0,
344            dev: self.dev.clone(),
345        }
346    }
347}
348
349/// Parameters for running the GSP sequencer.
350pub(crate) struct GspSequencerParams<'a> {
351    /// Bootloader application version.
352    pub(crate) bootloader_app_version: u32,
353    /// LibOS DMA handle address.
354    pub(crate) libos_dma_handle: u64,
355    /// GSP falcon for core operations.
356    pub(crate) gsp_falcon: &'a Falcon<Gsp>,
357    /// SEC2 falcon for core operations.
358    pub(crate) sec2_falcon: &'a Falcon<Sec2>,
359    /// Device for logging.
360    pub(crate) dev: ARef<device::Device>,
361    /// BAR0 for register access.
362    pub(crate) bar: &'a Bar0,
363}
364
365impl<'a> GspSequencer<'a> {
366    pub(crate) fn run(cmdq: &mut Cmdq, params: GspSequencerParams<'a>) -> Result {
367        let seq_info = loop {
368            match cmdq.receive_msg::<GspSequence>(Delta::from_secs(10)) {
369                Ok(seq_info) => break seq_info,
370                Err(ERANGE) => continue,
371                Err(e) => return Err(e),
372            }
373        };
374
375        let sequencer = GspSequencer {
376            seq_info,
377            bar: params.bar,
378            sec2_falcon: params.sec2_falcon,
379            gsp_falcon: params.gsp_falcon,
380            libos_dma_handle: params.libos_dma_handle,
381            bootloader_app_version: params.bootloader_app_version,
382            dev: params.dev,
383        };
384
385        dev_dbg!(sequencer.dev, "Running CPU Sequencer commands");
386
387        for cmd_result in sequencer.iter() {
388            match cmd_result {
389                Ok(cmd) => cmd.run(&sequencer)?,
390                Err(e) => {
391                    dev_err!(
392                        sequencer.dev,
393                        "Error running command at index {}",
394                        sequencer.seq_info.cmd_index
395                    );
396                    return Err(e);
397                }
398            }
399        }
400
401        dev_dbg!(
402            sequencer.dev,
403            "CPU Sequencer commands completed successfully"
404        );
405        Ok(())
406    }
407}
~/ Read-only file preview.