xref: /linux/drivers/gpu/nova-core/gsp/sequencer.rs (revision e386680e8dbb37ff2973a2cbcf7124899e5359df)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! GSP Sequencer implementation for Pre-hopper GSP boot sequence.
4 
5 use core::{
6     array,
7     mem::{
8         size_of,
9         size_of_val, //
10     },
11 };
12 
13 use 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 
25 use 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.
44 struct 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 
51 impl 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 
68 const 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)]
73 pub(crate) enum GspSeqCmd {
74     RegWrite(fw::RegWritePayload),
75     RegModify(fw::RegModifyPayload),
76     RegPoll(fw::RegPollPayload),
77     DelayUs(fw::DelayUsPayload),
78     RegStore(fw::RegStorePayload),
79 }
80 
81 impl GspSeqCmd {
82     /// Creates a new `GspSeqCmd` from raw data returning the command and its size in bytes.
83     pub(crate) fn new(data: &[u8], dev: &device::Device) -> Result<(Self, usize)> {
84         let fw_cmd = fw::SequencerBufferCmd::from_bytes(data).ok_or(EINVAL)?;
85         let opcode_size = core::mem::size_of::<u32>();
86 
87         let (cmd, size) = match fw_cmd.opcode()? {
88             fw::SeqBufOpcode::RegWrite => {
89                 let payload = fw_cmd.reg_write_payload()?;
90                 let size = opcode_size + size_of_val(&payload);
91                 (GspSeqCmd::RegWrite(payload), size)
92             }
93             fw::SeqBufOpcode::RegModify => {
94                 let payload = fw_cmd.reg_modify_payload()?;
95                 let size = opcode_size + size_of_val(&payload);
96                 (GspSeqCmd::RegModify(payload), size)
97             }
98             fw::SeqBufOpcode::RegPoll => {
99                 let payload = fw_cmd.reg_poll_payload()?;
100                 let size = opcode_size + size_of_val(&payload);
101                 (GspSeqCmd::RegPoll(payload), size)
102             }
103             fw::SeqBufOpcode::DelayUs => {
104                 let payload = fw_cmd.delay_us_payload()?;
105                 let size = opcode_size + size_of_val(&payload);
106                 (GspSeqCmd::DelayUs(payload), size)
107             }
108             fw::SeqBufOpcode::RegStore => {
109                 let payload = fw_cmd.reg_store_payload()?;
110                 let size = opcode_size + size_of_val(&payload);
111                 (GspSeqCmd::RegStore(payload), size)
112             }
113             _ => return Err(EINVAL),
114         };
115 
116         if data.len() < size {
117             dev_err!(dev, "Data is not enough for command");
118             return Err(EINVAL);
119         }
120 
121         Ok((cmd, size))
122     }
123 }
124 
125 /// GSP Sequencer for executing firmware commands during boot.
126 #[expect(dead_code)]
127 pub(crate) struct GspSequencer<'a> {
128     /// Sequencer information with command data.
129     seq_info: GspSequence,
130     /// `Bar0` for register access.
131     bar: &'a Bar0,
132     /// SEC2 falcon for core operations.
133     sec2_falcon: &'a Falcon<Sec2>,
134     /// GSP falcon for core operations.
135     gsp_falcon: &'a Falcon<Gsp>,
136     /// LibOS DMA handle address.
137     libos_dma_handle: u64,
138     /// Bootloader application version.
139     bootloader_app_version: u32,
140     /// Device for logging.
141     dev: ARef<device::Device>,
142 }
143 
144 /// Trait for running sequencer commands.
145 pub(crate) trait GspSeqCmdRunner {
146     fn run(&self, sequencer: &GspSequencer<'_>) -> Result;
147 }
148 
149 impl GspSeqCmdRunner for fw::RegWritePayload {
150     fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
151         let addr = usize::from_safe_cast(self.addr());
152 
153         sequencer.bar.try_write32(self.val(), addr)
154     }
155 }
156 
157 impl GspSeqCmdRunner for fw::RegModifyPayload {
158     fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
159         let addr = usize::from_safe_cast(self.addr());
160 
161         sequencer.bar.try_read32(addr).and_then(|val| {
162             sequencer
163                 .bar
164                 .try_write32((val & !self.mask()) | self.val(), addr)
165         })
166     }
167 }
168 
169 impl GspSeqCmdRunner for fw::RegPollPayload {
170     fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
171         let addr = usize::from_safe_cast(self.addr());
172 
173         // Default timeout to 4 seconds.
174         let timeout_us = if self.timeout() == 0 {
175             4_000_000
176         } else {
177             i64::from(self.timeout())
178         };
179 
180         // First read.
181         sequencer.bar.try_read32(addr)?;
182 
183         // Poll the requested register with requested timeout.
184         read_poll_timeout(
185             || sequencer.bar.try_read32(addr),
186             |current| (current & self.mask()) == self.val(),
187             Delta::ZERO,
188             Delta::from_micros(timeout_us),
189         )
190         .map(|_| ())
191     }
192 }
193 
194 impl GspSeqCmdRunner for fw::DelayUsPayload {
195     fn run(&self, _sequencer: &GspSequencer<'_>) -> Result {
196         fsleep(Delta::from_micros(i64::from(self.val())));
197         Ok(())
198     }
199 }
200 
201 impl GspSeqCmdRunner for fw::RegStorePayload {
202     fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
203         let addr = usize::from_safe_cast(self.addr());
204 
205         sequencer.bar.try_read32(addr).map(|_| ())
206     }
207 }
208 
209 impl GspSeqCmdRunner for GspSeqCmd {
210     fn run(&self, seq: &GspSequencer<'_>) -> Result {
211         match self {
212             GspSeqCmd::RegWrite(cmd) => cmd.run(seq),
213             GspSeqCmd::RegModify(cmd) => cmd.run(seq),
214             GspSeqCmd::RegPoll(cmd) => cmd.run(seq),
215             GspSeqCmd::DelayUs(cmd) => cmd.run(seq),
216             GspSeqCmd::RegStore(cmd) => cmd.run(seq),
217         }
218     }
219 }
220 
221 /// Iterator over GSP sequencer commands.
222 pub(crate) struct GspSeqIter<'a> {
223     /// Command data buffer.
224     cmd_data: &'a [u8],
225     /// Current position in the buffer.
226     current_offset: usize,
227     /// Total number of commands to process.
228     total_cmds: u32,
229     /// Number of commands processed so far.
230     cmds_processed: u32,
231     /// Device for logging.
232     dev: ARef<device::Device>,
233 }
234 
235 impl<'a> Iterator for GspSeqIter<'a> {
236     type Item = Result<GspSeqCmd>;
237 
238     fn next(&mut self) -> Option<Self::Item> {
239         // Stop if we've processed all commands or reached the end of data.
240         if self.cmds_processed >= self.total_cmds || self.current_offset >= self.cmd_data.len() {
241             return None;
242         }
243 
244         // Check if we have enough data for opcode.
245         if self.current_offset + core::mem::size_of::<u32>() > self.cmd_data.len() {
246             return Some(Err(EIO));
247         }
248 
249         let offset = self.current_offset;
250 
251         // Handle command creation based on available data,
252         // zero-pad if necessary (since last command may not be full size).
253         let mut buffer = [0u8; CMD_SIZE];
254         let copy_len = if offset + CMD_SIZE <= self.cmd_data.len() {
255             CMD_SIZE
256         } else {
257             self.cmd_data.len() - offset
258         };
259         buffer[..copy_len].copy_from_slice(&self.cmd_data[offset..offset + copy_len]);
260         let cmd_result = GspSeqCmd::new(&buffer, &self.dev);
261 
262         cmd_result.map_or_else(
263             |_err| {
264                 dev_err!(self.dev, "Error parsing command at offset {}", offset);
265                 None
266             },
267             |(cmd, size)| {
268                 self.current_offset += size;
269                 self.cmds_processed += 1;
270                 Some(Ok(cmd))
271             },
272         )
273     }
274 }
275 
276 impl<'a> GspSequencer<'a> {
277     fn iter(&self) -> GspSeqIter<'_> {
278         let cmd_data = &self.seq_info.cmd_data[..];
279 
280         GspSeqIter {
281             cmd_data,
282             current_offset: 0,
283             total_cmds: self.seq_info.cmd_index,
284             cmds_processed: 0,
285             dev: self.dev.clone(),
286         }
287     }
288 }
289 
290 /// Parameters for running the GSP sequencer.
291 pub(crate) struct GspSequencerParams<'a> {
292     /// Bootloader application version.
293     pub(crate) bootloader_app_version: u32,
294     /// LibOS DMA handle address.
295     pub(crate) libos_dma_handle: u64,
296     /// GSP falcon for core operations.
297     pub(crate) gsp_falcon: &'a Falcon<Gsp>,
298     /// SEC2 falcon for core operations.
299     pub(crate) sec2_falcon: &'a Falcon<Sec2>,
300     /// Device for logging.
301     pub(crate) dev: ARef<device::Device>,
302     /// BAR0 for register access.
303     pub(crate) bar: &'a Bar0,
304 }
305 
306 impl<'a> GspSequencer<'a> {
307     pub(crate) fn run(cmdq: &mut Cmdq, params: GspSequencerParams<'a>) -> Result {
308         let seq_info = loop {
309             match cmdq.receive_msg::<GspSequence>(Delta::from_secs(10)) {
310                 Ok(seq_info) => break seq_info,
311                 Err(ERANGE) => continue,
312                 Err(e) => return Err(e),
313             }
314         };
315 
316         let sequencer = GspSequencer {
317             seq_info,
318             bar: params.bar,
319             sec2_falcon: params.sec2_falcon,
320             gsp_falcon: params.gsp_falcon,
321             libos_dma_handle: params.libos_dma_handle,
322             bootloader_app_version: params.bootloader_app_version,
323             dev: params.dev,
324         };
325 
326         dev_dbg!(sequencer.dev, "Running CPU Sequencer commands");
327 
328         for cmd_result in sequencer.iter() {
329             match cmd_result {
330                 Ok(cmd) => cmd.run(&sequencer)?,
331                 Err(e) => {
332                     dev_err!(
333                         sequencer.dev,
334                         "Error running command at index {}",
335                         sequencer.seq_info.cmd_index
336                     );
337                     return Err(e);
338                 }
339             }
340         }
341 
342         dev_dbg!(
343             sequencer.dev,
344             "CPU Sequencer commands completed successfully"
345         );
346         Ok(())
347     }
348 }
349