summaryrefslogtreecommitdiff
path: root/drivers/gpu/nova-core/gsp/sequencer.rs
diff options
context:
space:
mode:
authorJoel Fernandes <joelagnelf@nvidia.com>2025-11-14 22:55:47 +0300
committerAlexandre Courbot <acourbot@nvidia.com>2025-11-15 15:54:18 +0300
commit2367ce2e9e5eae3bfe72ed79a7fbd86936158569 (patch)
treea00c94a1bc3beabf24ed554f2db1af5479c8fd66 /drivers/gpu/nova-core/gsp/sequencer.rs
parent6ddfc892a529cb314e4708c5654beb0fa37e2071 (diff)
downloadlinux-2367ce2e9e5eae3bfe72ed79a7fbd86936158569.tar.xz
gpu: nova-core: sequencer: Add register opcodes
These opcodes are used for register write, modify, poll and store (save) sequencer operations. Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com> [acourbot@nvidia.com: apply Lyude's suggested fixes.] Message-ID: <20251114195552.739371-9-joelagnelf@nvidia.com>
Diffstat (limited to 'drivers/gpu/nova-core/gsp/sequencer.rs')
-rw-r--r--drivers/gpu/nova-core/gsp/sequencer.rs117
1 files changed, 106 insertions, 11 deletions
diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs
index 1f4f0a0d999b..de5d6b4433a2 100644
--- a/drivers/gpu/nova-core/gsp/sequencer.rs
+++ b/drivers/gpu/nova-core/gsp/sequencer.rs
@@ -4,11 +4,15 @@
use core::{
array,
- mem::size_of, //
+ mem::{
+ size_of,
+ size_of_val, //
+ },
};
use kernel::{
device,
+ io::poll::read_poll_timeout,
prelude::*,
time::Delta,
transmute::FromBytes,
@@ -29,6 +33,7 @@ use crate::{
},
fw,
},
+ num::FromSafeCast,
sbuffer::SBufferIter,
};
@@ -61,18 +66,50 @@ const CMD_SIZE: usize = size_of::<fw::SequencerBufferCmd>();
/// GSP Sequencer Command types with payload data.
/// Commands have an opcode and an opcode-dependent struct.
-#[allow(dead_code)]
-pub(crate) enum GspSeqCmd {}
+#[allow(clippy::enum_variant_names)]
+pub(crate) enum GspSeqCmd {
+ RegWrite(fw::RegWritePayload),
+ RegModify(fw::RegModifyPayload),
+ RegPoll(fw::RegPollPayload),
+ RegStore(fw::RegStorePayload),
+}
impl GspSeqCmd {
/// Creates a new `GspSeqCmd` from raw data returning the command and its size in bytes.
- pub(crate) fn new(data: &[u8], _dev: &device::Device) -> Result<(Self, usize)> {
- let _fw_cmd = fw::SequencerBufferCmd::from_bytes(data).ok_or(EINVAL)?;
- let _opcode_size = core::mem::size_of::<u32>();
+ pub(crate) fn new(data: &[u8], dev: &device::Device) -> Result<(Self, usize)> {
+ let fw_cmd = fw::SequencerBufferCmd::from_bytes(data).ok_or(EINVAL)?;
+ let opcode_size = core::mem::size_of::<u32>();
- // NOTE: At this commit, NO opcodes exist yet, so just return error.
- // Later commits will add match arms here.
- Err(EINVAL)
+ let (cmd, size) = match fw_cmd.opcode()? {
+ fw::SeqBufOpcode::RegWrite => {
+ let payload = fw_cmd.reg_write_payload()?;
+ let size = opcode_size + size_of_val(&payload);
+ (GspSeqCmd::RegWrite(payload), size)
+ }
+ fw::SeqBufOpcode::RegModify => {
+ let payload = fw_cmd.reg_modify_payload()?;
+ let size = opcode_size + size_of_val(&payload);
+ (GspSeqCmd::RegModify(payload), size)
+ }
+ fw::SeqBufOpcode::RegPoll => {
+ let payload = fw_cmd.reg_poll_payload()?;
+ let size = opcode_size + size_of_val(&payload);
+ (GspSeqCmd::RegPoll(payload), size)
+ }
+ fw::SeqBufOpcode::RegStore => {
+ let payload = fw_cmd.reg_store_payload()?;
+ let size = opcode_size + size_of_val(&payload);
+ (GspSeqCmd::RegStore(payload), size)
+ }
+ _ => return Err(EINVAL),
+ };
+
+ if data.len() < size {
+ dev_err!(dev, "Data is not enough for command");
+ return Err(EINVAL);
+ }
+
+ Ok((cmd, size))
}
}
@@ -100,9 +137,67 @@ pub(crate) trait GspSeqCmdRunner {
fn run(&self, sequencer: &GspSequencer<'_>) -> Result;
}
+impl GspSeqCmdRunner for fw::RegWritePayload {
+ fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
+ let addr = usize::from_safe_cast(self.addr());
+
+ sequencer.bar.try_write32(self.val(), addr)
+ }
+}
+
+impl GspSeqCmdRunner for fw::RegModifyPayload {
+ fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
+ let addr = usize::from_safe_cast(self.addr());
+
+ sequencer.bar.try_read32(addr).and_then(|val| {
+ sequencer
+ .bar
+ .try_write32((val & !self.mask()) | self.val(), addr)
+ })
+ }
+}
+
+impl GspSeqCmdRunner for fw::RegPollPayload {
+ fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
+ let addr = usize::from_safe_cast(self.addr());
+
+ // Default timeout to 4 seconds.
+ let timeout_us = if self.timeout() == 0 {
+ 4_000_000
+ } else {
+ i64::from(self.timeout())
+ };
+
+ // First read.
+ sequencer.bar.try_read32(addr)?;
+
+ // Poll the requested register with requested timeout.
+ read_poll_timeout(
+ || sequencer.bar.try_read32(addr),
+ |current| (current & self.mask()) == self.val(),
+ Delta::ZERO,
+ Delta::from_micros(timeout_us),
+ )
+ .map(|_| ())
+ }
+}
+
+impl GspSeqCmdRunner for fw::RegStorePayload {
+ fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
+ let addr = usize::from_safe_cast(self.addr());
+
+ sequencer.bar.try_read32(addr).map(|_| ())
+ }
+}
+
impl GspSeqCmdRunner for GspSeqCmd {
- fn run(&self, _seq: &GspSequencer<'_>) -> Result {
- Ok(())
+ fn run(&self, seq: &GspSequencer<'_>) -> Result {
+ match self {
+ GspSeqCmd::RegWrite(cmd) => cmd.run(seq),
+ GspSeqCmd::RegModify(cmd) => cmd.run(seq),
+ GspSeqCmd::RegPoll(cmd) => cmd.run(seq),
+ GspSeqCmd::RegStore(cmd) => cmd.run(seq),
+ }
}
}