mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-05-14 21:38:46 +02:00
58809f614e
Pull drm updates from Dave Airlie:
"cross-subsystem:
- i2c-hid: Make elan touch controllers power on after panel is
enabled
- dt bindings for STM32MP25 SoC
- pci vgaarb: use screen_info helpers
- rust pin-init updates
- add MEI driver for late binding firmware update/load
uapi:
- add ioctl for reassigning GEM handles
- provide boot_display attribute on boot-up devices
core:
- document DRM_MODE_PAGE_FLIP_EVENT
- add vendor specific recovery method to drm device wedged uevent
gem:
- Simplify gpuvm locking
ttm:
- add interface to populate buffers
sched:
- Fix race condition in trace code
atomic:
- Reallow no-op async page flips
display:
- dp: Fix command length
video:
- Improve pixel-format handling for struct screen_info
rust:
- drop Opaque<> from ioctl args
- Alloc:
- BorrowedPage type and AsPageIter traits
- Implement Vmalloc::to_page() and VmallocPageIter
- DMA/Scatterlist:
- Add dma::DataDirection and type alias for dma_addr_t
- Abstraction for struct scatterlist and sg_table
- DRM:
- simplify use of generics
- add DriverFile type alias
- drop Object::SIZE
- Rust:
- pin-init tree merge
- Various methods for AsBytes and FromBytes traits
gpuvm:
- Support madvice in Xe driver
gpusvm:
- fix hmm_pfn_to_map_order usage in gpusvm
bridge:
- Improve and fix ref counting on bridge management
- cdns-dsi: Various improvements to mode setting
- Support Solomon SSD2825 plus DT bindings
- Support Waveshare DSI2DPI plus DT bindings
- Support Content Protection property
- display-connector: Improve DP display detection
- Add support for Radxa Ra620 plus DT bindings
- adv7511: Provide SPD and HDMI infoframes
- it6505: Replace crypto_shash with sha()
- synopsys: Add support for DW DPTX Controller plus DT bindings
- adv7511: Write full Audio infoframe
- ite6263: Support vendor-specific infoframes
- simple: Add support for Realtek RTD2171 DP-to-HDMI plus DT bindings
panel:
- panel-edp: Support mt8189 Chromebooks; Support BOE NV140WUM-N64;
Support SHP LQ134Z1; Fixes
- panel-simple: Support Olimex LCD-OLinuXino-5CTS plus DT bindings
- Support Samsung AMS561RA01
- Support Hydis HV101HD1 plus DT bindings
- ilitek-ili9881c: Refactor mode setting; Add support for Bestar
BSD1218-A101KL68 LCD plus DT bindings
- lvds: Add support for Ampire AMP19201200B5TZQW-T03 to DT bindings
- edp: Add support for additonal mt8189 Chromebook panels
- lvds: Add DT bindings for EDT ETML0700Z8DHA
amdgpu:
- add CRIU support for gem objects
- RAS updates
- VCN SRAM load fixes
- EDID read fixes
- eDP ALPM support
- Documentation updates
- Rework PTE flag generation
- DCE6 fixes
- VCN devcoredump cleanup
- MMHUB client id fixes
- VCN 5.0.1 RAS support
- SMU 13.0.x updates
- Expanded PCIe DPC support
- Expanded VCN reset support
- VPE per queue reset support
- give kernel jobs unique id for tracing
- pre-populate exported buffers
- cyan skillfish updates
- make vbios build number available in sysfs
- userq updates
- HDCP updates
- support MMIO remap page as ttm pool
- JPEG parser updates
- DCE6 DC updates
- use devm for i2c buses
- GPUVM locking updates
- Drop non-DC DCE11 code
- improve fallback handling for pixel encoding
amdkfd:
- SVM/page migration fixes
- debugfs fixes
- add CRIO support for gem objects
- SVM updates
radeon:
- use dev_warn_once in CS parsers
xe:
- add madvise interface
- add DRM_IOCTL_XE_VM_QUERY_MEMORY_RANGE_ATTRS to query VMA count
and memory attributes
- drop L# bank mask reporting from media GT3 on Xe3+.
- add SLPC power_profile sysfs interface
- add configs attribs to add post/mid context-switch commands
- handle firmware reported hardware errors notifying userspace with
device wedged uevent
- use same dir structure across sysfs/debugfs
- cleanup and future proof vram region init
- add G-states and PCI link states to debugfs
- Add SRIOV support for CCS surfaces on Xe2+
- Enable SRIOV PF mode by default on supported platforms
- move flush to common code
- extended core workarounds for Xe2/3
- use DRM scheduler for delayed GT TLB invalidations
- configs improvements and allow VF device enablement
- prep work to expose mmio regions to userspace
- VF migration support added
- prepare GPU SVM for THP migration
- start fixing XE_PAGE_SIZE vs PAGE_SIZE
- add PSMI support for hw validation
- resize VF bars to max possible size according to number of VFs
- Ensure GT is in C0 during resume
- pre-populate exported buffers
- replace xe_hmm with gpusvm
- add more SVM GT stats to debugfs
- improve fake pci and WA kunnit handle for new platform testing
- Test GuC to GuC comms to add debugging
- use attribute groups to simplify sysfs registration
- add Late Binding firmware code to interact with MEI
i915:
- apply multiple JSL/EHL/Gen7/Gen6 workarounds properly
- protect against overflow in active_engine()
- Use try_cmpxchg64() in __active_lookup()
- include GuC registers in error state
- get rid of dev->struct_mutex
- iopoll: generalize read_poll_timout
- lots more display refactoring
- Reject HBR3 in any eDP Panel
- Prune modes for YUV420
- Display Wa fix, additions, and updates
- DP: Fix 2.7 Gbps link training on g4x
- DP: Adjust the idle pattern handling
- DP: Shuffle the link training code a bit
- Don't set/read the DSI C clock divider on GLK
- Enable_psr kernel parameter changes
- Type-C enabled/disconnected dp-alt sink
- Wildcat Lake enabling
- DP HDR updates
- DRAM detection
- wait PSR idle on dsb commit
- Remove FBC modulo 4 restriction for ADL-P+
- panic: refactor framebuffer allocation
habanalabs:
- debug/visibility improvements
- vmalloc-backed coherent mmap support
- HLDIO infrastructure
nova-core:
- various register!() macro improvements
- minor vbios/firmware fixes/refactoring
- advance firmware boot stages; process Booter and patch signatures
- process GSP and GSP bootloader
- Add r570.144 firmware bindings and update to it
- Move GSP boot code to own module
- Use new pin-init features to store driver's private data in a
single allocation
- Update ARef import from sync::aref
nova-drm:
- Update ARef import from sync::aref
tyr:
- initial driver skeleton for a rust driver for ARM Mali GPUs
- capable of powering up, query metadata and provide it to userspace.
msm:
- GPU and Core:
- in DT bindings describe clocks per GPU type
- GMU bandwidth voting for x1-85
- a623/a663 speedbins
- cleanup some remaining no-iommu leftovers after VM_BIND conversion
- fix GEM obj 32b size truncation
- add missing VM_BIND param validation
- IFPC for x1-85 and a750
- register xml and gen_header.py sync from mesa
- Display:
- add missing bindings for display on SC8180X
- added DisplayPort MST bindings
- conversion from round_rate() to determine_rate()
amdxdna:
- add IOCTL_AMDXDNA_GET_ARRAY
- support user space allocated buffers
- streamline PM interfaces
- Refactoring wrt. hardware contexts
- improve error reporting
nouveau:
- use GSP firmware by default
- improve error reporting
- Pre-populate exported buffers
ast:
- Clean up detection of DRAM config
exynos:
- add DSIM bridge driver support for Exynos7870
- Document Exynos7870 DSIM compatible in dt-binding
panthor:
- Print task/pid on errors
- Add support for Mali G710, G510, G310, Gx15, Gx20, Gx25
- Improve cache flushing
- Fail VM bind if BO has offset
renesas:
- convert to RUNTIME_PM_OPS
rcar-du:
- Make number of lanes configurable
- Use RUNTIME_PM_OPS
- Add support for DSI commands
rocket:
- Add driver for Rockchip NPU plus DT bindings
- Use kfree() and sizeof() correctly
- Test DMA status
rockchip:
- dsi2: Add support for RK3576 plus DT bindings
- Add support for RK3588 DPTX output
tidss:
- Use crtc_ fields for programming display mode
- Remove other drivers from aperture
pixpaper:
- Add support for Mayqueen Pixpaper plus DT bindings
v3d:
- Support querying nubmer of GPU resets for KHR_robustness
stm:
- Clean up logging
- ltdc: Add support support for STM32MP257F-EV1 plus DT bindings
sitronix:
- st7571-i2c: Add support for inverted displays and 2-bit grayscale
tidss:
- Convert to kernel's FIELD_ macros
vesadrm:
- Support 8-bit palette mode
imagination:
- Improve power management
- Add support for TH1520 GPU
- Support Risc-V architectures
v3d:
- Improve job management and locking
vkms:
- Support variants of ARGB8888, ARGB16161616, RGB565, RGB888 and P01x
- Spport YUV with 16-bit components"
* tag 'drm-next-2025-10-01' of https://gitlab.freedesktop.org/drm/kernel: (1455 commits)
drm/amd: Add name to modes from amdgpu_connector_add_common_modes()
drm/amd: Drop some common modes from amdgpu_connector_add_common_modes()
drm/amdgpu: update MODULE_PARM_DESC for freesync_video
drm/amd: Use dynamic array size declaration for amdgpu_connector_add_common_modes()
drm/amd/display: Share dce100_validate_global with DCE6-8
drm/amd/display: Share dce100_validate_bandwidth with DCE6-8
drm/amdgpu: Fix fence signaling race condition in userqueue
amd/amdkfd: enhance kfd process check in switch partition
amd/amdkfd: resolve a race in amdgpu_amdkfd_device_fini_sw
drm/amd/display: Reject modes with too high pixel clock on DCE6-10
drm/amd: Drop unnecessary check in amdgpu_connector_add_common_modes()
drm/amd/display: Only enable common modes for eDP and LVDS
drm/amdgpu: remove the redeclaration of variable i
drm/amdgpu/userq: assign an error code for invalid userq va
drm/amdgpu: revert "rework reserved VMID handling" v2
drm/amdgpu: remove leftover from enforcing isolation by VMID
drm/amdgpu: Add fallback to pipe reset if KCQ ring reset fails
accel/habanalabs: add Infineon version check
accel/habanalabs/gaudi2: read preboot status after recovering from dirty state
accel/habanalabs: add HL_GET_P_STATE passthrough type
...
961 lines
36 KiB
Rust
961 lines
36 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//! `register!` macro to define register layout and accessors.
|
|
//!
|
|
//! A single register typically includes several fields, which are accessed through a combination
|
|
//! of bit-shift and mask operations that introduce a class of potential mistakes, notably because
|
|
//! not all possible field values are necessarily valid.
|
|
//!
|
|
//! The `register!` macro in this module provides an intuitive and readable syntax for defining a
|
|
//! dedicated type for each register. Each such type comes with its own field accessors that can
|
|
//! return an error if a field's value is invalid.
|
|
|
|
/// Trait providing a base address to be added to the offset of a relative register to obtain
|
|
/// its actual offset.
|
|
///
|
|
/// The `T` generic argument is used to distinguish which base to use, in case a type provides
|
|
/// several bases. It is given to the `register!` macro to restrict the use of the register to
|
|
/// implementors of this particular variant.
|
|
pub(crate) trait RegisterBase<T> {
|
|
const BASE: usize;
|
|
}
|
|
|
|
/// Defines a dedicated type for a register with an absolute offset, including getter and setter
|
|
/// methods for its fields and methods to read and write it from an `Io` region.
|
|
///
|
|
/// Example:
|
|
///
|
|
/// ```no_run
|
|
/// register!(BOOT_0 @ 0x00000100, "Basic revision information about the GPU" {
|
|
/// 3:0 minor_revision as u8, "Minor revision of the chip";
|
|
/// 7:4 major_revision as u8, "Major revision of the chip";
|
|
/// 28:20 chipset as u32 ?=> Chipset, "Chipset model";
|
|
/// });
|
|
/// ```
|
|
///
|
|
/// This defines a `BOOT_0` type which can be read or written from offset `0x100` of an `Io`
|
|
/// region. It is composed of 3 fields, for instance `minor_revision` is made of the 4 least
|
|
/// significant bits of the register. Each field can be accessed and modified using accessor
|
|
/// methods:
|
|
///
|
|
/// ```no_run
|
|
/// // Read from the register's defined offset (0x100).
|
|
/// let boot0 = BOOT_0::read(&bar);
|
|
/// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_revision());
|
|
///
|
|
/// // `Chipset::try_from` is called with the value of the `chipset` field and returns an
|
|
/// // error if it is invalid.
|
|
/// let chipset = boot0.chipset()?;
|
|
///
|
|
/// // Update some fields and write the value back.
|
|
/// boot0.set_major_revision(3).set_minor_revision(10).write(&bar);
|
|
///
|
|
/// // Or, just read and update the register in a single step:
|
|
/// BOOT_0::alter(&bar, |r| r.set_major_revision(3).set_minor_revision(10));
|
|
/// ```
|
|
///
|
|
/// Fields are defined as follows:
|
|
///
|
|
/// - `as <type>` simply returns the field value casted to <type>, typically `u32`, `u16`, `u8` or
|
|
/// `bool`. Note that `bool` fields must have a range of 1 bit.
|
|
/// - `as <type> => <into_type>` calls `<into_type>`'s `From::<<type>>` implementation and returns
|
|
/// the result.
|
|
/// - `as <type> ?=> <try_into_type>` calls `<try_into_type>`'s `TryFrom::<<type>>` implementation
|
|
/// and returns the result. This is useful with fields for which not all values are valid.
|
|
///
|
|
/// The documentation strings are optional. If present, they will be added to the type's
|
|
/// definition, or the field getter and setter methods they are attached to.
|
|
///
|
|
/// It is also possible to create a alias register by using the `=> ALIAS` syntax. This is useful
|
|
/// for cases where a register's interpretation depends on the context:
|
|
///
|
|
/// ```no_run
|
|
/// register!(SCRATCH @ 0x00000200, "Scratch register" {
|
|
/// 31:0 value as u32, "Raw value";
|
|
/// });
|
|
///
|
|
/// register!(SCRATCH_BOOT_STATUS => SCRATCH, "Boot status of the firmware" {
|
|
/// 0:0 completed as bool, "Whether the firmware has completed booting";
|
|
/// });
|
|
/// ```
|
|
///
|
|
/// In this example, `SCRATCH_0_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while also
|
|
/// providing its own `completed` field.
|
|
///
|
|
/// ## Relative registers
|
|
///
|
|
/// A register can be defined as being accessible from a fixed offset of a provided base. For
|
|
/// instance, imagine the following I/O space:
|
|
///
|
|
/// ```text
|
|
/// +-----------------------------+
|
|
/// | ... |
|
|
/// | |
|
|
/// 0x100--->+------------CPU0-------------+
|
|
/// | |
|
|
/// 0x110--->+-----------------------------+
|
|
/// | CPU_CTL |
|
|
/// +-----------------------------+
|
|
/// | ... |
|
|
/// | |
|
|
/// | |
|
|
/// 0x200--->+------------CPU1-------------+
|
|
/// | |
|
|
/// 0x210--->+-----------------------------+
|
|
/// | CPU_CTL |
|
|
/// +-----------------------------+
|
|
/// | ... |
|
|
/// +-----------------------------+
|
|
/// ```
|
|
///
|
|
/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O
|
|
/// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define
|
|
/// them twice and would prefer a way to select which one to use from a single definition
|
|
///
|
|
/// This can be done using the `Base[Offset]` syntax when specifying the register's address.
|
|
///
|
|
/// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the
|
|
/// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for
|
|
/// this register needs to implement `RegisterBase<Base>`. Here is the above example translated
|
|
/// into code:
|
|
///
|
|
/// ```no_run
|
|
/// // Type used to identify the base.
|
|
/// pub(crate) struct CpuCtlBase;
|
|
///
|
|
/// // ZST describing `CPU0`.
|
|
/// struct Cpu0;
|
|
/// impl RegisterBase<CpuCtlBase> for Cpu0 {
|
|
/// const BASE: usize = 0x100;
|
|
/// }
|
|
/// // Singleton of `CPU0` used to identify it.
|
|
/// const CPU0: Cpu0 = Cpu0;
|
|
///
|
|
/// // ZST describing `CPU1`.
|
|
/// struct Cpu1;
|
|
/// impl RegisterBase<CpuCtlBase> for Cpu1 {
|
|
/// const BASE: usize = 0x200;
|
|
/// }
|
|
/// // Singleton of `CPU1` used to identify it.
|
|
/// const CPU1: Cpu1 = Cpu1;
|
|
///
|
|
/// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`.
|
|
/// register!(CPU_CTL @ CpuCtlBase[0x10], "CPU core control" {
|
|
/// 0:0 start as bool, "Start the CPU core";
|
|
/// });
|
|
///
|
|
/// // The `read`, `write` and `alter` methods of relative registers take an extra `base` argument
|
|
/// // that is used to resolve its final address by adding its `BASE` to the offset of the
|
|
/// // register.
|
|
///
|
|
/// // Start `CPU0`.
|
|
/// CPU_CTL::alter(bar, &CPU0, |r| r.set_start(true));
|
|
///
|
|
/// // Start `CPU1`.
|
|
/// CPU_CTL::alter(bar, &CPU1, |r| r.set_start(true));
|
|
///
|
|
/// // Aliases can also be defined for relative register.
|
|
/// register!(CPU_CTL_ALIAS => CpuCtlBase[CPU_CTL], "Alias to CPU core control" {
|
|
/// 1:1 alias_start as bool, "Start the aliased CPU core";
|
|
/// });
|
|
///
|
|
/// // Start the aliased `CPU0`.
|
|
/// CPU_CTL_ALIAS::alter(bar, &CPU0, |r| r.set_alias_start(true));
|
|
/// ```
|
|
///
|
|
/// ## Arrays of registers
|
|
///
|
|
/// Some I/O areas contain consecutive values that can be interpreted in the same way. These areas
|
|
/// can be defined as an array of identical registers, allowing them to be accessed by index with
|
|
/// compile-time or runtime bound checking. Simply define their address as `Address[Size]`, and add
|
|
/// an `idx` parameter to their `read`, `write` and `alter` methods:
|
|
///
|
|
/// ```no_run
|
|
/// # fn no_run() -> Result<(), Error> {
|
|
/// # fn get_scratch_idx() -> usize {
|
|
/// # 0x15
|
|
/// # }
|
|
/// // Array of 64 consecutive registers with the same layout starting at offset `0x80`.
|
|
/// register!(SCRATCH @ 0x00000080[64], "Scratch registers" {
|
|
/// 31:0 value as u32;
|
|
/// });
|
|
///
|
|
/// // Read scratch register 0, i.e. I/O address `0x80`.
|
|
/// let scratch_0 = SCRATCH::read(bar, 0).value();
|
|
/// // Read scratch register 15, i.e. I/O address `0x80 + (15 * 4)`.
|
|
/// let scratch_15 = SCRATCH::read(bar, 15).value();
|
|
///
|
|
/// // This is out of bounds and won't build.
|
|
/// // let scratch_128 = SCRATCH::read(bar, 128).value();
|
|
///
|
|
/// // Runtime-obtained array index.
|
|
/// let scratch_idx = get_scratch_idx();
|
|
/// // Access on a runtime index returns an error if it is out-of-bounds.
|
|
/// let some_scratch = SCRATCH::try_read(bar, scratch_idx)?.value();
|
|
///
|
|
/// // Alias to a particular register in an array.
|
|
/// // Here `SCRATCH[8]` is used to convey the firmware exit code.
|
|
/// register!(FIRMWARE_STATUS => SCRATCH[8], "Firmware exit status code" {
|
|
/// 7:0 status as u8;
|
|
/// });
|
|
///
|
|
/// let status = FIRMWARE_STATUS::read(bar).status();
|
|
///
|
|
/// // Non-contiguous register arrays can be defined by adding a stride parameter.
|
|
/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the
|
|
/// // registers of the two declarations below are interleaved.
|
|
/// register!(SCRATCH_INTERLEAVED_0 @ 0x000000c0[16 ; 8], "Scratch registers bank 0" {
|
|
/// 31:0 value as u32;
|
|
/// });
|
|
/// register!(SCRATCH_INTERLEAVED_1 @ 0x000000c4[16 ; 8], "Scratch registers bank 1" {
|
|
/// 31:0 value as u32;
|
|
/// });
|
|
/// # Ok(())
|
|
/// # }
|
|
/// ```
|
|
///
|
|
/// ## Relative arrays of registers
|
|
///
|
|
/// Combining the two features described in the sections above, arrays of registers accessible from
|
|
/// a base can also be defined:
|
|
///
|
|
/// ```no_run
|
|
/// # fn no_run() -> Result<(), Error> {
|
|
/// # fn get_scratch_idx() -> usize {
|
|
/// # 0x15
|
|
/// # }
|
|
/// // Type used as parameter of `RegisterBase` to specify the base.
|
|
/// pub(crate) struct CpuCtlBase;
|
|
///
|
|
/// // ZST describing `CPU0`.
|
|
/// struct Cpu0;
|
|
/// impl RegisterBase<CpuCtlBase> for Cpu0 {
|
|
/// const BASE: usize = 0x100;
|
|
/// }
|
|
/// // Singleton of `CPU0` used to identify it.
|
|
/// const CPU0: Cpu0 = Cpu0;
|
|
///
|
|
/// // ZST describing `CPU1`.
|
|
/// struct Cpu1;
|
|
/// impl RegisterBase<CpuCtlBase> for Cpu1 {
|
|
/// const BASE: usize = 0x200;
|
|
/// }
|
|
/// // Singleton of `CPU1` used to identify it.
|
|
/// const CPU1: Cpu1 = Cpu1;
|
|
///
|
|
/// // 64 per-cpu scratch registers, arranged as an contiguous array.
|
|
/// register!(CPU_SCRATCH @ CpuCtlBase[0x00000080[64]], "Per-CPU scratch registers" {
|
|
/// 31:0 value as u32;
|
|
/// });
|
|
///
|
|
/// let cpu0_scratch_0 = CPU_SCRATCH::read(bar, &Cpu0, 0).value();
|
|
/// let cpu1_scratch_15 = CPU_SCRATCH::read(bar, &Cpu1, 15).value();
|
|
///
|
|
/// // This won't build.
|
|
/// // let cpu0_scratch_128 = CPU_SCRATCH::read(bar, &Cpu0, 128).value();
|
|
///
|
|
/// // Runtime-obtained array index.
|
|
/// let scratch_idx = get_scratch_idx();
|
|
/// // Access on a runtime value returns an error if it is out-of-bounds.
|
|
/// let cpu0_some_scratch = CPU_SCRATCH::try_read(bar, &Cpu0, scratch_idx)?.value();
|
|
///
|
|
/// // `SCRATCH[8]` is used to convey the firmware exit code.
|
|
/// register!(CPU_FIRMWARE_STATUS => CpuCtlBase[CPU_SCRATCH[8]],
|
|
/// "Per-CPU firmware exit status code" {
|
|
/// 7:0 status as u8;
|
|
/// });
|
|
///
|
|
/// let cpu0_status = CPU_FIRMWARE_STATUS::read(bar, &Cpu0).status();
|
|
///
|
|
/// // Non-contiguous register arrays can be defined by adding a stride parameter.
|
|
/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the
|
|
/// // registers of the two declarations below are interleaved.
|
|
/// register!(CPU_SCRATCH_INTERLEAVED_0 @ CpuCtlBase[0x00000d00[16 ; 8]],
|
|
/// "Scratch registers bank 0" {
|
|
/// 31:0 value as u32;
|
|
/// });
|
|
/// register!(CPU_SCRATCH_INTERLEAVED_1 @ CpuCtlBase[0x00000d04[16 ; 8]],
|
|
/// "Scratch registers bank 1" {
|
|
/// 31:0 value as u32;
|
|
/// });
|
|
/// # Ok(())
|
|
/// # }
|
|
/// ```
|
|
macro_rules! register {
|
|
// Creates a register at a fixed offset of the MMIO space.
|
|
($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => {
|
|
register!(@core $name $(, $comment)? { $($fields)* } );
|
|
register!(@io_fixed $name @ $offset);
|
|
};
|
|
|
|
// Creates an alias register of fixed offset register `alias` with its own fields.
|
|
($name:ident => $alias:ident $(, $comment:literal)? { $($fields:tt)* } ) => {
|
|
register!(@core $name $(, $comment)? { $($fields)* } );
|
|
register!(@io_fixed $name @ $alias::OFFSET);
|
|
};
|
|
|
|
// Creates a register at a relative offset from a base address provider.
|
|
($name:ident @ $base:ty [ $offset:literal ] $(, $comment:literal)? { $($fields:tt)* } ) => {
|
|
register!(@core $name $(, $comment)? { $($fields)* } );
|
|
register!(@io_relative $name @ $base [ $offset ]);
|
|
};
|
|
|
|
// Creates an alias register of relative offset register `alias` with its own fields.
|
|
($name:ident => $base:ty [ $alias:ident ] $(, $comment:literal)? { $($fields:tt)* }) => {
|
|
register!(@core $name $(, $comment)? { $($fields)* } );
|
|
register!(@io_relative $name @ $base [ $alias::OFFSET ]);
|
|
};
|
|
|
|
// Creates an array of registers at a fixed offset of the MMIO space.
|
|
(
|
|
$name:ident @ $offset:literal [ $size:expr ; $stride:expr ] $(, $comment:literal)? {
|
|
$($fields:tt)*
|
|
}
|
|
) => {
|
|
static_assert!(::core::mem::size_of::<u32>() <= $stride);
|
|
register!(@core $name $(, $comment)? { $($fields)* } );
|
|
register!(@io_array $name @ $offset [ $size ; $stride ]);
|
|
};
|
|
|
|
// Shortcut for contiguous array of registers (stride == size of element).
|
|
(
|
|
$name:ident @ $offset:literal [ $size:expr ] $(, $comment:literal)? {
|
|
$($fields:tt)*
|
|
}
|
|
) => {
|
|
register!($name @ $offset [ $size ; ::core::mem::size_of::<u32>() ] $(, $comment)? {
|
|
$($fields)*
|
|
} );
|
|
};
|
|
|
|
// Creates an array of registers at a relative offset from a base address provider.
|
|
(
|
|
$name:ident @ $base:ty [ $offset:literal [ $size:expr ; $stride:expr ] ]
|
|
$(, $comment:literal)? { $($fields:tt)* }
|
|
) => {
|
|
static_assert!(::core::mem::size_of::<u32>() <= $stride);
|
|
register!(@core $name $(, $comment)? { $($fields)* } );
|
|
register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]);
|
|
};
|
|
|
|
// Shortcut for contiguous array of relative registers (stride == size of element).
|
|
(
|
|
$name:ident @ $base:ty [ $offset:literal [ $size:expr ] ] $(, $comment:literal)? {
|
|
$($fields:tt)*
|
|
}
|
|
) => {
|
|
register!($name @ $base [ $offset [ $size ; ::core::mem::size_of::<u32>() ] ]
|
|
$(, $comment)? { $($fields)* } );
|
|
};
|
|
|
|
// Creates an alias of register `idx` of relative array of registers `alias` with its own
|
|
// fields.
|
|
(
|
|
$name:ident => $base:ty [ $alias:ident [ $idx:expr ] ] $(, $comment:literal)? {
|
|
$($fields:tt)*
|
|
}
|
|
) => {
|
|
static_assert!($idx < $alias::SIZE);
|
|
register!(@core $name $(, $comment)? { $($fields)* } );
|
|
register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] );
|
|
};
|
|
|
|
// Creates an alias of register `idx` of array of registers `alias` with its own fields.
|
|
// This rule belongs to the (non-relative) register arrays set, but needs to be put last
|
|
// to avoid it being interpreted in place of the relative register array alias rule.
|
|
($name:ident => $alias:ident [ $idx:expr ] $(, $comment:literal)? { $($fields:tt)* }) => {
|
|
static_assert!($idx < $alias::SIZE);
|
|
register!(@core $name $(, $comment)? { $($fields)* } );
|
|
register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );
|
|
};
|
|
|
|
// All rules below are helpers.
|
|
|
|
// Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`,
|
|
// `Default`, `BitOr`, and conversion to the value type) and field accessor methods.
|
|
(@core $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
|
|
$(
|
|
#[doc=$comment]
|
|
)?
|
|
#[repr(transparent)]
|
|
#[derive(Clone, Copy)]
|
|
pub(crate) struct $name(u32);
|
|
|
|
impl ::core::ops::BitOr for $name {
|
|
type Output = Self;
|
|
|
|
fn bitor(self, rhs: Self) -> Self::Output {
|
|
Self(self.0 | rhs.0)
|
|
}
|
|
}
|
|
|
|
impl ::core::convert::From<$name> for u32 {
|
|
fn from(reg: $name) -> u32 {
|
|
reg.0
|
|
}
|
|
}
|
|
|
|
register!(@fields_dispatcher $name { $($fields)* });
|
|
};
|
|
|
|
// Captures the fields and passes them to all the implementers that require field information.
|
|
//
|
|
// Used to simplify the matching rules for implementers, so they don't need to match the entire
|
|
// complex fields rule even though they only make use of part of it.
|
|
(@fields_dispatcher $name:ident {
|
|
$($hi:tt:$lo:tt $field:ident as $type:tt
|
|
$(?=> $try_into_type:ty)?
|
|
$(=> $into_type:ty)?
|
|
$(, $comment:literal)?
|
|
;
|
|
)*
|
|
}
|
|
) => {
|
|
register!(@field_accessors $name {
|
|
$(
|
|
$hi:$lo $field as $type
|
|
$(?=> $try_into_type)?
|
|
$(=> $into_type)?
|
|
$(, $comment)?
|
|
;
|
|
)*
|
|
});
|
|
register!(@debug $name { $($field;)* });
|
|
register!(@default $name { $($field;)* });
|
|
};
|
|
|
|
// Defines all the field getter/methods methods for `$name`.
|
|
(
|
|
@field_accessors $name:ident {
|
|
$($hi:tt:$lo:tt $field:ident as $type:tt
|
|
$(?=> $try_into_type:ty)?
|
|
$(=> $into_type:ty)?
|
|
$(, $comment:literal)?
|
|
;
|
|
)*
|
|
}
|
|
) => {
|
|
$(
|
|
register!(@check_field_bounds $hi:$lo $field as $type);
|
|
)*
|
|
|
|
#[allow(dead_code)]
|
|
impl $name {
|
|
$(
|
|
register!(@field_accessor $name $hi:$lo $field as $type
|
|
$(?=> $try_into_type)?
|
|
$(=> $into_type)?
|
|
$(, $comment)?
|
|
;
|
|
);
|
|
)*
|
|
}
|
|
};
|
|
|
|
// Boolean fields must have `$hi == $lo`.
|
|
(@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) => {
|
|
#[allow(clippy::eq_op)]
|
|
const _: () = {
|
|
::kernel::build_assert!(
|
|
$hi == $lo,
|
|
concat!("boolean field `", stringify!($field), "` covers more than one bit")
|
|
);
|
|
};
|
|
};
|
|
|
|
// Non-boolean fields must have `$hi >= $lo`.
|
|
(@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) => {
|
|
#[allow(clippy::eq_op)]
|
|
const _: () = {
|
|
::kernel::build_assert!(
|
|
$hi >= $lo,
|
|
concat!("field `", stringify!($field), "`'s MSB is smaller than its LSB")
|
|
);
|
|
};
|
|
};
|
|
|
|
// Catches fields defined as `bool` and convert them into a boolean value.
|
|
(
|
|
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
|
|
$(, $comment:literal)?;
|
|
) => {
|
|
register!(
|
|
@leaf_accessor $name $hi:$lo $field
|
|
{ |f| <$into_type>::from(if f != 0 { true } else { false }) }
|
|
$into_type => $into_type $(, $comment)?;
|
|
);
|
|
};
|
|
|
|
// Shortcut for fields defined as `bool` without the `=>` syntax.
|
|
(
|
|
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
|
|
) => {
|
|
register!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);
|
|
};
|
|
|
|
// Catches the `?=>` syntax for non-boolean fields.
|
|
(
|
|
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
|
|
$(, $comment:literal)?;
|
|
) => {
|
|
register!(@leaf_accessor $name $hi:$lo $field
|
|
{ |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
|
|
::core::result::Result<
|
|
$try_into_type,
|
|
<$try_into_type as ::core::convert::TryFrom<$type>>::Error
|
|
>
|
|
$(, $comment)?;);
|
|
};
|
|
|
|
// Catches the `=>` syntax for non-boolean fields.
|
|
(
|
|
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
|
|
$(, $comment:literal)?;
|
|
) => {
|
|
register!(@leaf_accessor $name $hi:$lo $field
|
|
{ |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
|
|
};
|
|
|
|
// Shortcut for non-boolean fields defined without the `=>` or `?=>` syntax.
|
|
(
|
|
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
|
|
$(, $comment:literal)?;
|
|
) => {
|
|
register!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);
|
|
};
|
|
|
|
// Generates the accessor methods for a single field.
|
|
(
|
|
@leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident
|
|
{ $process:expr } $to_type:ty => $res_type:ty $(, $comment:literal)?;
|
|
) => {
|
|
::kernel::macros::paste!(
|
|
const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
|
|
const [<$field:upper _MASK>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
|
|
const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros();
|
|
);
|
|
|
|
$(
|
|
#[doc="Returns the value of this field:"]
|
|
#[doc=$comment]
|
|
)?
|
|
#[inline(always)]
|
|
pub(crate) fn $field(self) -> $res_type {
|
|
::kernel::macros::paste!(
|
|
const MASK: u32 = $name::[<$field:upper _MASK>];
|
|
const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
|
|
);
|
|
let field = ((self.0 & MASK) >> SHIFT);
|
|
|
|
$process(field)
|
|
}
|
|
|
|
::kernel::macros::paste!(
|
|
$(
|
|
#[doc="Sets the value of this field:"]
|
|
#[doc=$comment]
|
|
)?
|
|
#[inline(always)]
|
|
pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
|
|
const MASK: u32 = $name::[<$field:upper _MASK>];
|
|
const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
|
|
let value = (u32::from(value) << SHIFT) & MASK;
|
|
self.0 = (self.0 & !MASK) | value;
|
|
|
|
self
|
|
}
|
|
);
|
|
};
|
|
|
|
// Generates the `Debug` implementation for `$name`.
|
|
(@debug $name:ident { $($field:ident;)* }) => {
|
|
impl ::kernel::fmt::Debug for $name {
|
|
fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kernel::fmt::Result {
|
|
f.debug_struct(stringify!($name))
|
|
.field("<raw>", &::kernel::prelude::fmt!("{:#x}", &self.0))
|
|
$(
|
|
.field(stringify!($field), &self.$field())
|
|
)*
|
|
.finish()
|
|
}
|
|
}
|
|
};
|
|
|
|
// Generates the `Default` implementation for `$name`.
|
|
(@default $name:ident { $($field:ident;)* }) => {
|
|
/// Returns a value for the register where all fields are set to their default value.
|
|
impl ::core::default::Default for $name {
|
|
fn default() -> Self {
|
|
#[allow(unused_mut)]
|
|
let mut value = Self(Default::default());
|
|
|
|
::kernel::macros::paste!(
|
|
$(
|
|
value.[<set_ $field>](Default::default());
|
|
)*
|
|
);
|
|
|
|
value
|
|
}
|
|
}
|
|
};
|
|
|
|
// Generates the IO accessors for a fixed offset register.
|
|
(@io_fixed $name:ident @ $offset:expr) => {
|
|
#[allow(dead_code)]
|
|
impl $name {
|
|
pub(crate) const OFFSET: usize = $offset;
|
|
|
|
/// Read the register from its address in `io`.
|
|
#[inline(always)]
|
|
pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
{
|
|
Self(io.read32($offset))
|
|
}
|
|
|
|
/// Write the value contained in `self` to the register address in `io`.
|
|
#[inline(always)]
|
|
pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
{
|
|
io.write32(self.0, $offset)
|
|
}
|
|
|
|
/// Read the register from its address in `io` and run `f` on its value to obtain a new
|
|
/// value to write back.
|
|
#[inline(always)]
|
|
pub(crate) fn alter<const SIZE: usize, T, F>(
|
|
io: &T,
|
|
f: F,
|
|
) where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
F: ::core::ops::FnOnce(Self) -> Self,
|
|
{
|
|
let reg = f(Self::read(io));
|
|
reg.write(io);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Generates the IO accessors for a relative offset register.
|
|
(@io_relative $name:ident @ $base:ty [ $offset:expr ]) => {
|
|
#[allow(dead_code)]
|
|
impl $name {
|
|
pub(crate) const OFFSET: usize = $offset;
|
|
|
|
/// Read the register from `io`, using the base address provided by `base` and adding
|
|
/// the register's offset to it.
|
|
#[inline(always)]
|
|
pub(crate) fn read<const SIZE: usize, T, B>(
|
|
io: &T,
|
|
#[allow(unused_variables)]
|
|
base: &B,
|
|
) -> Self where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
B: crate::regs::macros::RegisterBase<$base>,
|
|
{
|
|
const OFFSET: usize = $name::OFFSET;
|
|
|
|
let value = io.read32(
|
|
<B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
|
|
);
|
|
|
|
Self(value)
|
|
}
|
|
|
|
/// Write the value contained in `self` to `io`, using the base address provided by
|
|
/// `base` and adding the register's offset to it.
|
|
#[inline(always)]
|
|
pub(crate) fn write<const SIZE: usize, T, B>(
|
|
self,
|
|
io: &T,
|
|
#[allow(unused_variables)]
|
|
base: &B,
|
|
) where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
B: crate::regs::macros::RegisterBase<$base>,
|
|
{
|
|
const OFFSET: usize = $name::OFFSET;
|
|
|
|
io.write32(
|
|
self.0,
|
|
<B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
|
|
);
|
|
}
|
|
|
|
/// Read the register from `io`, using the base address provided by `base` and adding
|
|
/// the register's offset to it, then run `f` on its value to obtain a new value to
|
|
/// write back.
|
|
#[inline(always)]
|
|
pub(crate) fn alter<const SIZE: usize, T, B, F>(
|
|
io: &T,
|
|
base: &B,
|
|
f: F,
|
|
) where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
B: crate::regs::macros::RegisterBase<$base>,
|
|
F: ::core::ops::FnOnce(Self) -> Self,
|
|
{
|
|
let reg = f(Self::read(io, base));
|
|
reg.write(io, base);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Generates the IO accessors for an array of registers.
|
|
(@io_array $name:ident @ $offset:literal [ $size:expr ; $stride:expr ]) => {
|
|
#[allow(dead_code)]
|
|
impl $name {
|
|
pub(crate) const OFFSET: usize = $offset;
|
|
pub(crate) const SIZE: usize = $size;
|
|
pub(crate) const STRIDE: usize = $stride;
|
|
|
|
/// Read the array register at index `idx` from its address in `io`.
|
|
#[inline(always)]
|
|
pub(crate) fn read<const SIZE: usize, T>(
|
|
io: &T,
|
|
idx: usize,
|
|
) -> Self where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
{
|
|
build_assert!(idx < Self::SIZE);
|
|
|
|
let offset = Self::OFFSET + (idx * Self::STRIDE);
|
|
let value = io.read32(offset);
|
|
|
|
Self(value)
|
|
}
|
|
|
|
/// Write the value contained in `self` to the array register with index `idx` in `io`.
|
|
#[inline(always)]
|
|
pub(crate) fn write<const SIZE: usize, T>(
|
|
self,
|
|
io: &T,
|
|
idx: usize
|
|
) where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
{
|
|
build_assert!(idx < Self::SIZE);
|
|
|
|
let offset = Self::OFFSET + (idx * Self::STRIDE);
|
|
|
|
io.write32(self.0, offset);
|
|
}
|
|
|
|
/// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
|
|
/// new value to write back.
|
|
#[inline(always)]
|
|
pub(crate) fn alter<const SIZE: usize, T, F>(
|
|
io: &T,
|
|
idx: usize,
|
|
f: F,
|
|
) where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
F: ::core::ops::FnOnce(Self) -> Self,
|
|
{
|
|
let reg = f(Self::read(io, idx));
|
|
reg.write(io, idx);
|
|
}
|
|
|
|
/// Read the array register at index `idx` from its address in `io`.
|
|
///
|
|
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
|
/// access was out-of-bounds.
|
|
#[inline(always)]
|
|
pub(crate) fn try_read<const SIZE: usize, T>(
|
|
io: &T,
|
|
idx: usize,
|
|
) -> ::kernel::error::Result<Self> where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
{
|
|
if idx < Self::SIZE {
|
|
Ok(Self::read(io, idx))
|
|
} else {
|
|
Err(EINVAL)
|
|
}
|
|
}
|
|
|
|
/// Write the value contained in `self` to the array register with index `idx` in `io`.
|
|
///
|
|
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
|
/// access was out-of-bounds.
|
|
#[inline(always)]
|
|
pub(crate) fn try_write<const SIZE: usize, T>(
|
|
self,
|
|
io: &T,
|
|
idx: usize,
|
|
) -> ::kernel::error::Result where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
{
|
|
if idx < Self::SIZE {
|
|
Ok(self.write(io, idx))
|
|
} else {
|
|
Err(EINVAL)
|
|
}
|
|
}
|
|
|
|
/// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
|
|
/// new value to write back.
|
|
///
|
|
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
|
/// access was out-of-bounds.
|
|
#[inline(always)]
|
|
pub(crate) fn try_alter<const SIZE: usize, T, F>(
|
|
io: &T,
|
|
idx: usize,
|
|
f: F,
|
|
) -> ::kernel::error::Result where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
F: ::core::ops::FnOnce(Self) -> Self,
|
|
{
|
|
if idx < Self::SIZE {
|
|
Ok(Self::alter(io, idx, f))
|
|
} else {
|
|
Err(EINVAL)
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Generates the IO accessors for an array of relative registers.
|
|
(
|
|
@io_relative_array $name:ident @ $base:ty
|
|
[ $offset:literal [ $size:expr ; $stride:expr ] ]
|
|
) => {
|
|
#[allow(dead_code)]
|
|
impl $name {
|
|
pub(crate) const OFFSET: usize = $offset;
|
|
pub(crate) const SIZE: usize = $size;
|
|
pub(crate) const STRIDE: usize = $stride;
|
|
|
|
/// Read the array register at index `idx` from `io`, using the base address provided
|
|
/// by `base` and adding the register's offset to it.
|
|
#[inline(always)]
|
|
pub(crate) fn read<const SIZE: usize, T, B>(
|
|
io: &T,
|
|
#[allow(unused_variables)]
|
|
base: &B,
|
|
idx: usize,
|
|
) -> Self where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
B: crate::regs::macros::RegisterBase<$base>,
|
|
{
|
|
build_assert!(idx < Self::SIZE);
|
|
|
|
let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
|
|
Self::OFFSET + (idx * Self::STRIDE);
|
|
let value = io.read32(offset);
|
|
|
|
Self(value)
|
|
}
|
|
|
|
/// Write the value contained in `self` to `io`, using the base address provided by
|
|
/// `base` and adding the offset of array register `idx` to it.
|
|
#[inline(always)]
|
|
pub(crate) fn write<const SIZE: usize, T, B>(
|
|
self,
|
|
io: &T,
|
|
#[allow(unused_variables)]
|
|
base: &B,
|
|
idx: usize
|
|
) where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
B: crate::regs::macros::RegisterBase<$base>,
|
|
{
|
|
build_assert!(idx < Self::SIZE);
|
|
|
|
let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
|
|
Self::OFFSET + (idx * Self::STRIDE);
|
|
|
|
io.write32(self.0, offset);
|
|
}
|
|
|
|
/// Read the array register at index `idx` from `io`, using the base address provided
|
|
/// by `base` and adding the register's offset to it, then run `f` on its value to
|
|
/// obtain a new value to write back.
|
|
#[inline(always)]
|
|
pub(crate) fn alter<const SIZE: usize, T, B, F>(
|
|
io: &T,
|
|
base: &B,
|
|
idx: usize,
|
|
f: F,
|
|
) where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
B: crate::regs::macros::RegisterBase<$base>,
|
|
F: ::core::ops::FnOnce(Self) -> Self,
|
|
{
|
|
let reg = f(Self::read(io, base, idx));
|
|
reg.write(io, base, idx);
|
|
}
|
|
|
|
/// Read the array register at index `idx` from `io`, using the base address provided
|
|
/// by `base` and adding the register's offset to it.
|
|
///
|
|
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
|
/// access was out-of-bounds.
|
|
#[inline(always)]
|
|
pub(crate) fn try_read<const SIZE: usize, T, B>(
|
|
io: &T,
|
|
base: &B,
|
|
idx: usize,
|
|
) -> ::kernel::error::Result<Self> where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
B: crate::regs::macros::RegisterBase<$base>,
|
|
{
|
|
if idx < Self::SIZE {
|
|
Ok(Self::read(io, base, idx))
|
|
} else {
|
|
Err(EINVAL)
|
|
}
|
|
}
|
|
|
|
/// Write the value contained in `self` to `io`, using the base address provided by
|
|
/// `base` and adding the offset of array register `idx` to it.
|
|
///
|
|
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
|
/// access was out-of-bounds.
|
|
#[inline(always)]
|
|
pub(crate) fn try_write<const SIZE: usize, T, B>(
|
|
self,
|
|
io: &T,
|
|
base: &B,
|
|
idx: usize,
|
|
) -> ::kernel::error::Result where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
B: crate::regs::macros::RegisterBase<$base>,
|
|
{
|
|
if idx < Self::SIZE {
|
|
Ok(self.write(io, base, idx))
|
|
} else {
|
|
Err(EINVAL)
|
|
}
|
|
}
|
|
|
|
/// Read the array register at index `idx` from `io`, using the base address provided
|
|
/// by `base` and adding the register's offset to it, then run `f` on its value to
|
|
/// obtain a new value to write back.
|
|
///
|
|
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
|
/// access was out-of-bounds.
|
|
#[inline(always)]
|
|
pub(crate) fn try_alter<const SIZE: usize, T, B, F>(
|
|
io: &T,
|
|
base: &B,
|
|
idx: usize,
|
|
f: F,
|
|
) -> ::kernel::error::Result where
|
|
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
|
B: crate::regs::macros::RegisterBase<$base>,
|
|
F: ::core::ops::FnOnce(Self) -> Self,
|
|
{
|
|
if idx < Self::SIZE {
|
|
Ok(Self::alter(io, base, idx, f))
|
|
} else {
|
|
Err(EINVAL)
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|