From: Alexandre Courbot <acourbot@nvidia.com>
To: "Danilo Krummrich" <dakr@kernel.org>,
"David Airlie" <airlied@gmail.com>,
"John Hubbard" <jhubbard@nvidia.com>,
"Ben Skeggs" <bskeggs@nvidia.com>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Alex Gaynor" <alex.gaynor@gmail.com>,
"Boqun Feng" <boqun.feng@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <benno.lossin@proton.me>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Simona Vetter" <simona@ffwll.ch>
Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
nouveau@lists.freedesktop.org, dri-devel@lists.freedesktop.org,
Alexandre Courbot <acourbot@nvidia.com>
Subject: [PATCH RFC v3 6/7] gpu: nova-core: add basic timer device
Date: Thu, 20 Mar 2025 22:39:14 +0900 [thread overview]
Message-ID: <20250320-nova_timer-v3-6-79aa2ad25a79@nvidia.com> (raw)
In-Reply-To: <20250320-nova_timer-v3-0-79aa2ad25a79@nvidia.com>
Add a basic timer device and exercise it during device probing. This
first draft is probably very questionable.
One point in particular which should IMHO receive attention: the generic
wait_on() method aims at providing similar functionality to Nouveau's
nvkm_[num]sec() macros. Since this method will be heavily used with
different conditions to test, I'd like to avoid monomorphizing it
entirely with each instance ; that's something that is achieved in
nvkm_xsec() using functions that the macros invoke.
I have tried achieving the same result in Rust using closures (kept
as-is in the current code), but they seem to be monomorphized as well.
Calling extra functions could work better, but looks also less elegant
to me, so I am really open to suggestions here.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/driver.rs | 4 +-
drivers/gpu/nova-core/gpu.rs | 55 +++++++++++++++-
drivers/gpu/nova-core/nova_core.rs | 1 +
drivers/gpu/nova-core/regs.rs | 11 ++++
drivers/gpu/nova-core/timer.rs | 132 +++++++++++++++++++++++++++++++++++++
5 files changed, 201 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 63c19f140fbdd65d8fccf81669ac590807cc120f..0cd23aa306e4082405f480afc0530a41131485e7 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -10,7 +10,7 @@ pub(crate) struct NovaCore {
pub(crate) gpu: Gpu,
}
-const BAR0_SIZE: usize = 8;
+const BAR0_SIZE: usize = 0x9500;
pub(crate) type Bar0 = pci::Bar<BAR0_SIZE>;
kernel::pci_device_table!(
@@ -42,6 +42,8 @@ fn probe(pdev: &mut pci::Device, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>
GFP_KERNEL,
)?;
+ let _ = this.gpu.test_timer();
+
Ok(this)
}
}
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index d96901e5c8eace1e7c57c77da7def209e8149cd3..f010d3152530b1cec032ca620e59de51a2fc1a13 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -6,8 +6,10 @@
use crate::driver::Bar0;
use crate::regs;
+use crate::timer::Timer;
use crate::util;
use core::fmt;
+use core::time::Duration;
macro_rules! define_chipset {
({ $($variant:ident = $value:expr),* $(,)* }) =>
@@ -179,6 +181,7 @@ pub(crate) struct Gpu {
/// MMIO mapping of PCI BAR 0
bar: Devres<Bar0>,
fw: Firmware,
+ timer: Timer,
}
impl Gpu {
@@ -194,6 +197,56 @@ pub(crate) fn new(pdev: &pci::Device, bar: Devres<Bar0>) -> Result<impl PinInit<
spec.revision
);
- Ok(pin_init!(Self { spec, bar, fw }))
+ let timer = Timer::new();
+
+ Ok(pin_init!(Self {
+ spec,
+ bar,
+ fw,
+ timer,
+ }))
+ }
+
+ pub(crate) fn test_timer(&self) -> Result<()> {
+ pr_info!("testing timer subdev\n");
+ with_bar!(self.bar, |b| {
+ pr_info!("current timestamp: {}\n", self.timer.read(b))
+ })?;
+
+ if !matches!(
+ self.timer
+ .wait_on(&self.bar, Duration::from_millis(10), || Some(())),
+ Ok(())
+ ) {
+ pr_crit!("timer test failure\n");
+ }
+
+ let t1 = with_bar!(self.bar, |b| {
+ pr_info!("timestamp after immediate exit: {}\n", self.timer.read(b));
+ self.timer.read(b)
+ })?;
+
+ if self
+ .timer
+ .wait_on(&self.bar, Duration::from_millis(10), || Option::<()>::None)
+ != Err(ETIMEDOUT)
+ {
+ pr_crit!("timer test 2 failure\n");
+ }
+
+ let t2 = with_bar!(self.bar, |b| self.timer.read(b))?;
+ if t2 - t1 < Duration::from_millis(10) {
+ pr_crit!("timer test 3 failure\n");
+ }
+
+ with_bar!(self.bar, |b| {
+ pr_info!(
+ "timestamp after timeout: {} ({:?})\n",
+ self.timer.read(b),
+ t2 - t1
+ );
+ })?;
+
+ Ok(())
}
}
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index 94f4778c16f6a4d046c2f799129ed0cc68df6fd4..f54dcfc66490cb6b10090ef944ac14feca9f6972 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -18,6 +18,7 @@ macro_rules! with_bar {
mod firmware;
mod gpu;
mod regs;
+mod timer;
mod util;
kernel::module_pci_driver! {
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 7bfd2b575fe2184565d495012e55cd0829b0b1ad..0d06e09b1ba62d55688c633500f37d3fe1aeb30e 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -11,3 +11,14 @@
7:4 major_rev => as u8, "major revision of the chip";
28:20 chipset => try_into Chipset, "chipset model"
);
+
+/* PTIMER */
+
+register!(PtimerTime0@0x00009400;
+ 31:0 lo => as u32, "low 32-bits of the timer"
+);
+
+register!(PtimerTime1@0x00009410;
+ 31:0 hi => as u32, "high 32 bits of the timer"
+);
+
diff --git a/drivers/gpu/nova-core/timer.rs b/drivers/gpu/nova-core/timer.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1361e4ad10d923ce114972889cdfcfa6e58a691b
--- /dev/null
+++ b/drivers/gpu/nova-core/timer.rs
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Nova Core Timer subdevice
+
+use core::fmt::Display;
+use core::ops::{Add, Sub};
+use core::time::Duration;
+
+use kernel::devres::Devres;
+use kernel::num::U64Ext;
+use kernel::prelude::*;
+
+use crate::driver::Bar0;
+use crate::regs;
+
+/// A timestamp with nanosecond granularity obtained from the GPU timer.
+///
+/// A timestamp can also be substracted to another in order to obtain a [`Duration`].
+///
+/// TODO: add Kunit tests!
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub(crate) struct Timestamp(u64);
+
+impl Display for Timestamp {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ write!(f, "{}", self.0)
+ }
+}
+
+impl Add<Duration> for Timestamp {
+ type Output = Self;
+
+ fn add(mut self, rhs: Duration) -> Self::Output {
+ let mut nanos = rhs.as_nanos();
+ while nanos > u64::MAX as u128 {
+ self.0 = self.0.wrapping_add(nanos as u64);
+ nanos -= u64::MAX as u128;
+ }
+
+ Timestamp(self.0.wrapping_add(nanos as u64))
+ }
+}
+
+impl Sub for Timestamp {
+ type Output = Duration;
+
+ fn sub(self, rhs: Self) -> Self::Output {
+ Duration::from_nanos(self.0.wrapping_sub(rhs.0))
+ }
+}
+
+pub(crate) struct Timer {}
+
+impl Timer {
+ pub(crate) fn new() -> Self {
+ Self {}
+ }
+
+ /// Read the current timer timestamp.
+ pub(crate) fn read(&self, bar: &Bar0) -> Timestamp {
+ loop {
+ let hi = regs::PtimerTime1::read(bar);
+ let lo = regs::PtimerTime0::read(bar);
+
+ if hi.hi() == regs::PtimerTime1::read(bar).hi() {
+ return Timestamp(u64::from_u32s(hi.hi(), lo.lo()));
+ }
+ }
+ }
+
+ #[allow(dead_code)]
+ pub(crate) fn time(bar: &Bar0, time: u64) {
+ regs::PtimerTime1::default()
+ .set_hi(time.upper_32_bits())
+ .write(bar);
+ regs::PtimerTime0::default()
+ .set_lo(time.lower_32_bits())
+ .write(bar);
+ }
+
+ /// Wait until `cond` is true or `timeout` elapsed, based on GPU time.
+ ///
+ /// When `cond` evaluates to `Some`, its return value is returned.
+ ///
+ /// `Err(ETIMEDOUT)` is returned if `timeout` has been reached without `cond` evaluating to
+ /// `Some`, or if the timer device is stuck for some reason.
+ pub(crate) fn wait_on<R, F: Fn() -> Option<R>>(
+ &self,
+ bar: &Devres<Bar0>,
+ timeout: Duration,
+ cond: F,
+ ) -> Result<R> {
+ // Number of consecutive time reads after which we consider the timer frozen if it hasn't
+ // moved forward.
+ const MAX_STALLED_READS: usize = 16;
+
+ let (mut cur_time, mut prev_time, deadline) = {
+ let cur_time = with_bar!(bar, |b| self.read(b))?;
+ let deadline = cur_time + timeout;
+
+ (cur_time, cur_time, deadline)
+ };
+ let mut num_reads = 0;
+
+ loop {
+ if let Some(ret) = cond() {
+ return Ok(ret);
+ }
+
+ (|| {
+ cur_time = with_bar!(bar, |b| self.read(b))?;
+
+ /* Check if the timer is frozen for some reason. */
+ if cur_time == prev_time {
+ if num_reads >= MAX_STALLED_READS {
+ return Err(ETIMEDOUT);
+ }
+ num_reads += 1;
+ } else {
+ if cur_time >= deadline {
+ return Err(ETIMEDOUT);
+ }
+
+ num_reads = 0;
+ prev_time = cur_time;
+ }
+
+ Ok(())
+ })()?;
+ }
+ }
+}
--
2.48.1
next prev parent reply other threads:[~2025-03-20 13:40 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-20 13:39 [RFC PATCH v3 0/7] gpu: nova-core: register definitions and basic timer and falcon devices Alexandre Courbot
2025-03-20 13:39 ` [PATCH RFC v3 1/7] rust: add useful ops for u64 Alexandre Courbot
2025-03-20 13:39 ` [PATCH RFC v3 2/7] rust: make ETIMEDOUT error available Alexandre Courbot
2025-03-20 13:39 ` [PATCH RFC v3 3/7] gpu: nova-core: derive useful traits for Chipset Alexandre Courbot
2025-03-20 13:39 ` [PATCH RFC v3 4/7] gpu: nova-core: add missing GA100 definition Alexandre Courbot
2025-03-20 13:39 ` [PATCH RFC v3 5/7] gpu: nova-core: use register!() to define register layout Alexandre Courbot
2025-03-20 13:39 ` Alexandre Courbot [this message]
2025-03-20 15:54 ` [PATCH RFC v3 6/7] gpu: nova-core: add basic timer device Daniel Brooks
2025-03-21 3:09 ` Alexandre Courbot
2025-03-20 18:17 ` Boqun Feng
2025-03-21 5:41 ` Alexandre Courbot
2025-03-21 16:20 ` Daniel Brooks
2025-03-24 1:03 ` Alexandre Courbot
2025-03-20 13:39 ` [PATCH RFC v3 7/7] gpu: nova-core: add falcon register definitions and probe code Alexandre Courbot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250320-nova_timer-v3-6-79aa2ad25a79@nvidia.com \
--to=acourbot@nvidia.com \
--cc=a.hindborg@kernel.org \
--cc=airlied@gmail.com \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=benno.lossin@proton.me \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=bskeggs@nvidia.com \
--cc=dakr@kernel.org \
--cc=dri-devel@lists.freedesktop.org \
--cc=gary@garyguo.net \
--cc=jhubbard@nvidia.com \
--cc=linux-kernel@vger.kernel.org \
--cc=nouveau@lists.freedesktop.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=simona@ffwll.ch \
--cc=tmgross@umich.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.