1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
2 | /* |
3 | * Pointer abstraction for IO/system memory |
4 | */ |
5 | |
6 | #ifndef __IOSYS_MAP_H__ |
7 | #define __IOSYS_MAP_H__ |
8 | |
9 | #include <linux/compiler_types.h> |
10 | #include <linux/io.h> |
11 | #include <linux/string.h> |
12 | |
13 | /** |
14 | * DOC: overview |
15 | * |
16 | * When accessing a memory region, depending on its location, users may have to |
17 | * access it with I/O operations or memory load/store operations. For example, |
18 | * copying to system memory could be done with memcpy(), copying to I/O memory |
19 | * would be done with memcpy_toio(). |
20 | * |
21 | * .. code-block:: c |
22 | * |
23 | * void *vaddr = ...; // pointer to system memory |
24 | * memcpy(vaddr, src, len); |
25 | * |
26 | * void *vaddr_iomem = ...; // pointer to I/O memory |
27 | * memcpy_toio(vaddr_iomem, src, len); |
28 | * |
29 | * The user of such pointer may not have information about the mapping of that |
30 | * region or may want to have a single code path to handle operations on that |
31 | * buffer, regardless if it's located in system or IO memory. The type |
32 | * :c:type:`struct iosys_map <iosys_map>` and its helpers abstract that so the |
33 | * buffer can be passed around to other drivers or have separate duties inside |
34 | * the same driver for allocation, read and write operations. |
35 | * |
36 | * Open-coding access to :c:type:`struct iosys_map <iosys_map>` is considered |
37 | * bad style. Rather then accessing its fields directly, use one of the provided |
38 | * helper functions, or implement your own. For example, instances of |
39 | * :c:type:`struct iosys_map <iosys_map>` can be initialized statically with |
40 | * IOSYS_MAP_INIT_VADDR(), or at runtime with iosys_map_set_vaddr(). These |
41 | * helpers will set an address in system memory. |
42 | * |
43 | * .. code-block:: c |
44 | * |
45 | * struct iosys_map map = IOSYS_MAP_INIT_VADDR(0xdeadbeaf); |
46 | * |
47 | * iosys_map_set_vaddr(&map, 0xdeadbeaf); |
48 | * |
49 | * To set an address in I/O memory, use IOSYS_MAP_INIT_VADDR_IOMEM() or |
50 | * iosys_map_set_vaddr_iomem(). |
51 | * |
52 | * .. code-block:: c |
53 | * |
54 | * struct iosys_map map = IOSYS_MAP_INIT_VADDR_IOMEM(0xdeadbeaf); |
55 | * |
56 | * iosys_map_set_vaddr_iomem(&map, 0xdeadbeaf); |
57 | * |
58 | * Instances of struct iosys_map do not have to be cleaned up, but |
59 | * can be cleared to NULL with iosys_map_clear(). Cleared mappings |
60 | * always refer to system memory. |
61 | * |
62 | * .. code-block:: c |
63 | * |
64 | * iosys_map_clear(&map); |
65 | * |
66 | * Test if a mapping is valid with either iosys_map_is_set() or |
67 | * iosys_map_is_null(). |
68 | * |
69 | * .. code-block:: c |
70 | * |
71 | * if (iosys_map_is_set(&map) != iosys_map_is_null(&map)) |
72 | * // always true |
73 | * |
74 | * Instances of :c:type:`struct iosys_map <iosys_map>` can be compared for |
75 | * equality with iosys_map_is_equal(). Mappings that point to different memory |
76 | * spaces, system or I/O, are never equal. That's even true if both spaces are |
77 | * located in the same address space, both mappings contain the same address |
78 | * value, or both mappings refer to NULL. |
79 | * |
80 | * .. code-block:: c |
81 | * |
82 | * struct iosys_map sys_map; // refers to system memory |
83 | * struct iosys_map io_map; // refers to I/O memory |
84 | * |
85 | * if (iosys_map_is_equal(&sys_map, &io_map)) |
86 | * // always false |
87 | * |
88 | * A set up instance of struct iosys_map can be used to access or manipulate the |
89 | * buffer memory. Depending on the location of the memory, the provided helpers |
90 | * will pick the correct operations. Data can be copied into the memory with |
91 | * iosys_map_memcpy_to(). The address can be manipulated with iosys_map_incr(). |
92 | * |
93 | * .. code-block:: c |
94 | * |
95 | * const void *src = ...; // source buffer |
96 | * size_t len = ...; // length of src |
97 | * |
98 | * iosys_map_memcpy_to(&map, src, len); |
99 | * iosys_map_incr(&map, len); // go to first byte after the memcpy |
100 | */ |
101 | |
102 | /** |
103 | * struct iosys_map - Pointer to IO/system memory |
104 | * @vaddr_iomem: The buffer's address if in I/O memory |
105 | * @vaddr: The buffer's address if in system memory |
106 | * @is_iomem: True if the buffer is located in I/O memory, or false |
107 | * otherwise. |
108 | */ |
109 | struct iosys_map { |
110 | union { |
111 | void __iomem *vaddr_iomem; |
112 | void *vaddr; |
113 | }; |
114 | bool is_iomem; |
115 | }; |
116 | |
117 | /** |
118 | * IOSYS_MAP_INIT_VADDR - Initializes struct iosys_map to an address in system memory |
119 | * @vaddr_: A system-memory address |
120 | */ |
121 | #define IOSYS_MAP_INIT_VADDR(vaddr_) \ |
122 | { \ |
123 | .vaddr = (vaddr_), \ |
124 | .is_iomem = false, \ |
125 | } |
126 | |
127 | /** |
128 | * IOSYS_MAP_INIT_VADDR_IOMEM - Initializes struct iosys_map to an address in I/O memory |
129 | * @vaddr_iomem_: An I/O-memory address |
130 | */ |
131 | #define IOSYS_MAP_INIT_VADDR_IOMEM(vaddr_iomem_) \ |
132 | { \ |
133 | .vaddr_iomem = (vaddr_iomem_), \ |
134 | .is_iomem = true, \ |
135 | } |
136 | |
137 | /** |
138 | * IOSYS_MAP_INIT_OFFSET - Initializes struct iosys_map from another iosys_map |
139 | * @map_: The dma-buf mapping structure to copy from |
140 | * @offset_: Offset to add to the other mapping |
141 | * |
142 | * Initializes a new iosys_map struct based on another passed as argument. It |
143 | * does a shallow copy of the struct so it's possible to update the back storage |
144 | * without changing where the original map points to. It is the equivalent of |
145 | * doing: |
146 | * |
147 | * .. code-block:: c |
148 | * |
149 | * iosys_map map = other_map; |
150 | * iosys_map_incr(&map, &offset); |
151 | * |
152 | * Example usage: |
153 | * |
154 | * .. code-block:: c |
155 | * |
156 | * void foo(struct device *dev, struct iosys_map *base_map) |
157 | * { |
158 | * ... |
159 | * struct iosys_map map = IOSYS_MAP_INIT_OFFSET(base_map, FIELD_OFFSET); |
160 | * ... |
161 | * } |
162 | * |
163 | * The advantage of using the initializer over just increasing the offset with |
164 | * iosys_map_incr() like above is that the new map will always point to the |
165 | * right place of the buffer during its scope. It reduces the risk of updating |
166 | * the wrong part of the buffer and having no compiler warning about that. If |
167 | * the assignment to IOSYS_MAP_INIT_OFFSET() is forgotten, the compiler can warn |
168 | * about the use of uninitialized variable. |
169 | */ |
170 | #define IOSYS_MAP_INIT_OFFSET(map_, offset_) ({ \ |
171 | struct iosys_map copy = *map_; \ |
172 | iosys_map_incr(©, offset_); \ |
173 | copy; \ |
174 | }) |
175 | |
176 | /** |
177 | * iosys_map_set_vaddr - Sets a iosys mapping structure to an address in system memory |
178 | * @map: The iosys_map structure |
179 | * @vaddr: A system-memory address |
180 | * |
181 | * Sets the address and clears the I/O-memory flag. |
182 | */ |
183 | static inline void iosys_map_set_vaddr(struct iosys_map *map, void *vaddr) |
184 | { |
185 | map->vaddr = vaddr; |
186 | map->is_iomem = false; |
187 | } |
188 | |
189 | /** |
190 | * iosys_map_set_vaddr_iomem - Sets a iosys mapping structure to an address in I/O memory |
191 | * @map: The iosys_map structure |
192 | * @vaddr_iomem: An I/O-memory address |
193 | * |
194 | * Sets the address and the I/O-memory flag. |
195 | */ |
196 | static inline void iosys_map_set_vaddr_iomem(struct iosys_map *map, |
197 | void __iomem *vaddr_iomem) |
198 | { |
199 | map->vaddr_iomem = vaddr_iomem; |
200 | map->is_iomem = true; |
201 | } |
202 | |
203 | /** |
204 | * iosys_map_is_equal - Compares two iosys mapping structures for equality |
205 | * @lhs: The iosys_map structure |
206 | * @rhs: A iosys_map structure to compare with |
207 | * |
208 | * Two iosys mapping structures are equal if they both refer to the same type of memory |
209 | * and to the same address within that memory. |
210 | * |
211 | * Returns: |
212 | * True is both structures are equal, or false otherwise. |
213 | */ |
214 | static inline bool iosys_map_is_equal(const struct iosys_map *lhs, |
215 | const struct iosys_map *rhs) |
216 | { |
217 | if (lhs->is_iomem != rhs->is_iomem) |
218 | return false; |
219 | else if (lhs->is_iomem) |
220 | return lhs->vaddr_iomem == rhs->vaddr_iomem; |
221 | else |
222 | return lhs->vaddr == rhs->vaddr; |
223 | } |
224 | |
225 | /** |
226 | * iosys_map_is_null - Tests for a iosys mapping to be NULL |
227 | * @map: The iosys_map structure |
228 | * |
229 | * Depending on the state of struct iosys_map.is_iomem, tests if the |
230 | * mapping is NULL. |
231 | * |
232 | * Returns: |
233 | * True if the mapping is NULL, or false otherwise. |
234 | */ |
235 | static inline bool iosys_map_is_null(const struct iosys_map *map) |
236 | { |
237 | if (map->is_iomem) |
238 | return !map->vaddr_iomem; |
239 | return !map->vaddr; |
240 | } |
241 | |
242 | /** |
243 | * iosys_map_is_set - Tests if the iosys mapping has been set |
244 | * @map: The iosys_map structure |
245 | * |
246 | * Depending on the state of struct iosys_map.is_iomem, tests if the |
247 | * mapping has been set. |
248 | * |
249 | * Returns: |
250 | * True if the mapping is been set, or false otherwise. |
251 | */ |
252 | static inline bool iosys_map_is_set(const struct iosys_map *map) |
253 | { |
254 | return !iosys_map_is_null(map); |
255 | } |
256 | |
257 | /** |
258 | * iosys_map_clear - Clears a iosys mapping structure |
259 | * @map: The iosys_map structure |
260 | * |
261 | * Clears all fields to zero, including struct iosys_map.is_iomem, so |
262 | * mapping structures that were set to point to I/O memory are reset for |
263 | * system memory. Pointers are cleared to NULL. This is the default. |
264 | */ |
265 | static inline void iosys_map_clear(struct iosys_map *map) |
266 | { |
267 | if (map->is_iomem) { |
268 | map->vaddr_iomem = NULL; |
269 | map->is_iomem = false; |
270 | } else { |
271 | map->vaddr = NULL; |
272 | } |
273 | } |
274 | |
275 | /** |
276 | * iosys_map_memcpy_to - Memcpy into offset of iosys_map |
277 | * @dst: The iosys_map structure |
278 | * @dst_offset: The offset from which to copy |
279 | * @src: The source buffer |
280 | * @len: The number of byte in src |
281 | * |
282 | * Copies data into a iosys_map with an offset. The source buffer is in |
283 | * system memory. Depending on the buffer's location, the helper picks the |
284 | * correct method of accessing the memory. |
285 | */ |
286 | static inline void iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset, |
287 | const void *src, size_t len) |
288 | { |
289 | if (dst->is_iomem) |
290 | memcpy_toio(dst->vaddr_iomem + dst_offset, src, len); |
291 | else |
292 | memcpy(dst->vaddr + dst_offset, src, len); |
293 | } |
294 | |
295 | /** |
296 | * iosys_map_memcpy_from - Memcpy from iosys_map into system memory |
297 | * @dst: Destination in system memory |
298 | * @src: The iosys_map structure |
299 | * @src_offset: The offset from which to copy |
300 | * @len: The number of byte in src |
301 | * |
302 | * Copies data from a iosys_map with an offset. The dest buffer is in |
303 | * system memory. Depending on the mapping location, the helper picks the |
304 | * correct method of accessing the memory. |
305 | */ |
306 | static inline void iosys_map_memcpy_from(void *dst, const struct iosys_map *src, |
307 | size_t src_offset, size_t len) |
308 | { |
309 | if (src->is_iomem) |
310 | memcpy_fromio(dst, src->vaddr_iomem + src_offset, len); |
311 | else |
312 | memcpy(dst, src->vaddr + src_offset, len); |
313 | } |
314 | |
315 | /** |
316 | * iosys_map_incr - Increments the address stored in a iosys mapping |
317 | * @map: The iosys_map structure |
318 | * @incr: The number of bytes to increment |
319 | * |
320 | * Increments the address stored in a iosys mapping. Depending on the |
321 | * buffer's location, the correct value will be updated. |
322 | */ |
323 | static inline void iosys_map_incr(struct iosys_map *map, size_t incr) |
324 | { |
325 | if (map->is_iomem) |
326 | map->vaddr_iomem += incr; |
327 | else |
328 | map->vaddr += incr; |
329 | } |
330 | |
331 | /** |
332 | * iosys_map_memset - Memset iosys_map |
333 | * @dst: The iosys_map structure |
334 | * @offset: Offset from dst where to start setting value |
335 | * @value: The value to set |
336 | * @len: The number of bytes to set in dst |
337 | * |
338 | * Set value in iosys_map. Depending on the buffer's location, the helper |
339 | * picks the correct method of accessing the memory. |
340 | */ |
341 | static inline void iosys_map_memset(struct iosys_map *dst, size_t offset, |
342 | int value, size_t len) |
343 | { |
344 | if (dst->is_iomem) |
345 | memset_io(dst->vaddr_iomem + offset, value, len); |
346 | else |
347 | memset(dst->vaddr + offset, value, len); |
348 | } |
349 | |
350 | #ifdef CONFIG_64BIT |
351 | #define __iosys_map_rd_io_u64_case(val_, vaddr_iomem_) \ |
352 | u64: val_ = readq(vaddr_iomem_) |
353 | #define __iosys_map_wr_io_u64_case(val_, vaddr_iomem_) \ |
354 | u64: writeq(val_, vaddr_iomem_) |
355 | #else |
356 | #define __iosys_map_rd_io_u64_case(val_, vaddr_iomem_) \ |
357 | u64: memcpy_fromio(&(val_), vaddr_iomem_, sizeof(u64)) |
358 | #define __iosys_map_wr_io_u64_case(val_, vaddr_iomem_) \ |
359 | u64: memcpy_toio(vaddr_iomem_, &(val_), sizeof(u64)) |
360 | #endif |
361 | |
362 | #define __iosys_map_rd_io(val__, vaddr_iomem__, type__) _Generic(val__, \ |
363 | u8: val__ = readb(vaddr_iomem__), \ |
364 | u16: val__ = readw(vaddr_iomem__), \ |
365 | u32: val__ = readl(vaddr_iomem__), \ |
366 | __iosys_map_rd_io_u64_case(val__, vaddr_iomem__)) |
367 | |
368 | #define __iosys_map_rd_sys(val__, vaddr__, type__) \ |
369 | val__ = READ_ONCE(*(type__ *)(vaddr__)) |
370 | |
371 | #define __iosys_map_wr_io(val__, vaddr_iomem__, type__) _Generic(val__, \ |
372 | u8: writeb(val__, vaddr_iomem__), \ |
373 | u16: writew(val__, vaddr_iomem__), \ |
374 | u32: writel(val__, vaddr_iomem__), \ |
375 | __iosys_map_wr_io_u64_case(val__, vaddr_iomem__)) |
376 | |
377 | #define __iosys_map_wr_sys(val__, vaddr__, type__) \ |
378 | WRITE_ONCE(*(type__ *)(vaddr__), val__) |
379 | |
380 | /** |
381 | * iosys_map_rd - Read a C-type value from the iosys_map |
382 | * |
383 | * @map__: The iosys_map structure |
384 | * @offset__: The offset from which to read |
385 | * @type__: Type of the value being read |
386 | * |
387 | * Read a C type value (u8, u16, u32 and u64) from iosys_map. For other types or |
388 | * if pointer may be unaligned (and problematic for the architecture supported), |
389 | * use iosys_map_memcpy_from(). |
390 | * |
391 | * Returns: |
392 | * The value read from the mapping. |
393 | */ |
394 | #define iosys_map_rd(map__, offset__, type__) ({ \ |
395 | type__ val; \ |
396 | if ((map__)->is_iomem) { \ |
397 | __iosys_map_rd_io(val, (map__)->vaddr_iomem + (offset__), type__);\ |
398 | } else { \ |
399 | __iosys_map_rd_sys(val, (map__)->vaddr + (offset__), type__); \ |
400 | } \ |
401 | val; \ |
402 | }) |
403 | |
404 | /** |
405 | * iosys_map_wr - Write a C-type value to the iosys_map |
406 | * |
407 | * @map__: The iosys_map structure |
408 | * @offset__: The offset from the mapping to write to |
409 | * @type__: Type of the value being written |
410 | * @val__: Value to write |
411 | * |
412 | * Write a C type value (u8, u16, u32 and u64) to the iosys_map. For other types |
413 | * or if pointer may be unaligned (and problematic for the architecture |
414 | * supported), use iosys_map_memcpy_to() |
415 | */ |
416 | #define iosys_map_wr(map__, offset__, type__, val__) ({ \ |
417 | type__ val = (val__); \ |
418 | if ((map__)->is_iomem) { \ |
419 | __iosys_map_wr_io(val, (map__)->vaddr_iomem + (offset__), type__);\ |
420 | } else { \ |
421 | __iosys_map_wr_sys(val, (map__)->vaddr + (offset__), type__); \ |
422 | } \ |
423 | }) |
424 | |
425 | /** |
426 | * iosys_map_rd_field - Read a member from a struct in the iosys_map |
427 | * |
428 | * @map__: The iosys_map structure |
429 | * @struct_offset__: Offset from the beginning of the map, where the struct |
430 | * is located |
431 | * @struct_type__: The struct describing the layout of the mapping |
432 | * @field__: Member of the struct to read |
433 | * |
434 | * Read a value from iosys_map considering its layout is described by a C struct |
435 | * starting at @struct_offset__. The field offset and size is calculated and its |
436 | * value read. If the field access would incur in un-aligned access, then either |
437 | * iosys_map_memcpy_from() needs to be used or the architecture must support it. |
438 | * For example: suppose there is a @struct foo defined as below and the value |
439 | * ``foo.field2.inner2`` needs to be read from the iosys_map: |
440 | * |
441 | * .. code-block:: c |
442 | * |
443 | * struct foo { |
444 | * int field1; |
445 | * struct { |
446 | * int inner1; |
447 | * int inner2; |
448 | * } field2; |
449 | * int field3; |
450 | * } __packed; |
451 | * |
452 | * This is the expected memory layout of a buffer using iosys_map_rd_field(): |
453 | * |
454 | * +------------------------------+--------------------------+ |
455 | * | Address | Content | |
456 | * +==============================+==========================+ |
457 | * | buffer + 0000 | start of mmapped buffer | |
458 | * | | pointed by iosys_map | |
459 | * +------------------------------+--------------------------+ |
460 | * | ... | ... | |
461 | * +------------------------------+--------------------------+ |
462 | * | buffer + ``struct_offset__`` | start of ``struct foo`` | |
463 | * +------------------------------+--------------------------+ |
464 | * | ... | ... | |
465 | * +------------------------------+--------------------------+ |
466 | * | buffer + wwww | ``foo.field2.inner2`` | |
467 | * +------------------------------+--------------------------+ |
468 | * | ... | ... | |
469 | * +------------------------------+--------------------------+ |
470 | * | buffer + yyyy | end of ``struct foo`` | |
471 | * +------------------------------+--------------------------+ |
472 | * | ... | ... | |
473 | * +------------------------------+--------------------------+ |
474 | * | buffer + zzzz | end of mmaped buffer | |
475 | * +------------------------------+--------------------------+ |
476 | * |
477 | * Values automatically calculated by this macro or not needed are denoted by |
478 | * wwww, yyyy and zzzz. This is the code to read that value: |
479 | * |
480 | * .. code-block:: c |
481 | * |
482 | * x = iosys_map_rd_field(&map, offset, struct foo, field2.inner2); |
483 | * |
484 | * Returns: |
485 | * The value read from the mapping. |
486 | */ |
487 | #define iosys_map_rd_field(map__, struct_offset__, struct_type__, field__) ({ \ |
488 | struct_type__ *s; \ |
489 | iosys_map_rd(map__, struct_offset__ + offsetof(struct_type__, field__), \ |
490 | typeof(s->field__)); \ |
491 | }) |
492 | |
493 | /** |
494 | * iosys_map_wr_field - Write to a member of a struct in the iosys_map |
495 | * |
496 | * @map__: The iosys_map structure |
497 | * @struct_offset__: Offset from the beginning of the map, where the struct |
498 | * is located |
499 | * @struct_type__: The struct describing the layout of the mapping |
500 | * @field__: Member of the struct to read |
501 | * @val__: Value to write |
502 | * |
503 | * Write a value to the iosys_map considering its layout is described by a C |
504 | * struct starting at @struct_offset__. The field offset and size is calculated |
505 | * and the @val__ is written. If the field access would incur in un-aligned |
506 | * access, then either iosys_map_memcpy_to() needs to be used or the |
507 | * architecture must support it. Refer to iosys_map_rd_field() for expected |
508 | * usage and memory layout. |
509 | */ |
510 | #define iosys_map_wr_field(map__, struct_offset__, struct_type__, field__, val__) ({ \ |
511 | struct_type__ *s; \ |
512 | iosys_map_wr(map__, struct_offset__ + offsetof(struct_type__, field__), \ |
513 | typeof(s->field__), val__); \ |
514 | }) |
515 | |
516 | #endif /* __IOSYS_MAP_H__ */ |
517 | |