summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/tyr
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/tyr')
-rw-r--r--drivers/gpu/drm/tyr/Kconfig21
-rw-r--r--drivers/gpu/drm/tyr/Makefile3
-rw-r--r--drivers/gpu/drm/tyr/driver.rs211
-rw-r--r--drivers/gpu/drm/tyr/file.rs60
-rw-r--r--drivers/gpu/drm/tyr/gem.rs43
-rw-r--r--drivers/gpu/drm/tyr/gpu.rs175
-rw-r--r--drivers/gpu/drm/tyr/regs.rs1656
-rw-r--r--drivers/gpu/drm/tyr/tyr.rs22
8 files changed, 2191 insertions, 0 deletions
diff --git a/drivers/gpu/drm/tyr/Kconfig b/drivers/gpu/drm/tyr/Kconfig
new file mode 100644
index 000000000000..51a68ef8212c
--- /dev/null
+++ b/drivers/gpu/drm/tyr/Kconfig
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0 or MIT
+
+config DRM_TYR
+ tristate "Tyr (Rust DRM support for ARM Mali CSF-based GPUs)"
+ depends on DRM=y
+ depends on RUST
+ depends on ARM || ARM64 || COMPILE_TEST
+ depends on !GENERIC_ATOMIC64 # for IOMMU_IO_PGTABLE_LPAE
+ depends on COMMON_CLK
+ default n
+ select RUST_DRM_GEM_SHMEM_HELPER
+ help
+ Rust DRM driver for ARM Mali CSF-based GPUs.
+
+ This driver is for Mali (or Immortalis) Valhall Gxxx GPUs.
+
+ Note that the Mali-G68 and Mali-G78, while Valhall architecture, will
+ be supported with the panfrost driver as they are not CSF GPUs.
+
+ if M is selected, the module will be called tyr. This driver is work
+ in progress and may not be functional.
diff --git a/drivers/gpu/drm/tyr/Makefile b/drivers/gpu/drm/tyr/Makefile
new file mode 100644
index 000000000000..ba545f65f2c0
--- /dev/null
+++ b/drivers/gpu/drm/tyr/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0 or MIT
+
+obj-$(CONFIG_DRM_TYR) += tyr.o
diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
new file mode 100644
index 000000000000..d063bc664cc1
--- /dev/null
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+use kernel::{
+ clk::{
+ Clk,
+ OptionalClk, //
+ },
+ device::{
+ Core,
+ Device, //
+ },
+ dma::{
+ Device as DmaDevice,
+ DmaMask, //
+ },
+ drm,
+ drm::ioctl,
+ io::{
+ poll,
+ Io, //
+ },
+ new_mutex,
+ of,
+ platform,
+ prelude::*,
+ regulator,
+ regulator::Regulator,
+ sizes::SZ_2M,
+ sync::{
+ aref::ARef,
+ Mutex, //
+ },
+ time, //
+};
+
+use crate::{
+ file::TyrDrmFileData,
+ gem::BoData,
+ gpu,
+ gpu::GpuInfo,
+ regs::gpu_control::*, //
+};
+
+pub(crate) type IoMem<'a> = kernel::io::mem::IoMem<'a, SZ_2M>;
+
+pub(crate) struct TyrDrmDriver;
+
+/// Convenience type alias for the DRM device type for this driver.
+pub(crate) type TyrDrmDevice<Ctx = drm::Registered> = drm::Device<TyrDrmDriver, Ctx>;
+
+pub(crate) struct TyrPlatformDriver;
+
+#[pin_data(PinnedDrop)]
+pub(crate) struct TyrPlatformDriverData {
+ _device: ARef<TyrDrmDevice>,
+}
+
+#[pin_data]
+pub(crate) struct TyrDrmDeviceData {
+ pub(crate) pdev: ARef<platform::Device>,
+
+ #[pin]
+ clks: Mutex<Clocks>,
+
+ #[pin]
+ regulators: Mutex<Regulators>,
+
+ /// Some information on the GPU.
+ ///
+ /// This is mainly queried by userspace, i.e.: Mesa.
+ pub(crate) gpu_info: GpuInfo,
+}
+
+fn issue_soft_reset(dev: &Device, iomem: &IoMem<'_>) -> Result {
+ iomem.write_reg(GPU_COMMAND::reset(ResetMode::SoftReset));
+
+ poll::read_poll_timeout(
+ || Ok(iomem.read(GPU_IRQ_RAWSTAT)),
+ |status| status.reset_completed(),
+ time::Delta::from_millis(1),
+ time::Delta::from_millis(100),
+ )
+ .inspect_err(|_| dev_err!(dev, "GPU reset failed."))?;
+
+ Ok(())
+}
+
+kernel::of_device_table!(
+ OF_TABLE,
+ MODULE_OF_TABLE,
+ <TyrPlatformDriver as platform::Driver>::IdInfo,
+ [
+ (of::DeviceId::new(c"rockchip,rk3588-mali"), ()),
+ (of::DeviceId::new(c"arm,mali-valhall-csf"), ())
+ ]
+);
+
+impl platform::Driver for TyrPlatformDriver {
+ type IdInfo = ();
+ type Data<'bound> = TyrPlatformDriverData;
+ const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
+
+ fn probe<'bound>(
+ pdev: &'bound platform::Device<Core<'_>>,
+ _info: Option<&'bound Self::IdInfo>,
+ ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
+ let core_clk = Clk::get(pdev.as_ref(), Some(c"core"))?;
+ let stacks_clk = OptionalClk::get(pdev.as_ref(), Some(c"stacks"))?;
+ let coregroup_clk = OptionalClk::get(pdev.as_ref(), Some(c"coregroup"))?;
+
+ core_clk.prepare_enable()?;
+ stacks_clk.prepare_enable()?;
+ coregroup_clk.prepare_enable()?;
+
+ let mali_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"mali")?;
+ let sram_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"sram")?;
+
+ let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
+ let iomem = request.iomap_sized::<SZ_2M>()?;
+
+ issue_soft_reset(pdev.as_ref(), &iomem)?;
+ gpu::l2_power_on(pdev.as_ref(), &iomem)?;
+
+ let gpu_info = GpuInfo::new(&iomem);
+ gpu_info.log(pdev.as_ref());
+
+ let pa_bits = MMU_FEATURES::from_raw(gpu_info.mmu_features)
+ .pa_bits()
+ .get();
+ // SAFETY: No concurrent DMA allocations or mappings can be made because
+ // the device is still being probed and therefore isn't being used by
+ // other threads of execution.
+ unsafe { pdev.dma_set_mask_and_coherent(DmaMask::try_new(pa_bits)?)? };
+
+ let platform: ARef<platform::Device> = pdev.into();
+
+ let data = try_pin_init!(TyrDrmDeviceData {
+ pdev: platform.clone(),
+ clks <- new_mutex!(Clocks {
+ core: core_clk,
+ stacks: stacks_clk,
+ coregroup: coregroup_clk,
+ }),
+ regulators <- new_mutex!(Regulators {
+ _mali: mali_regulator,
+ _sram: sram_regulator,
+ }),
+ gpu_info,
+ });
+
+ let tdev = drm::UnregisteredDevice::<TyrDrmDriver>::new(pdev.as_ref(), data)?;
+ let tdev = drm::driver::Registration::new_foreign_owned(tdev, pdev.as_ref(), 0)?;
+
+ let driver = TyrPlatformDriverData {
+ _device: tdev.into(),
+ };
+
+ // We need this to be dev_info!() because dev_dbg!() does not work at
+ // all in Rust for now, and we need to see whether probe succeeded.
+ dev_info!(pdev, "Tyr initialized correctly.\n");
+ Ok(driver)
+ }
+}
+
+#[pinned_drop]
+impl PinnedDrop for TyrPlatformDriverData {
+ fn drop(self: Pin<&mut Self>) {}
+}
+
+// We need to retain the name "panthor" to achieve drop-in compatibility with
+// the C driver in the userspace stack.
+const INFO: drm::DriverInfo = drm::DriverInfo {
+ major: 1,
+ minor: 5,
+ patchlevel: 0,
+ name: c"panthor",
+ desc: c"ARM Mali Tyr DRM driver",
+};
+
+#[vtable]
+impl drm::Driver for TyrDrmDriver {
+ type Data = TyrDrmDeviceData;
+ type File = TyrDrmFileData;
+ type Object<R: drm::DeviceContext> = drm::gem::shmem::Object<BoData, R>;
+
+ const INFO: drm::DriverInfo = INFO;
+ const FEAT_RENDER: bool = true;
+
+ kernel::declare_drm_ioctls! {
+ (PANTHOR_DEV_QUERY, drm_panthor_dev_query, ioctl::RENDER_ALLOW, TyrDrmFileData::dev_query),
+ }
+}
+
+struct Clocks {
+ core: Clk,
+ stacks: OptionalClk,
+ coregroup: OptionalClk,
+}
+
+impl Drop for Clocks {
+ fn drop(&mut self) {
+ self.core.disable_unprepare();
+ self.stacks.disable_unprepare();
+ self.coregroup.disable_unprepare();
+ }
+}
+
+struct Regulators {
+ _mali: Regulator<regulator::Enabled>,
+ _sram: Regulator<regulator::Enabled>,
+}
diff --git a/drivers/gpu/drm/tyr/file.rs b/drivers/gpu/drm/tyr/file.rs
new file mode 100644
index 000000000000..31411da203c5
--- /dev/null
+++ b/drivers/gpu/drm/tyr/file.rs
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+use kernel::{
+ drm,
+ prelude::*,
+ uaccess::UserSlice,
+ uapi, //
+};
+
+use crate::driver::{
+ TyrDrmDevice,
+ TyrDrmDriver, //
+};
+
+#[pin_data]
+pub(crate) struct TyrDrmFileData {}
+
+/// Convenience type alias for our DRM `File` type
+pub(crate) type TyrDrmFile = drm::file::File<TyrDrmFileData>;
+
+impl drm::file::DriverFile for TyrDrmFileData {
+ type Driver = TyrDrmDriver;
+
+ fn open(_dev: &drm::Device<Self::Driver>) -> Result<Pin<KBox<Self>>> {
+ KBox::try_pin_init(try_pin_init!(Self {}), GFP_KERNEL)
+ }
+}
+
+impl TyrDrmFileData {
+ pub(crate) fn dev_query(
+ ddev: &TyrDrmDevice,
+ devquery: &mut uapi::drm_panthor_dev_query,
+ _file: &TyrDrmFile,
+ ) -> Result<u32> {
+ if devquery.pointer == 0 {
+ match devquery.type_ {
+ uapi::drm_panthor_dev_query_type_DRM_PANTHOR_DEV_QUERY_GPU_INFO => {
+ devquery.size = core::mem::size_of_val(&ddev.gpu_info) as u32;
+ Ok(0)
+ }
+ _ => Err(EINVAL),
+ }
+ } else {
+ match devquery.type_ {
+ uapi::drm_panthor_dev_query_type_DRM_PANTHOR_DEV_QUERY_GPU_INFO => {
+ let mut writer = UserSlice::new(
+ UserPtr::from_addr(devquery.pointer as usize),
+ devquery.size as usize,
+ )
+ .writer();
+
+ writer.write(&ddev.gpu_info)?;
+
+ Ok(0)
+ }
+ _ => Err(EINVAL),
+ }
+ }
+ }
+}
diff --git a/drivers/gpu/drm/tyr/gem.rs b/drivers/gpu/drm/tyr/gem.rs
new file mode 100644
index 000000000000..c6d4d6f9bae3
--- /dev/null
+++ b/drivers/gpu/drm/tyr/gem.rs
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+//! GEM buffer object management for the Tyr driver.
+//!
+//! This module provides buffer object (BO) management functionality using
+//! DRM's GEM subsystem with shmem backing.
+
+use kernel::{
+ drm::{
+ gem,
+ DeviceContext, //
+ },
+ prelude::*, //
+};
+
+use crate::driver::{
+ TyrDrmDevice,
+ TyrDrmDriver, //
+};
+
+/// Tyr's DriverObject type for GEM objects.
+#[pin_data]
+pub(crate) struct BoData {
+ flags: u32,
+}
+
+/// Provides a way to pass arguments when creating BoData
+/// as required by the gem::DriverObject trait.
+pub(crate) struct BoCreateArgs {
+ flags: u32,
+}
+
+impl gem::DriverObject for BoData {
+ type Driver = TyrDrmDriver;
+ type Args = BoCreateArgs;
+
+ fn new<Ctx: DeviceContext>(
+ _dev: &TyrDrmDevice<Ctx>,
+ _size: usize,
+ args: BoCreateArgs,
+ ) -> impl PinInit<Self, Error> {
+ try_pin_init!(Self { flags: args.flags })
+ }
+}
diff --git a/drivers/gpu/drm/tyr/gpu.rs b/drivers/gpu/drm/tyr/gpu.rs
new file mode 100644
index 000000000000..592b8bb16eba
--- /dev/null
+++ b/drivers/gpu/drm/tyr/gpu.rs
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+use core::ops::{
+ Deref,
+ DerefMut, //
+};
+use kernel::{
+ device::{
+ Bound,
+ Device, //
+ },
+ io::{
+ poll,
+ register::Array,
+ Io, //
+ },
+ prelude::*,
+ time::Delta,
+ transmute::AsBytes,
+ uapi, //
+};
+
+use crate::{
+ driver::IoMem,
+ regs::{
+ gpu_control::*,
+ join_u64, //
+ }, //
+};
+
+/// Struct containing information that can be queried by userspace. This is read from
+/// the GPU's registers.
+///
+/// # Invariants
+///
+/// - The layout of this struct is identical to the C `struct drm_panthor_gpu_info`.
+#[repr(transparent)]
+#[derive(Clone, Copy)]
+pub(crate) struct GpuInfo(pub(crate) uapi::drm_panthor_gpu_info);
+
+impl GpuInfo {
+ pub(crate) fn new(io: &IoMem<'_>) -> Self {
+ Self(uapi::drm_panthor_gpu_info {
+ gpu_id: io.read(GPU_ID).into_raw(),
+ gpu_rev: io.read(REVIDR).into_raw(),
+ csf_id: io.read(CSF_ID).into_raw(),
+ l2_features: io.read(L2_FEATURES).into_raw(),
+ tiler_features: io.read(TILER_FEATURES).into_raw(),
+ mem_features: io.read(MEM_FEATURES).into_raw(),
+ mmu_features: io.read(MMU_FEATURES).into_raw(),
+ thread_features: io.read(THREAD_FEATURES).into_raw(),
+ max_threads: io.read(THREAD_MAX_THREADS).into_raw(),
+ thread_max_workgroup_size: io.read(THREAD_MAX_WORKGROUP_SIZE).into_raw(),
+ thread_max_barrier_size: io.read(THREAD_MAX_BARRIER_SIZE).into_raw(),
+ coherency_features: io.read(COHERENCY_FEATURES).into_raw(),
+ texture_features: [
+ io.read(TEXTURE_FEATURES::at(0)).supported_formats().get(),
+ io.read(TEXTURE_FEATURES::at(1)).supported_formats().get(),
+ io.read(TEXTURE_FEATURES::at(2)).supported_formats().get(),
+ io.read(TEXTURE_FEATURES::at(3)).supported_formats().get(),
+ ],
+ as_present: io.read(AS_PRESENT).into_raw(),
+ selected_coherency: uapi::drm_panthor_gpu_coherency_DRM_PANTHOR_GPU_COHERENCY_NONE,
+ shader_present: join_u64(
+ io.read(SHADER_PRESENT_LO).into_raw(),
+ io.read(SHADER_PRESENT_HI).into_raw(),
+ ),
+ l2_present: join_u64(
+ io.read(L2_PRESENT_LO).into_raw(),
+ io.read(L2_PRESENT_HI).into_raw(),
+ ),
+ tiler_present: join_u64(
+ io.read(TILER_PRESENT_LO).into_raw(),
+ io.read(TILER_PRESENT_HI).into_raw(),
+ ),
+ core_features: io.read(CORE_FEATURES).into_raw(),
+ // Padding must be zero.
+ pad: 0,
+ //GPU_FEATURES register is not available; it was introduced in arch 11.x.
+ gpu_features: 0,
+ })
+ }
+
+ pub(crate) fn log(&self, dev: &Device<Bound>) {
+ let gpu_id = GPU_ID::from_raw(self.gpu_id);
+
+ let model_name = if let Some(model) = GPU_MODELS.iter().find(|&f| {
+ f.arch_major == gpu_id.arch_major().get() && f.prod_major == gpu_id.prod_major().get()
+ }) {
+ model.name
+ } else {
+ "unknown"
+ };
+
+ dev_info!(
+ dev,
+ "mali-{} GPU_ID 0x{:x} major 0x{:x} minor 0x{:x} status 0x{:x}",
+ model_name,
+ gpu_id.into_raw(),
+ gpu_id.ver_major().get(),
+ gpu_id.ver_minor().get(),
+ gpu_id.ver_status().get()
+ );
+
+ dev_info!(
+ dev,
+ "Features: L2:{:#x} Tiler:{:#x} Mem:{:#x} MMU:{:#x} AS:{:#x}",
+ self.l2_features,
+ self.tiler_features,
+ self.mem_features,
+ self.mmu_features,
+ self.as_present,
+ );
+
+ dev_info!(
+ dev,
+ "shader_present=0x{:016x} l2_present=0x{:016x} tiler_present=0x{:016x}",
+ self.shader_present,
+ self.l2_present,
+ self.tiler_present,
+ );
+ }
+}
+
+impl Deref for GpuInfo {
+ type Target = uapi::drm_panthor_gpu_info;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl DerefMut for GpuInfo {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+// SAFETY: `GpuInfo`'s invariant guarantees that it is the same type that is
+// already exposed to userspace by the C driver. This implies that it fulfills
+// the requirements for `AsBytes`.
+//
+// This means:
+//
+// - No implicit padding,
+// - No kernel pointers,
+// - No interior mutability.
+unsafe impl AsBytes for GpuInfo {}
+
+struct GpuModels {
+ name: &'static str,
+ arch_major: u32,
+ prod_major: u32,
+}
+
+const GPU_MODELS: [GpuModels; 1] = [GpuModels {
+ name: "g610",
+ arch_major: 10,
+ prod_major: 7,
+}];
+
+/// Powers on the l2 block.
+pub(crate) fn l2_power_on(dev: &Device, io: &IoMem<'_>) -> Result {
+ io.write_reg(L2_PWRON_LO::zeroed().with_const_request::<1>());
+
+ poll::read_poll_timeout(
+ || Ok(io.read(L2_READY_LO)),
+ |status| status.ready() == 1,
+ Delta::from_millis(1),
+ Delta::from_millis(100),
+ )
+ .inspect_err(|_| dev_err!(dev, "Failed to power on the GPU."))?;
+
+ Ok(())
+}
diff --git a/drivers/gpu/drm/tyr/regs.rs b/drivers/gpu/drm/tyr/regs.rs
new file mode 100644
index 000000000000..562023e5df2f
--- /dev/null
+++ b/drivers/gpu/drm/tyr/regs.rs
@@ -0,0 +1,1656 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+//! # Definitions
+//!
+//! - **CEU**: Command Execution Unit - A hardware component that executes commands (instructions)
+//! from the command stream.
+//! - **CS**: Command Stream - A sequence of instructions (commands) used to control a particular
+//! job or sequence of jobs. The instructions exist in one or more command buffers.
+//! - **CSF**: Command Stream Frontend - The interface and implementation for job submission
+//! exposed to the host CPU driver. This includes the global interface, as well as CSG and CS
+//! interfaces.
+//! - **CSG**: Command Stream Group - A group of related command streams. The CSF manages multiple
+//! CSGs, and each CSG contains multiple CSs.
+//! - **CSHW**: Command Stream Hardware - The hardware interpreting command streams, including the
+//! iterator control aspects. Implements the CSF in conjunction with the MCU.
+//! - **GLB**: Global - Prefix for global interface registers that control operations common to
+//! all CSs.
+//! - **JASID**: Job Address Space ID - Identifies the address space for a job.
+//! - **MCU**: Microcontroller Unit - Implements the CSF in conjunction with the command stream
+//! hardware.
+//! - **MMU**: Memory Management Unit - Handles address translation and memory access protection.
+
+// We don't expect that all the registers and fields will be used, even in the
+// future.
+//
+// Nevertheless, it is useful to have most of them defined, like the C driver
+// does.
+#![allow(dead_code)]
+
+/// Combine two 32-bit values into a single 64-bit value.
+pub(crate) fn join_u64(lo: u32, hi: u32) -> u64 {
+ (u64::from(lo)) | ((u64::from(hi)) << 32)
+}
+
+/// Read a logical 64-bit value from split 32-bit registers without tearing.
+pub(crate) fn read_u64_no_tearing(lo_read: impl Fn() -> u32, hi_read: impl Fn() -> u32) -> u64 {
+ loop {
+ let hi1 = hi_read();
+ let lo = lo_read();
+ let hi2 = hi_read();
+
+ if hi1 == hi2 {
+ return join_u64(lo, hi1);
+ }
+ }
+}
+
+/// These registers correspond to the GPU_CONTROL register page.
+/// They are involved in GPU configuration and control.
+pub(crate) mod gpu_control {
+ use core::convert::TryFrom;
+ use kernel::{
+ error::{
+ code::EINVAL,
+ Error, //
+ },
+ num::Bounded,
+ register,
+ uapi, //
+ };
+ use pin_init::Zeroable;
+
+ register! {
+ /// GPU identification register.
+ pub(crate) GPU_ID(u32) @ 0x0 {
+ /// Status of the GPU release.
+ 3:0 ver_status;
+ /// Minor release version number.
+ 11:4 ver_minor;
+ /// Major release version number.
+ 15:12 ver_major;
+ /// Product identifier.
+ 19:16 prod_major;
+ /// Architecture patch revision.
+ 23:20 arch_rev;
+ /// Architecture minor revision.
+ 27:24 arch_minor;
+ /// Architecture major revision.
+ 31:28 arch_major;
+ }
+
+ /// Level 2 cache features register.
+ pub(crate) L2_FEATURES(u32) @ 0x4 {
+ /// Cache line size.
+ 7:0 line_size;
+ /// Cache associativity.
+ 15:8 associativity;
+ /// Cache slice size.
+ 23:16 cache_size;
+ /// External bus width.
+ 31:24 bus_width;
+ }
+
+ /// Shader core features.
+ pub(crate) CORE_FEATURES(u32) @ 0x8 {
+ /// Shader core variant.
+ 7:0 core_variant;
+ }
+
+ /// Tiler features.
+ pub(crate) TILER_FEATURES(u32) @ 0xc {
+ /// Log of the tiler's bin size.
+ 5:0 bin_size;
+ /// Maximum number of active levels.
+ 11:8 max_levels;
+ }
+
+ /// Memory system features.
+ pub(crate) MEM_FEATURES(u32) @ 0x10 {
+ 0:0 coherent_core_group => bool;
+ 1:1 coherent_super_group => bool;
+ 11:8 l2_slices;
+ }
+
+ /// Memory management unit features.
+ pub(crate) MMU_FEATURES(u32) @ 0x14 {
+ /// Number of bits supported in virtual addresses.
+ 7:0 va_bits;
+ /// Number of bits supported in physical addresses.
+ 15:8 pa_bits;
+ }
+
+ /// Address spaces present.
+ pub(crate) AS_PRESENT(u32) @ 0x18 {
+ 31:0 present;
+ }
+
+ /// CSF version information.
+ pub(crate) CSF_ID(u32) @ 0x1c {
+ /// MCU revision ID.
+ 3:0 mcu_rev;
+ /// MCU minor revision number.
+ 9:4 mcu_minor;
+ /// MCU major revision number.
+ 15:10 mcu_major;
+ /// CSHW revision ID.
+ 19:16 cshw_rev;
+ /// CSHW minor revision number.
+ 25:20 cshw_minor;
+ /// CSHW major revision number.
+ 31:26 cshw_major;
+ }
+
+ /// IRQ sources raw status.
+ /// Writing to this register forces bits on, but does not clear them.
+ pub(crate) GPU_IRQ_RAWSTAT(u32) @ 0x20 {
+ /// A GPU fault has occurred, a 1-bit boolean flag.
+ 0:0 gpu_fault => bool;
+ /// A GPU fault has occurred, a 1-bit boolean flag.
+ 1:1 gpu_protected_fault => bool;
+ /// Reset has completed, a 1-bit boolean flag.
+ 8:8 reset_completed => bool;
+ /// Set when a single power domain has powered up or down, a 1-bit boolean flag.
+ 9:9 power_changed_single => bool;
+ /// Set when the all pending power domain changes are completed, a 1-bit boolean flag.
+ 10:10 power_changed_all => bool;
+ /// Set when cache cleaning has completed, a 1-bit boolean flag.
+ 17:17 clean_caches_completed => bool;
+ /// Mirrors the doorbell interrupt line to the CPU, a 1-bit boolean flag.
+ 18:18 doorbell_mirror => bool;
+ /// MCU requires attention, a 1-bit boolean flag.
+ 19:19 mcu_status => bool;
+ }
+
+ /// IRQ sources to clear. Write only.
+ pub(crate) GPU_IRQ_CLEAR(u32) @ 0x24 {
+ /// Clear the GPU_FAULT interrupt, a 1-bit boolean flag.
+ 0:0 gpu_fault => bool;
+ /// Clear the GPU_PROTECTED_FAULT interrupt, a 1-bit boolean flag.
+ 1:1 gpu_protected_fault => bool;
+ /// Clear the RESET_COMPLETED interrupt, a 1-bit boolean flag.
+ 8:8 reset_completed => bool;
+ /// Clear the POWER_CHANGED_SINGLE interrupt, a 1-bit boolean flag.
+ 9:9 power_changed_single => bool;
+ /// Clear the POWER_CHANGED_ALL interrupt, a 1-bit boolean flag.
+ 10:10 power_changed_all => bool;
+ /// Clear the CLEAN_CACHES_COMPLETED interrupt, a 1-bit boolean flag.
+ 17:17 clean_caches_completed => bool;
+ /// Clear the MCU_STATUS interrupt, a 1-bit boolean flag.
+ 19:19 mcu_status => bool;
+ }
+
+ /// IRQ sources enabled.
+ pub(crate) GPU_IRQ_MASK(u32) @ 0x28 {
+ /// Enable the GPU_FAULT interrupt, a 1-bit boolean flag.
+ 0:0 gpu_fault => bool;
+ /// Enable the GPU_PROTECTED_FAULT interrupt, a 1-bit boolean flag.
+ 1:1 gpu_protected_fault => bool;
+ /// Enable the RESET_COMPLETED interrupt, a 1-bit boolean flag.
+ 8:8 reset_completed => bool;
+ /// Enable the POWER_CHANGED_SINGLE interrupt, a 1-bit boolean flag.
+ 9:9 power_changed_single => bool;
+ /// Enable the POWER_CHANGED_ALL interrupt, a 1-bit boolean flag.
+ 10:10 power_changed_all => bool;
+ /// Enable the CLEAN_CACHES_COMPLETED interrupt, a 1-bit boolean flag.
+ 17:17 clean_caches_completed => bool;
+ /// Enable the DOORBELL_MIRROR interrupt, a 1-bit boolean flag.
+ 18:18 doorbell_mirror => bool;
+ /// Enable the MCU_STATUS interrupt, a 1-bit boolean flag.
+ 19:19 mcu_status => bool;
+ }
+
+ /// IRQ status for enabled sources. Read only.
+ pub(crate) GPU_IRQ_STATUS(u32) @ 0x2c {
+ /// GPU_FAULT interrupt status, a 1-bit boolean flag.
+ 0:0 gpu_fault => bool;
+ /// GPU_PROTECTED_FAULT interrupt status, a 1-bit boolean flag.
+ 1:1 gpu_protected_fault => bool;
+ /// RESET_COMPLETED interrupt status, a 1-bit boolean flag.
+ 8:8 reset_completed => bool;
+ /// POWER_CHANGED_SINGLE interrupt status, a 1-bit boolean flag.
+ 9:9 power_changed_single => bool;
+ /// POWER_CHANGED_ALL interrupt status, a 1-bit boolean flag.
+ 10:10 power_changed_all => bool;
+ /// CLEAN_CACHES_COMPLETED interrupt status, a 1-bit boolean flag.
+ 17:17 clean_caches_completed => bool;
+ /// DOORBELL_MIRROR interrupt status, a 1-bit boolean flag.
+ 18:18 doorbell_mirror => bool;
+ /// MCU_STATUS interrupt status, a 1-bit boolean flag.
+ 19:19 mcu_status => bool;
+ }
+ }
+
+ /// Helpers for GPU_COMMAND Register
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum GpuCommand {
+ /// No operation. This is the default value.
+ Nop = 0,
+ /// Reset the GPU.
+ Reset = 1,
+ /// Flush caches.
+ FlushCaches = 4,
+ /// Clear GPU faults.
+ ClearFault = 7,
+ }
+
+ impl TryFrom<Bounded<u32, 8>> for GpuCommand {
+ type Error = Error;
+
+ fn try_from(val: Bounded<u32, 8>) -> Result<Self, Self::Error> {
+ match val.get() {
+ 0 => Ok(GpuCommand::Nop),
+ 1 => Ok(GpuCommand::Reset),
+ 4 => Ok(GpuCommand::FlushCaches),
+ 7 => Ok(GpuCommand::ClearFault),
+ _ => Err(EINVAL),
+ }
+ }
+ }
+
+ impl From<GpuCommand> for Bounded<u32, 8> {
+ fn from(cmd: GpuCommand) -> Self {
+ (cmd as u8).into()
+ }
+ }
+
+ /// Reset mode for [`GPU_COMMAND::reset()`].
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum ResetMode {
+ /// Stop all external bus interfaces, then reset the entire GPU.
+ SoftReset = 1,
+ /// Force a full GPU reset.
+ HardReset = 2,
+ }
+
+ impl TryFrom<Bounded<u32, 4>> for ResetMode {
+ type Error = Error;
+
+ fn try_from(val: Bounded<u32, 4>) -> Result<Self, Self::Error> {
+ match val.get() {
+ 1 => Ok(ResetMode::SoftReset),
+ 2 => Ok(ResetMode::HardReset),
+ _ => Err(EINVAL),
+ }
+ }
+ }
+
+ impl From<ResetMode> for Bounded<u32, 4> {
+ fn from(mode: ResetMode) -> Self {
+ Bounded::try_new(mode as u32).unwrap()
+ }
+ }
+
+ /// Cache flush mode for [`GPU_COMMAND::flush_caches()`].
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum FlushMode {
+ /// No flush.
+ None = 0,
+ /// Clean the caches.
+ Clean = 1,
+ /// Invalidate the caches.
+ Invalidate = 2,
+ /// Clean and invalidate the caches.
+ CleanInvalidate = 3,
+ }
+
+ impl TryFrom<Bounded<u32, 4>> for FlushMode {
+ type Error = Error;
+
+ fn try_from(val: Bounded<u32, 4>) -> Result<Self, Self::Error> {
+ match val.get() {
+ 0 => Ok(FlushMode::None),
+ 1 => Ok(FlushMode::Clean),
+ 2 => Ok(FlushMode::Invalidate),
+ 3 => Ok(FlushMode::CleanInvalidate),
+ _ => Err(EINVAL),
+ }
+ }
+ }
+
+ impl From<FlushMode> for Bounded<u32, 4> {
+ fn from(mode: FlushMode) -> Self {
+ Bounded::try_new(mode as u32).unwrap()
+ }
+ }
+
+ register! {
+ /// GPU command register.
+ ///
+ /// Use the constructor methods to create commands:
+ /// - [`GPU_COMMAND::nop()`]
+ /// - [`GPU_COMMAND::reset()`]
+ /// - [`GPU_COMMAND::flush_caches()`]
+ /// - [`GPU_COMMAND::clear_fault()`]
+ pub(crate) GPU_COMMAND (u32) @ 0x30 {
+ 7:0 command ?=> GpuCommand;
+ }
+ /// Internal alias for GPU_COMMAND in reset mode.
+ /// Use [`GPU_COMMAND::reset()`] instead.
+ GPU_COMMAND_RESET (u32) => GPU_COMMAND {
+ 7:0 command ?=> GpuCommand;
+ 11:8 reset_mode ?=> ResetMode;
+ }
+
+ /// Internal alias for GPU_COMMAND in cache flush mode.
+ /// Use [`GPU_COMMAND::flush_caches()`] instead.
+ GPU_COMMAND_FLUSH (u32) => GPU_COMMAND {
+ 7:0 command ?=> GpuCommand;
+ /// L2 cache flush mode.
+ 11:8 l2_flush ?=> FlushMode;
+ /// Shader core load/store cache flush mode.
+ 15:12 lsc_flush ?=> FlushMode;
+ /// Shader core other caches flush mode.
+ 19:16 other_flush ?=> FlushMode;
+ }
+ }
+
+ impl GPU_COMMAND {
+ /// Create a NOP command.
+ pub(crate) fn nop() -> Self {
+ Self::zeroed()
+ }
+
+ /// Create a reset command with the specified reset mode.
+ pub(crate) fn reset(mode: ResetMode) -> Self {
+ Self::from_raw(
+ GPU_COMMAND_RESET::zeroed()
+ .with_command(GpuCommand::Reset)
+ .with_reset_mode(mode)
+ .into_raw(),
+ )
+ }
+
+ /// Create a cache flush command with the specified flush modes.
+ pub(crate) fn flush_caches(l2: FlushMode, lsc: FlushMode, other: FlushMode) -> Self {
+ Self::from_raw(
+ GPU_COMMAND_FLUSH::zeroed()
+ .with_command(GpuCommand::FlushCaches)
+ .with_l2_flush(l2)
+ .with_lsc_flush(lsc)
+ .with_other_flush(other)
+ .into_raw(),
+ )
+ }
+
+ /// Create a clear fault command.
+ pub(crate) fn clear_fault() -> Self {
+ Self::zeroed().with_command(GpuCommand::ClearFault)
+ }
+ }
+
+ register! {
+ /// GPU status register. Read only.
+ pub(crate) GPU_STATUS(u32) @ 0x34 {
+ /// GPU active, a 1-bit boolean flag.
+ 0:0 gpu_active => bool;
+ /// Power manager active, a 1-bit boolean flag
+ 1:1 pwr_active => bool;
+ /// Page fault active, a 1-bit boolean flag.
+ 4:4 page_fault => bool;
+ /// Protected mode active, a 1-bit boolean flag.
+ 7:7 protected_mode_active => bool;
+ /// Debug mode active, a 1-bit boolean flag.
+ 8:8 gpu_dbg_enabled => bool;
+ }
+ }
+
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum ExceptionType {
+ /// Exception type: No error.
+ Ok = 0x00,
+ /// Exception type: GPU external bus error.
+ GpuBusFault = 0x80,
+ /// Exception type: GPU shareability error.
+ GpuShareabilityFault = 0x88,
+ /// Exception type: System shareability error.
+ SystemShareabilityFault = 0x89,
+ /// Exception type: GPU cacheability error.
+ GpuCacheabilityFault = 0x8A,
+ }
+
+ impl TryFrom<Bounded<u32, 8>> for ExceptionType {
+ type Error = Error;
+
+ fn try_from(val: Bounded<u32, 8>) -> Result<Self, Self::Error> {
+ match val.get() {
+ 0x00 => Ok(ExceptionType::Ok),
+ 0x80 => Ok(ExceptionType::GpuBusFault),
+ 0x88 => Ok(ExceptionType::GpuShareabilityFault),
+ 0x89 => Ok(ExceptionType::SystemShareabilityFault),
+ 0x8A => Ok(ExceptionType::GpuCacheabilityFault),
+ _ => Err(EINVAL),
+ }
+ }
+ }
+
+ impl From<ExceptionType> for Bounded<u32, 8> {
+ fn from(exc: ExceptionType) -> Self {
+ (exc as u8).into()
+ }
+ }
+
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum AccessType {
+ /// Access type: An atomic (read/write) transaction.
+ Atomic = 0,
+ /// Access type: An execute transaction.
+ Execute = 1,
+ /// Access type: A read transaction.
+ Read = 2,
+ /// Access type: A write transaction.
+ Write = 3,
+ }
+
+ impl From<Bounded<u32, 2>> for AccessType {
+ fn from(val: Bounded<u32, 2>) -> Self {
+ match val.get() {
+ 0 => AccessType::Atomic,
+ 1 => AccessType::Execute,
+ 2 => AccessType::Read,
+ 3 => AccessType::Write,
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ impl From<AccessType> for Bounded<u32, 2> {
+ fn from(access: AccessType) -> Self {
+ Bounded::try_new(access as u32).unwrap()
+ }
+ }
+
+ register! {
+ /// GPU fault status register. Read only.
+ pub(crate) GPU_FAULTSTATUS(u32) @ 0x3c {
+ /// Exception type.
+ 7:0 exception_type ?=> ExceptionType;
+ /// Access type.
+ 9:8 access_type => AccessType;
+ /// The GPU_FAULTADDRESS is valid, a 1-bit boolean flag.
+ 10:10 address_valid => bool;
+ /// The JASID field is valid, a 1-bit boolean flag.
+ 11:11 jasid_valid => bool;
+ /// JASID of the fault, if known.
+ 15:12 jasid;
+ /// ID of the source that triggered the fault.
+ 31:16 source_id;
+ }
+
+ /// GPU fault address. Read only.
+ /// Once a fault is reported, it must be manually cleared by issuing a
+ /// [`GPU_COMMAND::clear_fault()`] command to the [`GPU_COMMAND`] register. No further GPU
+ /// faults will be reported until the previous fault has been cleared.
+ pub(crate) GPU_FAULTADDRESS_LO(u32) @ 0x40 {
+ 31:0 pointer;
+ }
+
+ pub(crate) GPU_FAULTADDRESS_HI(u32) @ 0x44 {
+ 31:0 pointer;
+ }
+
+ /// Level 2 cache configuration.
+ pub(crate) L2_CONFIG(u32) @ 0x48 {
+ /// Requested cache size.
+ 23:16 cache_size;
+ /// Requested hash function index.
+ 31:24 hash_function;
+ }
+
+ /// Global time stamp offset.
+ pub(crate) TIMESTAMP_OFFSET_LO(u32) @ 0x88 {
+ 31:0 offset;
+ }
+
+ pub(crate) TIMESTAMP_OFFSET_HI(u32) @ 0x8c {
+ 31:0 offset;
+ }
+
+ /// GPU cycle counter. Read only.
+ pub(crate) CYCLE_COUNT_LO(u32) @ 0x90 {
+ 31:0 count;
+ }
+
+ pub(crate) CYCLE_COUNT_HI(u32) @ 0x94 {
+ 31:0 count;
+ }
+
+ /// Global time stamp. Read only.
+ pub(crate) TIMESTAMP_LO(u32) @ 0x98 {
+ 31:0 timestamp;
+ }
+
+ pub(crate) TIMESTAMP_HI(u32) @ 0x9c {
+ 31:0 timestamp;
+ }
+
+ /// Maximum number of threads per core. Read only constant.
+ pub(crate) THREAD_MAX_THREADS(u32) @ 0xa0 {
+ 31:0 threads;
+ }
+
+ /// Maximum number of threads per workgroup. Read only constant.
+ pub(crate) THREAD_MAX_WORKGROUP_SIZE(u32) @ 0xa4 {
+ 31:0 threads;
+ }
+
+ /// Maximum number of threads per barrier. Read only constant.
+ pub(crate) THREAD_MAX_BARRIER_SIZE(u32) @ 0xa8 {
+ 31:0 threads;
+ }
+
+ /// Thread features. Read only constant.
+ pub(crate) THREAD_FEATURES(u32) @ 0xac {
+ /// Total number of registers per core.
+ 21:0 max_registers;
+ /// Implementation technology type.
+ 23:22 implementation_technology;
+ /// Maximum number of compute tasks waiting.
+ 31:24 max_task_queue;
+ }
+
+ /// Support flags for compressed texture formats. Read only constant.
+ ///
+ /// A bitmap where each bit indicates support for a specific compressed texture format.
+ /// The bit position maps to an opaque format ID (`texture_features_key_t` in spec).
+ pub(crate) TEXTURE_FEATURES(u32)[4] @ 0xb0 {
+ 31:0 supported_formats;
+ }
+
+ /// Shader core present bitmap. Read only constant.
+ pub(crate) SHADER_PRESENT_LO(u32) @ 0x100 {
+ 31:0 value;
+ }
+
+ pub(crate) SHADER_PRESENT_HI(u32) @ 0x104 {
+ 31:0 value;
+ }
+
+ /// Tiler present bitmap. Read only constant.
+ pub(crate) TILER_PRESENT_LO(u32) @ 0x110 {
+ 31:0 present;
+ }
+
+ pub(crate) TILER_PRESENT_HI(u32) @ 0x114 {
+ 31:0 present;
+ }
+
+ /// L2 cache present bitmap. Read only constant.
+ pub(crate) L2_PRESENT_LO(u32) @ 0x120 {
+ 31:0 present;
+ }
+
+ pub(crate) L2_PRESENT_HI(u32) @ 0x124 {
+ 31:0 present;
+ }
+
+ /// Shader core ready bitmap. Read only.
+ pub(crate) SHADER_READY_LO(u32) @ 0x140 {
+ 31:0 ready;
+ }
+
+ pub(crate) SHADER_READY_HI(u32) @ 0x144 {
+ 31:0 ready;
+ }
+
+ /// Tiler ready bitmap. Read only.
+ pub(crate) TILER_READY_LO(u32) @ 0x150 {
+ 31:0 ready;
+ }
+
+ pub(crate) TILER_READY_HI(u32) @ 0x154 {
+ 31:0 ready;
+ }
+
+ /// L2 ready bitmap. Read only.
+ pub(crate) L2_READY_LO(u32) @ 0x160 {
+ 31:0 ready;
+ }
+
+ pub(crate) L2_READY_HI(u32) @ 0x164 {
+ 31:0 ready;
+ }
+
+ /// Shader core power up bitmap.
+ pub(crate) SHADER_PWRON_LO(u32) @ 0x180 {
+ 31:0 request;
+ }
+
+ pub(crate) SHADER_PWRON_HI(u32) @ 0x184 {
+ 31:0 request;
+ }
+
+ /// Tiler power up bitmap.
+ pub(crate) TILER_PWRON_LO(u32) @ 0x190 {
+ 31:0 request;
+ }
+
+ pub(crate) TILER_PWRON_HI(u32) @ 0x194 {
+ 31:0 request;
+ }
+
+ /// L2 power up bitmap.
+ pub(crate) L2_PWRON_LO(u32) @ 0x1a0 {
+ 31:0 request;
+ }
+
+ pub(crate) L2_PWRON_HI(u32) @ 0x1a4 {
+ 31:0 request;
+ }
+
+ /// Shader core power down bitmap.
+ pub(crate) SHADER_PWROFF_LO(u32) @ 0x1c0 {
+ 31:0 request;
+ }
+
+ pub(crate) SHADER_PWROFF_HI(u32) @ 0x1c4 {
+ 31:0 request;
+ }
+
+ /// Tiler power down bitmap.
+ pub(crate) TILER_PWROFF_LO(u32) @ 0x1d0 {
+ 31:0 request;
+ }
+
+ pub(crate) TILER_PWROFF_HI(u32) @ 0x1d4 {
+ 31:0 request;
+ }
+
+ /// L2 power down bitmap.
+ pub(crate) L2_PWROFF_LO(u32) @ 0x1e0 {
+ 31:0 request;
+ }
+
+ pub(crate) L2_PWROFF_HI(u32) @ 0x1e4 {
+ 31:0 request;
+ }
+
+ /// Shader core power transition bitmap. Read-only.
+ pub(crate) SHADER_PWRTRANS_LO(u32) @ 0x200 {
+ 31:0 changing;
+ }
+
+ pub(crate) SHADER_PWRTRANS_HI(u32) @ 0x204 {
+ 31:0 changing;
+ }
+
+ /// Tiler power transition bitmap. Read-only.
+ pub(crate) TILER_PWRTRANS_LO(u32) @ 0x210 {
+ 31:0 changing;
+ }
+
+ pub(crate) TILER_PWRTRANS_HI(u32) @ 0x214 {
+ 31:0 changing;
+ }
+
+ /// L2 power transition bitmap. Read-only.
+ pub(crate) L2_PWRTRANS_LO(u32) @ 0x220 {
+ 31:0 changing;
+ }
+
+ pub(crate) L2_PWRTRANS_HI(u32) @ 0x224 {
+ 31:0 changing;
+ }
+
+ /// Shader core active bitmap. Read-only.
+ pub(crate) SHADER_PWRACTIVE_LO(u32) @ 0x240 {
+ 31:0 active;
+ }
+
+ pub(crate) SHADER_PWRACTIVE_HI(u32) @ 0x244 {
+ 31:0 active;
+ }
+
+ /// Tiler active bitmap. Read-only.
+ pub(crate) TILER_PWRACTIVE_LO(u32) @ 0x250 {
+ 31:0 active;
+ }
+
+ pub(crate) TILER_PWRACTIVE_HI(u32) @ 0x254 {
+ 31:0 active;
+ }
+
+ /// L2 active bitmap. Read-only.
+ pub(crate) L2_PWRACTIVE_LO(u32) @ 0x260 {
+ 31:0 active;
+ }
+
+ pub(crate) L2_PWRACTIVE_HI(u32) @ 0x264 {
+ 31:0 active;
+ }
+
+ /// Revision ID. Read only constant.
+ pub(crate) REVIDR(u32) @ 0x280 {
+ 31:0 revision;
+ }
+
+ /// Coherency features present. Read only constant.
+ /// Supported protocols on the interconnect between the GPU and the
+ /// system into which it is integrated.
+ pub(crate) COHERENCY_FEATURES(u32) @ 0x300 {
+ /// ACE-Lite protocol supported, a 1-bit boolean flag.
+ 0:0 ace_lite => bool;
+ /// ACE protocol supported, a 1-bit boolean flag.
+ 1:1 ace => bool;
+ }
+ }
+
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum CoherencyMode {
+ /// ACE-Lite coherency protocol.
+ AceLite = uapi::drm_panthor_gpu_coherency_DRM_PANTHOR_GPU_COHERENCY_ACE_LITE as u8,
+ /// ACE coherency protocol.
+ Ace = uapi::drm_panthor_gpu_coherency_DRM_PANTHOR_GPU_COHERENCY_ACE as u8,
+ /// No coherency protocol.
+ None = uapi::drm_panthor_gpu_coherency_DRM_PANTHOR_GPU_COHERENCY_NONE as u8,
+ }
+
+ impl TryFrom<Bounded<u32, 32>> for CoherencyMode {
+ type Error = Error;
+
+ fn try_from(val: Bounded<u32, 32>) -> Result<Self, Self::Error> {
+ match val.get() {
+ 0 => Ok(CoherencyMode::AceLite),
+ 1 => Ok(CoherencyMode::Ace),
+ 31 => Ok(CoherencyMode::None),
+ _ => Err(EINVAL),
+ }
+ }
+ }
+
+ impl From<CoherencyMode> for Bounded<u32, 32> {
+ fn from(mode: CoherencyMode) -> Self {
+ (mode as u8).into()
+ }
+ }
+
+ register! {
+ /// Coherency enable. An index of which coherency protocols should be used.
+ /// This register only selects the protocol for coherency messages on the
+ /// interconnect. This is not to enable or disable coherency controlled by MMU.
+ pub(crate) COHERENCY_ENABLE(u32) @ 0x304 {
+ 31:0 l2_cache_protocol_select ?=> CoherencyMode;
+ }
+ }
+
+ /// Helpers for MCU_CONTROL register
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum McuControlMode {
+ /// Disable the MCU.
+ Disable = 0,
+ /// Enable the MCU.
+ Enable = 1,
+ /// Enable the MCU to execute and automatically reboot after a fast reset.
+ Auto = 2,
+ }
+
+ impl TryFrom<Bounded<u32, 2>> for McuControlMode {
+ type Error = Error;
+
+ fn try_from(val: Bounded<u32, 2>) -> Result<Self, Self::Error> {
+ match val.get() {
+ 0 => Ok(McuControlMode::Disable),
+ 1 => Ok(McuControlMode::Enable),
+ 2 => Ok(McuControlMode::Auto),
+ _ => Err(EINVAL),
+ }
+ }
+ }
+
+ impl From<McuControlMode> for Bounded<u32, 2> {
+ fn from(mode: McuControlMode) -> Self {
+ Bounded::try_new(mode as u32).unwrap()
+ }
+ }
+
+ register! {
+ /// MCU control.
+ pub(crate) MCU_CONTROL(u32) @ 0x700 {
+ /// Request MCU state change.
+ 1:0 req ?=> McuControlMode;
+ }
+ }
+
+ /// Helpers for MCU_STATUS register
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum McuStatus {
+ /// MCU is disabled.
+ Disabled = 0,
+ /// MCU is enabled.
+ Enabled = 1,
+ /// The MCU has halted by itself in an orderly manner to enable the core group to be
+ /// powered down.
+ Halt = 2,
+ /// The MCU has encountered an error that prevents it from continuing.
+ Fatal = 3,
+ }
+
+ impl From<Bounded<u32, 2>> for McuStatus {
+ fn from(val: Bounded<u32, 2>) -> Self {
+ match val.get() {
+ 0 => McuStatus::Disabled,
+ 1 => McuStatus::Enabled,
+ 2 => McuStatus::Halt,
+ 3 => McuStatus::Fatal,
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ impl From<McuStatus> for Bounded<u32, 2> {
+ fn from(status: McuStatus) -> Self {
+ Bounded::try_new(status as u32).unwrap()
+ }
+ }
+
+ register! {
+ /// MCU status. Read only.
+ pub(crate) MCU_STATUS(u32) @ 0x704 {
+ /// Read current state of MCU.
+ 1:0 value => McuStatus;
+ }
+ }
+}
+
+/// These registers correspond to the JOB_CONTROL register page.
+/// They are involved in communication between the firmware running on the MCU and the host.
+pub(crate) mod job_control {
+ use kernel::register;
+
+ register! {
+ /// Raw status of job interrupts.
+ ///
+ /// Write to this register to trigger these interrupts.
+ /// Writing a 1 to a bit forces that bit on.
+ pub(crate) JOB_IRQ_RAWSTAT(u32) @ 0x1000 {
+ /// CSG request. These bits indicate that CSGn requires attention from the host.
+ 30:0 csg;
+ /// GLB request. Indicates that the GLB interface requires attention from the host.
+ 31:31 glb => bool;
+ }
+
+ /// Clear job interrupts. Write only.
+ ///
+ /// Write a 1 to a bit to clear the corresponding bit in [`JOB_IRQ_RAWSTAT`].
+ pub(crate) JOB_IRQ_CLEAR(u32) @ 0x1004 {
+ /// Clear CSG request interrupts.
+ 30:0 csg;
+ /// Clear GLB request interrupt.
+ 31:31 glb => bool;
+ }
+
+ /// Mask for job interrupts.
+ ///
+ /// Set each bit to 1 to enable the corresponding interrupt source or to 0 to disable it.
+ pub(crate) JOB_IRQ_MASK(u32) @ 0x1008 {
+ /// Enable CSG request interrupts.
+ 30:0 csg;
+ /// Enable GLB request interrupt.
+ 31:31 glb => bool;
+ }
+
+ /// Active job interrupts. Read only.
+ ///
+ /// This register contains the result of ANDing together [`JOB_IRQ_RAWSTAT`] and
+ /// [`JOB_IRQ_MASK`].
+ pub(crate) JOB_IRQ_STATUS(u32) @ 0x100c {
+ /// CSG request interrupt status.
+ 30:0 csg;
+ /// GLB request interrupt status.
+ 31:31 glb => bool;
+ }
+ }
+}
+
+/// These registers correspond to the MMU_CONTROL register page.
+/// They are involved in MMU configuration and control.
+pub(crate) mod mmu_control {
+ use kernel::register;
+
+ register! {
+ /// IRQ sources raw status.
+ ///
+ /// This register contains the raw unmasked interrupt sources for MMU status and exception
+ /// handling.
+ ///
+ /// Writing to this register forces bits on.
+ /// Use [`IRQ_CLEAR`] to clear interrupts.
+ pub(crate) IRQ_RAWSTAT(u32) @ 0x2000 {
+ /// Page fault for address spaces.
+ 15:0 page_fault;
+ /// Command completed in address spaces.
+ 31:16 command_completed;
+ }
+
+ /// IRQ sources to clear.
+ /// Write a 1 to a bit to clear the corresponding bit in [`IRQ_RAWSTAT`].
+ pub(crate) IRQ_CLEAR(u32) @ 0x2004 {
+ /// Clear the PAGE_FAULT interrupt.
+ 15:0 page_fault;
+ /// Clear the COMMAND_COMPLETED interrupt.
+ 31:16 command_completed;
+ }
+
+ /// IRQ sources enabled.
+ ///
+ /// Set each bit to 1 to enable the corresponding interrupt source, and to 0 to disable it.
+ pub(crate) IRQ_MASK(u32) @ 0x2008 {
+ /// Enable the PAGE_FAULT interrupt.
+ 15:0 page_fault;
+ /// Enable the COMMAND_COMPLETED interrupt.
+ 31:16 command_completed;
+ }
+
+ /// IRQ status for enabled sources. Read only.
+ ///
+ /// This register contains the result of ANDing together [`IRQ_RAWSTAT`] and [`IRQ_MASK`].
+ pub(crate) IRQ_STATUS(u32) @ 0x200c {
+ /// PAGE_FAULT interrupt status.
+ 15:0 page_fault;
+ /// COMMAND_COMPLETED interrupt status.
+ 31:16 command_completed;
+ }
+ }
+
+ /// Per-address space registers ASn [0..15] within the MMU_CONTROL page.
+ ///
+ /// This array contains 16 instances of the MMU_AS_CONTROL register page.
+ pub(crate) mod mmu_as_control {
+ use core::convert::TryFrom;
+
+ use kernel::{
+ error::{
+ code::EINVAL,
+ Error, //
+ },
+ num::Bounded,
+ register, //
+ };
+
+ /// Maximum number of hardware address space slots.
+ /// The actual number of slots available is usually lower.
+ pub(crate) const MAX_AS: usize = 16;
+
+ /// Address space register stride. The elements in the array are spaced 64B apart.
+ const STRIDE: usize = 0x40;
+
+ register! {
+ /// Translation table base address. A 64-bit pointer.
+ ///
+ /// This field contains the address of the top level of a translation table structure.
+ /// This must be 16-byte-aligned, so address bits [3:0] are assumed to be zero.
+ pub(crate) TRANSTAB(u64)[MAX_AS, stride = STRIDE] @ 0x2400 {
+ /// Base address of the translation table.
+ 63:0 base;
+ }
+
+ // TRANSTAB is a logical 64-bit register, but it is laid out in hardware as two
+ // 32-bit halves. Define it as separate low/high u32 registers so accesses match
+ // the MMIO register layout and do not rely on native 64-bit MMIO transactions.
+ pub(crate) TRANSTAB_LO(u32)[MAX_AS, stride = STRIDE] @ 0x2400 {
+ 31:0 value;
+ }
+
+ pub(crate) TRANSTAB_HI(u32)[MAX_AS, stride = STRIDE] @ 0x2404 {
+ 31:0 value;
+ }
+ }
+
+ /// Helpers for MEMATTR Register.
+
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum AllocPolicySelect {
+ /// Ignore ALLOC_R/ALLOC_W fields.
+ Impl = 2,
+ /// Use ALLOC_R/ALLOC_W fields for allocation policy.
+ Alloc = 3,
+ }
+
+ impl TryFrom<Bounded<u8, 2>> for AllocPolicySelect {
+ type Error = Error;
+
+ fn try_from(val: Bounded<u8, 2>) -> Result<Self, Self::Error> {
+ match val.get() {
+ 2 => Ok(Self::Impl),
+ 3 => Ok(Self::Alloc),
+ _ => Err(EINVAL),
+ }
+ }
+ }
+
+ impl From<AllocPolicySelect> for Bounded<u8, 2> {
+ fn from(val: AllocPolicySelect) -> Self {
+ Bounded::try_new(val as u8).unwrap()
+ }
+ }
+
+ /// Coherency policy for memory attributes. Indicates the shareability of cached accesses.
+ ///
+ /// The hardware spec defines different interpretations of these values depending on
+ /// whether TRANSCFG.MODE is set to IDENTITY or not. IDENTITY mode does not use translation
+ /// tables (all input addresses map to the same output address); it is deprecated and not
+ /// used by the driver. This enum assumes that TRANSCFG.MODE is not set to IDENTITY.
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum Coherency {
+ /// Midgard inner domain coherency.
+ ///
+ /// Most flexible mode - can map non-coherent, internally coherent, and system/IO
+ /// coherent memory. Used for non-cacheable memory in MAIR conversion.
+ MidgardInnerDomain = 0,
+ /// CPU inner domain coherency.
+ ///
+ /// Can map non-coherent and system/IO coherent memory. Used for write-back
+ /// cacheable memory in MAIR conversion to maintain CPU-GPU cache coherency.
+ CpuInnerDomain = 1,
+ /// CPU inner domain with shader coherency.
+ ///
+ /// Can map internally coherent and system/IO coherent memory. Used for
+ /// GPU-internal shared buffers requiring shader coherency.
+ CpuInnerDomainShaderCoh = 2,
+ }
+
+ impl TryFrom<Bounded<u8, 2>> for Coherency {
+ type Error = Error;
+
+ fn try_from(val: Bounded<u8, 2>) -> Result<Self, Self::Error> {
+ match val.get() {
+ 0 => Ok(Self::MidgardInnerDomain),
+ 1 => Ok(Self::CpuInnerDomain),
+ 2 => Ok(Self::CpuInnerDomainShaderCoh),
+ _ => Err(EINVAL),
+ }
+ }
+ }
+
+ impl From<Coherency> for Bounded<u8, 2> {
+ fn from(val: Coherency) -> Self {
+ Bounded::try_new(val as u8).unwrap()
+ }
+ }
+
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum MemoryType {
+ /// Normal memory (shared).
+ Shared = 0,
+ /// Normal memory, inner/outer non-cacheable.
+ NonCacheable = 1,
+ /// Normal memory, inner/outer write-back cacheable.
+ WriteBack = 2,
+ /// Triggers MEMORY_ATTRIBUTE_FAULT.
+ Fault = 3,
+ }
+
+ impl From<Bounded<u8, 2>> for MemoryType {
+ fn from(val: Bounded<u8, 2>) -> Self {
+ match val.get() {
+ 0 => Self::Shared,
+ 1 => Self::NonCacheable,
+ 2 => Self::WriteBack,
+ 3 => Self::Fault,
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ impl From<MemoryType> for Bounded<u8, 2> {
+ fn from(val: MemoryType) -> Self {
+ Bounded::try_new(val as u8).unwrap()
+ }
+ }
+
+ register! {
+ /// Stage 1 memory attributes (8-bit bitfield).
+ ///
+ /// This is not an actual register, but a bitfield definition used by the MEMATTR
+ /// register. Each of the 8 bytes in MEMATTR follows this layout.
+ MMU_MEMATTR_STAGE1(u8) @ 0x0 {
+ /// Inner cache write allocation policy.
+ 0:0 alloc_w => bool;
+ /// Inner cache read allocation policy.
+ 1:1 alloc_r => bool;
+ /// Inner allocation policy select.
+ 3:2 alloc_sel ?=> AllocPolicySelect;
+ /// Coherency policy.
+ 5:4 coherency ?=> Coherency;
+ /// Memory type.
+ 7:6 memory_type => MemoryType;
+ }
+ }
+
+ impl TryFrom<Bounded<u64, 8>> for MMU_MEMATTR_STAGE1 {
+ type Error = Error;
+
+ fn try_from(val: Bounded<u64, 8>) -> Result<Self, Self::Error> {
+ Ok(Self::from_raw(val.get() as u8))
+ }
+ }
+
+ impl From<MMU_MEMATTR_STAGE1> for Bounded<u64, 8> {
+ fn from(val: MMU_MEMATTR_STAGE1) -> Self {
+ Bounded::try_new(u64::from(val.into_raw())).unwrap()
+ }
+ }
+
+ register! {
+ /// Memory attributes.
+ ///
+ /// Each address space can configure up to 8 different memory attribute profiles.
+ /// Each attribute profile follows the MMU_MEMATTR_STAGE1 layout.
+ pub(crate) MEMATTR(u64)[MAX_AS, stride = STRIDE] @ 0x2408 {
+ 7:0 attribute0 ?=> MMU_MEMATTR_STAGE1;
+ 15:8 attribute1 ?=> MMU_MEMATTR_STAGE1;
+ 23:16 attribute2 ?=> MMU_MEMATTR_STAGE1;
+ 31:24 attribute3 ?=> MMU_MEMATTR_STAGE1;
+ 39:32 attribute4 ?=> MMU_MEMATTR_STAGE1;
+ 47:40 attribute5 ?=> MMU_MEMATTR_STAGE1;
+ 55:48 attribute6 ?=> MMU_MEMATTR_STAGE1;
+ 63:56 attribute7 ?=> MMU_MEMATTR_STAGE1;
+ }
+
+ // MEMATTR is a logical 64-bit register, but it is laid out in hardware as two
+ // 32-bit halves. Define it as separate low/high u32 registers so accesses match
+ // the MMIO register layout and do not rely on native 64-bit MMIO transactions.
+ pub(crate) MEMATTR_LO(u32)[MAX_AS, stride = STRIDE] @ 0x2408 {
+ 31:0 value;
+ }
+
+ pub(crate) MEMATTR_HI(u32)[MAX_AS, stride = STRIDE] @ 0x240c {
+ 31:0 value;
+ }
+
+ /// Lock region address for each address space.
+ pub(crate) LOCKADDR(u64)[MAX_AS, stride = STRIDE] @ 0x2410 {
+ /// Lock region size.
+ 5:0 size;
+ /// Lock region base address.
+ 63:12 base;
+ }
+
+ // LOCKADDR is a logical 64-bit register, but it is laid out in hardware as two
+ // 32-bit halves. Define it as separate low/high u32 registers so accesses match
+ // the MMIO register layout and do not rely on native 64-bit MMIO transactions.
+ pub(crate) LOCKADDR_LO(u32)[MAX_AS, stride = STRIDE] @ 0x2410 {
+ 31:0 value;
+ }
+
+ pub(crate) LOCKADDR_HI(u32)[MAX_AS, stride = STRIDE] @ 0x2414 {
+ 31:0 value;
+ }
+ }
+
+ /// Helpers for MMU COMMAND register.
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum MmuCommand {
+ /// No operation, nothing happens.
+ Nop = 0,
+ /// Propagate settings to the MMU.
+ Update = 1,
+ /// Lock an address region.
+ Lock = 2,
+ /// Unlock an address region.
+ Unlock = 3,
+ /// Clean and invalidate the L2 cache, then unlock.
+ FlushPt = 4,
+ /// Clean and invalidate all caches, then unlock.
+ FlushMem = 5,
+ }
+
+ impl TryFrom<Bounded<u32, 8>> for MmuCommand {
+ type Error = Error;
+
+ fn try_from(val: Bounded<u32, 8>) -> Result<Self, Self::Error> {
+ match val.get() {
+ 0 => Ok(MmuCommand::Nop),
+ 1 => Ok(MmuCommand::Update),
+ 2 => Ok(MmuCommand::Lock),
+ 3 => Ok(MmuCommand::Unlock),
+ 4 => Ok(MmuCommand::FlushPt),
+ 5 => Ok(MmuCommand::FlushMem),
+ _ => Err(EINVAL),
+ }
+ }
+ }
+
+ impl From<MmuCommand> for Bounded<u32, 8> {
+ fn from(cmd: MmuCommand) -> Self {
+ (cmd as u8).into()
+ }
+ }
+
+ register! {
+ /// MMU command register for each address space. Write only.
+ pub(crate) COMMAND(u32)[MAX_AS, stride = STRIDE] @ 0x2418 {
+ 7:0 command ?=> MmuCommand;
+ }
+ }
+
+ /// MMU exception types for FAULTSTATUS register.
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum MmuExceptionType {
+ /// No error.
+ Ok = 0x00,
+ /// Invalid translation table entry, level 0.
+ TranslationFault0 = 0xC0,
+ /// Invalid translation table entry, level 1.
+ TranslationFault1 = 0xC1,
+ /// Invalid translation table entry, level 2.
+ TranslationFault2 = 0xC2,
+ /// Invalid translation table entry, level 3.
+ TranslationFault3 = 0xC3,
+ /// Invalid block descriptor.
+ TranslationFault4 = 0xC4,
+ /// Page permission error, level 0.
+ PermissionFault0 = 0xC8,
+ /// Page permission error, level 1.
+ PermissionFault1 = 0xC9,
+ /// Page permission error, level 2.
+ PermissionFault2 = 0xCA,
+ /// Page permission error, level 3.
+ PermissionFault3 = 0xCB,
+ /// Access flag not set, level 1.
+ AccessFlag1 = 0xD9,
+ /// Access flag not set, level 2.
+ AccessFlag2 = 0xDA,
+ /// Access flag not set, level 3.
+ AccessFlag3 = 0xDB,
+ /// Virtual address out of range.
+ AddressSizeFaultIn = 0xE0,
+ /// Physical address out of range, level 0.
+ AddressSizeFaultOut0 = 0xE4,
+ /// Physical address out of range, level 1.
+ AddressSizeFaultOut1 = 0xE5,
+ /// Physical address out of range, level 2.
+ AddressSizeFaultOut2 = 0xE6,
+ /// Physical address out of range, level 3.
+ AddressSizeFaultOut3 = 0xE7,
+ /// Page attribute error, level 0.
+ MemoryAttributeFault0 = 0xE8,
+ /// Page attribute error, level 1.
+ MemoryAttributeFault1 = 0xE9,
+ /// Page attribute error, level 2.
+ MemoryAttributeFault2 = 0xEA,
+ /// Page attribute error, level 3.
+ MemoryAttributeFault3 = 0xEB,
+ }
+
+ impl TryFrom<Bounded<u32, 8>> for MmuExceptionType {
+ type Error = Error;
+
+ fn try_from(val: Bounded<u32, 8>) -> Result<Self, Self::Error> {
+ match val.get() {
+ 0x00 => Ok(MmuExceptionType::Ok),
+ 0xC0 => Ok(MmuExceptionType::TranslationFault0),
+ 0xC1 => Ok(MmuExceptionType::TranslationFault1),
+ 0xC2 => Ok(MmuExceptionType::TranslationFault2),
+ 0xC3 => Ok(MmuExceptionType::TranslationFault3),
+ 0xC4 => Ok(MmuExceptionType::TranslationFault4),
+ 0xC8 => Ok(MmuExceptionType::PermissionFault0),
+ 0xC9 => Ok(MmuExceptionType::PermissionFault1),
+ 0xCA => Ok(MmuExceptionType::PermissionFault2),
+ 0xCB => Ok(MmuExceptionType::PermissionFault3),
+ 0xD9 => Ok(MmuExceptionType::AccessFlag1),
+ 0xDA => Ok(MmuExceptionType::AccessFlag2),
+ 0xDB => Ok(MmuExceptionType::AccessFlag3),
+ 0xE0 => Ok(MmuExceptionType::AddressSizeFaultIn),
+ 0xE4 => Ok(MmuExceptionType::AddressSizeFaultOut0),
+ 0xE5 => Ok(MmuExceptionType::AddressSizeFaultOut1),
+ 0xE6 => Ok(MmuExceptionType::AddressSizeFaultOut2),
+ 0xE7 => Ok(MmuExceptionType::AddressSizeFaultOut3),
+ 0xE8 => Ok(MmuExceptionType::MemoryAttributeFault0),
+ 0xE9 => Ok(MmuExceptionType::MemoryAttributeFault1),
+ 0xEA => Ok(MmuExceptionType::MemoryAttributeFault2),
+ 0xEB => Ok(MmuExceptionType::MemoryAttributeFault3),
+ _ => Err(EINVAL),
+ }
+ }
+ }
+
+ impl From<MmuExceptionType> for Bounded<u32, 8> {
+ fn from(exc: MmuExceptionType) -> Self {
+ (exc as u8).into()
+ }
+ }
+
+ /// Access type for MMU faults.
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum MmuAccessType {
+ /// An atomic (read/write) transaction.
+ Atomic = 0,
+ /// An execute transaction.
+ Execute = 1,
+ /// A read transaction.
+ Read = 2,
+ /// A write transaction.
+ Write = 3,
+ }
+
+ impl From<Bounded<u32, 2>> for MmuAccessType {
+ fn from(val: Bounded<u32, 2>) -> Self {
+ match val.get() {
+ 0 => MmuAccessType::Atomic,
+ 1 => MmuAccessType::Execute,
+ 2 => MmuAccessType::Read,
+ 3 => MmuAccessType::Write,
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ impl From<MmuAccessType> for Bounded<u32, 2> {
+ fn from(access: MmuAccessType) -> Self {
+ Bounded::try_new(access as u32).unwrap()
+ }
+ }
+
+ register! {
+ /// Fault status register for each address space. Read only.
+ pub(crate) FAULTSTATUS(u32)[MAX_AS, stride = STRIDE] @ 0x241c {
+ /// Exception type.
+ 7:0 exception_type ?=> MmuExceptionType;
+ /// Access type.
+ 9:8 access_type => MmuAccessType;
+ /// ID of the source that triggered the fault.
+ 31:16 source_id;
+ }
+
+ /// Fault address for each address space. Read only.
+ pub(crate) FAULTADDRESS_LO(u32)[MAX_AS, stride = STRIDE] @ 0x2420 {
+ 31:0 pointer;
+ }
+
+ pub(crate) FAULTADDRESS_HI(u32)[MAX_AS, stride = STRIDE] @ 0x2424 {
+ 31:0 pointer;
+ }
+
+ /// MMU status register for each address space. Read only.
+ pub(crate) STATUS(u32)[MAX_AS, stride = STRIDE] @ 0x2428 {
+ /// External address space command is active, a 1-bit boolean flag.
+ 0:0 active_ext => bool;
+ /// Internal address space command is active, a 1-bit boolean flag.
+ 1:1 active_int => bool;
+ }
+ }
+
+ /// Helpers for TRANSCFG register.
+ ///
+ /// Address space mode for TRANSCFG register.
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum AddressSpaceMode {
+ /// The MMU forces all memory access to fail with a decode fault.
+ Unmapped = 1,
+ /// All input addresses map to the same output address (deprecated).
+ Identity = 2,
+ /// Translation tables interpreted according to AArch64 4kB granule specification.
+ Aarch64_4K = 6,
+ /// Translation tables interpreted according to AArch64 64kB granule specification.
+ Aarch64_64K = 8,
+ }
+
+ impl TryFrom<Bounded<u64, 4>> for AddressSpaceMode {
+ type Error = Error;
+
+ fn try_from(val: Bounded<u64, 4>) -> Result<Self, Self::Error> {
+ match val.get() {
+ 1 => Ok(AddressSpaceMode::Unmapped),
+ 2 => Ok(AddressSpaceMode::Identity),
+ 6 => Ok(AddressSpaceMode::Aarch64_4K),
+ 8 => Ok(AddressSpaceMode::Aarch64_64K),
+ _ => Err(EINVAL),
+ }
+ }
+ }
+
+ impl From<AddressSpaceMode> for Bounded<u64, 4> {
+ fn from(mode: AddressSpaceMode) -> Self {
+ Bounded::try_new(mode as u64).unwrap()
+ }
+ }
+
+ /// Input address range restriction for TRANSCFG register.
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum InaBits {
+ /// Invalid VA range (reset value).
+ Reset = 0,
+ /// 48-bit VA range.
+ Bits48 = 7,
+ /// 47-bit VA range.
+ Bits47 = 8,
+ /// 46-bit VA range.
+ Bits46 = 9,
+ /// 45-bit VA range.
+ Bits45 = 10,
+ /// 44-bit VA range.
+ Bits44 = 11,
+ /// 43-bit VA range.
+ Bits43 = 12,
+ /// 42-bit VA range.
+ Bits42 = 13,
+ /// 41-bit VA range.
+ Bits41 = 14,
+ /// 40-bit VA range.
+ Bits40 = 15,
+ /// 39-bit VA range.
+ Bits39 = 16,
+ /// 38-bit VA range.
+ Bits38 = 17,
+ /// 37-bit VA range.
+ Bits37 = 18,
+ /// 36-bit VA range.
+ Bits36 = 19,
+ /// 35-bit VA range.
+ Bits35 = 20,
+ /// 34-bit VA range.
+ Bits34 = 21,
+ /// 33-bit VA range.
+ Bits33 = 22,
+ /// 32-bit VA range.
+ Bits32 = 23,
+ /// 31-bit VA range.
+ Bits31 = 24,
+ /// 30-bit VA range.
+ Bits30 = 25,
+ /// 29-bit VA range.
+ Bits29 = 26,
+ /// 28-bit VA range.
+ Bits28 = 27,
+ /// 27-bit VA range.
+ Bits27 = 28,
+ /// 26-bit VA range.
+ Bits26 = 29,
+ /// 25-bit VA range.
+ Bits25 = 30,
+ }
+
+ impl TryFrom<Bounded<u64, 5>> for InaBits {
+ type Error = Error;
+
+ fn try_from(val: Bounded<u64, 5>) -> Result<Self, Self::Error> {
+ match val.get() {
+ 0 => Ok(InaBits::Reset),
+ 7 => Ok(InaBits::Bits48),
+ 8 => Ok(InaBits::Bits47),
+ 9 => Ok(InaBits::Bits46),
+ 10 => Ok(InaBits::Bits45),
+ 11 => Ok(InaBits::Bits44),
+ 12 => Ok(InaBits::Bits43),
+ 13 => Ok(InaBits::Bits42),
+ 14 => Ok(InaBits::Bits41),
+ 15 => Ok(InaBits::Bits40),
+ 16 => Ok(InaBits::Bits39),
+ 17 => Ok(InaBits::Bits38),
+ 18 => Ok(InaBits::Bits37),
+ 19 => Ok(InaBits::Bits36),
+ 20 => Ok(InaBits::Bits35),
+ 21 => Ok(InaBits::Bits34),
+ 22 => Ok(InaBits::Bits33),
+ 23 => Ok(InaBits::Bits32),
+ 24 => Ok(InaBits::Bits31),
+ 25 => Ok(InaBits::Bits30),
+ 26 => Ok(InaBits::Bits29),
+ 27 => Ok(InaBits::Bits28),
+ 28 => Ok(InaBits::Bits27),
+ 29 => Ok(InaBits::Bits26),
+ 30 => Ok(InaBits::Bits25),
+ _ => Err(EINVAL),
+ }
+ }
+ }
+
+ impl From<InaBits> for Bounded<u64, 5> {
+ fn from(bits: InaBits) -> Self {
+ Bounded::try_new(bits as u64).unwrap()
+ }
+ }
+
+ /// Translation table memory attributes for TRANSCFG register.
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ pub(crate) enum PtwMemattr {
+ /// Invalid (reset value, not valid for enabled address space).
+ Invalid = 0,
+ /// Normal memory, inner/outer non-cacheable.
+ NonCacheable = 1,
+ /// Normal memory, inner/outer write-back cacheable.
+ WriteBack = 2,
+ }
+
+ impl TryFrom<Bounded<u64, 2>> for PtwMemattr {
+ type Error = Error;
+
+ fn try_from(val: Bounded<u64, 2>) -> Result<Self, Self::Error> {
+ match val.get() {
+ 0 => Ok(PtwMemattr::Invalid),
+ 1 => Ok(PtwMemattr::NonCacheable),
+ 2 => Ok(PtwMemattr::WriteBack),
+ _ => Err(EINVAL),
+ }
+ }
+ }
+
+ impl From<PtwMemattr> for Bounded<u64, 2> {
+ fn from(attr: PtwMemattr) -> Self {
+ Bounded::try_new(attr as u64).unwrap()
+ }
+ }
+
+ /// Translation table memory shareability for TRANSCFG register.
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ #[repr(u8)]
+ #[allow(clippy::enum_variant_names)]
+ pub(crate) enum PtwShareability {
+ /// Non-shareable.
+ NonShareable = 0,
+ /// Outer shareable.
+ OuterShareable = 2,
+ /// Inner shareable.
+ InnerShareable = 3,
+ }
+
+ impl TryFrom<Bounded<u64, 2>> for PtwShareability {
+ type Error = Error;
+
+ fn try_from(val: Bounded<u64, 2>) -> Result<Self, Self::Error> {
+ match val.get() {
+ 0 => Ok(PtwShareability::NonShareable),
+ 2 => Ok(PtwShareability::OuterShareable),
+ 3 => Ok(PtwShareability::InnerShareable),
+ _ => Err(EINVAL),
+ }
+ }
+ }
+
+ impl From<PtwShareability> for Bounded<u64, 2> {
+ fn from(sh: PtwShareability) -> Self {
+ Bounded::try_new(sh as u64).unwrap()
+ }
+ }
+
+ register! {
+ /// Translation configuration and control.
+ pub(crate) TRANSCFG(u64)[MAX_AS, stride = STRIDE] @ 0x2430 {
+ /// Address space mode.
+ 3:0 mode ?=> AddressSpaceMode;
+ /// Address input restriction.
+ 10:6 ina_bits ?=> InaBits;
+ /// Address output restriction.
+ 18:14 outa_bits;
+ /// Translation table concatenation enable, a 1-bit boolean flag.
+ 22:22 sl_concat_en => bool;
+ /// Translation table memory attributes.
+ 25:24 ptw_memattr ?=> PtwMemattr;
+ /// Translation table memory shareability.
+ 29:28 ptw_sh ?=> PtwShareability;
+ /// Inner read allocation hint for translation table walks, a 1-bit boolean flag.
+ 30:30 r_allocate => bool;
+ /// Disable hierarchical access permissions.
+ 33:33 disable_hier_ap => bool;
+ /// Disable access fault checking.
+ 34:34 disable_af_fault => bool;
+ /// Disable execution on all writable pages.
+ 35:35 wxn => bool;
+ /// Enable execution on readable pages.
+ 36:36 xreadable => bool;
+ /// Page-based hardware attributes for translation table walks.
+ 63:60 ptw_pbha;
+ }
+
+ // TRANSCFG is a logical 64-bit register, but it is laid out in hardware as two
+ // 32-bit halves. Define it as separate low/high u32 registers so accesses match
+ // the MMIO register layout and do not rely on native 64-bit MMIO transactions.
+ pub(crate) TRANSCFG_LO(u32)[MAX_AS, stride = STRIDE] @ 0x2430 {
+ 31:0 value;
+ }
+
+ pub(crate) TRANSCFG_HI(u32)[MAX_AS, stride = STRIDE] @ 0x2434 {
+ 31:0 value;
+ }
+
+ /// Extra fault information for each address space. Read only.
+ pub(crate) FAULTEXTRA_LO(u32)[MAX_AS, stride = STRIDE] @ 0x2438 {
+ 31:0 value;
+ }
+
+ pub(crate) FAULTEXTRA_HI(u32)[MAX_AS, stride = STRIDE] @ 0x243c {
+ 31:0 value;
+ }
+ }
+ }
+}
+
+/// This module corresponds to the DOORBELL_BLOCK_n[0-63] register pages.
+pub(crate) mod doorbell_block {
+ use kernel::register;
+
+ /// Number of doorbells available.
+ pub(crate) const NUM_DOORBELLS: usize = 64;
+
+ /// Doorbell block stride (64KiB).
+ ///
+ /// Each block occupies a full page, allowing it to be mapped
+ /// separately into a virtual address space.
+ const STRIDE: usize = 0x10000;
+
+ register! {
+ /// Doorbell request register. Write-only.
+ pub(crate) DOORBELL(u32)[NUM_DOORBELLS, stride = STRIDE] @ 0x80000 {
+ /// Doorbell set. Writing 1 triggers the doorbell.
+ 0:0 ring => bool;
+ }
+ }
+}
diff --git a/drivers/gpu/drm/tyr/tyr.rs b/drivers/gpu/drm/tyr/tyr.rs
new file mode 100644
index 000000000000..95cda7b0962f
--- /dev/null
+++ b/drivers/gpu/drm/tyr/tyr.rs
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+//! Arm Mali Tyr DRM driver.
+//!
+//! The name "Tyr" is inspired by Norse mythology, reflecting Arm's tradition of
+//! naming their GPUs after Nordic mythological figures and places.
+
+use crate::driver::TyrPlatformDriver;
+
+mod driver;
+mod file;
+mod gem;
+mod gpu;
+mod regs;
+
+kernel::module_platform_driver! {
+ type: TyrPlatformDriver,
+ name: "tyr",
+ authors: ["The Tyr driver authors"],
+ description: "Arm Mali Tyr DRM driver",
+ license: "Dual MIT/GPL",
+}