Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: GPL-2.0-only */
0002 /*
0003  * Pointer abstraction for IO/system memory
0004  */
0005 
0006 #ifndef __IOSYS_MAP_H__
0007 #define __IOSYS_MAP_H__
0008 
0009 #include <linux/compiler_types.h>
0010 #include <linux/io.h>
0011 #include <linux/string.h>
0012 
0013 /**
0014  * DOC: overview
0015  *
0016  * When accessing a memory region, depending on its location, users may have to
0017  * access it with I/O operations or memory load/store operations. For example,
0018  * copying to system memory could be done with memcpy(), copying to I/O memory
0019  * would be done with memcpy_toio().
0020  *
0021  * .. code-block:: c
0022  *
0023  *  void *vaddr = ...; // pointer to system memory
0024  *  memcpy(vaddr, src, len);
0025  *
0026  *  void *vaddr_iomem = ...; // pointer to I/O memory
0027  *  memcpy_toio(vaddr_iomem, src, len);
0028  *
0029  * The user of such pointer may not have information about the mapping of that
0030  * region or may want to have a single code path to handle operations on that
0031  * buffer, regardless if it's located in system or IO memory. The type
0032  * :c:type:`struct iosys_map <iosys_map>` and its helpers abstract that so the
0033  * buffer can be passed around to other drivers or have separate duties inside
0034  * the same driver for allocation, read and write operations.
0035  *
0036  * Open-coding access to :c:type:`struct iosys_map <iosys_map>` is considered
0037  * bad style. Rather then accessing its fields directly, use one of the provided
0038  * helper functions, or implement your own. For example, instances of
0039  * :c:type:`struct iosys_map <iosys_map>` can be initialized statically with
0040  * IOSYS_MAP_INIT_VADDR(), or at runtime with iosys_map_set_vaddr(). These
0041  * helpers will set an address in system memory.
0042  *
0043  * .. code-block:: c
0044  *
0045  *  struct iosys_map map = IOSYS_MAP_INIT_VADDR(0xdeadbeaf);
0046  *
0047  *  iosys_map_set_vaddr(&map, 0xdeadbeaf);
0048  *
0049  * To set an address in I/O memory, use iosys_map_set_vaddr_iomem().
0050  *
0051  * .. code-block:: c
0052  *
0053  *  iosys_map_set_vaddr_iomem(&map, 0xdeadbeaf);
0054  *
0055  * Instances of struct iosys_map do not have to be cleaned up, but
0056  * can be cleared to NULL with iosys_map_clear(). Cleared mappings
0057  * always refer to system memory.
0058  *
0059  * .. code-block:: c
0060  *
0061  *  iosys_map_clear(&map);
0062  *
0063  * Test if a mapping is valid with either iosys_map_is_set() or
0064  * iosys_map_is_null().
0065  *
0066  * .. code-block:: c
0067  *
0068  *  if (iosys_map_is_set(&map) != iosys_map_is_null(&map))
0069  *      // always true
0070  *
0071  * Instances of :c:type:`struct iosys_map <iosys_map>` can be compared for
0072  * equality with iosys_map_is_equal(). Mappings that point to different memory
0073  * spaces, system or I/O, are never equal. That's even true if both spaces are
0074  * located in the same address space, both mappings contain the same address
0075  * value, or both mappings refer to NULL.
0076  *
0077  * .. code-block:: c
0078  *
0079  *  struct iosys_map sys_map; // refers to system memory
0080  *  struct iosys_map io_map; // refers to I/O memory
0081  *
0082  *  if (iosys_map_is_equal(&sys_map, &io_map))
0083  *      // always false
0084  *
0085  * A set up instance of struct iosys_map can be used to access or manipulate the
0086  * buffer memory. Depending on the location of the memory, the provided helpers
0087  * will pick the correct operations. Data can be copied into the memory with
0088  * iosys_map_memcpy_to(). The address can be manipulated with iosys_map_incr().
0089  *
0090  * .. code-block:: c
0091  *
0092  *  const void *src = ...; // source buffer
0093  *  size_t len = ...; // length of src
0094  *
0095  *  iosys_map_memcpy_to(&map, src, len);
0096  *  iosys_map_incr(&map, len); // go to first byte after the memcpy
0097  */
0098 
0099 /**
0100  * struct iosys_map - Pointer to IO/system memory
0101  * @vaddr_iomem:    The buffer's address if in I/O memory
0102  * @vaddr:      The buffer's address if in system memory
0103  * @is_iomem:       True if the buffer is located in I/O memory, or false
0104  *          otherwise.
0105  */
0106 struct iosys_map {
0107     union {
0108         void __iomem *vaddr_iomem;
0109         void *vaddr;
0110     };
0111     bool is_iomem;
0112 };
0113 
0114 /**
0115  * IOSYS_MAP_INIT_VADDR - Initializes struct iosys_map to an address in system memory
0116  * @vaddr_: A system-memory address
0117  */
0118 #define IOSYS_MAP_INIT_VADDR(vaddr_)    \
0119     {               \
0120         .vaddr = (vaddr_),  \
0121         .is_iomem = false,  \
0122     }
0123 
0124 /**
0125  * IOSYS_MAP_INIT_OFFSET - Initializes struct iosys_map from another iosys_map
0126  * @map_:   The dma-buf mapping structure to copy from
0127  * @offset_:    Offset to add to the other mapping
0128  *
0129  * Initializes a new iosys_map struct based on another passed as argument. It
0130  * does a shallow copy of the struct so it's possible to update the back storage
0131  * without changing where the original map points to. It is the equivalent of
0132  * doing:
0133  *
0134  * .. code-block:: c
0135  *
0136  *  iosys_map map = other_map;
0137  *  iosys_map_incr(&map, &offset);
0138  *
0139  * Example usage:
0140  *
0141  * .. code-block:: c
0142  *
0143  *  void foo(struct device *dev, struct iosys_map *base_map)
0144  *  {
0145  *      ...
0146  *      struct iosys_map map = IOSYS_MAP_INIT_OFFSET(base_map, FIELD_OFFSET);
0147  *      ...
0148  *  }
0149  *
0150  * The advantage of using the initializer over just increasing the offset with
0151  * iosys_map_incr() like above is that the new map will always point to the
0152  * right place of the buffer during its scope. It reduces the risk of updating
0153  * the wrong part of the buffer and having no compiler warning about that. If
0154  * the assignment to IOSYS_MAP_INIT_OFFSET() is forgotten, the compiler can warn
0155  * about the use of uninitialized variable.
0156  */
0157 #define IOSYS_MAP_INIT_OFFSET(map_, offset_) ({             \
0158     struct iosys_map copy = *map_;                  \
0159     iosys_map_incr(&copy, offset_);                 \
0160     copy;                               \
0161 })
0162 
0163 /**
0164  * iosys_map_set_vaddr - Sets a iosys mapping structure to an address in system memory
0165  * @map:    The iosys_map structure
0166  * @vaddr:  A system-memory address
0167  *
0168  * Sets the address and clears the I/O-memory flag.
0169  */
0170 static inline void iosys_map_set_vaddr(struct iosys_map *map, void *vaddr)
0171 {
0172     map->vaddr = vaddr;
0173     map->is_iomem = false;
0174 }
0175 
0176 /**
0177  * iosys_map_set_vaddr_iomem - Sets a iosys mapping structure to an address in I/O memory
0178  * @map:        The iosys_map structure
0179  * @vaddr_iomem:    An I/O-memory address
0180  *
0181  * Sets the address and the I/O-memory flag.
0182  */
0183 static inline void iosys_map_set_vaddr_iomem(struct iosys_map *map,
0184                          void __iomem *vaddr_iomem)
0185 {
0186     map->vaddr_iomem = vaddr_iomem;
0187     map->is_iomem = true;
0188 }
0189 
0190 /**
0191  * iosys_map_is_equal - Compares two iosys mapping structures for equality
0192  * @lhs:    The iosys_map structure
0193  * @rhs:    A iosys_map structure to compare with
0194  *
0195  * Two iosys mapping structures are equal if they both refer to the same type of memory
0196  * and to the same address within that memory.
0197  *
0198  * Returns:
0199  * True is both structures are equal, or false otherwise.
0200  */
0201 static inline bool iosys_map_is_equal(const struct iosys_map *lhs,
0202                       const struct iosys_map *rhs)
0203 {
0204     if (lhs->is_iomem != rhs->is_iomem)
0205         return false;
0206     else if (lhs->is_iomem)
0207         return lhs->vaddr_iomem == rhs->vaddr_iomem;
0208     else
0209         return lhs->vaddr == rhs->vaddr;
0210 }
0211 
0212 /**
0213  * iosys_map_is_null - Tests for a iosys mapping to be NULL
0214  * @map:    The iosys_map structure
0215  *
0216  * Depending on the state of struct iosys_map.is_iomem, tests if the
0217  * mapping is NULL.
0218  *
0219  * Returns:
0220  * True if the mapping is NULL, or false otherwise.
0221  */
0222 static inline bool iosys_map_is_null(const struct iosys_map *map)
0223 {
0224     if (map->is_iomem)
0225         return !map->vaddr_iomem;
0226     return !map->vaddr;
0227 }
0228 
0229 /**
0230  * iosys_map_is_set - Tests if the iosys mapping has been set
0231  * @map:    The iosys_map structure
0232  *
0233  * Depending on the state of struct iosys_map.is_iomem, tests if the
0234  * mapping has been set.
0235  *
0236  * Returns:
0237  * True if the mapping is been set, or false otherwise.
0238  */
0239 static inline bool iosys_map_is_set(const struct iosys_map *map)
0240 {
0241     return !iosys_map_is_null(map);
0242 }
0243 
0244 /**
0245  * iosys_map_clear - Clears a iosys mapping structure
0246  * @map:    The iosys_map structure
0247  *
0248  * Clears all fields to zero, including struct iosys_map.is_iomem, so
0249  * mapping structures that were set to point to I/O memory are reset for
0250  * system memory. Pointers are cleared to NULL. This is the default.
0251  */
0252 static inline void iosys_map_clear(struct iosys_map *map)
0253 {
0254     if (map->is_iomem) {
0255         map->vaddr_iomem = NULL;
0256         map->is_iomem = false;
0257     } else {
0258         map->vaddr = NULL;
0259     }
0260 }
0261 
0262 /**
0263  * iosys_map_memcpy_to - Memcpy into offset of iosys_map
0264  * @dst:    The iosys_map structure
0265  * @dst_offset: The offset from which to copy
0266  * @src:    The source buffer
0267  * @len:    The number of byte in src
0268  *
0269  * Copies data into a iosys_map with an offset. The source buffer is in
0270  * system memory. Depending on the buffer's location, the helper picks the
0271  * correct method of accessing the memory.
0272  */
0273 static inline void iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset,
0274                        const void *src, size_t len)
0275 {
0276     if (dst->is_iomem)
0277         memcpy_toio(dst->vaddr_iomem + dst_offset, src, len);
0278     else
0279         memcpy(dst->vaddr + dst_offset, src, len);
0280 }
0281 
0282 /**
0283  * iosys_map_memcpy_from - Memcpy from iosys_map into system memory
0284  * @dst:    Destination in system memory
0285  * @src:    The iosys_map structure
0286  * @src_offset: The offset from which to copy
0287  * @len:    The number of byte in src
0288  *
0289  * Copies data from a iosys_map with an offset. The dest buffer is in
0290  * system memory. Depending on the mapping location, the helper picks the
0291  * correct method of accessing the memory.
0292  */
0293 static inline void iosys_map_memcpy_from(void *dst, const struct iosys_map *src,
0294                      size_t src_offset, size_t len)
0295 {
0296     if (src->is_iomem)
0297         memcpy_fromio(dst, src->vaddr_iomem + src_offset, len);
0298     else
0299         memcpy(dst, src->vaddr + src_offset, len);
0300 }
0301 
0302 /**
0303  * iosys_map_incr - Increments the address stored in a iosys mapping
0304  * @map:    The iosys_map structure
0305  * @incr:   The number of bytes to increment
0306  *
0307  * Increments the address stored in a iosys mapping. Depending on the
0308  * buffer's location, the correct value will be updated.
0309  */
0310 static inline void iosys_map_incr(struct iosys_map *map, size_t incr)
0311 {
0312     if (map->is_iomem)
0313         map->vaddr_iomem += incr;
0314     else
0315         map->vaddr += incr;
0316 }
0317 
0318 /**
0319  * iosys_map_memset - Memset iosys_map
0320  * @dst:    The iosys_map structure
0321  * @offset: Offset from dst where to start setting value
0322  * @value:  The value to set
0323  * @len:    The number of bytes to set in dst
0324  *
0325  * Set value in iosys_map. Depending on the buffer's location, the helper
0326  * picks the correct method of accessing the memory.
0327  */
0328 static inline void iosys_map_memset(struct iosys_map *dst, size_t offset,
0329                     int value, size_t len)
0330 {
0331     if (dst->is_iomem)
0332         memset_io(dst->vaddr_iomem + offset, value, len);
0333     else
0334         memset(dst->vaddr + offset, value, len);
0335 }
0336 
0337 #ifdef CONFIG_64BIT
0338 #define __iosys_map_rd_io_u64_case(val_, vaddr_iomem_)              \
0339     u64: val_ = readq(vaddr_iomem_)
0340 #define __iosys_map_wr_io_u64_case(val_, vaddr_iomem_)              \
0341     u64: writeq(val_, vaddr_iomem_)
0342 #else
0343 #define __iosys_map_rd_io_u64_case(val_, vaddr_iomem_)              \
0344     u64: memcpy_fromio(&(val_), vaddr_iomem_, sizeof(u64))
0345 #define __iosys_map_wr_io_u64_case(val_, vaddr_iomem_)              \
0346     u64: memcpy_toio(vaddr_iomem_, &(val_), sizeof(u64))
0347 #endif
0348 
0349 #define __iosys_map_rd_io(val__, vaddr_iomem__, type__) _Generic(val__,     \
0350     u8: val__ = readb(vaddr_iomem__),                   \
0351     u16: val__ = readw(vaddr_iomem__),                  \
0352     u32: val__ = readl(vaddr_iomem__),                  \
0353     __iosys_map_rd_io_u64_case(val__, vaddr_iomem__))
0354 
0355 #define __iosys_map_rd_sys(val__, vaddr__, type__)              \
0356     val__ = READ_ONCE(*(type__ *)(vaddr__))
0357 
0358 #define __iosys_map_wr_io(val__, vaddr_iomem__, type__) _Generic(val__,     \
0359     u8: writeb(val__, vaddr_iomem__),                   \
0360     u16: writew(val__, vaddr_iomem__),                  \
0361     u32: writel(val__, vaddr_iomem__),                  \
0362     __iosys_map_wr_io_u64_case(val__, vaddr_iomem__))
0363 
0364 #define __iosys_map_wr_sys(val__, vaddr__, type__)              \
0365     WRITE_ONCE(*(type__ *)(vaddr__), val__)
0366 
0367 /**
0368  * iosys_map_rd - Read a C-type value from the iosys_map
0369  *
0370  * @map__:  The iosys_map structure
0371  * @offset__:   The offset from which to read
0372  * @type__: Type of the value being read
0373  *
0374  * Read a C type value (u8, u16, u32 and u64) from iosys_map. For other types or
0375  * if pointer may be unaligned (and problematic for the architecture supported),
0376  * use iosys_map_memcpy_from().
0377  *
0378  * Returns:
0379  * The value read from the mapping.
0380  */
0381 #define iosys_map_rd(map__, offset__, type__) ({                \
0382     type__ val;                             \
0383     if ((map__)->is_iomem) {                        \
0384         __iosys_map_rd_io(val, (map__)->vaddr_iomem + (offset__), type__);\
0385     } else {                                \
0386         __iosys_map_rd_sys(val, (map__)->vaddr + (offset__), type__);   \
0387     }                                   \
0388     val;                                    \
0389 })
0390 
0391 /**
0392  * iosys_map_wr - Write a C-type value to the iosys_map
0393  *
0394  * @map__:  The iosys_map structure
0395  * @offset__:   The offset from the mapping to write to
0396  * @type__: Type of the value being written
0397  * @val__:  Value to write
0398  *
0399  * Write a C type value (u8, u16, u32 and u64) to the iosys_map. For other types
0400  * or if pointer may be unaligned (and problematic for the architecture
0401  * supported), use iosys_map_memcpy_to()
0402  */
0403 #define iosys_map_wr(map__, offset__, type__, val__) ({             \
0404     type__ val = (val__);                           \
0405     if ((map__)->is_iomem) {                        \
0406         __iosys_map_wr_io(val, (map__)->vaddr_iomem + (offset__), type__);\
0407     } else {                                \
0408         __iosys_map_wr_sys(val, (map__)->vaddr + (offset__), type__);   \
0409     }                                   \
0410 })
0411 
0412 /**
0413  * iosys_map_rd_field - Read a member from a struct in the iosys_map
0414  *
0415  * @map__:      The iosys_map structure
0416  * @struct_offset__:    Offset from the beggining of the map, where the struct
0417  *          is located
0418  * @struct_type__:  The struct describing the layout of the mapping
0419  * @field__:        Member of the struct to read
0420  *
0421  * Read a value from iosys_map considering its layout is described by a C struct
0422  * starting at @struct_offset__. The field offset and size is calculated and its
0423  * value read. If the field access would incur in un-aligned access, then either
0424  * iosys_map_memcpy_from() needs to be used or the architecture must support it.
0425  * For example: suppose there is a @struct foo defined as below and the value
0426  * ``foo.field2.inner2`` needs to be read from the iosys_map:
0427  *
0428  * .. code-block:: c
0429  *
0430  *  struct foo {
0431  *      int field1;
0432  *      struct {
0433  *          int inner1;
0434  *          int inner2;
0435  *      } field2;
0436  *      int field3;
0437  *  } __packed;
0438  *
0439  * This is the expected memory layout of a buffer using iosys_map_rd_field():
0440  *
0441  * +------------------------------+--------------------------+
0442  * | Address                      | Content                  |
0443  * +==============================+==========================+
0444  * | buffer + 0000                | start of mmapped buffer  |
0445  * |                              | pointed by iosys_map     |
0446  * +------------------------------+--------------------------+
0447  * | ...                          | ...                      |
0448  * +------------------------------+--------------------------+
0449  * | buffer + ``struct_offset__`` | start of ``struct foo``  |
0450  * +------------------------------+--------------------------+
0451  * | ...                          | ...                      |
0452  * +------------------------------+--------------------------+
0453  * | buffer + wwww                | ``foo.field2.inner2``    |
0454  * +------------------------------+--------------------------+
0455  * | ...                          | ...                      |
0456  * +------------------------------+--------------------------+
0457  * | buffer + yyyy                | end of ``struct foo``    |
0458  * +------------------------------+--------------------------+
0459  * | ...                          | ...                      |
0460  * +------------------------------+--------------------------+
0461  * | buffer + zzzz                | end of mmaped buffer     |
0462  * +------------------------------+--------------------------+
0463  *
0464  * Values automatically calculated by this macro or not needed are denoted by
0465  * wwww, yyyy and zzzz. This is the code to read that value:
0466  *
0467  * .. code-block:: c
0468  *
0469  *  x = iosys_map_rd_field(&map, offset, struct foo, field2.inner2);
0470  *
0471  * Returns:
0472  * The value read from the mapping.
0473  */
0474 #define iosys_map_rd_field(map__, struct_offset__, struct_type__, field__) ({   \
0475     struct_type__ *s;                           \
0476     iosys_map_rd(map__, struct_offset__ + offsetof(struct_type__, field__), \
0477              typeof(s->field__));                   \
0478 })
0479 
0480 /**
0481  * iosys_map_wr_field - Write to a member of a struct in the iosys_map
0482  *
0483  * @map__:      The iosys_map structure
0484  * @struct_offset__:    Offset from the beggining of the map, where the struct
0485  *          is located
0486  * @struct_type__:  The struct describing the layout of the mapping
0487  * @field__:        Member of the struct to read
0488  * @val__:      Value to write
0489  *
0490  * Write a value to the iosys_map considering its layout is described by a C
0491  * struct starting at @struct_offset__. The field offset and size is calculated
0492  * and the @val__ is written. If the field access would incur in un-aligned
0493  * access, then either iosys_map_memcpy_to() needs to be used or the
0494  * architecture must support it. Refer to iosys_map_rd_field() for expected
0495  * usage and memory layout.
0496  */
0497 #define iosys_map_wr_field(map__, struct_offset__, struct_type__, field__, val__) ({    \
0498     struct_type__ *s;                               \
0499     iosys_map_wr(map__, struct_offset__ + offsetof(struct_type__, field__),     \
0500              typeof(s->field__), val__);                    \
0501 })
0502 
0503 #endif /* __IOSYS_MAP_H__ */