Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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}