[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250320-nova_timer-v3-7-79aa2ad25a79@nvidia.com>
Date: Thu, 20 Mar 2025 22:39:15 +0900
From: Alexandre Courbot <acourbot@...dia.com>
To: Danilo Krummrich <dakr@...nel.org>, David Airlie <airlied@...il.com>,
John Hubbard <jhubbard@...dia.com>, Ben Skeggs <bskeggs@...dia.com>,
Miguel Ojeda <ojeda@...nel.org>, Alex Gaynor <alex.gaynor@...il.com>,
Boqun Feng <boqun.feng@...il.com>, Gary Guo <gary@...yguo.net>,
Björn Roy Baron <bjorn3_gh@...tonmail.com>,
Benno Lossin <benno.lossin@...ton.me>,
Andreas Hindborg <a.hindborg@...nel.org>, Alice Ryhl <aliceryhl@...gle.com>,
Trevor Gross <tmgross@...ch.edu>, Simona Vetter <simona@...ll.ch>
Cc: linux-kernel@...r.kernel.org, rust-for-linux@...r.kernel.org,
nouveau@...ts.freedesktop.org, dri-devel@...ts.freedesktop.org,
Alexandre Courbot <acourbot@...dia.com>
Subject: [PATCH RFC v3 7/7] gpu: nova-core: add falcon register definitions
and probe code
This is still very preliminary work, and is mostly designed to show how
register fields can be turned into safe types that force us to handle
invalid values.
Signed-off-by: Alexandre Courbot <acourbot@...dia.com>
---
drivers/gpu/nova-core/driver.rs | 2 +-
drivers/gpu/nova-core/falcon.rs | 618 +++++++++++++++++++++++++++++++++++++
drivers/gpu/nova-core/gpu.rs | 13 +
drivers/gpu/nova-core/nova_core.rs | 1 +
drivers/gpu/nova-core/regs.rs | 188 ++++++++++-
5 files changed, 820 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 0cd23aa306e4082405f480afc0530a41131485e7..dee5fd22eecb2ce1f4ea765338b0c1b68853b2d3 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 = 0x9500;
+const BAR0_SIZE: usize = 0x1000000;
pub(crate) type Bar0 = pci::Bar<BAR0_SIZE>;
kernel::pci_device_table!(
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0dd4b45abbe0a62238efe24d899c55d5db348586
--- /dev/null
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -0,0 +1,618 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Falcon microprocessor base support
+
+// TODO: remove this once this module is actively used.
+#![allow(dead_code)]
+
+use core::hint::unreachable_unchecked;
+use core::marker::PhantomData;
+use core::time::Duration;
+use kernel::bindings;
+use kernel::devres::Devres;
+use kernel::{pci, prelude::*};
+
+use crate::driver::Bar0;
+use crate::gpu::Chipset;
+use crate::regs;
+use crate::timer::Timer;
+
+#[repr(u8)]
+#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub(crate) enum FalconCoreRev {
+ #[default]
+ Rev1 = 1,
+ Rev2 = 2,
+ Rev3 = 3,
+ Rev4 = 4,
+ Rev5 = 5,
+ Rev6 = 6,
+ Rev7 = 7,
+}
+
+impl TryFrom<u32> for FalconCoreRev {
+ type Error = Error;
+
+ fn try_from(value: u32) -> core::result::Result<Self, Self::Error> {
+ use FalconCoreRev::*;
+
+ let rev = match value {
+ 1 => Rev1,
+ 2 => Rev2,
+ 3 => Rev3,
+ 4 => Rev4,
+ 5 => Rev5,
+ 6 => Rev6,
+ 7 => Rev7,
+ _ => return Err(EINVAL),
+ };
+
+ Ok(rev)
+ }
+}
+
+#[repr(u8)]
+#[derive(Debug, Default, Copy, Clone)]
+pub(crate) enum FalconSecurityModel {
+ #[default]
+ None = 0,
+ Light = 2,
+ Heavy = 3,
+}
+
+impl TryFrom<u32> for FalconSecurityModel {
+ type Error = Error;
+
+ fn try_from(value: u32) -> core::result::Result<Self, Self::Error> {
+ use FalconSecurityModel::*;
+
+ let sec_model = match value {
+ 0 => None,
+ 2 => Light,
+ 3 => Heavy,
+ _ => return Err(EINVAL),
+ };
+
+ Ok(sec_model)
+ }
+}
+
+#[repr(u8)]
+#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub(crate) enum FalconCoreRevSubversion {
+ #[default]
+ Subversion0 = 0,
+ Subversion1 = 1,
+ Subversion2 = 2,
+ Subversion3 = 3,
+}
+
+impl From<u32> for FalconCoreRevSubversion {
+ fn from(value: u32) -> Self {
+ use FalconCoreRevSubversion::*;
+
+ match value & 0b11 {
+ 0 => Subversion0,
+ 1 => Subversion1,
+ 2 => Subversion2,
+ 3 => Subversion3,
+ // SAFETY: the `0b11` mask limits the possible values to `0..=3`.
+ 4..=u32::MAX => unsafe { unreachable_unchecked() },
+ }
+ }
+}
+
+#[repr(u8)]
+#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
+pub(crate) enum FalconModSelAlgo {
+ #[default]
+ Rsa3k = 1,
+}
+
+impl TryFrom<u32> for FalconModSelAlgo {
+ type Error = Error;
+
+ fn try_from(value: u32) -> core::result::Result<Self, Self::Error> {
+ match value {
+ 1 => Ok(FalconModSelAlgo::Rsa3k),
+ _ => Err(EINVAL),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) enum RiscvCoreSelect {
+ Falcon = 0,
+ Riscv = 1,
+}
+
+impl From<bool> for RiscvCoreSelect {
+ fn from(value: bool) -> Self {
+ match value {
+ false => RiscvCoreSelect::Falcon,
+ true => RiscvCoreSelect::Riscv,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) enum FalconMem {
+ Imem,
+ Dmem,
+}
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub(crate) struct FalconUCodeDescV3 {
+ pub(crate) hdr: u32,
+ pub(crate) stored_size: u32,
+ pub(crate) pkc_data_offset: u32,
+ pub(crate) interface_offset: u32,
+ pub(crate) imem_phys_base: u32,
+ pub(crate) imem_load_size: u32,
+ pub(crate) imem_virt_base: u32,
+ pub(crate) dmem_phys_base: u32,
+ pub(crate) dmem_load_size: u32,
+ pub(crate) engine_id_mask: u16,
+ pub(crate) ucode_id: u8,
+ pub(crate) signature_count: u8,
+ pub(crate) signature_versions: u16,
+ _reserved: u16,
+}
+
+impl FalconUCodeDescV3 {
+ pub(crate) fn ver(&self) -> u8 {
+ ((self.hdr & 0xff00) >> 8) as u8
+ }
+
+ pub(crate) fn size(&self) -> usize {
+ ((self.hdr & 0xffff0000) >> 16) as usize
+ }
+}
+
+/// Trait defining the parameters of a given Falcon instance.
+pub(crate) trait FalconInstance {
+ /// Base I/O address for the falcon, relative from which its registers are accessed.
+ const BASE: usize;
+}
+
+pub(crate) struct Gsp;
+impl FalconInstance for Gsp {
+ const BASE: usize = 0x00110000;
+}
+pub(crate) type GspFalcon = Falcon<Gsp>;
+
+pub(crate) struct Sec2;
+impl FalconInstance for Sec2 {
+ const BASE: usize = 0x00840000;
+}
+pub(crate) type Sec2Falcon = Falcon<Sec2>;
+
+/// Contains the base parameters common to all Falcon instances.
+#[derive(Debug)]
+pub(crate) struct Falcon<I: FalconInstance> {
+ /// Chipset this falcon belongs to.
+ chipset: Chipset,
+ /// Whether this falcon is part of a dual falcon/riscv engine.
+ has_riscv: bool,
+ _instance: PhantomData<I>,
+}
+
+impl<I: FalconInstance> Falcon<I> {
+ pub(crate) fn new(
+ pdev: &pci::Device,
+ chipset: Chipset,
+ bar: &Devres<Bar0>,
+ need_riscv: bool,
+ ) -> Result<Self> {
+ let hwcfg1 = with_bar!(bar, |b| regs::FalconHwcfg1::read(b, I::BASE))?;
+ let rev = hwcfg1.core_rev()?;
+ let subver = hwcfg1.core_rev_subversion();
+ let sec_model = hwcfg1.security_model()?;
+
+ if need_riscv {
+ let hwcfg2 = with_bar!(bar, |b| regs::FalconHwcfg2::read(b, I::BASE))?;
+ if !hwcfg2.riscv() {
+ dev_err!(
+ pdev.as_ref(),
+ "riscv support requested on falcon that does not support it\n"
+ );
+ return Err(EINVAL);
+ }
+ }
+
+ dev_info!(
+ pdev.as_ref(),
+ "new falcon: {:?} {:?} {:?}",
+ rev,
+ subver,
+ sec_model
+ );
+
+ Ok(Self {
+ chipset,
+ has_riscv: need_riscv,
+ _instance: PhantomData,
+ })
+ }
+
+ fn select_falcon_core(&self, bar: &Devres<Bar0>, timer: &Timer) -> Result<()> {
+ if self.has_riscv {
+ let bcr_ctrl = with_bar!(bar, |b| regs::RiscvBcrCtrl::read(b, I::BASE))?;
+ if bcr_ctrl.core_select() != RiscvCoreSelect::Falcon {
+ with_bar!(bar, |b| regs::RiscvBcrCtrl::default()
+ .set_core_select(RiscvCoreSelect::Falcon)
+ .write(b, I::BASE))?;
+
+ timer.wait_on(bar, Duration::from_millis(10), || {
+ bar.try_access_with(|b| regs::RiscvBcrCtrl::read(b, I::BASE))
+ .and_then(|v| if v.valid() { Some(()) } else { None })
+ })?;
+ }
+ }
+
+ Ok(())
+ }
+
+ fn reset_wait_mem_scrubbing(&self, bar: &Devres<Bar0>, timer: &Timer) -> Result<()> {
+ /* TODO: is this needed? */
+ with_bar!(bar, |b| regs::FalconMailbox0::alter(b, I::BASE, |v| v))?;
+
+ timer.wait_on(bar, Duration::from_millis(20), || {
+ bar.try_access_with(|b| regs::FalconHwcfg2::read(b, I::BASE))
+ .and_then(|r| if r.mem_scrubbing() { Some(()) } else { None })
+ })
+ }
+
+ fn reset_prep(&self, bar: &Devres<Bar0>, timer: &Timer) -> Result<()> {
+ let _ = with_bar!(bar, |b| regs::FalconHwcfg2::read(b, I::BASE))?;
+
+ // Expected to timeout apparently?
+ // TODO: check why with OpenRM.
+ let _ = timer.wait_on(bar, Duration::from_micros(150), || {
+ bar.try_access_with(|b| regs::FalconHwcfg2::read(b, I::BASE))
+ .and_then(|r| if r.unk_31() { Some(()) } else { None })
+ });
+
+ Ok(())
+ }
+
+ fn reset_eng(&self, bar: &Devres<Bar0>, timer: &Timer) -> Result<()> {
+ self.reset_prep(bar, timer)?;
+
+ with_bar!(bar, |b| regs::RiscvUnk3c0::alter(b, I::BASE, |v| v
+ .set_unk0(true)))?;
+
+ let _: Result<()> = timer.wait_on(bar, Duration::from_micros(10), || None);
+
+ with_bar!(bar, |b| regs::RiscvUnk3c0::alter(b, I::BASE, |v| v
+ .set_unk0(false)))?;
+
+ self.reset_wait_mem_scrubbing(bar, timer)?;
+
+ Ok(())
+ }
+
+ fn disable(&self, bar: &Devres<Bar0>, timer: &Timer) -> Result<()> {
+ self.select_falcon_core(bar, timer)?;
+
+ with_bar!(bar, |b| {
+ regs::FalconUnk0048::alter(b, I::BASE, |r| r.set_val0(0));
+
+ regs::FalconIrqmclr::default()
+ .set_val(u32::MAX)
+ .write(b, I::BASE)
+ })?;
+
+ self.reset_eng(bar, timer)
+ }
+
+ fn enable(&self, bar: &Devres<Bar0>, timer: &Timer) -> Result<()> {
+ self.reset_eng(bar, timer)?;
+ self.select_falcon_core(bar, timer)?;
+ self.reset_wait_mem_scrubbing(bar, timer)?;
+
+ with_bar!(bar, |b| {
+ // We write Boot0 into FalconRm, for some reason...
+ regs::FalconRm::default()
+ .set_val(regs::Boot0::read(b).into())
+ .write(b, I::BASE)
+ })
+ }
+
+ pub(crate) fn reset(&self, bar: &Devres<Bar0>, timer: &Timer) -> Result<()> {
+ self.disable(bar, timer)?;
+ self.enable(bar, timer)
+ }
+
+ fn dma_init(
+ &self,
+ bar: &Devres<Bar0>,
+ dma_handle: bindings::dma_addr_t,
+ mem: FalconMem,
+ xfer_len: u32,
+ sec: bool,
+ ) -> Result<regs::FalconDmaTrfCmd> {
+ with_bar!(bar, |b| {
+ regs::FalconDmaTrfBase::default()
+ .set_base((dma_handle >> 8) as u32)
+ .write(b, I::BASE);
+ regs::FalconDmaTrfBase1::default()
+ .set_base((dma_handle >> 40) as u16)
+ .write(b, I::BASE)
+ })?;
+
+ Ok(regs::FalconDmaTrfCmd::default()
+ .set_size((xfer_len.ilog2() - 2) as u8)
+ .set_imem(mem == FalconMem::Imem)
+ .set_sec(if sec { 1 } else { 0 }))
+ }
+
+ fn dma_xfer(
+ &self,
+ bar: &Devres<Bar0>,
+ mem_base: u32,
+ dma_base: u32,
+ cmd: regs::FalconDmaTrfCmd,
+ ) -> Result<()> {
+ with_bar!(bar, |b| {
+ regs::FalconDmaTrfMOffs::default()
+ .set_offs(mem_base)
+ .write(b, I::BASE);
+ regs::FalconDmaTrfBOffs::default()
+ .set_offs(dma_base)
+ .write(b, I::BASE);
+
+ cmd.write(b, I::BASE)
+ })
+ }
+
+ fn dma_done(&self, bar: &Devres<Bar0>, timer: &Timer) -> Result<()> {
+ timer.wait_on(bar, Duration::from_millis(2000), || {
+ bar.try_access_with(|b| regs::FalconDmaTrfCmd::read(b, I::BASE))
+ .and_then(|v| if v.idle() { Some(()) } else { None })
+ })
+ }
+
+ fn dma_wr(
+ &self,
+ bar: &Devres<Bar0>,
+ timer: &Timer,
+ dma_handle: bindings::dma_addr_t,
+ dma_base: u32,
+ mem: FalconMem,
+ mem_base: u32,
+ len: u32,
+ sec: bool,
+ ) -> Result<()> {
+ const DMA_LEN: u32 = 256;
+
+ let (dma_start, dma_addr) = match mem {
+ FalconMem::Imem => (0, dma_handle),
+ FalconMem::Dmem => (dma_base, dma_handle + dma_base as bindings::dma_addr_t),
+ };
+
+ pr_info!(
+ "dma write {:?}: dma_handle {:x} dma_start {:x} dma_addr {:x} len {:x}\n",
+ mem,
+ dma_handle,
+ dma_start,
+ dma_addr,
+ len
+ );
+
+ let cmd = self.dma_init(bar, dma_addr, mem, DMA_LEN, sec)?;
+
+ let mut dst = mem_base;
+ let mut src = dma_base;
+ let mut remain = len;
+
+ while remain >= DMA_LEN {
+ self.dma_xfer(bar, dst, src - dma_start, cmd)?;
+ self.dma_done(bar, timer)?;
+
+ src += DMA_LEN;
+ dst += DMA_LEN;
+ remain -= DMA_LEN;
+ }
+
+ pr_info!("dma write remain: {} bytes\n", remain);
+
+ Ok(())
+ }
+
+ pub(crate) fn dma_load(
+ &self,
+ bar: &Devres<Bar0>,
+ timer: &Timer,
+ dma_handle: bindings::dma_addr_t,
+ imem_params: (u32, u32, u32),
+ dmem_params: (u32, u32, u32),
+ ) -> Result<()> {
+ pr_info!("dma_load: {:?} {:?}\n", imem_params, dmem_params);
+
+ with_bar!(bar, |b| {
+ regs::FalconUnk624::alter(b, I::BASE, |v| v.set_unk7(true));
+ regs::FalconDmaCtl::default().write(b, I::BASE);
+ regs::FalconUnk600::alter(b, I::BASE, |v| v.set_unk16(false).set_unk2((1 << 2) | 1));
+ })?;
+
+ self.dma_wr(
+ bar,
+ timer,
+ dma_handle,
+ imem_params.0,
+ FalconMem::Imem,
+ imem_params.1,
+ imem_params.2,
+ true,
+ )?;
+ self.dma_wr(
+ bar,
+ timer,
+ dma_handle,
+ dmem_params.0,
+ FalconMem::Dmem,
+ dmem_params.1,
+ dmem_params.2,
+ true,
+ )?;
+
+ Ok(())
+ }
+
+ pub(crate) fn boot(
+ &self,
+ bar: &Devres<Bar0>,
+ timer: &Timer,
+ v3_desc: &FalconUCodeDescV3,
+ mbox0: Option<u32>,
+ mbox1: Option<u32>,
+ ) -> Result<(u32, u32)> {
+ pr_info!("boot 0\n");
+
+ // Program BROM registers for PKC signature validation.
+ if self.chipset >= Chipset::GA102 {
+ let pkc_data_offset = v3_desc.pkc_data_offset;
+ let engine_id_mask = v3_desc.engine_id_mask;
+ let ucode_id = v3_desc.ucode_id;
+
+ pr_info!(
+ "dmem_sign: {:#x}, engine_id: {:#x}, ucode_id: {:#x}",
+ pkc_data_offset,
+ engine_id_mask,
+ ucode_id
+ );
+
+ with_bar!(bar, |b| {
+ regs::FalconBromParaaddr0::default()
+ .set_addr(pkc_data_offset)
+ .write(b, I::BASE);
+ regs::FalconBromEngidmask::default()
+ .set_mask(engine_id_mask as u32)
+ .write(b, I::BASE);
+ regs::FalconBromCurrUcodeId::default()
+ .set_ucode_id(ucode_id as u32)
+ .write(b, I::BASE);
+ regs::FalconModSel::default()
+ .set_algo(FalconModSelAlgo::Rsa3k)
+ .write(b, I::BASE);
+ })?;
+ }
+
+ pr_info!("boot 1\n");
+
+ with_bar!(bar, |b| {
+ if let Some(mbox0) = mbox0 {
+ regs::FalconMailbox0::default()
+ .set_mailbox0(mbox0)
+ .write(b, I::BASE);
+ }
+
+ if let Some(mbox1) = mbox1 {
+ regs::FalconMailbox1::default()
+ .set_mailbox1(mbox1)
+ .write(b, I::BASE);
+ }
+
+ // Set `BootVec` to start of non-secure code.
+ // TODO: use boot vector variable - apparently this is 0 on v3 hdr?
+ regs::FalconBootVec::default()
+ .set_boot_vec(0)
+ .write(b, I::BASE);
+
+ regs::FalconCpuCtl::default()
+ .set_start_cpu(true)
+ .write(b, I::BASE);
+ })?;
+
+ pr_info!("booted!\n");
+ timer.wait_on(bar, Duration::from_secs(2), || {
+ bar.try_access()
+ .map(|b| regs::FalconCpuCtl::read(&*b, I::BASE))
+ .and_then(|v| if v.halted() { Some(()) } else { None })
+ })?;
+
+ let (mbox0, mbox1) = with_bar!(bar, |b| {
+ let mbox0 = regs::FalconMailbox0::read(b, I::BASE).mailbox0();
+ let mbox1 = regs::FalconMailbox1::read(b, I::BASE).mailbox1();
+
+ (mbox0, mbox1)
+ })?;
+
+ pr_info!("successfully returned {} {}\n", mbox0, mbox1);
+
+ Ok((mbox0, mbox1))
+ }
+}
+
+#[repr(C)]
+#[derive(Debug)]
+struct FalconAppifHdrV1 {
+ ver: u8,
+ hdr: u8,
+ len: u8,
+ cnt: u8,
+}
+
+#[repr(C)]
+#[derive(Debug)]
+struct FalconAppifV1 {
+ id: u32,
+ dmem_base: u32,
+}
+
+const NVFW_FALCON_APPIF_ID_DMEMMAPPER: u32 = 0x4;
+
+#[repr(C)]
+#[derive(Debug)]
+struct FalconAppifDmemmapperV3 {
+ signature: u32,
+ version: u16,
+ size: u16,
+ cmd_in_buffer_offset: u32,
+ cmd_in_buffer_size: u32,
+ cmd_out_buffer_offset: u32,
+ cmd_out_buffer_size: u32,
+ nvf_img_data_buffer_offset: u32,
+ nvf_img_data_buffer_size: u32,
+ printf_buffer_hdr: u32,
+ ucode_build_time_stamp: u32,
+ ucode_signature: u32,
+ init_cmd: u32,
+ ucode_feature: u32,
+ ucode_cmd_mask0: u32,
+ ucode_cmd_mask1: u32,
+ multi_tgt_tbl: u32,
+}
+
+pub(crate) const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS: u32 = 0x15;
+
+#[derive(Debug)]
+#[repr(C)]
+struct ReadVbios {
+ ver: u32,
+ hdr: u32,
+ addr: u64,
+ size: u32,
+ flags: u32,
+}
+
+#[derive(Debug)]
+#[repr(C)]
+struct FrtsRegion {
+ ver: u32,
+ hdr: u32,
+ addr: u32,
+ size: u32,
+ ftype: u32,
+}
+
+const NVFW_FRTS_CMD_REGION_TYPE_FB: u32 = 2;
+
+#[derive(Debug)]
+#[repr(C)]
+struct FrtsCmd {
+ read_vbios: ReadVbios,
+ frts_region: FrtsRegion,
+}
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index f010d3152530b1cec032ca620e59de51a2fc1a13..ec745dd8175bd3164ed1b865293a526b09c59ab3 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -5,6 +5,7 @@
};
use crate::driver::Bar0;
+use crate::falcon::{GspFalcon, Sec2Falcon};
use crate::regs;
use crate::timer::Timer;
use crate::util;
@@ -198,6 +199,18 @@ pub(crate) fn new(pdev: &pci::Device, bar: Devres<Bar0>) -> Result<impl PinInit<
);
let timer = Timer::new();
+ let _gsp_falcon = GspFalcon::new(
+ pdev,
+ spec.chipset,
+ &bar,
+ if spec.chipset > Chipset::GA100 {
+ true
+ } else {
+ false
+ },
+ )?;
+
+ let _sec2_falcon = Sec2Falcon::new(pdev, spec.chipset, &bar, false)?;
Ok(pin_init!(Self {
spec,
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index f54dcfc66490cb6b10090ef944ac14feca9f6972..35c030485532633a5dd59a8a4a1f6d385cb46c98 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -15,6 +15,7 @@ macro_rules! with_bar {
}
mod driver;
+mod falcon;
mod firmware;
mod gpu;
mod regs;
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 0d06e09b1ba62d55688c633500f37d3fe1aeb30e..2952fa7f84c274f122bc12e5506b0b2ac0fbb82d 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -2,8 +2,11 @@
use core::ops::Deref;
use kernel::io::Io;
-use kernel::register;
+use kernel::{register, register_rel};
+use crate::falcon::{
+ FalconCoreRev, FalconCoreRevSubversion, FalconModSelAlgo, FalconSecurityModel, RiscvCoreSelect,
+};
use crate::gpu::Chipset;
register!(Boot0@...0000000, "Basic revision information about the GPU";
@@ -22,3 +25,186 @@
31:0 hi => as u32, "high 32 bits of the timer"
);
+/* PFALCON */
+
+register_rel!(FalconIrqsclr@...0000004;
+ 4:4 halt => as_bit bool;
+ 6:6 swgen0 => as_bit bool;
+);
+
+register_rel!(FalconIrqstat@...0000008;
+ 4:4 halt => as_bit bool;
+ 6:6 swgen0 => as_bit bool;
+);
+
+register_rel!(FalconIrqmclr@...0000014;
+ 31:0 val => as u32
+);
+
+register_rel!(FalconIrqmask@...0000018;
+ 31:0 val => as u32
+);
+
+register_rel!(FalconRm@...0000084;
+ 31:0 val => as u32
+);
+
+register_rel!(FalconIrqdest@...000001c;
+ 31:0 val => as u32
+);
+
+register_rel!(FalconMailbox0@...0000040;
+ 31:0 mailbox0 => as u32
+);
+register_rel!(FalconMailbox1@...0000044;
+ 31:0 mailbox1 => as u32
+);
+
+register_rel!(FalconUnk0048@...0000048;
+ 1:0 val0 => as u32
+);
+
+register_rel!(FalconHwcfg2@...00000f4;
+ 10:10 riscv => as_bit bool;
+ 12:12 mem_scrubbing => as_bit bool;
+ 31:31 unk_31 => as_bit bool;
+);
+
+register_rel!(FalconCpuCtl@...0000100;
+ 1:1 start_cpu => as_bit bool;
+ 4:4 halted => as_bit bool;
+ 6:6 alias_en => as_bit bool;
+);
+register_rel!(FalconBootVec@...0000104;
+ 31:0 boot_vec => as u32
+);
+
+register_rel!(FalconHwCfg@...0000108;
+ 8:0 imem_size => as u32;
+ 17:9 dmem_size => as u32;
+);
+
+register_rel!(FalconDmaCtl@...000010c;
+ 0:0 require_ctx => as_bit bool;
+ 1:1 dmem_scrubbing => as_bit bool;
+ 2:2 imem_scrubbing => as_bit bool;
+ 6:3 dmaq_num => as_bit u8;
+ 7:7 secure_stat => as_bit bool;
+);
+
+register_rel!(FalconDmaTrfBase@...0000110;
+ 31:0 base => as u32;
+);
+
+register_rel!(FalconDmaTrfMOffs@...0000114;
+ 23:0 offs => as u32;
+);
+
+register_rel!(FalconDmaTrfCmd@...0000118;
+ 0:0 full => as_bit bool;
+ 1:1 idle => as_bit bool;
+ 3:2 sec => as_bit u8;
+ 4:4 imem => as_bit bool;
+ 5:5 is_write => as_bit bool;
+ 10:8 size => as u8;
+ 14:12 ctxdma => as u8;
+ 16:16 set_dmtag => as u8;
+);
+
+register_rel!(FalconDmaTrfBOffs@...000011c;
+ 31:0 offs => as u32;
+);
+
+register_rel!(FalconDmaTrfBase1@...0000128;
+ 8:0 base => as u16;
+);
+
+register_rel!(FalconHwcfg1@...000012c;
+ 3:0 core_rev => try_into FalconCoreRev, "core revision of the falcon";
+ 5:4 security_model => try_into FalconSecurityModel, "security model of the falcon";
+ 7:6 core_rev_subversion => into FalconCoreRevSubversion;
+ 11:8 imem_ports => as u8;
+ 15:12 dmem_ports => as u8;
+);
+
+// TODO: This should be able to take an index, like +0x180[16; 8]? Then the constructor or read
+// method take the port we want to address as argument.
+register_rel!(FalconImemC@...0000180;
+ 7:2 offs => as u8;
+ 23:8 blk => as u8;
+ 24:24 aincw => as_bit bool;
+ 25:25 aincr => as_bit bool;
+ 28:28 secure => as_bit bool;
+ 29:29 sec_atomic => as_bit bool;
+);
+
+register_rel!(FalconImemD@...0000184;
+ 31:0 data => as u32;
+);
+
+register_rel!(FalconImemT@...0000188;
+ 15:0 data => as u16;
+);
+
+register_rel!(FalconDmemC@...00001c0;
+ 23:0 addr => as u32;
+ 7:2 offs => as u8;
+ 23:8 blk => as u8;
+ 24:24 aincw => as_bit bool;
+ 25:25 aincr => as_bit bool;
+ 26:26 settag => as_bit bool;
+ 27:27 setlvl => as_bit bool;
+ 28:28 va => as_bit bool;
+ 29:29 miss => as_bit bool;
+);
+
+register_rel!(FalconDmemD@...00001c4;
+ 31:0 data => as u32;
+);
+
+register_rel!(FalconModSel@...0001180;
+ 7:0 algo => try_into FalconModSelAlgo;
+);
+register_rel!(FalconBromCurrUcodeId@...0001198;
+ 31:0 ucode_id => as u32;
+);
+register_rel!(FalconBromEngidmask@...000119c;
+ 31:0 mask => as u32;
+);
+register_rel!(FalconBromParaaddr0@...0001210;
+ 31:0 addr => as u32;
+);
+
+register_rel!(RiscvCpuCtl@...0000388;
+ 0:0 startcpu => as_bit bool;
+ 4:4 halted => as_bit bool;
+ 5:5 stopped => as_bit bool;
+ 7:7 active_stat => as_bit bool;
+);
+
+register_rel!(RiscvUnk3c0@...00003c0;
+ 0:0 unk0 => as_bit bool;
+);
+
+register_rel!(RiscvIrqmask@...0000528;
+ 31:0 mask => as u32;
+);
+
+register_rel!(RiscvIrqdest@...000052c;
+ 31:0 dest => as u32;
+);
+
+register_rel!(FalconUnk600@...0000600;
+ 16:16 unk16 => as_bit bool;
+ 2:0 unk2 => as u8;
+);
+
+register_rel!(FalconUnk624@...0000624;
+ 7:7 unk7 => as_bit bool;
+);
+
+register_rel!(RiscvBcrCtrl@...0001668;
+ 0:0 valid => as_bit bool;
+ 4:4 core_select => as_bit RiscvCoreSelect;
+ 8:8 br_fetch => as_bit bool;
+);
--
2.48.1
Powered by blists - more mailing lists