xref: /linux-6.15/include/linux/iosys-map.h (revision cccd73d6)
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 
123 /**
124  * iosys_map_set_vaddr - Sets a iosys mapping structure to an address in system memory
125  * @map:	The iosys_map structure
126  * @vaddr:	A system-memory address
127  *
128  * Sets the address and clears the I/O-memory flag.
129  */
130 static inline void iosys_map_set_vaddr(struct iosys_map *map, void *vaddr)
131 {
132 	map->vaddr = vaddr;
133 	map->is_iomem = false;
134 }
135 
136 /**
137  * iosys_map_set_vaddr_iomem - Sets a iosys mapping structure to an address in I/O memory
138  * @map:		The iosys_map structure
139  * @vaddr_iomem:	An I/O-memory address
140  *
141  * Sets the address and the I/O-memory flag.
142  */
143 static inline void iosys_map_set_vaddr_iomem(struct iosys_map *map,
144 					     void __iomem *vaddr_iomem)
145 {
146 	map->vaddr_iomem = vaddr_iomem;
147 	map->is_iomem = true;
148 }
149 
150 /**
151  * iosys_map_is_equal - Compares two iosys mapping structures for equality
152  * @lhs:	The iosys_map structure
153  * @rhs:	A iosys_map structure to compare with
154  *
155  * Two iosys mapping structures are equal if they both refer to the same type of memory
156  * and to the same address within that memory.
157  *
158  * Returns:
159  * True is both structures are equal, or false otherwise.
160  */
161 static inline bool iosys_map_is_equal(const struct iosys_map *lhs,
162 				      const struct iosys_map *rhs)
163 {
164 	if (lhs->is_iomem != rhs->is_iomem)
165 		return false;
166 	else if (lhs->is_iomem)
167 		return lhs->vaddr_iomem == rhs->vaddr_iomem;
168 	else
169 		return lhs->vaddr == rhs->vaddr;
170 }
171 
172 /**
173  * iosys_map_is_null - Tests for a iosys mapping to be NULL
174  * @map:	The iosys_map structure
175  *
176  * Depending on the state of struct iosys_map.is_iomem, tests if the
177  * mapping is NULL.
178  *
179  * Returns:
180  * True if the mapping is NULL, or false otherwise.
181  */
182 static inline bool iosys_map_is_null(const struct iosys_map *map)
183 {
184 	if (map->is_iomem)
185 		return !map->vaddr_iomem;
186 	return !map->vaddr;
187 }
188 
189 /**
190  * iosys_map_is_set - Tests if the iosys mapping has been set
191  * @map:	The iosys_map structure
192  *
193  * Depending on the state of struct iosys_map.is_iomem, tests if the
194  * mapping has been set.
195  *
196  * Returns:
197  * True if the mapping is been set, or false otherwise.
198  */
199 static inline bool iosys_map_is_set(const struct iosys_map *map)
200 {
201 	return !iosys_map_is_null(map);
202 }
203 
204 /**
205  * iosys_map_clear - Clears a iosys mapping structure
206  * @map:	The iosys_map structure
207  *
208  * Clears all fields to zero, including struct iosys_map.is_iomem, so
209  * mapping structures that were set to point to I/O memory are reset for
210  * system memory. Pointers are cleared to NULL. This is the default.
211  */
212 static inline void iosys_map_clear(struct iosys_map *map)
213 {
214 	if (map->is_iomem) {
215 		map->vaddr_iomem = NULL;
216 		map->is_iomem = false;
217 	} else {
218 		map->vaddr = NULL;
219 	}
220 }
221 
222 /**
223  * iosys_map_memcpy_to - Memcpy into offset of iosys_map
224  * @dst:	The iosys_map structure
225  * @dst_offset:	The offset from which to copy
226  * @src:	The source buffer
227  * @len:	The number of byte in src
228  *
229  * Copies data into a iosys_map with an offset. The source buffer is in
230  * system memory. Depending on the buffer's location, the helper picks the
231  * correct method of accessing the memory.
232  */
233 static inline void iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset,
234 				       const void *src, size_t len)
235 {
236 	if (dst->is_iomem)
237 		memcpy_toio(dst->vaddr_iomem + dst_offset, src, len);
238 	else
239 		memcpy(dst->vaddr + dst_offset, src, len);
240 }
241 
242 /**
243  * iosys_map_incr - Increments the address stored in a iosys mapping
244  * @map:	The iosys_map structure
245  * @incr:	The number of bytes to increment
246  *
247  * Increments the address stored in a iosys mapping. Depending on the
248  * buffer's location, the correct value will be updated.
249  */
250 static inline void iosys_map_incr(struct iosys_map *map, size_t incr)
251 {
252 	if (map->is_iomem)
253 		map->vaddr_iomem += incr;
254 	else
255 		map->vaddr += incr;
256 }
257 
258 #endif /* __IOSYS_MAP_H__ */
259