Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/nova-core/gsp/sequencer.rs
38186 views
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
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");
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
/// 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.
151
pub(crate) trait GspSeqCmdRunner {
152
fn run(&self, sequencer: &GspSequencer<'_>) -> Result;
153
}
154
155
impl 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
163
impl 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
175
impl 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
200
impl 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
207
impl 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
215
impl 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.
281
pub(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
294
impl<'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
335
impl<'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.
350
pub(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
365
impl<'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
}
408
409