Commit e62f25e8 authored by Lucas De Marchi's avatar Lucas De Marchi
Browse files

iosys-map: Add a few more helpers



First the simplest ones:

	- iosys_map_memset(): when abstracting system and I/O memory,
	  just like the memcpy() use case, memset() also has dedicated
	  functions to be called for using IO memory.
	- iosys_map_memcpy_from(): we may need to copy data from I/O
	  memory, not only to.

In certain situations it's useful to be able to read or write to an
offset that is calculated by having the memory layout given by a struct
declaration. Usually we are going to read/write a u8, u16, u32 or u64.

As a pre-requisite for the implementation, add iosys_map_memcpy_from()
to be the equivalent of iosys_map_memcpy_to(), but in the other
direction. Then add 2 pairs of macros:

	- iosys_map_rd() / iosys_map_wr()
	- iosys_map_rd_field() / iosys_map_wr_field()

The first pair takes the C-type and offset to read/write. The second
pair uses a struct describing the layout of the mapping in order to
calculate the offset and size being read/written.

We could use readb, readw, readl, readq and the write* counterparts,
however due to alignment issues this may not work on all architectures.
If alignment needs to be checked to call the right function, it's not
possible to decide at compile-time which function to call: so just leave
the decision to the memcpy function that will do exactly that.

Finally, in order to use the above macros with a map derived from
another, add another initializer: IOSYS_MAP_INIT_OFFSET().

v2:
  - Rework IOSYS_MAP_INIT_OFFSET() so it doesn't rely on aliasing rules
    within the union
  - Add offset to both iosys_map_rd_field() and iosys_map_wr_field() to
    allow the struct itself to be at an offset from the mapping
  - Add documentation to iosys_map_rd_field() with example and expected
    memory layout
v3:
  - Drop kernel.h include as it's not needed anymore

Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: Christian König <christian.koenig@amd.com>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
Cc: dri-devel@lists.freedesktop.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: default avatarLucas De Marchi <lucas.demarchi@intel.com>
Reviewed-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>
Reviewed-by: default avatarMatt Atwood <matthew.s.atwood@intel.com>
Reviewed-by: default avatarThomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20220216174147.3073235-3-lucas.demarchi@intel.com
parent cccd73d6
Loading
Loading
Loading
Loading
+201 −0
Original line number Diff line number Diff line
@@ -120,6 +120,45 @@ struct iosys_map {
		.is_iomem = false,	\
	}

/**
 * IOSYS_MAP_INIT_OFFSET - Initializes struct iosys_map from another iosys_map
 * @map_:	The dma-buf mapping structure to copy from
 * @offset_:	Offset to add to the other mapping
 *
 * Initializes a new iosys_map struct based on another passed as argument. It
 * does a shallow copy of the struct so it's possible to update the back storage
 * without changing where the original map points to. It is the equivalent of
 * doing:
 *
 * .. code-block:: c
 *
 *	iosys_map map = other_map;
 *	iosys_map_incr(&map, &offset);
 *
 * Example usage:
 *
 * .. code-block:: c
 *
 *	void foo(struct device *dev, struct iosys_map *base_map)
 *	{
 *		...
 *		struct iosys_map map = IOSYS_MAP_INIT_OFFSET(base_map, FIELD_OFFSET);
 *		...
 *	}
 *
 * The advantage of using the initializer over just increasing the offset with
 * iosys_map_incr() like above is that the new map will always point to the
 * right place of the buffer during its scope. It reduces the risk of updating
 * the wrong part of the buffer and having no compiler warning about that. If
 * the assignment to IOSYS_MAP_INIT_OFFSET() is forgotten, the compiler can warn
 * about the use of uninitialized variable.
 */
#define IOSYS_MAP_INIT_OFFSET(map_, offset_) ({				\
	struct iosys_map copy = *map_;					\
	iosys_map_incr(&copy, offset_);					\
	copy;								\
})

/**
 * iosys_map_set_vaddr - Sets a iosys mapping structure to an address in system memory
 * @map:	The iosys_map structure
@@ -239,6 +278,26 @@ static inline void iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset,
		memcpy(dst->vaddr + dst_offset, src, len);
}

/**
 * iosys_map_memcpy_from - Memcpy from iosys_map into system memory
 * @dst:	Destination in system memory
 * @src:	The iosys_map structure
 * @src_offset:	The offset from which to copy
 * @len:	The number of byte in src
 *
 * Copies data from a iosys_map with an offset. The dest buffer is in
 * system memory. Depending on the mapping location, the helper picks the
 * correct method of accessing the memory.
 */
static inline void iosys_map_memcpy_from(void *dst, const struct iosys_map *src,
					 size_t src_offset, size_t len)
{
	if (src->is_iomem)
		memcpy_fromio(dst, src->vaddr_iomem + src_offset, len);
	else
		memcpy(dst, src->vaddr + src_offset, len);
}

