Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/nova-core/regs/macros.rs
26481 views
1
// SPDX-License-Identifier: GPL-2.0
2
3
//! 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 macro in this module allow to define, using an intruitive and readable syntax, a dedicated
10
//! type for each register with its own field accessors that can return an error is a field's value
11
//! is invalid.
12
13
/// Defines a dedicated type for a register with an absolute offset, alongside with getter and
14
/// setter methods for its fields and methods to read and write it from an `Io` region.
15
///
16
/// Example:
17
///
18
/// ```no_run
19
/// register!(BOOT_0 @ 0x00000100, "Basic revision information about the GPU" {
20
/// 3:0 minor_revision as u8, "Minor revision of the chip";
21
/// 7:4 major_revision as u8, "Major revision of the chip";
22
/// 28:20 chipset as u32 ?=> Chipset, "Chipset model";
23
/// });
24
/// ```
25
///
26
/// This defines a `BOOT_0` type which can be read or written from offset `0x100` of an `Io`
27
/// region. It is composed of 3 fields, for instance `minor_revision` is made of the 4 less
28
/// significant bits of the register. Each field can be accessed and modified using accessor
29
/// methods:
30
///
31
/// ```no_run
32
/// // Read from the register's defined offset (0x100).
33
/// let boot0 = BOOT_0::read(&bar);
34
/// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_revision());
35
///
36
/// // `Chipset::try_from` will be called with the value of the field and returns an error if the
37
/// // value is invalid.
38
/// let chipset = boot0.chipset()?;
39
///
40
/// // Update some fields and write the value back.
41
/// boot0.set_major_revision(3).set_minor_revision(10).write(&bar);
42
///
43
/// // Or just read and update the register in a single step:
44
/// BOOT_0::alter(&bar, |r| r.set_major_revision(3).set_minor_revision(10));
45
/// ```
46
///
47
/// Fields can be defined as follows:
48
///
49
/// - `as <type>` simply returns the field value casted as the requested integer type, typically
50
/// `u32`, `u16`, `u8` or `bool`. Note that `bool` fields must have a range of 1 bit.
51
/// - `as <type> => <into_type>` calls `<into_type>`'s `From::<<type>>` implementation and returns
52
/// the result.
53
/// - `as <type> ?=> <try_into_type>` calls `<try_into_type>`'s `TryFrom::<<type>>` implementation
54
/// and returns the result. This is useful on fields for which not all values are value.
55
///
56
/// The documentation strings are optional. If present, they will be added to the type's
57
/// definition, or the field getter and setter methods they are attached to.
58
///
59
/// Putting a `+` before the address of the register makes it relative to a base: the `read` and
60
/// `write` methods take a `base` argument that is added to the specified address before access,
61
/// and `try_read` and `try_write` methods are also created, allowing access with offsets unknown
62
/// at compile-time:
63
///
64
/// ```no_run
65
/// register!(CPU_CTL @ +0x0000010, "CPU core control" {
66
/// 0:0 start as bool, "Start the CPU core";
67
/// });
68
///
69
/// // Flip the `start` switch for the CPU core which base address is at `CPU_BASE`.
70
/// let cpuctl = CPU_CTL::read(&bar, CPU_BASE);
71
/// pr_info!("CPU CTL: {:#x}", cpuctl);
72
/// cpuctl.set_start(true).write(&bar, CPU_BASE);
73
/// ```
74
///
75
/// It is also possible to create a alias register by using the `=> ALIAS` syntax. This is useful
76
/// for cases where a register's interpretation depends on the context:
77
///
78
/// ```no_run
79
/// register!(SCRATCH_0 @ 0x0000100, "Scratch register 0" {
80
/// 31:0 value as u32, "Raw value";
81
///
82
/// register!(SCRATCH_0_BOOT_STATUS => SCRATCH_0, "Boot status of the firmware" {
83
/// 0:0 completed as bool, "Whether the firmware has completed booting";
84
/// ```
85
///
86
/// In this example, `SCRATCH_0_BOOT_STATUS` uses the same I/O address as `SCRATCH_0`, while also
87
/// providing its own `completed` method.
88
macro_rules! register {
89
// Creates a register at a fixed offset of the MMIO space.
90
(
91
$name:ident @ $offset:literal $(, $comment:literal)? {
92
$($fields:tt)*
93
}
94
) => {
95
register!(@common $name @ $offset $(, $comment)?);
96
register!(@field_accessors $name { $($fields)* });
97
register!(@io $name @ $offset);
98
};
99
100
// Creates a alias register of fixed offset register `alias` with its own fields.
101
(
102
$name:ident => $alias:ident $(, $comment:literal)? {
103
$($fields:tt)*
104
}
105
) => {
106
register!(@common $name @ $alias::OFFSET $(, $comment)?);
107
register!(@field_accessors $name { $($fields)* });
108
register!(@io $name @ $alias::OFFSET);
109
};
110
111
// Creates a register at a relative offset from a base address.
112
(
113
$name:ident @ + $offset:literal $(, $comment:literal)? {
114
$($fields:tt)*
115
}
116
) => {
117
register!(@common $name @ $offset $(, $comment)?);
118
register!(@field_accessors $name { $($fields)* });
119
register!(@io$name @ + $offset);
120
};
121
122
// Creates a alias register of relative offset register `alias` with its own fields.
123
(
124
$name:ident => + $alias:ident $(, $comment:literal)? {
125
$($fields:tt)*
126
}
127
) => {
128
register!(@common $name @ $alias::OFFSET $(, $comment)?);
129
register!(@field_accessors $name { $($fields)* });
130
register!(@io $name @ + $alias::OFFSET);
131
};
132
133
// All rules below are helpers.
134
135
// Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`, `BitOr`,
136
// and conversion to regular `u32`).
137
(@common $name:ident @ $offset:expr $(, $comment:literal)?) => {
138
$(
139
#[doc=$comment]
140
)?
141
#[repr(transparent)]
142
#[derive(Clone, Copy, Default)]
143
pub(crate) struct $name(u32);
144
145
#[allow(dead_code)]
146
impl $name {
147
pub(crate) const OFFSET: usize = $offset;
148
}
149
150
// TODO[REGA]: display the raw hex value, then the value of all the fields. This requires
151
// matching the fields, which will complexify the syntax considerably...
152
impl ::core::fmt::Debug for $name {
153
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
154
f.debug_tuple(stringify!($name))
155
.field(&format_args!("0x{0:x}", &self.0))
156
.finish()
157
}
158
}
159
160
impl ::core::ops::BitOr for $name {
161
type Output = Self;
162
163
fn bitor(self, rhs: Self) -> Self::Output {
164
Self(self.0 | rhs.0)
165
}
166
}
167
168
impl ::core::convert::From<$name> for u32 {
169
fn from(reg: $name) -> u32 {
170
reg.0
171
}
172
}
173
};
174
175
// Defines all the field getter/methods methods for `$name`.
176
(
177
@field_accessors $name:ident {
178
$($hi:tt:$lo:tt $field:ident as $type:tt
179
$(?=> $try_into_type:ty)?
180
$(=> $into_type:ty)?
181
$(, $comment:literal)?
182
;
183
)*
184
}
185
) => {
186
$(
187
register!(@check_field_bounds $hi:$lo $field as $type);
188
)*
189
190
#[allow(dead_code)]
191
impl $name {
192
$(
193
register!(@field_accessor $name $hi:$lo $field as $type
194
$(?=> $try_into_type)?
195
$(=> $into_type)?
196
$(, $comment)?
197
;
198
);
199
)*
200
}
201
};
202
203
// Boolean fields must have `$hi == $lo`.
204
(@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) => {
205
#[allow(clippy::eq_op)]
206
const _: () = {
207
::kernel::build_assert!(
208
$hi == $lo,
209
concat!("boolean field `", stringify!($field), "` covers more than one bit")
210
);
211
};
212
};
213
214
// Non-boolean fields must have `$hi >= $lo`.
215
(@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) => {
216
#[allow(clippy::eq_op)]
217
const _: () = {
218
::kernel::build_assert!(
219
$hi >= $lo,
220
concat!("field `", stringify!($field), "`'s MSB is smaller than its LSB")
221
);
222
};
223
};
224
225
// Catches fields defined as `bool` and convert them into a boolean value.
226
(
227
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
228
$(, $comment:literal)?;
229
) => {
230
register!(
231
@leaf_accessor $name $hi:$lo $field as bool
232
{ |f| <$into_type>::from(if f != 0 { true } else { false }) }
233
$into_type => $into_type $(, $comment)?;
234
);
235
};
236
237
// Shortcut for fields defined as `bool` without the `=>` syntax.
238
(
239
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
240
) => {
241
register!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);
242
};
243
244
// Catches the `?=>` syntax for non-boolean fields.
245
(
246
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
247
$(, $comment:literal)?;
248
) => {
249
register!(@leaf_accessor $name $hi:$lo $field as $type
250
{ |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
251
::core::result::Result<
252
$try_into_type,
253
<$try_into_type as ::core::convert::TryFrom<$type>>::Error
254
>
255
$(, $comment)?;);
256
};
257
258
// Catches the `=>` syntax for non-boolean fields.
259
(
260
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
261
$(, $comment:literal)?;
262
) => {
263
register!(@leaf_accessor $name $hi:$lo $field as $type
264
{ |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
265
};
266
267
// Shortcut for fields defined as non-`bool` without the `=>` or `?=>` syntax.
268
(
269
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
270
$(, $comment:literal)?;
271
) => {
272
register!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);
273
};
274
275
// Generates the accessor methods for a single field.
276
(
277
@leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:ty
278
{ $process:expr } $to_type:ty => $res_type:ty $(, $comment:literal)?;
279
) => {
280
::kernel::macros::paste!(
281
const [<$field:upper>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
282
const [<$field:upper _MASK>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
283
const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros();
284
);
285
286
$(
287
#[doc="Returns the value of this field:"]
288
#[doc=$comment]
289
)?
290
#[inline]
291
pub(crate) fn $field(self) -> $res_type {
292
::kernel::macros::paste!(
293
const MASK: u32 = $name::[<$field:upper _MASK>];
294
const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
295
);
296
let field = ((self.0 & MASK) >> SHIFT);
297
298
$process(field)
299
}
300
301
::kernel::macros::paste!(
302
$(
303
#[doc="Sets the value of this field:"]
304
#[doc=$comment]
305
)?
306
#[inline]
307
pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
308
const MASK: u32 = $name::[<$field:upper _MASK>];
309
const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
310
let value = (u32::from(value) << SHIFT) & MASK;
311
self.0 = (self.0 & !MASK) | value;
312
313
self
314
}
315
);
316
};
317
318
// Creates the IO accessors for a fixed offset register.
319
(@io $name:ident @ $offset:expr) => {
320
#[allow(dead_code)]
321
impl $name {
322
#[inline]
323
pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where
324
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
325
{
326
Self(io.read32($offset))
327
}
328
329
#[inline]
330
pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where
331
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
332
{
333
io.write32(self.0, $offset)
334
}
335
336
#[inline]
337
pub(crate) fn alter<const SIZE: usize, T, F>(
338
io: &T,
339
f: F,
340
) where
341
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
342
F: ::core::ops::FnOnce(Self) -> Self,
343
{
344
let reg = f(Self::read(io));
345
reg.write(io);
346
}
347
}
348
};
349
350
// Create the IO accessors for a relative offset register.
351
(@io $name:ident @ + $offset:literal) => {
352
#[allow(dead_code)]
353
impl $name {
354
#[inline]
355
pub(crate) fn read<const SIZE: usize, T>(
356
io: &T,
357
base: usize,
358
) -> Self where
359
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
360
{
361
Self(io.read32(base + $offset))
362
}
363
364
#[inline]
365
pub(crate) fn write<const SIZE: usize, T>(
366
self,
367
io: &T,
368
base: usize,
369
) where
370
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
371
{
372
io.write32(self.0, base + $offset)
373
}
374
375
#[inline]
376
pub(crate) fn alter<const SIZE: usize, T, F>(
377
io: &T,
378
base: usize,
379
f: F,
380
) where
381
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
382
F: ::core::ops::FnOnce(Self) -> Self,
383
{
384
let reg = f(Self::read(io, base));
385
reg.write(io, base);
386
}
387
388
#[inline]
389
pub(crate) fn try_read<const SIZE: usize, T>(
390
io: &T,
391
base: usize,
392
) -> ::kernel::error::Result<Self> where
393
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
394
{
395
io.try_read32(base + $offset).map(Self)
396
}
397
398
#[inline]
399
pub(crate) fn try_write<const SIZE: usize, T>(
400
self,
401
io: &T,
402
base: usize,
403
) -> ::kernel::error::Result<()> where
404
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
405
{
406
io.try_write32(self.0, base + $offset)
407
}
408
409
#[inline]
410
pub(crate) fn try_alter<const SIZE: usize, T, F>(
411
io: &T,
412
base: usize,
413
f: F,
414
) -> ::kernel::error::Result<()> where
415
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
416
F: ::core::ops::FnOnce(Self) -> Self,
417
{
418
let reg = f(Self::try_read(io, base)?);
419
reg.try_write(io, base)
420
}
421
}
422
};
423
}
424
425