Software APIs
mmio.h
Go to the documentation of this file.
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
5 #ifndef OPENTITAN_SW_DEVICE_LIB_BASE_MMIO_H_
6 #define OPENTITAN_SW_DEVICE_LIB_BASE_MMIO_H_
7 
8 #include <stdbool.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 
14 
15 #ifdef __cplusplus
16 extern "C" {
17 #endif // __cplusplus
18 
19 /**
20  * @file
21  * @brief Memory-mapped IO functions, for volatile access.
22  *
23  * Memory-mapped IO functions, which either map to volatile accesses, or can be
24  * replaced with instrumentation calls at compile time, for use with tests.
25  *
26  * Compiling translation units that pull in this header with `-DMOCK_MMIO` will
27  * disable the definitions of `mmio_region_read` and `mmio_region_write`. These
28  * symbols can then be defined by a test harness to allow for instrumentation of
29  * MMIO accesses.
30  */
31 
32 /**
33  * 2020-06: We're transitioning to a more efficient manner of using our MMIO
34  * APIs, where the DIFs explicitly read, then modify, then write. All the
35  * `*_nonatomic_*` functions in this DIF are deprecated and will be removed
36  * eventually, leaving only the `read<N>`, `write<N>`, and `memcpy` functions.
37  *
38  * For the moment, we are not adding `__attribute__((deprecated(reason)))` using
39  * this macro, because most code still uses the old version, but at some point
40  * we will add that expansion. This should be seen as a note to humans, not
41  * computers (yet).
42  */
43 #define MMIO_DEPRECATED
44 
45 #ifdef OT_PLATFORM_RV32
46 /**
47  * An mmio_region_t is an opaque handle to an MMIO region; it should only be
48  * modified using the functions provided in this header.
49  */
50 typedef struct mmio_region {
51  volatile void *base;
53 
54 /**
55  * Create a new `mmio_region_t` from the given address.
56  *
57  * @param address an address to an MMIO region.
58  * @return a `mmio_region_t` value representing that region.
59  */
61 inline mmio_region_t mmio_region_from_addr(uintptr_t address) {
62  return (mmio_region_t){
63  .base = (volatile void *)address,
64  };
65 }
66 
67 /**
68  * Reads an aligned uint8_t from the MMIO region `base` at the given byte
69  * offset.
70  *
71  * This function is guaranteed to commit a read to memory, and will not be
72  * reordered with respect to other MMIO manipulations.
73  *
74  * @param base the region to read from.
75  * @param offset the offset to read at, in bytes.
76  * @return the read value.
77  */
79 inline uint8_t mmio_region_read8(mmio_region_t base, ptrdiff_t offset) {
80  return ((volatile uint8_t *)base.base)[OT_UNSIGNED(offset) / sizeof(uint8_t)];
81 }
82 
83 /**
84  * Reads an aligned uint32_t from the MMIO region `base` at the given byte
85  * offset.
86  *
87  * This function is guaranteed to commit a read to memory, and will not be
88  * reordered with respect to other MMIO manipulations.
89  *
90  * @param base the region to read from.
91  * @param offset the offset to read at, in bytes.
92  * @return the read value.
93  */
95 inline uint32_t mmio_region_read32(mmio_region_t base, ptrdiff_t offset) {
96  return (
97  (volatile uint32_t *)base.base)[OT_UNSIGNED(offset) / sizeof(uint32_t)];
98 }
99 
100 /**
101  * Writes an aligned uint8_t to the MMIO region `base` at the given byte
102  * offset.
103  *
104  * This function is guaranteed to commit a write to memory, and will not be
105  * reordered with respect to other region manipulations.
106  *
107  * @param base the region to write to.
108  * @param offset the offset to write at, in bytes.
109  * @param value the value to write.
110  */
111 inline void mmio_region_write8(mmio_region_t base, ptrdiff_t offset,
112  uint8_t value) {
113  ((volatile uint8_t *)base.base)[OT_UNSIGNED(offset) / sizeof(uint8_t)] =
114  value;
115 }
116 
117 /**
118  * Writes an aligned uint8_t to the MMIO region `base` at the given byte
119  * offset via two subsequent write operations.
120  *
121  * This function is guaranteed to commit a write to memory, and will not be
122  * reordered with respect to other region manipulations.
123  *
124  * @param base the region to write to.
125  * @param offset the offset to write at, in bytes.
126  * @param value the value to write.
127  */
128 inline void mmio_region_write8_shadowed(mmio_region_t base, ptrdiff_t offset,
129  uint8_t value) {
130  ((volatile uint8_t *)base.base)[OT_UNSIGNED(offset) / sizeof(uint8_t)] =
131  value;
132  ((volatile uint8_t *)base.base)[OT_UNSIGNED(offset) / sizeof(uint8_t)] =
133  value;
134 }
135 
136 /**
137  * Writes an aligned uint32_t to the MMIO region `base` at the given byte
138  * offset.
139  *
140  * This function is guaranteed to commit a write to memory, and will not be
141  * reordered with respect to other region manipulations.
142  *
143  * @param base the region to write to.
144  * @param offset the offset to write at, in bytes.
145  * @param value the value to write.
146  */
147 inline void mmio_region_write32(mmio_region_t base, ptrdiff_t offset,
148  uint32_t value) {
149  ((volatile uint32_t *)base.base)[OT_UNSIGNED(offset) / sizeof(uint32_t)] =
150  value;
151 }
152 
153 /**
154  * Writes an aligned uint32_t to the MMIO region `base` at the given byte
155  * offset via two subsequent write operations.
156  *
157  * This function is guaranteed to commit a write to memory, and will not be
158  * reordered with respect to other region manipulations.
159  *
160  * @param base the region to write to.
161  * @param offset the offset to write at, in bytes.
162  * @param value the value to write.
163  */
164 inline void mmio_region_write32_shadowed(mmio_region_t base, ptrdiff_t offset,
165  uint32_t value) {
166  ((volatile uint32_t *)base.base)[OT_UNSIGNED(offset) / sizeof(uint32_t)] =
167  value;
168  ((volatile uint32_t *)base.base)[OT_UNSIGNED(offset) / sizeof(uint32_t)] =
169  value;
170 }
171 #else // OT_PLATFORM_RV32
172 /**
173  * "Instrumented" mmio_region_t.
174  *
175  * Instead of containing a volatile pointer, mmio_region_t becomes a `void *`
176  * when `-DMOCK_MMIO` is enabled. This makes it incompatible with the non-mock
177  * version of `mmio_region_t`, which prevents users from being able to access
178  * the pointer inside.
179  */
180 typedef struct mmio_region {
181  void *mock;
183 
184 /**
185  * Stubbed-out read/write operations for overriding by a testing library.
186  */
190 uint8_t mmio_region_read8(mmio_region_t base, ptrdiff_t offset);
192 uint32_t mmio_region_read32(mmio_region_t base, ptrdiff_t offset);
193 
194 void mmio_region_write8(mmio_region_t base, ptrdiff_t offset, uint8_t value);
195 void mmio_region_write32(mmio_region_t base, ptrdiff_t offset, uint32_t value);
196 void mmio_region_write8_shadowed(mmio_region_t base, ptrdiff_t offset,
197  uint8_t value);
198 void mmio_region_write32_shadowed(mmio_region_t base, ptrdiff_t offset,
199  uint32_t value);
200 #endif // OT_PLATFORM_RV32
201 
202 /**
203  * Reads the bits in `mask` from the MMIO region `base` at the given offset.
204  *
205  * This function has the same guarantees as `mmio_region_read32()` and
206  * `mmio_region_write32()`.
207  *
208  * @param base the region to mask.
209  * @param offset the offset to apply the mask at, in bytes.
210  * @param mask the mask to read from the selected register.
211  * @param mask_index mask position within the selected register.
212  * @return return the value of the read mask.
213  */
216 inline uint32_t mmio_region_read_mask32(mmio_region_t base, ptrdiff_t offset,
217  uint32_t mask, uint32_t mask_index) {
218  return bitfield_field32_read(
219  mmio_region_read32(base, offset),
220  (bitfield_field32_t){.mask = mask, .index = mask_index});
221 }
222 
223 /**
224  * Checks whether the `bit_index`th bit is set in the MMIO region `base` at
225  * the given offset.
226  *
227  * This function has the same guarantees as `mmio_region_read32()` and
228  * `mmio_region_write32()`.
229  *
230  * @param base the region to mask.
231  * @param offset the offset to apply the mask at.
232  * @param bit_index the bit to check.
233  * @return true if the bit is set, false otherwise
234  */
237 inline bool mmio_region_get_bit32(mmio_region_t base, ptrdiff_t offset,
238  uint32_t bit_index) {
239  return bitfield_bit32_read(mmio_region_read32(base, offset), bit_index);
240 }
241 
242 /**
243  * Clears the bits in `mask` from the MMIO region `base` at the given offset.
244  *
245  * This function performs a non-atomic read-write-modify operation on a
246  * MMIO region.
247  *
248  * @param base the region to mask.
249  * @param offset the offset to apply the mask at, in bytes.
250  * @param mask the mask to clear from the selected register.
251  * @param mask_index mask position within the selected register.
252  */
255  ptrdiff_t offset, uint32_t mask,
256  uint32_t mask_index) {
257  uint32_t register_value = mmio_region_read32(base, offset);
258  register_value = bitfield_field32_write(
259  register_value, (bitfield_field32_t){.mask = mask, .index = mask_index},
260  0x0);
261  mmio_region_write32(base, offset, register_value);
262 }
263 
264 /**
265  * Sets the bits in `mask` from the MMIO region `base` at the given offset.
266  *
267  * This function performs a non-atomic read-write-modify operation on a
268  * MMIO region.
269  *
270  * @param base the region to mask.
271  * @param offset the offset to apply the mask at, in bytes.
272  * @param mask the mask to set on the selected register.
273  * @param mask_index mask position within the selected register.
274  */
277  ptrdiff_t offset, uint32_t mask,
278  uint32_t mask_index) {
279  uint32_t register_value = mmio_region_read32(base, offset);
280  register_value = bitfield_field32_write(
281  register_value, (bitfield_field32_t){.mask = mask, .index = mask_index},
282  ~0x0u);
283  mmio_region_write32(base, offset, register_value);
284 }
285 
286 /**
287  * Sets the bits in `mask` from the MMIO region `base` at the given offset.
288  *
289  * This function is like `nonatomic_set_mask32`, but does not perform a
290  * read, for use with write-only memory.
291  *
292  * @param base the region to mask.
293  * @param offset the offset to apply the mask at, in bytes.
294  * @param mask the mask to set on the selected register.
295  * @param mask_index mask position within the selected register.
296  */
299  ptrdiff_t offset, uint32_t mask,
300  uint32_t mask_index) {
301  uint32_t register_value = 0x0u;
302  register_value = bitfield_field32_write(
303  register_value, (bitfield_field32_t){.mask = mask, .index = mask_index},
304  ~0x0u);
305  mmio_region_write32(base, offset, register_value);
306 }
307 
308 /**
309  * Sets the `field` from the MMIO region `base` at the given `offset`.
310  *
311  * This function performs a non-atomic read-write-modify operation on a
312  * MMIO region. The information of which portion of the register to set, is
313  * stored in the `field`. The semantics of this operation are similar to the
314  * `mmio_region_nonatomic_set_mask32`, however the appropriate portion of the
315  * register is zeroed before it is written to.
316  *
317  * @param base the region to set the field in.
318  * @param offset the offset to set the field at, in bytes.
319  * @param field field within selected register field to be set.
320  * @param value value to set the field to.
321  */
324  ptrdiff_t offset,
325  bitfield_field32_t field,
326  uint32_t value) {
327  uint32_t register_value = mmio_region_read32(base, offset);
328  register_value = bitfield_field32_write(register_value, field, value);
329  mmio_region_write32(base, offset, register_value);
330 }
331 
332 /**
333  * Sets the `field` from the MMIO region `base` at the given `offset`.
334  *
335  * This function is like `nonatomic_set_field32`, but does not perform a
336  * read, for use with write-only memory.
337  *
338  * @param base the region to set the field in.
339  * @param offset the offset to set the field at, in bytes.
340  * @param field field within selected register field to be set.
341  * @param value value to set field to.
342  */
345  ptrdiff_t offset,
346  bitfield_field32_t field,
347  uint32_t value) {
348  uint32_t register_value = 0x0u;
349  register_value = bitfield_field32_write(register_value, field, value);
350  mmio_region_write32(base, offset, register_value);
351 }
352 
353 /**
354  * Clears the `bit_index`th bit in the MMIO region `base` at the given offset.
355  *
356  * This function has the same guarantees as
357  * `mmio_region_nonatomic_clear_mask()`.
358  *
359  * @param base the region to mask.
360  * @param offset the offset to apply the mask at.
361  * @param bit_index the bit to clear.
362  */
365  ptrdiff_t offset,
366  uint32_t bit_index) {
367  uint32_t register_value = mmio_region_read32(base, offset);
368  register_value = bitfield_bit32_write(register_value, bit_index, false);
369  mmio_region_write32(base, offset, register_value);
370 }
371 
372 /**
373  * Sets the `bit_index`th bit in the MMIO region `base` at the given offset.
374  *
375  * This function has the same guarantees as `mmio_region_nonatomic_set_mask()`.
376  *
377  * @param base the region to mask.
378  * @param offset the offset to apply the mask at.
379  * @param bit_index the bit to set.
380  */
383  ptrdiff_t offset,
384  uint32_t bit_index) {
385  uint32_t register_value = mmio_region_read32(base, offset);
386  register_value = bitfield_bit32_write(register_value, bit_index, true);
387  mmio_region_write32(base, offset, register_value);
388 }
389 
390 /**
391  * Sets the `bit_index`th bit in the MMIO region `base` at the given offset.
392  *
393  * This function is like `nonatomic_set_bit32`, but does not perform a read, for
394  * use with write-only memory.
395  *
396  * There is no `write_only_clear32`, since such a function would be a no-op.
397  *
398  * @param base the region to mask.
399  * @param offset the offset to apply the mask at.
400  * @param bit_index the bit to set.
401  */
404  ptrdiff_t offset,
405  uint32_t bit_index) {
406  uint32_t register_value = 0x0u;
407  register_value = bitfield_bit32_write(register_value, bit_index, true);
408  mmio_region_write32(base, offset, register_value);
409 }
410 
411 /**
412  * Copies a block of memory from MMIO to main memory while ensuring that MMIO
413  * accesses are both word-sized and word-aligned.
414  *
415  * This function may perform up to `len/4 + 2` volatile reads to handle
416  * unaligned accesses.
417  *
418  * @param base the MMIO region to read from.
419  * @param offset the offset to start reading from, in bytes.
420  * @param dest the main memory location to start writing to.
421  * @param len number of bytes to copy.
422  */
423 void mmio_region_memcpy_from_mmio32(mmio_region_t base, uint32_t offset,
424  void *dest, size_t len);
425 
426 /**
427  * Copies a block of memory from main memory to MMIO while ensuring that MMIO
428  * accesses are both word-sized and word-aligned.
429  *
430  * Unaligned MMIO blocks are handled by performing a read-modify-write for the
431  * boundary words.
432  *
433  * @param base the MMIO region to write to.
434  * @param offset the offset to start writing to, in bytes.
435  * @param src the main memory location to start reading from.
436  * @param len number of bytes to copy.
437  */
438 void mmio_region_memcpy_to_mmio32(mmio_region_t base, uint32_t offset,
439  const void *src, size_t len);
440 
441 #ifdef __cplusplus
442 } // extern "C"
443 #endif // __cplusplus
444 
445 #endif // OPENTITAN_SW_DEVICE_LIB_BASE_MMIO_H_