/**
 * iosys_map_incr - Increments the address stored in a iosys mapping
 * @map:	The iosys_map structure
@@ -255,4 +314,146 @@ static inline void iosys_map_incr(struct iosys_map *map, size_t incr)
		map->vaddr += incr;
}

/**
 * iosys_map_memset - Memset iosys_map
 * @dst:	The iosys_map structure
 * @offset:	Offset from dst where to start setting value
 * @value:	The value to set
 * @len:	The number of bytes to set in dst
 *
 * Set value in iosys_map. Depending on the buffer's location, the helper
 * picks the correct method of accessing the memory.
 */
static inline void iosys_map_memset(struct iosys_map *dst, size_t offset,
				    int value, size_t len)
{
	if (dst->is_iomem)
		memset_io(dst->vaddr_iomem + offset, value, len);
	else
		memset(dst->vaddr + offset, value, len);
}

/**
 * iosys_map_rd - Read a C-type value from the iosys_map
 *
 * @map__:	The iosys_map structure
 * @offset__:	The offset from which to read
 * @type__:	Type of the value being read
 *
 * Read a C type value from iosys_map, handling possible un-aligned accesses to
 * the mapping.
 *
 * Returns:
 * The value read from the mapping.
 */
#define iosys_map_rd(map__, offset__, type__) ({			\
	type__ val;							\
	iosys_map_memcpy_from(&val, map__, offset__, sizeof(val));	\
	val;								\
})

/**
 * iosys_map_wr - Write a C-type value to the iosys_map
 *
 * @map__:	The iosys_map structure
 * @offset__:	The offset from the mapping to write to
 * @type__:	Type of the value being written
 * @val__:	Value to write
 *
 * Write a C-type value to the iosys_map, handling possible un-aligned accesses
 * to the mapping.
 */
#define iosys_map_wr(map__, offset__, type__, val__) ({			\
	type__ val = (val__);						\
	iosys_map_memcpy_to(map__, offset__, &val, sizeof(val));	\
})

/**
 * iosys_map_rd_field - Read a member from a struct in the iosys_map
 *
 * @map__:		The iosys_map structure
 * @struct_offset__:	Offset from the beggining of the map, where the struct
 *			is located
 * @struct_type__:	The struct describing the layout of the mapping
 * @field__:		Member of the struct to read
 *
 * Read a value from iosys_map considering its layout is described by a C struct
 * starting at @struct_offset__. The field offset and size is calculated and its
 * value read handling possible un-aligned memory accesses. For example: suppose
 * there is a @struct foo defined as below and the value ``foo.field2.inner2``
 * needs to be read from the iosys_map:
 *
 * .. code-block:: c
 *
 *	struct foo {
 *		int field1;
 *		struct {
 *			int inner1;
 *			int inner2;
 *		} field2;
 *		int field3;
 *	} __packed;
 *
 * This is the expected memory layout of a buffer using iosys_map_rd_field():
 *
 * +------------------------------+--------------------------+
 * | Address                      | Content                  |
 * +==============================+==========================+
 * | buffer + 0000                | start of mmapped buffer  |
 * |                              | pointed by iosys_map     |
 * +------------------------------+--------------------------+
 * | ...                          | ...                      |
 * +------------------------------+--------------------------+
 * | buffer + ``struct_offset__`` | start of ``struct foo``  |
 * +------------------------------+--------------------------+
 * | ...                          | ...                      |
 * +------------------------------+--------------------------+
 * | buffer + wwww                | ``foo.field2.inner2``    |
 * +------------------------------+--------------------------+
 * | ...                          | ...                      |
 * +------------------------------+--------------------------+
 * | buffer + yyyy                | end of ``struct foo``    |
 * +------------------------------+--------------------------+
 * | ...                          | ...                      |
 * +------------------------------+--------------------------+
 * | buffer + zzzz                | end of mmaped buffer     |
 * +------------------------------+--------------------------+
 *
 * Values automatically calculated by this macro or not needed are denoted by
 * wwww, yyyy and zzzz. This is the code to read that value:
 *
 * .. code-block:: c
 *
 *	x = iosys_map_rd_field(&map, offset, struct foo, field2.inner2);
 *
 * Returns:
 * The value read from the mapping.
 */
#define iosys_map_rd_field(map__, struct_offset__, struct_type__, field__) ({	\
	struct_type__ *s;							\
	iosys_map_rd(map__, struct_offset__ + offsetof(struct_type__, field__),	\
		     typeof(s->field__));					\
})

/**
 * iosys_map_wr_field - Write to a member of a struct in the iosys_map
 *
 * @map__:		The iosys_map structure
 * @struct_offset__:	Offset from the beggining of the map, where the struct
 *			is located
 * @struct_type__:	The struct describing the layout of the mapping
 * @field__:		Member of the struct to read
 * @val__:		Value to write
 *
 * Write a value to the iosys_map considering its layout is described by a C struct
 * starting at @struct_offset__. The field offset and size is calculated and the
 * @val__ is written handling possible un-aligned memory accesses. Refer to
 * iosys_map_rd_field() for expected usage and memory layout.
 */
#define iosys_map_wr_field(map__, struct_offset__, struct_type__, field__, val__) ({	\
	struct_type__ *s;								\
	iosys_map_wr(map__, struct_offset__ + offsetof(struct_type__, field__),		\
		     typeof(s->field__), val__);					\
})

#endif /* __IOSYS_MAP_H__ */