projects/linux·blob master · refs

385 lines · 11.8 KiB

1// SPDX-License-Identifier: GPL-2.0
2
3//! GSP Sequencer implementation for Pre-hopper GSP boot sequence.
4
5use core::array;
6
7use 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
22use 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.
43struct 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
50impl 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
67const 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)]
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\n");
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 /// `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
148impl 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
156impl 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
168impl 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
193impl fw::DelayUsPayload {
194 fn run(&self, _sequencer: &GspSequencer<'_>) -> Result {
195 fsleep(Delta::from_micros(i64::from(self.val())));
196 Ok(())
197 }
198}
199
200impl 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
208impl 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.
273struct 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
286impl<'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
298impl<'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
339impl<'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}