From 9b77fe3b80b038af7114f7dae4934773bb026f8e Mon Sep 17 00:00:00 2001 From: Mario Six Date: Mon, 15 Oct 2018 09:24:14 +0200 Subject: [PATCH] regmap: Add endianness support Add support for switching the endianness of regmap accesses via the "little-endian", "big-endian", and "native-endian" boolean properties in the device tree. The default endianness is native endianness. Signed-off-by: Mario Six Reviewed-by: Simon Glass Reviewed-by: Daniel Schwierzeck --- drivers/core/regmap.c | 134 ++++++++++++++++++++++++++++++++++++++---- include/regmap.h | 14 +++++ 2 files changed, 138 insertions(+), 10 deletions(-) diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index 9b2e02af2e..5ef0f71c8b 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -164,6 +164,15 @@ int regmap_init_mem(ofnode node, struct regmap **mapp) return ret; } + if (ofnode_read_bool(node, "little-endian")) + map->endianness = REGMAP_LITTLE_ENDIAN; + else if (ofnode_read_bool(node, "big-endian")) + map->endianness = REGMAP_BIG_ENDIAN; + else if (ofnode_read_bool(node, "native-endian")) + map->endianness = REGMAP_NATIVE_ENDIAN; + else /* Default: native endianness */ + map->endianness = REGMAP_NATIVE_ENDIAN; + *mapp = map; return 0; @@ -188,6 +197,55 @@ int regmap_uninit(struct regmap *map) return 0; } +static inline u8 __read_8(u8 *addr, enum regmap_endianness_t endianness) +{ + return readb(addr); +} + +static inline u16 __read_16(u16 *addr, enum regmap_endianness_t endianness) +{ + switch (endianness) { + case REGMAP_LITTLE_ENDIAN: + return in_le16(addr); + case REGMAP_BIG_ENDIAN: + return in_be16(addr); + case REGMAP_NATIVE_ENDIAN: + return readw(addr); + } + + return readw(addr); +} + +static inline u32 __read_32(u32 *addr, enum regmap_endianness_t endianness) +{ + switch (endianness) { + case REGMAP_LITTLE_ENDIAN: + return in_le32(addr); + case REGMAP_BIG_ENDIAN: + return in_be32(addr); + case REGMAP_NATIVE_ENDIAN: + return readl(addr); + } + + return readl(addr); +} + +#if defined(in_le64) && defined(in_be64) && defined(readq) +static inline u64 __read_64(u64 *addr, enum regmap_endianness_t endianness) +{ + switch (endianness) { + case REGMAP_LITTLE_ENDIAN: + return in_le64(addr); + case REGMAP_BIG_ENDIAN: + return in_be64(addr); + case REGMAP_NATIVE_ENDIAN: + return readq(addr); + } + + return readq(addr); +} +#endif + int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset, void *valp, size_t val_len) { @@ -210,17 +268,17 @@ int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset, switch (val_len) { case REGMAP_SIZE_8: - *((u8 *)valp) = readb((u8 *)ptr); + *((u8 *)valp) = __read_8(ptr, map->endianness); break; case REGMAP_SIZE_16: - *((u16 *)valp) = readw((u16 *)ptr); + *((u16 *)valp) = __read_16(ptr, map->endianness); break; case REGMAP_SIZE_32: - *((u32 *)valp) = readl((u32 *)ptr); + *((u32 *)valp) = __read_32(ptr, map->endianness); break; -#if defined(readq) +#if defined(in_le64) && defined(in_be64) && defined(readq) case REGMAP_SIZE_64: - *((u64 *)valp) = readq((u64 *)ptr); + *((u64 *)valp) = __read_64(ptr, map->endianness); break; #endif default: @@ -241,6 +299,62 @@ int regmap_read(struct regmap *map, uint offset, uint *valp) return regmap_raw_read(map, offset, valp, REGMAP_SIZE_32); } +static inline void __write_8(u8 *addr, const u8 *val, + enum regmap_endianness_t endianness) +{ + writeb(*val, addr); +} + +static inline void __write_16(u16 *addr, const u16 *val, + enum regmap_endianness_t endianness) +{ + switch (endianness) { + case REGMAP_NATIVE_ENDIAN: + writew(*val, addr); + break; + case REGMAP_LITTLE_ENDIAN: + out_le16(addr, *val); + break; + case REGMAP_BIG_ENDIAN: + out_be16(addr, *val); + break; + } +} + +static inline void __write_32(u32 *addr, const u32 *val, + enum regmap_endianness_t endianness) +{ + switch (endianness) { + case REGMAP_NATIVE_ENDIAN: + writel(*val, addr); + break; + case REGMAP_LITTLE_ENDIAN: + out_le32(addr, *val); + break; + case REGMAP_BIG_ENDIAN: + out_be32(addr, *val); + break; + } +} + +#if defined(out_le64) && defined(out_be64) && defined(writeq) +static inline void __write_64(u64 *addr, const u64 *val, + enum regmap_endianness_t endianness) +{ + switch (endianness) { + case REGMAP_NATIVE_ENDIAN: + writeq(*val, addr); + break; + case REGMAP_LITTLE_ENDIAN: + out_le64(addr, *val); + break; + case REGMAP_BIG_ENDIAN: + out_be64(addr, *val); + break; + } +} +#endif + int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset, const void *val, size_t val_len) { @@ -263,17 +377,17 @@ int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset, switch (val_len) { case REGMAP_SIZE_8: - writeb(*((u8 *)val), (u8 *)ptr); + __write_8(ptr, val, map->endianness); break; case REGMAP_SIZE_16: - writew(*((u16 *)val), (u16 *)ptr); + __write_16(ptr, val, map->endianness); break; case REGMAP_SIZE_32: - writel(*((u32 *)val), (u32 *)ptr); + __write_32(ptr, val, map->endianness); break; -#if defined(writeq) +#if defined(out_le64) && defined(out_be64) && defined(writeq) case REGMAP_SIZE_64: - writeq(*((u64 *)val), (u64 *)ptr); + __write_64(ptr, val, map->endianness); break; #endif default: diff --git a/include/regmap.h b/include/regmap.h index 3b7eea5f49..98860c2732 100644 --- a/include/regmap.h +++ b/include/regmap.h @@ -22,6 +22,19 @@ enum regmap_size_t { REGMAP_SIZE_64 = 8, }; +/** + * enum regmap_endianness_t - Endianness for regmap reads and writes + * + * @REGMAP_NATIVE_ENDIAN: Native endian read/write accesses + * @REGMAP_LITTLE_ENDIAN: Little endian read/write accesses + * @REGMAP_BIG_ENDIAN: Big endian read/write accesses + */ +enum regmap_endianness_t { + REGMAP_NATIVE_ENDIAN, + REGMAP_LITTLE_ENDIAN, + REGMAP_BIG_ENDIAN, +}; + /** * struct regmap_range - a register map range * @@ -40,6 +53,7 @@ struct regmap_range { * @ranges: Array of ranges */ struct regmap { + enum regmap_endianness_t endianness; int range_count; struct regmap_range ranges[0]; }; -- 2.25.1