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
16extern "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 */
50typedef 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 */
61inline 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 */
79inline 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 */
95inline 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 */
111inline 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 */
128inline 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 */
147inline 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 */
164inline 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 */
180typedef struct mmio_region {
181 void *mock;
183
184/**
185 * Stubbed-out read/write operations for overriding by a testing library.
186 */
190uint8_t mmio_region_read8(mmio_region_t base, ptrdiff_t offset);
192uint32_t mmio_region_read32(mmio_region_t base, ptrdiff_t offset);
193
194void mmio_region_write8(mmio_region_t base, ptrdiff_t offset, uint8_t value);
195void mmio_region_write32(mmio_region_t base, ptrdiff_t offset, uint32_t value);
196void mmio_region_write8_shadowed(mmio_region_t base, ptrdiff_t offset,
197 uint8_t value);
198void 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 */
216inline 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 */
237inline 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 */
423void 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 */
438void 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_