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