diff options
Diffstat (limited to 'drivers/gpu/drm/tyr')
| -rw-r--r-- | drivers/gpu/drm/tyr/Kconfig | 21 | ||||
| -rw-r--r-- | drivers/gpu/drm/tyr/Makefile | 3 | ||||
| -rw-r--r-- | drivers/gpu/drm/tyr/driver.rs | 211 | ||||
| -rw-r--r-- | drivers/gpu/drm/tyr/file.rs | 60 | ||||
| -rw-r--r-- | drivers/gpu/drm/tyr/gem.rs | 43 | ||||
| -rw-r--r-- | drivers/gpu/drm/tyr/gpu.rs | 175 | ||||
| -rw-r--r-- | drivers/gpu/drm/tyr/regs.rs | 1656 | ||||
| -rw-r--r-- | drivers/gpu/drm/tyr/tyr.rs | 22 |
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", +} |
