1 /*
2 * Copyright (c) 2015 - 2017, Xilinx Inc. and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 /*
8 * @file io.h
9 * @brief I/O access primitives for libmetal.
10 */
11
12 #ifndef __METAL_IO__H__
13 #define __METAL_IO__H__
14
15 #include <limits.h>
16 #include <stdint.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <metal/assert.h>
20 #include <metal/compiler.h>
21 #include <metal/atomic.h>
22 #include <metal/sys.h>
23 #include <metal/cpu.h>
24
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28
29 /** \defgroup io IO Interfaces
30 * @{
31 */
32
33 #ifdef __MICROBLAZE__
34 #define NO_ATOMIC_64_SUPPORT
35 #endif
36
37 struct metal_io_region;
38
39 /** Generic I/O operations. */
40 struct metal_io_ops {
41 uint64_t (*read)(struct metal_io_region *io,
42 unsigned long offset,
43 memory_order order,
44 int width);
45 void (*write)(struct metal_io_region *io,
46 unsigned long offset,
47 uint64_t value,
48 memory_order order,
49 int width);
50 int (*block_read)(struct metal_io_region *io,
51 unsigned long offset,
52 void *restrict dst,
53 memory_order order,
54 int len);
55 int (*block_write)(struct metal_io_region *io,
56 unsigned long offset,
57 const void *restrict src,
58 memory_order order,
59 int len);
60 void (*block_set)(struct metal_io_region *io,
61 unsigned long offset,
62 unsigned char value,
63 memory_order order,
64 int len);
65 void (*close)(struct metal_io_region *io);
66 metal_phys_addr_t (*offset_to_phys)(struct metal_io_region *io,
67 unsigned long offset);
68 unsigned long (*phys_to_offset)(struct metal_io_region *io,
69 metal_phys_addr_t phys);
70 };
71
72 /** Libmetal I/O region structure. */
73 struct metal_io_region {
74 void *virt; /**< base virtual address */
75 const metal_phys_addr_t *physmap; /**< table of base physical address
76 of each of the pages in the I/O
77 region */
78 size_t size; /**< size of the I/O region */
79 unsigned long page_shift; /**< page shift of I/O region */
80 metal_phys_addr_t page_mask; /**< page mask of I/O region */
81 unsigned int mem_flags; /**< memory attribute of the
82 I/O region */
83 struct metal_io_ops ops; /**< I/O region operations */
84 };
85
86 /**
87 * @brief Open a libmetal I/O region.
88 *
89 * @param[in, out] io I/O region handle.
90 * @param[in] virt Virtual address of region.
91 * @param[in] physmap Array of physical addresses per page.
92 * @param[in] size Size of region.
93 * @param[in] page_shift Log2 of page size (-1 for single page).
94 * @param[in] mem_flags Memory flags
95 * @param[in] ops ops
96 */
97 void
98 metal_io_init(struct metal_io_region *io, void *virt,
99 const metal_phys_addr_t *physmap, size_t size,
100 unsigned int page_shift, unsigned int mem_flags,
101 const struct metal_io_ops *ops);
102
103 /**
104 * @brief Close a libmetal shared memory segment.
105 * @param[in] io I/O region handle.
106 */
metal_io_finish(struct metal_io_region * io)107 static inline void metal_io_finish(struct metal_io_region *io)
108 {
109 if (io->ops.close)
110 (*io->ops.close)(io);
111 memset(io, 0, sizeof(*io));
112 }
113
114 /**
115 * @brief Get size of I/O region.
116 *
117 * @param[in] io I/O region handle.
118 * @return Size of I/O region.
119 */
metal_io_region_size(struct metal_io_region * io)120 static inline size_t metal_io_region_size(struct metal_io_region *io)
121 {
122 return io->size;
123 }
124
125 /**
126 * @brief Get virtual address for a given offset into the I/O region.
127 * @param[in] io I/O region handle.
128 * @param[in] offset Offset into shared memory segment.
129 * @return NULL if offset is out of range, or pointer to offset.
130 */
131 static inline void *
metal_io_virt(struct metal_io_region * io,unsigned long offset)132 metal_io_virt(struct metal_io_region *io, unsigned long offset)
133 {
134 return (io->virt != METAL_BAD_VA && offset < io->size
135 ? (void *)((uintptr_t)io->virt + offset)
136 : NULL);
137 }
138
139 /**
140 * @brief Convert a virtual address to offset within I/O region.
141 * @param[in] io I/O region handle.
142 * @param[in] virt Virtual address within segment.
143 * @return METAL_BAD_OFFSET if out of range, or offset.
144 */
145 static inline unsigned long
metal_io_virt_to_offset(struct metal_io_region * io,void * virt)146 metal_io_virt_to_offset(struct metal_io_region *io, void *virt)
147 {
148 size_t offset = (uintptr_t)virt - (uintptr_t)io->virt;
149
150 return (offset < io->size ? offset : METAL_BAD_OFFSET);
151 }
152
153 /**
154 * @brief Get physical address for a given offset into the I/O region.
155 * @param[in] io I/O region handle.
156 * @param[in] offset Offset into shared memory segment.
157 * @return METAL_BAD_PHYS if offset is out of range, or physical address
158 * of offset.
159 */
160 static inline metal_phys_addr_t
metal_io_phys(struct metal_io_region * io,unsigned long offset)161 metal_io_phys(struct metal_io_region *io, unsigned long offset)
162 {
163 if (!io->ops.offset_to_phys) {
164 unsigned long page = (io->page_shift >=
165 sizeof(offset) * CHAR_BIT ?
166 0 : offset >> io->page_shift);
167 return (io->physmap && offset < io->size
168 ? io->physmap[page] + (offset & io->page_mask)
169 : METAL_BAD_PHYS);
170 }
171
172 return io->ops.offset_to_phys(io, offset);
173 }
174
175 /**
176 * @brief Convert a physical address to offset within I/O region.
177 * @param[in] io I/O region handle.
178 * @param[in] phys Physical address within segment.
179 * @return METAL_BAD_OFFSET if out of range, or offset.
180 */
181 static inline unsigned long
metal_io_phys_to_offset(struct metal_io_region * io,metal_phys_addr_t phys)182 metal_io_phys_to_offset(struct metal_io_region *io, metal_phys_addr_t phys)
183 {
184 if (!io->ops.phys_to_offset) {
185 unsigned long offset =
186 (io->page_mask == (metal_phys_addr_t)(-1) ?
187 phys - io->physmap[0] : phys & io->page_mask);
188 do {
189 if (metal_io_phys(io, offset) == phys)
190 return offset;
191 offset += io->page_mask + 1;
192 } while (offset < io->size);
193 return METAL_BAD_OFFSET;
194 }
195
196 return (*io->ops.phys_to_offset)(io, phys);
197 }
198
199 /**
200 * @brief Convert a physical address to virtual address.
201 * @param[in] io Shared memory segment handle.
202 * @param[in] phys Physical address within segment.
203 * @return NULL if out of range, or corresponding virtual address.
204 */
205 static inline void *
metal_io_phys_to_virt(struct metal_io_region * io,metal_phys_addr_t phys)206 metal_io_phys_to_virt(struct metal_io_region *io, metal_phys_addr_t phys)
207 {
208 return metal_io_virt(io, metal_io_phys_to_offset(io, phys));
209 }
210
211 /**
212 * @brief Convert a virtual address to physical address.
213 * @param[in] io Shared memory segment handle.
214 * @param[in] virt Virtual address within segment.
215 * @return METAL_BAD_PHYS if out of range, or corresponding
216 * physical address.
217 */
218 static inline metal_phys_addr_t
metal_io_virt_to_phys(struct metal_io_region * io,void * virt)219 metal_io_virt_to_phys(struct metal_io_region *io, void *virt)
220 {
221 return metal_io_phys(io, metal_io_virt_to_offset(io, virt));
222 }
223
224 /**
225 * @brief Read a value from an I/O region.
226 * @param[in] io I/O region handle.
227 * @param[in] offset Offset into I/O region.
228 * @param[in] order Memory ordering.
229 * @param[in] width Width in bytes of datatype to read. This must be 1, 2,
230 * 4, or 8, and a compile time constant for this function
231 * to inline cleanly.
232 * @return Value.
233 */
234 static inline uint64_t
metal_io_read(struct metal_io_region * io,unsigned long offset,memory_order order,int width)235 metal_io_read(struct metal_io_region *io, unsigned long offset,
236 memory_order order, int width)
237 {
238 void *ptr = metal_io_virt(io, offset);
239
240 if (io->ops.read)
241 return (*io->ops.read)(io, offset, order, width);
242 else if (ptr && sizeof(atomic_uchar) == width)
243 return atomic_load_explicit((atomic_uchar *)ptr, order);
244 else if (ptr && sizeof(atomic_ushort) == width)
245 return atomic_load_explicit((atomic_ushort *)ptr, order);
246 else if (ptr && sizeof(atomic_uint) == width)
247 return atomic_load_explicit((atomic_uint *)ptr, order);
248 else if (ptr && sizeof(atomic_ulong) == width)
249 return atomic_load_explicit((atomic_ulong *)ptr, order);
250 #ifndef NO_ATOMIC_64_SUPPORT
251 else if (ptr && sizeof(atomic_ullong) == width)
252 return atomic_load_explicit((atomic_ullong *)ptr, order);
253 #endif
254 metal_assert(0);
255 return 0; /* quiet compiler */
256 }
257
258 /**
259 * @brief Write a value into an I/O region.
260 * @param[in] io I/O region handle.
261 * @param[in] offset Offset into I/O region.
262 * @param[in] value Value to write.
263 * @param[in] order Memory ordering.
264 * @param[in] width Width in bytes of datatype to read. This must be 1, 2,
265 * 4, or 8, and a compile time constant for this function
266 * to inline cleanly.
267 */
268 static inline void
metal_io_write(struct metal_io_region * io,unsigned long offset,uint64_t value,memory_order order,int width)269 metal_io_write(struct metal_io_region *io, unsigned long offset,
270 uint64_t value, memory_order order, int width)
271 {
272 void *ptr = metal_io_virt(io, offset);
273
274 if (io->ops.write)
275 (*io->ops.write)(io, offset, value, order, width);
276 else if (ptr && sizeof(atomic_uchar) == width)
277 atomic_store_explicit((atomic_uchar *)ptr, (unsigned char)value,
278 order);
279 else if (ptr && sizeof(atomic_ushort) == width)
280 atomic_store_explicit((atomic_ushort *)ptr,
281 (unsigned short)value, order);
282 else if (ptr && sizeof(atomic_uint) == width)
283 atomic_store_explicit((atomic_uint *)ptr, (unsigned int)value,
284 order);
285 else if (ptr && sizeof(atomic_ulong) == width)
286 atomic_store_explicit((atomic_ulong *)ptr, (unsigned long)value,
287 order);
288 #ifndef NO_ATOMIC_64_SUPPORT
289 else if (ptr && sizeof(atomic_ullong) == width)
290 atomic_store_explicit((atomic_ullong *)ptr,
291 (unsigned long long)value, order);
292 #endif
293 else
294 metal_assert(0);
295 }
296
297 #define metal_io_read8_explicit(_io, _ofs, _order) \
298 metal_io_read((_io), (_ofs), (_order), 1)
299 #define metal_io_read8(_io, _ofs) \
300 metal_io_read((_io), (_ofs), memory_order_seq_cst, 1)
301 #define metal_io_write8_explicit(_io, _ofs, _val, _order) \
302 metal_io_write((_io), (_ofs), (_val), (_order), 1)
303 #define metal_io_write8(_io, _ofs, _val) \
304 metal_io_write((_io), (_ofs), (_val), memory_order_seq_cst, 1)
305
306 #define metal_io_read16_explicit(_io, _ofs, _order) \
307 metal_io_read((_io), (_ofs), (_order), 2)
308 #define metal_io_read16(_io, _ofs) \
309 metal_io_read((_io), (_ofs), memory_order_seq_cst, 2)
310 #define metal_io_write16_explicit(_io, _ofs, _val, _order) \
311 metal_io_write((_io), (_ofs), (_val), (_order), 2)
312 #define metal_io_write16(_io, _ofs, _val) \
313 metal_io_write((_io), (_ofs), (_val), memory_order_seq_cst, 2)
314
315 #define metal_io_read32_explicit(_io, _ofs, _order) \
316 metal_io_read((_io), (_ofs), (_order), 4)
317 #define metal_io_read32(_io, _ofs) \
318 metal_io_read((_io), (_ofs), memory_order_seq_cst, 4)
319 #define metal_io_write32_explicit(_io, _ofs, _val, _order) \
320 metal_io_write((_io), (_ofs), (_val), (_order), 4)
321 #define metal_io_write32(_io, _ofs, _val) \
322 metal_io_write((_io), (_ofs), (_val), memory_order_seq_cst, 4)
323
324 #define metal_io_read64_explicit(_io, _ofs, _order) \
325 metal_io_read((_io), (_ofs), (_order), 8)
326 #define metal_io_read64(_io, _ofs) \
327 metal_io_read((_io), (_ofs), memory_order_seq_cst, 8)
328 #define metal_io_write64_explicit(_io, _ofs, _val, _order) \
329 metal_io_write((_io), (_ofs), (_val), (_order), 8)
330 #define metal_io_write64(_io, _ofs, _val) \
331 metal_io_write((_io), (_ofs), (_val), memory_order_seq_cst, 8)
332
333 /**
334 * @brief Read a block from an I/O region.
335 * @param[in] io I/O region handle.
336 * @param[in] offset Offset into I/O region.
337 * @param[in] dst destination to store the read data.
338 * @param[in] len length in bytes to read.
339 * @return On success, number of bytes read. On failure, negative value
340 */
341 int metal_io_block_read(struct metal_io_region *io, unsigned long offset,
342 void *restrict dst, int len);
343
344 /**
345 * @brief Write a block into an I/O region.
346 * @param[in] io I/O region handle.
347 * @param[in] offset Offset into I/O region.
348 * @param[in] src source to write.
349 * @param[in] len length in bytes to write.
350 * @return On success, number of bytes written. On failure, negative value
351 */
352 int metal_io_block_write(struct metal_io_region *io, unsigned long offset,
353 const void *restrict src, int len);
354
355 /**
356 * @brief fill a block of an I/O region.
357 * @param[in] io I/O region handle.
358 * @param[in] offset Offset into I/O region.
359 * @param[in] value value to fill into the block
360 * @param[in] len length in bytes to fill.
361 * @return On success, number of bytes filled. On failure, negative value
362 */
363 int metal_io_block_set(struct metal_io_region *io, unsigned long offset,
364 unsigned char value, int len);
365
366 /** @} */
367
368 #ifdef __cplusplus
369 }
370 #endif
371
372 #include <metal/system/@PROJECT_SYSTEM@/io.h>
373
374 #endif /* __METAL_IO__H__ */
375