Line data Source code
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 160 : map->vaddr = vaddr;
186 160 : 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 0 : if (lhs->is_iomem != rhs->is_iomem)
218 : return false;
219 0 : else if (lhs->is_iomem)
220 0 : return lhs->vaddr_iomem == rhs->vaddr_iomem;
221 : else
222 0 : 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 0 : if (map->is_iomem)
238 0 : return !map->vaddr_iomem;
239 0 : 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 0 : 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 0 : if (map->is_iomem) {
268 0 : map->vaddr_iomem = NULL;
269 0 : map->is_iomem = false;
270 : } else {
271 0 : 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 108 : static inline void iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset,
287 : const void *src, size_t len)
288 : {
289 108 : if (dst->is_iomem)
290 0 : memcpy_toio(dst->vaddr_iomem + dst_offset, src, len);
291 : else
292 216 : memcpy(dst->vaddr + dst_offset, src, len);
293 108 : }
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 264 : if (map->is_iomem)
326 0 : map->vaddr_iomem += incr;
327 : else
328 264 : 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 beggining 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 beggining 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__ */
|