Software APIs
mmio.c
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 
6 
7 #include <stdalign.h>
8 
10 
11 /**
12  * Copies a block of memory between MMIO and main memory while ensuring that
13  * MMIO accesses are word-aligned.
14  *
15  * If `from_mmio` is true, data is copied from MMIO to main memory. Otherwise,
16  * data is copied from main memory to MMIO. This is implemented as a single
17  * function to avoid code duplication.
18  *
19  * @param base the MMIO region to copy from/to.
20  * @param offset the offset to start copying from/to, in bytes.
21  * @param buf the main memory location to start copying to/from.
22  * @param len number of bytes to copy.
23  * @param from_mmio if true, copy from MMIO to main memory. Otherwise, copy from
24  * main memory to MMIO.
25  */
26 static void mmio_region_memcpy32(mmio_region_t base, uint32_t offset,
27  uint8_t *buf, size_t len, bool from_mmio) {
28  if (len == 0) {
29  return;
30  }
31 
32  // First, bring the MMIO address into word alignment, so we can do
33  // full-word I/O rather than partial word I/O.
34  ptrdiff_t misalignment = misalignment32_of(offset);
35  if (misalignment != 0) {
36  // The number of bytes missing to bring `offset` back into alignment.
37  // For example, 0x3 has misalignment of 3 and realignment of 1.
38  size_t realignment = sizeof(uint32_t) - OT_UNSIGNED(misalignment);
39  // Note that we might be doing less I/O than the misalignment requires; we
40  // might be off by a single byte, but not have the full three bytes for full
41  // realignment.
42  if (realignment > len) {
43  realignment = len;
44  }
45 
46  // Converts `offset`, which points to a subword boundary, to point to the
47  // start of the current word it points into.
48  ptrdiff_t current_word_offset = OT_SIGNED(offset) - misalignment;
49  uint32_t current_word = mmio_region_read32(base, current_word_offset);
50 
51  // Act on only to a suffix of `current_word`, corresponding to the necessary
52  // realignment.
53  uint8_t *current_byte = ((uint8_t *)&current_word) + misalignment;
54  if (from_mmio) {
55  memcpy(buf, current_byte, realignment);
56  } else {
57  // When writing, we need to write the modified word.
58  memcpy(current_byte, buf, realignment);
59  mmio_region_write32(base, current_word_offset, current_word);
60  }
61 
62  offset += realignment;
63  buf += realignment;
64  len -= realignment;
65  }
66 
67  // Now, we just do full word I/O until we run out of stuff to act on.
68  while (len > 0) {
69  // At the end, we may not have a full word to copy, but it's otherwise
70  // the same case as a full word, since we're already word aligned (if
71  // this would be a subword read, it would end the loop anyway).
72  uint32_t bytes_to_copy = sizeof(uint32_t);
73  if (bytes_to_copy > len) {
74  bytes_to_copy = len;
75  }
76 
77  // Read the current word from MMIO.
78  uint32_t current_word = 0;
79  if (from_mmio || bytes_to_copy != sizeof(uint32_t)) {
80  // If reading from MMIO, we need to read this word always.
81  // If writing to MMIO, we only need to write a prefix when writing a
82  // subword. In that case, we need to avoid clobbering the word at
83  // `offset`.
84  current_word = mmio_region_read32(base, OT_SIGNED(offset));
85  }
86 
87  // Copy a prefix; most of the time, this will be the whole word.
88  if (from_mmio) {
89  memcpy(buf, &current_word, bytes_to_copy);
90  } else {
91  // When writing to MMIO, we need to write the modified word.
92  memcpy(&current_word, buf, bytes_to_copy);
93  mmio_region_write32(base, OT_SIGNED(offset), current_word);
94  }
95 
96  offset += bytes_to_copy;
97  buf += bytes_to_copy;
98  len -= bytes_to_copy;
99  }
100 }
101 
103  void *dest, size_t len) {
104  mmio_region_memcpy32(base, offset, dest, len, true);
105 }
106 
107 void mmio_region_memcpy_to_mmio32(mmio_region_t base, uint32_t offset,
108  const void *src, size_t len) {
109  // Below `const` cast is necessary to be able to use `mmio_region_memcpy32`
110  // for both read and write operations but `from_mmio = false` means that `src`
111  // will never be written to.
112  mmio_region_memcpy32(base, offset, (void *)src, len, false);
113 }
114 
115 // `extern` declarations to give the inline functions in the
116 // corresponding header a link location.
117 extern mmio_region_t mmio_region_from_addr(uintptr_t address);
118 extern uint8_t mmio_region_read8(mmio_region_t base, ptrdiff_t offset);
119 extern uint32_t mmio_region_read32(mmio_region_t base, ptrdiff_t offset);
120 extern void mmio_region_write8(mmio_region_t base, ptrdiff_t offset,
121  uint8_t value);
122 extern void mmio_region_write32(mmio_region_t base, ptrdiff_t offset,
123  uint32_t value);
124 extern void mmio_region_write8_shadowed(mmio_region_t base, ptrdiff_t offset,
125  uint8_t value);
126 extern void mmio_region_write32_shadowed(mmio_region_t base, ptrdiff_t offset,
127  uint32_t value);
128 extern uint32_t mmio_region_read_mask32(mmio_region_t base, ptrdiff_t offset,
129  uint32_t mask, uint32_t mask_index);
130 extern bool mmio_region_get_bit32(mmio_region_t base, ptrdiff_t offset,
131  uint32_t bit_index);
133  ptrdiff_t offset, uint32_t mask,
134  uint32_t mask_index);
136  ptrdiff_t offset, uint32_t mask,
137  uint32_t mask_index);
139  ptrdiff_t offset, uint32_t mask,
140  uint32_t mask_index);
142  ptrdiff_t offset,
143  bitfield_field32_t field,
144  uint32_t value);
146  ptrdiff_t offset,
147  bitfield_field32_t field,
148  uint32_t value);
150  ptrdiff_t offset,
151  uint32_t bit_index);
153  ptrdiff_t offset,
154  uint32_t bit_index);
156  ptrdiff_t offset,
157  uint32_t bit_index);