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