NVIDIA GPU driver infrastructure
 help / color / mirror / Atom feed
From: "Danilo Krummrich" <dakr@kernel.org>
To: "Dave Airlie via B4 Relay" <devnull+airlied.redhat.com@kernel.org>
Cc: <airlied@redhat.com>, "Andreas Hindborg" <a.hindborg@kernel.org>,
	"FUJITA Tomonori" <fujita.tomonori@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Daniel Almeida" <daniel.almeida@collabora.com>,
	"Alexandre Courbot" <acourbot@nvidia.com>,
	<nova-gpu@lists.linux.dev>, <dri-devel@lists.freedesktop.org>
Subject: Re: [PATCH v3 2/2] gpu: nova-core: add support for GPU timer block. (v3)
Date: Wed, 04 Nov 2026 00:52:22 +0100	[thread overview]
Message-ID: <DMLZABRA2JIK.1T11DDWNFKGJM@kernel.org> (raw)
In-Reply-To: <20261103-nova-timer-v3-2-0cf9bbae8fd9@redhat.com>

On Tue Nov 3, 2026 at 1:33 AM CET, Dave Airlie via B4 Relay wrote:
> +// struct that just holds the sys timer offset
> +// after initialising the GPU timer block.
> +pub(crate) struct Timer {
> +    sys_timer_offset_ns: i64,
> +}

I feel like this should actually be TimerState, as it is not really a timer
(instance) representation in the sense of carrying the HAL and providing methods
to interact with the hardware (which we do not intend).

