Files
linux-stable-mirror/drivers/gpu/drm/tyr/regs.rs
T
Deborah BrouwerandAlice Ryhl d9a6809478 drm/tyr: Add DOORBELL_BLOCK registers
DOORBELL_BLOCK_n[0-63] is an array of GPU control register pages.
Each block is memory-mappable and contains a single DOORBELL register
used to trigger actions in the GPU.

Add definitions for the DOORBELL_BLOCK registers using the register! macro
so they can be used by future Tyr interfaces.

Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Deborah Brouwer <deborah.brouwer@collabora.com>
Link: https://patch.msgid.link/20260409-b4-tyr-use-register-macro-v5-v5-6-8abfff8a0204@collabora.com
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
2026-04-27 18:43:05 +00:00

1657 lines
58 KiB
Rust

// 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;
}
}
}