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