Commit 9b77fe3b80b038af7114f7dae4934773bb026f8e
Committed by
Simon Glass
1 parent
45ef7f55c7
Exists in
smarc_8mq_lf_v2020.04
and in
12 other branches
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 <mario.six@gdsys.cc> Reviewed-by: Simon Glass <sjg@chromium.org> Reviewed-by: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
Showing 2 changed files with 138 additions and 10 deletions Side-by-side Diff
drivers/core/regmap.c
... | ... | @@ -164,6 +164,15 @@ |
164 | 164 | return ret; |
165 | 165 | } |
166 | 166 | |
167 | + if (ofnode_read_bool(node, "little-endian")) | |
168 | + map->endianness = REGMAP_LITTLE_ENDIAN; | |
169 | + else if (ofnode_read_bool(node, "big-endian")) | |
170 | + map->endianness = REGMAP_BIG_ENDIAN; | |
171 | + else if (ofnode_read_bool(node, "native-endian")) | |
172 | + map->endianness = REGMAP_NATIVE_ENDIAN; | |
173 | + else /* Default: native endianness */ | |
174 | + map->endianness = REGMAP_NATIVE_ENDIAN; | |
175 | + | |
167 | 176 | *mapp = map; |
168 | 177 | |
169 | 178 | return 0; |
... | ... | @@ -188,6 +197,55 @@ |
188 | 197 | return 0; |
189 | 198 | } |
190 | 199 | |
200 | +static inline u8 __read_8(u8 *addr, enum regmap_endianness_t endianness) | |
201 | +{ | |
202 | + return readb(addr); | |
203 | +} | |
204 | + | |
205 | +static inline u16 __read_16(u16 *addr, enum regmap_endianness_t endianness) | |
206 | +{ | |
207 | + switch (endianness) { | |
208 | + case REGMAP_LITTLE_ENDIAN: | |
209 | + return in_le16(addr); | |
210 | + case REGMAP_BIG_ENDIAN: | |
211 | + return in_be16(addr); | |
212 | + case REGMAP_NATIVE_ENDIAN: | |
213 | + return readw(addr); | |
214 | + } | |
215 | + | |
216 | + return readw(addr); | |
217 | +} | |
218 | + | |
219 | +static inline u32 __read_32(u32 *addr, enum regmap_endianness_t endianness) | |
220 | +{ | |
221 | + switch (endianness) { | |
222 | + case REGMAP_LITTLE_ENDIAN: | |
223 | + return in_le32(addr); | |
224 | + case REGMAP_BIG_ENDIAN: | |
225 | + return in_be32(addr); | |
226 | + case REGMAP_NATIVE_ENDIAN: | |
227 | + return readl(addr); | |
228 | + } | |
229 | + | |
230 | + return readl(addr); | |
231 | +} | |
232 | + | |
233 | +#if defined(in_le64) && defined(in_be64) && defined(readq) | |
234 | +static inline u64 __read_64(u64 *addr, enum regmap_endianness_t endianness) | |
235 | +{ | |
236 | + switch (endianness) { | |
237 | + case REGMAP_LITTLE_ENDIAN: | |
238 | + return in_le64(addr); | |
239 | + case REGMAP_BIG_ENDIAN: | |
240 | + return in_be64(addr); | |
241 | + case REGMAP_NATIVE_ENDIAN: | |
242 | + return readq(addr); | |
243 | + } | |
244 | + | |
245 | + return readq(addr); | |
246 | +} | |
247 | +#endif | |
248 | + | |
191 | 249 | int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset, |
192 | 250 | void *valp, size_t val_len) |
193 | 251 | { |
194 | 252 | |
195 | 253 | |
196 | 254 | |
197 | 255 | |
... | ... | @@ -210,17 +268,17 @@ |
210 | 268 | |
211 | 269 | switch (val_len) { |
212 | 270 | case REGMAP_SIZE_8: |
213 | - *((u8 *)valp) = readb((u8 *)ptr); | |
271 | + *((u8 *)valp) = __read_8(ptr, map->endianness); | |
214 | 272 | break; |
215 | 273 | case REGMAP_SIZE_16: |
216 | - *((u16 *)valp) = readw((u16 *)ptr); | |
274 | + *((u16 *)valp) = __read_16(ptr, map->endianness); | |
217 | 275 | break; |
218 | 276 | case REGMAP_SIZE_32: |
219 | - *((u32 *)valp) = readl((u32 *)ptr); | |
277 | + *((u32 *)valp) = __read_32(ptr, map->endianness); | |
220 | 278 | break; |
221 | -#if defined(readq) | |
279 | +#if defined(in_le64) && defined(in_be64) && defined(readq) | |
222 | 280 | case REGMAP_SIZE_64: |
223 | - *((u64 *)valp) = readq((u64 *)ptr); | |
281 | + *((u64 *)valp) = __read_64(ptr, map->endianness); | |
224 | 282 | break; |
225 | 283 | #endif |
226 | 284 | default: |
... | ... | @@ -241,6 +299,62 @@ |
241 | 299 | return regmap_raw_read(map, offset, valp, REGMAP_SIZE_32); |
242 | 300 | } |
243 | 301 | |
302 | +static inline void __write_8(u8 *addr, const u8 *val, | |
303 | + enum regmap_endianness_t endianness) | |
304 | +{ | |
305 | + writeb(*val, addr); | |
306 | +} | |
307 | + | |
308 | +static inline void __write_16(u16 *addr, const u16 *val, | |
309 | + enum regmap_endianness_t endianness) | |
310 | +{ | |
311 | + switch (endianness) { | |
312 | + case REGMAP_NATIVE_ENDIAN: | |
313 | + writew(*val, addr); | |
314 | + break; | |
315 | + case REGMAP_LITTLE_ENDIAN: | |
316 | + out_le16(addr, *val); | |
317 | + break; | |
318 | + case REGMAP_BIG_ENDIAN: | |
319 | + out_be16(addr, *val); | |
320 | + break; | |
321 | + } | |
322 | +} | |
323 | + | |
324 | +static inline void __write_32(u32 *addr, const u32 *val, | |
325 | + enum regmap_endianness_t endianness) | |
326 | +{ | |
327 | + switch (endianness) { | |
328 | + case REGMAP_NATIVE_ENDIAN: | |
329 | + writel(*val, addr); | |
330 | + break; | |
331 | + case REGMAP_LITTLE_ENDIAN: | |
332 | + out_le32(addr, *val); | |
333 | + break; | |
334 | + case REGMAP_BIG_ENDIAN: | |
335 | + out_be32(addr, *val); | |
336 | + break; | |
337 | + } | |
338 | +} | |
339 | + | |
340 | +#if defined(out_le64) && defined(out_be64) && defined(writeq) | |
341 | +static inline void __write_64(u64 *addr, const u64 *val, | |
342 | + enum regmap_endianness_t endianness) | |
343 | +{ | |
344 | + switch (endianness) { | |
345 | + case REGMAP_NATIVE_ENDIAN: | |
346 | + writeq(*val, addr); | |
347 | + break; | |
348 | + case REGMAP_LITTLE_ENDIAN: | |
349 | + out_le64(addr, *val); | |
350 | + break; | |
351 | + case REGMAP_BIG_ENDIAN: | |
352 | + out_be64(addr, *val); | |
353 | + break; | |
354 | + } | |
355 | +} | |
356 | +#endif | |
357 | + | |
244 | 358 | int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset, |
245 | 359 | const void *val, size_t val_len) |
246 | 360 | { |
247 | 361 | |
248 | 362 | |
249 | 363 | |
250 | 364 | |
... | ... | @@ -263,17 +377,17 @@ |
263 | 377 | |
264 | 378 | switch (val_len) { |
265 | 379 | case REGMAP_SIZE_8: |
266 | - writeb(*((u8 *)val), (u8 *)ptr); | |
380 | + __write_8(ptr, val, map->endianness); | |
267 | 381 | break; |
268 | 382 | case REGMAP_SIZE_16: |
269 | - writew(*((u16 *)val), (u16 *)ptr); | |
383 | + __write_16(ptr, val, map->endianness); | |
270 | 384 | break; |
271 | 385 | case REGMAP_SIZE_32: |
272 | - writel(*((u32 *)val), (u32 *)ptr); | |
386 | + __write_32(ptr, val, map->endianness); | |
273 | 387 | break; |
274 | -#if defined(writeq) | |
388 | +#if defined(out_le64) && defined(out_be64) && defined(writeq) | |
275 | 389 | case REGMAP_SIZE_64: |
276 | - writeq(*((u64 *)val), (u64 *)ptr); | |
390 | + __write_64(ptr, val, map->endianness); | |
277 | 391 | break; |
278 | 392 | #endif |
279 | 393 | default: |
include/regmap.h
... | ... | @@ -23,6 +23,19 @@ |
23 | 23 | }; |
24 | 24 | |
25 | 25 | /** |
26 | + * enum regmap_endianness_t - Endianness for regmap reads and writes | |
27 | + * | |
28 | + * @REGMAP_NATIVE_ENDIAN: Native endian read/write accesses | |
29 | + * @REGMAP_LITTLE_ENDIAN: Little endian read/write accesses | |
30 | + * @REGMAP_BIG_ENDIAN: Big endian read/write accesses | |
31 | + */ | |
32 | +enum regmap_endianness_t { | |
33 | + REGMAP_NATIVE_ENDIAN, | |
34 | + REGMAP_LITTLE_ENDIAN, | |
35 | + REGMAP_BIG_ENDIAN, | |
36 | +}; | |
37 | + | |
38 | +/** | |
26 | 39 | * struct regmap_range - a register map range |
27 | 40 | * |
28 | 41 | * @start: Start address |
... | ... | @@ -40,6 +53,7 @@ |
40 | 53 | * @ranges: Array of ranges |
41 | 54 | */ |
42 | 55 | struct regmap { |
56 | + enum regmap_endianness_t endianness; | |
43 | 57 | int range_count; |
44 | 58 | struct regmap_range ranges[0]; |
45 | 59 | }; |