I'd suggest making Timer a unit struct and giving it an init() function, i.e.
something like this:

	struct Timer;

	struct TimerState {
	    sys_timer_offset_ns: i64,
	}

	impl Timer {
	    /// Initialize the GPU's system timer.
	    pub(crate) fn init(bar: Bar0<'_>, chipset: Chipset) -> Result<TimerState>;
	}

> +
> +impl Timer {
> +    pub(crate) fn new(bar: Bar0<'_>, chipset: Chipset) -> Result<Self> {
> +        Ok(Self {
> +            sys_timer_offset_ns: hal::timer_hal(chipset).init(bar)?,
> +        })
> +    }
> +
> +    // this value will be passed to GspInitRequest SYS_TIMER_OFFSET_NS

NIT: This should describe what the returned value represents, although in this
specific case it may also still make sense to give a hint how it is used.

Please use intra-doc links and markdown where it makes sense.

> +    #[expect(dead_code)]
> +    pub(crate) fn sys_timer_offset_ns(&self) -> i64 {
> +        self.sys_timer_offset_ns
> +    }
> +}
> diff --git a/drivers/gpu/nova-core/timer/hal.rs b/drivers/gpu/nova-core/timer/hal.rs
> new file mode 100644
> index 000000000000..fd702867b470
> --- /dev/null
> +++ b/drivers/gpu/nova-core/timer/hal.rs
> @@ -0,0 +1,27 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +use kernel::prelude::*;
> +
> +use crate::{
> +    driver::Bar0,
> +    gpu::{
> +        Architecture,
> +        Chipset, //
> +    },
> +};
> +
> +mod gh100;
> +mod tu102;
> +
> +pub(super) trait TimerHal {
> +    /// return system timer offset ns

NIT:

	/// Initialize the GPU's system timer.
	///
	/// Returns the system timer offset in nanoseconds.

> +    fn init(&self, bar: Bar0<'_>) -> Result<i64>;
> +}
> +
> +pub(super) fn timer_hal(chipset: Chipset) -> &'static dyn TimerHal {
> +    match chipset.arch() {
> +        Architecture::Turing | Architecture::Ampere | Architecture::Ada => tu102::TU102_HAL,
> +        Architecture::Hopper => gh100::GH100_HAL,
> +        Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => gh100::GB100_HAL,
> +    }
> +}
> diff --git a/drivers/gpu/nova-core/timer/hal/gh100.rs b/drivers/gpu/nova-core/timer/hal/gh100.rs
> new file mode 100644
> index 000000000000..a9d46bdd2527
> --- /dev/null
> +++ b/drivers/gpu/nova-core/timer/hal/gh100.rs
> @@ -0,0 +1,94 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +use super::TimerHal;
> +
> +use kernel::{
> +    io::Io,
> +    prelude::*,
> +    time::*, //
> +};
> +
> +use crate::{
> +    driver::Bar0,
> +    timer::regs, //
> +};
> +
> +/// Timer HAL for Hopper and Blackwell.
> +struct Gh100 {
> +    /// `true` on Hopper, the only architecture where the FSP ucode can predate the fix for
> +    /// bug 3957833 and hence the sys timer offset must not be programmed. Blackwell FSP
> +    /// always includes the fix.
> +    fsp_bug3957833_war: bool,
> +}
> +
> +/// FSP ucode version from which on the fix for bug 3957833 is included.
> +const FSP_BUG_3957833_FIX_VERSION: u32 = 0x44c;
> +
> +impl Gh100 {
> +    /// kfspRequiresBug3957833WAR_HAL: returns `true` if the FSP ucode predates the fix for
> +    /// bug 3957833 and hence the sys timer offset must not be programmed. The RM implements
> +    /// the version check for GH100 only; for all later chips it defaults to `false`.
> +    fn requires_bug3957833_war(&self, bar: Bar0<'_>) -> bool {
> +        self.fsp_bug3957833_war
> +            && bar.read(regs::gh100::NV_GFW_FSP_UCODE_VERSION).full().get()
> +                < FSP_BUG_3957833_FIX_VERSION

I'd move this into regs.rs and make it a method of NV_GFW_FSP_UCODE_VERSION,
e.g.

	impl NV_GFW_FSP_UCODE_VERSION {
	    pub(crate) fn is_bug_3957833_affected(self) -> bool {
	        const FSP_BUG_3957833_FIX_VERSION: u32 = 0x44c;
	
	        self.full().get() < FSP_BUG_3957833_FIX_VERSION
	    }
	}

so you can just call

	bar.read(regs::gh100::NV_GFW_FSP_UCODE_VERSION).is_bug_3957833_affected()
	
This also keeps FSP_BUG_3957833_FIX_VERSION local to regs.rs and gets us rid of
this HAL function.

> +    }
> +
> +    fn timer_time(&self, bar: Bar0<'_>, time_ns: i64) -> Result<i64> {

Can't we just use unsigned for the argument and return type? None of those are
ever expected to be negative, no?

The invariant for the argument to be non-negative comes from Instant, so I'd convert
before calling into timer_time(). This should avoid the corresponding as casts below.

> +        let mut sec_hi2 = u32::from(bar.read(regs::gh100::NV_PGC6_SCI_SEC_TIMER_TIME_1));
> +        let sec_timer_ns: u64;
> +        loop {
> +            let sec_lo = u32::from(bar.read(regs::gh100::NV_PGC6_SCI_SEC_TIMER_TIME_0));
> +            let sec_hi = u32::from(bar.read(regs::gh100::NV_PGC6_SCI_SEC_TIMER_TIME_1));
> +            if sec_hi == sec_hi2 {
> +                sec_timer_ns = u64::from(sec_hi) << 32 | u64::from(sec_lo);
> +                break;
> +            }
> +            sec_hi2 = u32::from(bar.read(regs::gh100::NV_PGC6_SCI_SEC_TIMER_TIME_1));
> +        }
> +
> +        // The system and the GPU timer are both free-running with unrelated epochs, so the
> +        // difference can be negative. Both operands are < 2^62, hence this cannot overflow.

I think we accumulated a bunch of those comments in nova-core, I think it would
be nice to consider whether we want a prefix for this, similar to TIMEOUT or
CALC. Maybe OVERFLOW? But this can be a follow-up of course.

On the other hand, this is a fallible method already, so I'd just go with
checked_sub().

> +        let sys_timer_offset_ns = time_ns - sec_timer_ns as i64;
> +
> +        // The offset is passed to the GSP as an unsigned value and, like in the RM's
> +        // tmrSetCurrentTime_GH100, is required to be non-negative. With wall clock time this
> +        // can only fail if the system clock is set behind the GPU power-on time.
> +        if sys_timer_offset_ns < 0 {
> +            return Err(EINVAL);
> +        }
> +
> +        if !self.requires_bug3957833_war(bar) {
> +            let offset_ns = sys_timer_offset_ns as u64;
> +            bar.write_reg(
> +                regs::gh100::NV_PGC6_SCI_SYS_TIMER_OFFSET_1::zeroed()
> +                    .try_with_nsec(((offset_ns >> 32) & 0x1fff_ffff) as u32)?,
> +            );
> +            bar.write_reg(
> +                regs::gh100::NV_PGC6_SCI_SYS_TIMER_OFFSET_0::zeroed()
> +                    .try_with_nsec(((offset_ns >> 5) & 0x7ff_ffff) as u32)?
> +                    .with_update(true),
> +            );

This looks similar to NV_TLB_FLUSH_PDB_LO::from_pdb_addr(). I think we should do
this in the register impl:

	impl NV_PGC6_SCI_SYS_TIMER_OFFSET_1 {
	    /// Creates a register value from bits 60:32 of the offset.
	    pub(crate) fn from_offset_ns(offset_ns: u64) -> Self {
	        let nsec = Bounded::<u32, 29>::from_expr(
	            ((offset_ns >> 32) & 0x1fff_ffff) as u32,
	        );
	
	        Self::zeroed().with_nsec(nsec)
	    }
	}
	
> +    // FSP PRI space; the timer is its only consumer so far.
> +    register! {
> +        base: NovaRegisters;
> +
> +        /// FSP ucode version, encoded as `major.minor` in bits 11:8 and 7:0, written by the FSP
> +        /// during boot. (`NV_PFSP_FALCON_COMMON_SCRATCH_GROUP_3(1)` in the hardware headers.)
> +        pub(crate) NV_GFW_FSP_UCODE_VERSION(u32) @ 0x008f0334 {
> +            11:0 full;
> +        }
> +    }
> +}

Hm...I feel like it should go into fsp/regs.rs regardless.

      reply	other threads:[~2026-11-03 23:52 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03  0:33 [PATCH v3 0/2] nova-core: add initial support for gpu timer (v3) Dave Airlie via B4 Relay
2026-11-03  0:33 ` [PATCH v3 1/2] rust: time: expose as_nanos for clock sources Dave Airlie via B4 Relay
2026-11-03  0:33 ` [PATCH v3 2/2] gpu: nova-core: add support for GPU timer block. (v3) Dave Airlie via B4 Relay
2026-11-03 23:52   ` Danilo Krummrich [this message]

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=DMLZABRA2JIK.1T11DDWNFKGJM@kernel.org \
    --to=dakr@kernel.org \
    --cc=a.hindborg@kernel.org \
    --cc=acourbot@nvidia.com \
    --cc=airlied@redhat.com \
    --cc=aliceryhl@google.com \
    --cc=daniel.almeida@collabora.com \
    --cc=devnull+airlied.redhat.com@kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=fujita.tomonori@gmail.com \
    --cc=gary@garyguo.net \
    --cc=nova-gpu@lists.linux.dev \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox