Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/nova-core/regs/macros.rs
50141 views
1
// SPDX-License-Identifier: GPL-2.0
2
3
//! `register!` macro to define register layout and accessors.
4
//!
5
//! A single register typically includes several fields, which are accessed through a combination
6
//! of bit-shift and mask operations that introduce a class of potential mistakes, notably because
7
//! not all possible field values are necessarily valid.
8
//!
9
//! The `register!` macro in this module provides an intuitive and readable syntax for defining a
10
//! dedicated type for each register. Each such type comes with its own field accessors that can
11
//! return an error if a field's value is invalid. Please look at the [`bitfield`] macro for the
12
//! complete syntax of fields definitions.
13
14
/// Trait providing a base address to be added to the offset of a relative register to obtain
15
/// its actual offset.
16
///
17
/// The `T` generic argument is used to distinguish which base to use, in case a type provides
18
/// several bases. It is given to the `register!` macro to restrict the use of the register to
19
/// implementors of this particular variant.
20
pub(crate) trait RegisterBase<T> {
21
const BASE: usize;
22
}
23
24
/// Defines a dedicated type for a register with an absolute offset, including getter and setter
25
/// methods for its fields and methods to read and write it from an `Io` region.
26
///
27
/// Example:
28
///
29
/// ```no_run
30
/// register!(BOOT_0 @ 0x00000100, "Basic revision information about the GPU" {
31
/// 3:0 minor_revision as u8, "Minor revision of the chip";
32
/// 7:4 major_revision as u8, "Major revision of the chip";
33
/// 28:20 chipset as u32 ?=> Chipset, "Chipset model";
34
/// });
35
/// ```
36
///
37
/// This defines a `BOOT_0` type which can be read or written from offset `0x100` of an `Io`
38
/// region. It is composed of 3 fields, for instance `minor_revision` is made of the 4 least
39
/// significant bits of the register. Each field can be accessed and modified using accessor
40
/// methods:
41
///
42
/// ```no_run
43
/// // Read from the register's defined offset (0x100).
44
/// let boot0 = BOOT_0::read(&bar);
45
/// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_revision());
46
///
47
/// // `Chipset::try_from` is called with the value of the `chipset` field and returns an
48
/// // error if it is invalid.
49
/// let chipset = boot0.chipset()?;
50
///
51
/// // Update some fields and write the value back.
52
/// boot0.set_major_revision(3).set_minor_revision(10).write(&bar);
53
///
54
/// // Or, just read and update the register in a single step:
55
/// BOOT_0::update(&bar, |r| r.set_major_revision(3).set_minor_revision(10));
56
/// ```
57
///
58
/// The documentation strings are optional. If present, they will be added to the type's
59
/// definition, or the field getter and setter methods they are attached to.
60
///
61
/// It is also possible to create a alias register by using the `=> ALIAS` syntax. This is useful
62
/// for cases where a register's interpretation depends on the context:
63
///
64
/// ```no_run
65
/// register!(SCRATCH @ 0x00000200, "Scratch register" {
66
/// 31:0 value as u32, "Raw value";
67
/// });
68
///
69
/// register!(SCRATCH_BOOT_STATUS => SCRATCH, "Boot status of the firmware" {
70
/// 0:0 completed as bool, "Whether the firmware has completed booting";
71
/// });
72
/// ```
73
///
74
/// In this example, `SCRATCH_0_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while also
75
/// providing its own `completed` field.
76
///
77
/// ## Relative registers
78
///
79
/// A register can be defined as being accessible from a fixed offset of a provided base. For
80
/// instance, imagine the following I/O space:
81
///
82
/// ```text
83
/// +-----------------------------+
84
/// | ... |
85
/// | |
86
/// 0x100--->+------------CPU0-------------+
87
/// | |
88
/// 0x110--->+-----------------------------+
89
/// | CPU_CTL |
90
/// +-----------------------------+
91
/// | ... |
92
/// | |
93
/// | |
94
/// 0x200--->+------------CPU1-------------+
95
/// | |
96
/// 0x210--->+-----------------------------+
97
/// | CPU_CTL |
98
/// +-----------------------------+
99
/// | ... |
100
/// +-----------------------------+
101
/// ```
102
///
103
/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O
104
/// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define
105
/// them twice and would prefer a way to select which one to use from a single definition
106
///
107
/// This can be done using the `Base[Offset]` syntax when specifying the register's address.
108
///
109
/// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the
110
/// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for
111
/// this register needs to implement `RegisterBase<Base>`. Here is the above example translated
112
/// into code:
113
///
114
/// ```no_run
115
/// // Type used to identify the base.
116
/// pub(crate) struct CpuCtlBase;
117
///
118
/// // ZST describing `CPU0`.
119
/// struct Cpu0;
120
/// impl RegisterBase<CpuCtlBase> for Cpu0 {
121
/// const BASE: usize = 0x100;
122
/// }
123
/// // Singleton of `CPU0` used to identify it.
124
/// const CPU0: Cpu0 = Cpu0;
125
///
126
/// // ZST describing `CPU1`.
127
/// struct Cpu1;
128
/// impl RegisterBase<CpuCtlBase> for Cpu1 {
129
/// const BASE: usize = 0x200;
130
/// }
131
/// // Singleton of `CPU1` used to identify it.
132
/// const CPU1: Cpu1 = Cpu1;
133
///
134
/// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`.
135
/// register!(CPU_CTL @ CpuCtlBase[0x10], "CPU core control" {
136
/// 0:0 start as bool, "Start the CPU core";
137
/// });
138
///
139
/// // The `read`, `write` and `update` methods of relative registers take an extra `base` argument
140
/// // that is used to resolve its final address by adding its `BASE` to the offset of the
141
/// // register.
142
///
143
/// // Start `CPU0`.
144
/// CPU_CTL::update(bar, &CPU0, |r| r.set_start(true));
145
///
146
/// // Start `CPU1`.
147
/// CPU_CTL::update(bar, &CPU1, |r| r.set_start(true));
148
///
149
/// // Aliases can also be defined for relative register.
150
/// register!(CPU_CTL_ALIAS => CpuCtlBase[CPU_CTL], "Alias to CPU core control" {
151
/// 1:1 alias_start as bool, "Start the aliased CPU core";
152
/// });
153
///
154
/// // Start the aliased `CPU0`.
155
/// CPU_CTL_ALIAS::update(bar, &CPU0, |r| r.set_alias_start(true));
156
/// ```
157
///
158
/// ## Arrays of registers
159
///
160
/// Some I/O areas contain consecutive values that can be interpreted in the same way. These areas
161
/// can be defined as an array of identical registers, allowing them to be accessed by index with
162
/// compile-time or runtime bound checking. Simply define their address as `Address[Size]`, and add
163
/// an `idx` parameter to their `read`, `write` and `update` methods:
164
///
165
/// ```no_run
166
/// # fn no_run() -> Result<(), Error> {
167
/// # fn get_scratch_idx() -> usize {
168
/// # 0x15
169
/// # }
170
/// // Array of 64 consecutive registers with the same layout starting at offset `0x80`.
171
/// register!(SCRATCH @ 0x00000080[64], "Scratch registers" {
172
/// 31:0 value as u32;
173
/// });
174
///
175
/// // Read scratch register 0, i.e. I/O address `0x80`.
176
/// let scratch_0 = SCRATCH::read(bar, 0).value();
177
/// // Read scratch register 15, i.e. I/O address `0x80 + (15 * 4)`.
178
/// let scratch_15 = SCRATCH::read(bar, 15).value();
179
///
180
/// // This is out of bounds and won't build.
181
/// // let scratch_128 = SCRATCH::read(bar, 128).value();
182
///
183
/// // Runtime-obtained array index.
184
/// let scratch_idx = get_scratch_idx();
185
/// // Access on a runtime index returns an error if it is out-of-bounds.
186
/// let some_scratch = SCRATCH::try_read(bar, scratch_idx)?.value();
187
///
188
/// // Alias to a particular register in an array.
189
/// // Here `SCRATCH[8]` is used to convey the firmware exit code.
190
/// register!(FIRMWARE_STATUS => SCRATCH[8], "Firmware exit status code" {
191
/// 7:0 status as u8;
192
/// });
193
///
194
/// let status = FIRMWARE_STATUS::read(bar).status();
195
///
196
/// // Non-contiguous register arrays can be defined by adding a stride parameter.
197
/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the
198
/// // registers of the two declarations below are interleaved.
199
/// register!(SCRATCH_INTERLEAVED_0 @ 0x000000c0[16 ; 8], "Scratch registers bank 0" {
200
/// 31:0 value as u32;
201
/// });
202
/// register!(SCRATCH_INTERLEAVED_1 @ 0x000000c4[16 ; 8], "Scratch registers bank 1" {
203
/// 31:0 value as u32;
204
/// });
205
/// # Ok(())
206
/// # }
207
/// ```
208
///
209
/// ## Relative arrays of registers
210
///
211
/// Combining the two features described in the sections above, arrays of registers accessible from
212
/// a base can also be defined:
213
///
214
/// ```no_run
215
/// # fn no_run() -> Result<(), Error> {
216
/// # fn get_scratch_idx() -> usize {
217
/// # 0x15
218
/// # }
219
/// // Type used as parameter of `RegisterBase` to specify the base.
220
/// pub(crate) struct CpuCtlBase;
221
///
222
/// // ZST describing `CPU0`.
223
/// struct Cpu0;
224
/// impl RegisterBase<CpuCtlBase> for Cpu0 {
225
/// const BASE: usize = 0x100;
226
/// }
227
/// // Singleton of `CPU0` used to identify it.
228
/// const CPU0: Cpu0 = Cpu0;
229
///
230
/// // ZST describing `CPU1`.
231
/// struct Cpu1;
232
/// impl RegisterBase<CpuCtlBase> for Cpu1 {
233
/// const BASE: usize = 0x200;
234
/// }
235
/// // Singleton of `CPU1` used to identify it.
236
/// const CPU1: Cpu1 = Cpu1;
237
///
238
/// // 64 per-cpu scratch registers, arranged as an contiguous array.
239
/// register!(CPU_SCRATCH @ CpuCtlBase[0x00000080[64]], "Per-CPU scratch registers" {
240
/// 31:0 value as u32;
241
/// });
242
///
243
/// let cpu0_scratch_0 = CPU_SCRATCH::read(bar, &Cpu0, 0).value();
244
/// let cpu1_scratch_15 = CPU_SCRATCH::read(bar, &Cpu1, 15).value();
245
///
246
/// // This won't build.
247
/// // let cpu0_scratch_128 = CPU_SCRATCH::read(bar, &Cpu0, 128).value();
248
///
249
/// // Runtime-obtained array index.
250
/// let scratch_idx = get_scratch_idx();
251
/// // Access on a runtime value returns an error if it is out-of-bounds.
252
/// let cpu0_some_scratch = CPU_SCRATCH::try_read(bar, &Cpu0, scratch_idx)?.value();
253
///
254
/// // `SCRATCH[8]` is used to convey the firmware exit code.
255
/// register!(CPU_FIRMWARE_STATUS => CpuCtlBase[CPU_SCRATCH[8]],
256
/// "Per-CPU firmware exit status code" {
257
/// 7:0 status as u8;
258
/// });
259
///
260
/// let cpu0_status = CPU_FIRMWARE_STATUS::read(bar, &Cpu0).status();
261
///
262
/// // Non-contiguous register arrays can be defined by adding a stride parameter.
263
/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the
264
/// // registers of the two declarations below are interleaved.
265
/// register!(CPU_SCRATCH_INTERLEAVED_0 @ CpuCtlBase[0x00000d00[16 ; 8]],
266
/// "Scratch registers bank 0" {
267
/// 31:0 value as u32;
268
/// });
269
/// register!(CPU_SCRATCH_INTERLEAVED_1 @ CpuCtlBase[0x00000d04[16 ; 8]],
270
/// "Scratch registers bank 1" {
271
/// 31:0 value as u32;
272
/// });
273
/// # Ok(())
274
/// # }
275
/// ```
276
macro_rules! register {
277
// Creates a register at a fixed offset of the MMIO space.
278
($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => {
279
bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
280
register!(@io_fixed $name @ $offset);
281
};
282
283
// Creates an alias register of fixed offset register `alias` with its own fields.
284
($name:ident => $alias:ident $(, $comment:literal)? { $($fields:tt)* } ) => {
285
bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
286
register!(@io_fixed $name @ $alias::OFFSET);
287
};
288
289
// Creates a register at a relative offset from a base address provider.
290
($name:ident @ $base:ty [ $offset:literal ] $(, $comment:literal)? { $($fields:tt)* } ) => {
291
bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
292
register!(@io_relative $name @ $base [ $offset ]);
293
};
294
295
// Creates an alias register of relative offset register `alias` with its own fields.
296
($name:ident => $base:ty [ $alias:ident ] $(, $comment:literal)? { $($fields:tt)* }) => {
297
bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
298
register!(@io_relative $name @ $base [ $alias::OFFSET ]);
299
};
300
301
// Creates an array of registers at a fixed offset of the MMIO space.
302
(
303
$name:ident @ $offset:literal [ $size:expr ; $stride:expr ] $(, $comment:literal)? {
304
$($fields:tt)*
305
}
306
) => {
307
static_assert!(::core::mem::size_of::<u32>() <= $stride);
308
bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
309
register!(@io_array $name @ $offset [ $size ; $stride ]);
310
};
311
312
// Shortcut for contiguous array of registers (stride == size of element).
313
(
314
$name:ident @ $offset:literal [ $size:expr ] $(, $comment:literal)? {
315
$($fields:tt)*
316
}
317
) => {
318
register!($name @ $offset [ $size ; ::core::mem::size_of::<u32>() ] $(, $comment)? {
319
$($fields)*
320
} );
321
};
322
323
// Creates an array of registers at a relative offset from a base address provider.
324
(
325
$name:ident @ $base:ty [ $offset:literal [ $size:expr ; $stride:expr ] ]
326
$(, $comment:literal)? { $($fields:tt)* }
327
) => {
328
static_assert!(::core::mem::size_of::<u32>() <= $stride);
329
bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
330
register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]);
331
};
332
333
// Shortcut for contiguous array of relative registers (stride == size of element).
334
(
335
$name:ident @ $base:ty [ $offset:literal [ $size:expr ] ] $(, $comment:literal)? {
336
$($fields:tt)*
337
}
338
) => {
339
register!($name @ $base [ $offset [ $size ; ::core::mem::size_of::<u32>() ] ]
340
$(, $comment)? { $($fields)* } );
341
};
342
343
// Creates an alias of register `idx` of relative array of registers `alias` with its own
344
// fields.
345
(
346
$name:ident => $base:ty [ $alias:ident [ $idx:expr ] ] $(, $comment:literal)? {
347
$($fields:tt)*
348
}
349
) => {
350
static_assert!($idx < $alias::SIZE);
351
bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
352
register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] );
353
};
354
355
// Creates an alias of register `idx` of array of registers `alias` with its own fields.
356
// This rule belongs to the (non-relative) register arrays set, but needs to be put last
357
// to avoid it being interpreted in place of the relative register array alias rule.
358
($name:ident => $alias:ident [ $idx:expr ] $(, $comment:literal)? { $($fields:tt)* }) => {
359
static_assert!($idx < $alias::SIZE);
360
bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
361
register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );
362
};
363
364
// Generates the IO accessors for a fixed offset register.
365
(@io_fixed $name:ident @ $offset:expr) => {
366
#[allow(dead_code)]
367
impl $name {
368
pub(crate) const OFFSET: usize = $offset;
369
370
/// Read the register from its address in `io`.
371
#[inline(always)]
372
pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where
373
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
374
{
375
Self(io.read32($offset))
376
}
377
378
/// Write the value contained in `self` to the register address in `io`.
379
#[inline(always)]
380
pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where
381
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
382
{
383
io.write32(self.0, $offset)
384
}
385
386
/// Read the register from its address in `io` and run `f` on its value to obtain a new
387
/// value to write back.
388
#[inline(always)]
389
pub(crate) fn update<const SIZE: usize, T, F>(
390
io: &T,
391
f: F,
392
) where
393
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
394
F: ::core::ops::FnOnce(Self) -> Self,
395
{
396
let reg = f(Self::read(io));
397
reg.write(io);
398
}
399
}
400
};
401
402
// Generates the IO accessors for a relative offset register.
403
(@io_relative $name:ident @ $base:ty [ $offset:expr ]) => {
404
#[allow(dead_code)]
405
impl $name {
406
pub(crate) const OFFSET: usize = $offset;
407
408
/// Read the register from `io`, using the base address provided by `base` and adding
409
/// the register's offset to it.
410
#[inline(always)]
411
pub(crate) fn read<const SIZE: usize, T, B>(
412
io: &T,
413
#[allow(unused_variables)]
414
base: &B,
415
) -> Self where
416
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
417
B: crate::regs::macros::RegisterBase<$base>,
418
{
419
const OFFSET: usize = $name::OFFSET;
420
421
let value = io.read32(
422
<B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
423
);
424
425
Self(value)
426
}
427
428
/// Write the value contained in `self` to `io`, using the base address provided by
429
/// `base` and adding the register's offset to it.
430
#[inline(always)]
431
pub(crate) fn write<const SIZE: usize, T, B>(
432
self,
433
io: &T,
434
#[allow(unused_variables)]
435
base: &B,
436
) where
437
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
438
B: crate::regs::macros::RegisterBase<$base>,
439
{
440
const OFFSET: usize = $name::OFFSET;
441
442
io.write32(
443
self.0,
444
<B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
445
);
446
}
447
448
/// Read the register from `io`, using the base address provided by `base` and adding
449
/// the register's offset to it, then run `f` on its value to obtain a new value to
450
/// write back.
451
#[inline(always)]
452
pub(crate) fn update<const SIZE: usize, T, B, F>(
453
io: &T,
454
base: &B,
455
f: F,
456
) where
457
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
458
B: crate::regs::macros::RegisterBase<$base>,
459
F: ::core::ops::FnOnce(Self) -> Self,
460
{
461
let reg = f(Self::read(io, base));
462
reg.write(io, base);
463
}
464
}
465
};
466
467
// Generates the IO accessors for an array of registers.
468
(@io_array $name:ident @ $offset:literal [ $size:expr ; $stride:expr ]) => {
469
#[allow(dead_code)]
470
impl $name {
471
pub(crate) const OFFSET: usize = $offset;
472
pub(crate) const SIZE: usize = $size;
473
pub(crate) const STRIDE: usize = $stride;
474
475
/// Read the array register at index `idx` from its address in `io`.
476
#[inline(always)]
477
pub(crate) fn read<const SIZE: usize, T>(
478
io: &T,
479
idx: usize,
480
) -> Self where
481
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
482
{
483
build_assert!(idx < Self::SIZE);
484
485
let offset = Self::OFFSET + (idx * Self::STRIDE);
486
let value = io.read32(offset);
487
488
Self(value)
489
}
490
491
/// Write the value contained in `self` to the array register with index `idx` in `io`.
492
#[inline(always)]
493
pub(crate) fn write<const SIZE: usize, T>(
494
self,
495
io: &T,
496
idx: usize
497
) where
498
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
499
{
500
build_assert!(idx < Self::SIZE);
501
502
let offset = Self::OFFSET + (idx * Self::STRIDE);
503
504
io.write32(self.0, offset);
505
}
506
507
/// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
508
/// new value to write back.
509
#[inline(always)]
510
pub(crate) fn update<const SIZE: usize, T, F>(
511
io: &T,
512
idx: usize,
513
f: F,
514
) where
515
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
516
F: ::core::ops::FnOnce(Self) -> Self,
517
{
518
let reg = f(Self::read(io, idx));
519
reg.write(io, idx);
520
}
521
522
/// Read the array register at index `idx` from its address in `io`.
523
///
524
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
525
/// access was out-of-bounds.
526
#[inline(always)]
527
pub(crate) fn try_read<const SIZE: usize, T>(
528
io: &T,
529
idx: usize,
530
) -> ::kernel::error::Result<Self> where
531
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
532
{
533
if idx < Self::SIZE {
534
Ok(Self::read(io, idx))
535
} else {
536
Err(EINVAL)
537
}
538
}
539
540
/// Write the value contained in `self` to the array register with index `idx` in `io`.
541
///
542
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
543
/// access was out-of-bounds.
544
#[inline(always)]
545
pub(crate) fn try_write<const SIZE: usize, T>(
546
self,
547
io: &T,
548
idx: usize,
549
) -> ::kernel::error::Result where
550
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
551
{
552
if idx < Self::SIZE {
553
Ok(self.write(io, idx))
554
} else {
555
Err(EINVAL)
556
}
557
}
558
559
/// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
560
/// new value to write back.
561
///
562
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
563
/// access was out-of-bounds.
564
#[inline(always)]
565
pub(crate) fn try_update<const SIZE: usize, T, F>(
566
io: &T,
567
idx: usize,
568
f: F,
569
) -> ::kernel::error::Result where
570
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
571
F: ::core::ops::FnOnce(Self) -> Self,
572
{
573
if idx < Self::SIZE {
574
Ok(Self::update(io, idx, f))
575
} else {
576
Err(EINVAL)
577
}
578
}
579
}
580
};
581
582
// Generates the IO accessors for an array of relative registers.
583
(
584
@io_relative_array $name:ident @ $base:ty
585
[ $offset:literal [ $size:expr ; $stride:expr ] ]
586
) => {
587
#[allow(dead_code)]
588
impl $name {
589
pub(crate) const OFFSET: usize = $offset;
590
pub(crate) const SIZE: usize = $size;
591
pub(crate) const STRIDE: usize = $stride;
592
593
/// Read the array register at index `idx` from `io`, using the base address provided
594
/// by `base` and adding the register's offset to it.
595
#[inline(always)]
596
pub(crate) fn read<const SIZE: usize, T, B>(
597
io: &T,
598
#[allow(unused_variables)]
599
base: &B,
600
idx: usize,
601
) -> Self where
602
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
603
B: crate::regs::macros::RegisterBase<$base>,
604
{
605
build_assert!(idx < Self::SIZE);
606
607
let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
608
Self::OFFSET + (idx * Self::STRIDE);
609
let value = io.read32(offset);
610
611
Self(value)
612
}
613
614
/// Write the value contained in `self` to `io`, using the base address provided by
615
/// `base` and adding the offset of array register `idx` to it.
616
#[inline(always)]
617
pub(crate) fn write<const SIZE: usize, T, B>(
618
self,
619
io: &T,
620
#[allow(unused_variables)]
621
base: &B,
622
idx: usize
623
) where
624
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
625
B: crate::regs::macros::RegisterBase<$base>,
626
{
627
build_assert!(idx < Self::SIZE);
628
629
let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
630
Self::OFFSET + (idx * Self::STRIDE);
631
632
io.write32(self.0, offset);
633
}
634
635
/// Read the array register at index `idx` from `io`, using the base address provided
636
/// by `base` and adding the register's offset to it, then run `f` on its value to
637
/// obtain a new value to write back.
638
#[inline(always)]
639
pub(crate) fn update<const SIZE: usize, T, B, F>(
640
io: &T,
641
base: &B,
642
idx: usize,
643
f: F,
644
) where
645
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
646
B: crate::regs::macros::RegisterBase<$base>,
647
F: ::core::ops::FnOnce(Self) -> Self,
648
{
649
let reg = f(Self::read(io, base, idx));
650
reg.write(io, base, idx);
651
}
652
653
/// Read the array register at index `idx` from `io`, using the base address provided
654
/// by `base` and adding the register's offset to it.
655
///
656
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
657
/// access was out-of-bounds.
658
#[inline(always)]
659
pub(crate) fn try_read<const SIZE: usize, T, B>(
660
io: &T,
661
base: &B,
662
idx: usize,
663
) -> ::kernel::error::Result<Self> where
664
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
665
B: crate::regs::macros::RegisterBase<$base>,
666
{
667
if idx < Self::SIZE {
668
Ok(Self::read(io, base, idx))
669
} else {
670
Err(EINVAL)
671
}
672
}
673
674
/// Write the value contained in `self` to `io`, using the base address provided by
675
/// `base` and adding the offset of array register `idx` to it.
676
///
677
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
678
/// access was out-of-bounds.
679
#[inline(always)]
680
pub(crate) fn try_write<const SIZE: usize, T, B>(
681
self,
682
io: &T,
683
base: &B,
684
idx: usize,
685
) -> ::kernel::error::Result where
686
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
687
B: crate::regs::macros::RegisterBase<$base>,
688
{
689
if idx < Self::SIZE {
690
Ok(self.write(io, base, idx))
691
} else {
692
Err(EINVAL)
693
}
694
}
695
696
/// Read the array register at index `idx` from `io`, using the base address provided
697
/// by `base` and adding the register's offset to it, then run `f` on its value to
698
/// obtain a new value to write back.
699
///
700
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
701
/// access was out-of-bounds.
702
#[inline(always)]
703
pub(crate) fn try_update<const SIZE: usize, T, B, F>(
704
io: &T,
705
base: &B,
706
idx: usize,
707
f: F,
708
) -> ::kernel::error::Result where
709
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
710
B: crate::regs::macros::RegisterBase<$base>,
711
F: ::core::ops::FnOnce(Self) -> Self,
712
{
713
if idx < Self::SIZE {
714
Ok(Self::update(io, base, idx, f))
715
} else {
716
Err(EINVAL)
717
}
718
}
719
}
720
};
721
}
722
723