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