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