]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
6f98b750 SG |
2 | /* |
3 | * Copyright (c) 2015 Google, Inc | |
4 | * Written by Simon Glass <[email protected]> | |
6f98b750 SG |
5 | */ |
6 | ||
e21ec17d SG |
7 | #define LOG_CATEGORY LOGC_DM |
8 | ||
6f98b750 SG |
9 | #include <dm.h> |
10 | #include <errno.h> | |
f7ae49fc | 11 | #include <log.h> |
401d1c4f | 12 | #include <asm/global_data.h> |
b08c8c48 | 13 | #include <linux/libfdt.h> |
6f98b750 SG |
14 | #include <malloc.h> |
15 | #include <mapmem.h> | |
16 | #include <regmap.h> | |
3bfb8cb4 | 17 | #include <asm/io.h> |
23d63267 | 18 | #include <dm/of_addr.h> |
ffb22f6b | 19 | #include <dm/devres.h> |
6afdb158 | 20 | #include <dm/util.h> |
23d63267 | 21 | #include <linux/ioport.h> |
ffb22f6b JJH |
22 | #include <linux/compat.h> |
23 | #include <linux/err.h> | |
1c4db59d JJH |
24 | #include <linux/bitops.h> |
25 | ||
26 | /* | |
27 | * Internal representation of a regmap field. Instead of storing the MSB and | |
28 | * LSB, store the shift and mask. This makes the code a bit cleaner and faster | |
29 | * because the shift and mask don't have to be calculated every time. | |
30 | */ | |
31 | struct regmap_field { | |
32 | struct regmap *regmap; | |
33 | unsigned int mask; | |
34 | /* lsb */ | |
35 | unsigned int shift; | |
36 | unsigned int reg; | |
37 | }; | |
3bfb8cb4 | 38 | |
6f98b750 SG |
39 | DECLARE_GLOBAL_DATA_PTR; |
40 | ||
e21ec17d SG |
41 | /** |
42 | * do_range_check() - Control whether range checks are done | |
43 | * | |
44 | * Returns: true to do range checks, false to skip | |
45 | * | |
46 | * This is used to reduce code size on SPL where range checks are known not to | |
47 | * be needed | |
48 | * | |
49 | * Add this to the top of the file to enable them: #define LOG_DEBUG | |
50 | */ | |
51 | static inline bool do_range_check(void) | |
52 | { | |
53 | return _LOG_DEBUG || !IS_ENABLED(CONFIG_SPL); | |
54 | ||
55 | } | |
56 | ||
9b076095 MS |
57 | /** |
58 | * regmap_alloc() - Allocate a regmap with a given number of ranges. | |
59 | * | |
60 | * @count: Number of ranges to be allocated for the regmap. | |
78aaedba PY |
61 | * |
62 | * The default regmap width is set to REGMAP_SIZE_32. Callers can override it | |
63 | * if they need. | |
64 | * | |
9b076095 MS |
65 | * Return: A pointer to the newly allocated regmap, or NULL on error. |
66 | */ | |
8c1de5e0 | 67 | static struct regmap *regmap_alloc(int count) |
a951431e SG |
68 | { |
69 | struct regmap *map; | |
97d8a697 | 70 | size_t size = sizeof(*map) + sizeof(map->ranges[0]) * count; |
a951431e | 71 | |
97d8a697 | 72 | map = calloc(1, size); |
a951431e SG |
73 | if (!map) |
74 | return NULL; | |
a951431e | 75 | map->range_count = count; |
78aaedba | 76 | map->width = REGMAP_SIZE_32; |
a951431e SG |
77 | |
78 | return map; | |
79 | } | |
80 | ||
3b2a29e0 | 81 | #if CONFIG_IS_ENABLED(OF_PLATDATA) |
0fbb9696 | 82 | int regmap_init_mem_plat(struct udevice *dev, void *reg, int size, int count, |
8a8d24bd | 83 | struct regmap **mapp) |
3b2a29e0 | 84 | { |
1e6ca1a6 SG |
85 | struct regmap_range *range; |
86 | struct regmap *map; | |
87 | ||
8c1de5e0 | 88 | map = regmap_alloc(count); |
1e6ca1a6 SG |
89 | if (!map) |
90 | return -ENOMEM; | |
91 | ||
0fbb9696 JJ |
92 | if (size == sizeof(fdt32_t)) { |
93 | fdt32_t *ptr = (fdt32_t *)reg; | |
94 | ||
95 | for (range = map->ranges; count > 0; | |
96 | ptr += 2, range++, count--) { | |
97 | range->start = *ptr; | |
98 | range->size = ptr[1]; | |
99 | } | |
100 | } else if (size == sizeof(fdt64_t)) { | |
101 | fdt64_t *ptr = (fdt64_t *)reg; | |
102 | ||
103 | for (range = map->ranges; count > 0; | |
104 | ptr += 2, range++, count--) { | |
105 | range->start = *ptr; | |
106 | range->size = ptr[1]; | |
107 | } | |
108 | } else { | |
109 | return -EINVAL; | |
1e6ca1a6 SG |
110 | } |
111 | ||
112 | *mapp = map; | |
113 | ||
3b2a29e0 SG |
114 | return 0; |
115 | } | |
116 | #else | |
7f0e3669 MS |
117 | /** |
118 | * init_range() - Initialize a single range of a regmap | |
119 | * @node: Device node that will use the map in question | |
120 | * @range: Pointer to a regmap_range structure that will be initialized | |
121 | * @addr_len: The length of the addr parts of the reg property | |
122 | * @size_len: The length of the size parts of the reg property | |
123 | * @index: The index of the range to initialize | |
124 | * | |
125 | * This function will read the necessary 'reg' information from the device tree | |
126 | * (the 'addr' part, and the 'length' part), and initialize the range in | |
127 | * quesion. | |
128 | * | |
129 | * Return: 0 if OK, -ve on error | |
130 | */ | |
131 | static int init_range(ofnode node, struct regmap_range *range, int addr_len, | |
132 | int size_len, int index) | |
133 | { | |
134 | fdt_size_t sz; | |
135 | struct resource r; | |
136 | ||
137 | if (of_live_active()) { | |
138 | int ret; | |
139 | ||
140 | ret = of_address_to_resource(ofnode_to_np(node), | |
141 | index, &r); | |
142 | if (ret) { | |
6afdb158 QS |
143 | dm_warn("%s: Could not read resource of range %d (ret = %d)\n", |
144 | ofnode_get_name(node), index, ret); | |
7f0e3669 MS |
145 | return ret; |
146 | } | |
147 | ||
148 | range->start = r.start; | |
149 | range->size = r.end - r.start + 1; | |
150 | } else { | |
151 | int offset = ofnode_to_offset(node); | |
152 | ||
153 | range->start = fdtdec_get_addr_size_fixed(gd->fdt_blob, offset, | |
154 | "reg", index, | |
155 | addr_len, size_len, | |
156 | &sz, true); | |
157 | if (range->start == FDT_ADDR_T_NONE) { | |
6afdb158 QS |
158 | dm_warn("%s: Could not read start of range %d\n", |
159 | ofnode_get_name(node), index); | |
7f0e3669 MS |
160 | return -EINVAL; |
161 | } | |
162 | ||
163 | range->size = sz; | |
164 | } | |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
55a1a09b FA |
169 | int regmap_init_mem_index(ofnode node, struct regmap **mapp, int index) |
170 | { | |
6f5b1686 | 171 | ofnode parent; |
55a1a09b FA |
172 | struct regmap *map; |
173 | int addr_len, size_len; | |
174 | int ret; | |
175 | ||
6f5b1686 JK |
176 | parent = ofnode_get_parent(node); |
177 | ||
178 | addr_len = ofnode_read_simple_addr_cells(parent); | |
55a1a09b | 179 | if (addr_len < 0) { |
6afdb158 QS |
180 | dm_warn("%s: Error while reading the addr length (ret = %d)\n", |
181 | ofnode_get_name(node), addr_len); | |
55a1a09b FA |
182 | return addr_len; |
183 | } | |
184 | ||
6f5b1686 | 185 | size_len = ofnode_read_simple_size_cells(parent); |
55a1a09b | 186 | if (size_len < 0) { |
6afdb158 QS |
187 | dm_warn("%s: Error while reading the size length: (ret = %d)\n", |
188 | ofnode_get_name(node), size_len); | |
55a1a09b FA |
189 | return size_len; |
190 | } | |
191 | ||
192 | map = regmap_alloc(1); | |
193 | if (!map) | |
194 | return -ENOMEM; | |
195 | ||
196 | ret = init_range(node, map->ranges, addr_len, size_len, index); | |
197 | if (ret) | |
42a4ee8a | 198 | goto err; |
55a1a09b FA |
199 | |
200 | if (ofnode_read_bool(node, "little-endian")) | |
201 | map->endianness = REGMAP_LITTLE_ENDIAN; | |
202 | else if (ofnode_read_bool(node, "big-endian")) | |
203 | map->endianness = REGMAP_BIG_ENDIAN; | |
204 | else if (ofnode_read_bool(node, "native-endian")) | |
205 | map->endianness = REGMAP_NATIVE_ENDIAN; | |
206 | else /* Default: native endianness */ | |
207 | map->endianness = REGMAP_NATIVE_ENDIAN; | |
208 | ||
209 | *mapp = map; | |
210 | ||
42a4ee8a FA |
211 | return 0; |
212 | err: | |
213 | regmap_uninit(map); | |
214 | ||
55a1a09b FA |
215 | return ret; |
216 | } | |
217 | ||
0e01a7c3 PY |
218 | int regmap_init_mem_range(ofnode node, ulong r_start, ulong r_size, |
219 | struct regmap **mapp) | |
220 | { | |
221 | struct regmap *map; | |
222 | struct regmap_range *range; | |
223 | ||
224 | map = regmap_alloc(1); | |
225 | if (!map) | |
226 | return -ENOMEM; | |
227 | ||
228 | range = &map->ranges[0]; | |
229 | range->start = r_start; | |
230 | range->size = r_size; | |
231 | ||
232 | if (ofnode_read_bool(node, "little-endian")) | |
233 | map->endianness = REGMAP_LITTLE_ENDIAN; | |
234 | else if (ofnode_read_bool(node, "big-endian")) | |
235 | map->endianness = REGMAP_BIG_ENDIAN; | |
236 | else if (ofnode_read_bool(node, "native-endian")) | |
237 | map->endianness = REGMAP_NATIVE_ENDIAN; | |
238 | else /* Default: native endianness */ | |
239 | map->endianness = REGMAP_NATIVE_ENDIAN; | |
240 | ||
241 | *mapp = map; | |
242 | return 0; | |
243 | } | |
244 | ||
d3581236 | 245 | int regmap_init_mem(ofnode node, struct regmap **mapp) |
6f98b750 | 246 | { |
6f5b1686 | 247 | ofnode parent; |
6f98b750 | 248 | struct regmap_range *range; |
6f98b750 SG |
249 | struct regmap *map; |
250 | int count; | |
251 | int addr_len, size_len, both_len; | |
6f98b750 | 252 | int len; |
1804044f | 253 | int index; |
42a4ee8a | 254 | int ret; |
6f98b750 | 255 | |
6f5b1686 JK |
256 | parent = ofnode_get_parent(node); |
257 | ||
258 | addr_len = ofnode_read_simple_addr_cells(parent); | |
b6f58bfd | 259 | if (addr_len < 0) { |
6afdb158 QS |
260 | dm_warn("%s: Error while reading the addr length (ret = %d)\n", |
261 | ofnode_get_name(node), addr_len); | |
b6f58bfd MS |
262 | return addr_len; |
263 | } | |
264 | ||
6f5b1686 | 265 | size_len = ofnode_read_simple_size_cells(parent); |
b6f58bfd | 266 | if (size_len < 0) { |
6afdb158 QS |
267 | dm_warn("%s: Error while reading the size length: (ret = %d)\n", |
268 | ofnode_get_name(node), size_len); | |
b6f58bfd MS |
269 | return size_len; |
270 | } | |
271 | ||
6f98b750 | 272 | both_len = addr_len + size_len; |
b6f58bfd | 273 | if (!both_len) { |
6afdb158 QS |
274 | dm_warn("%s: Both addr and size length are zero\n", |
275 | ofnode_get_name(node)); | |
b6f58bfd MS |
276 | return -EINVAL; |
277 | } | |
6f98b750 | 278 | |
d3581236 | 279 | len = ofnode_read_size(node, "reg"); |
2448f607 | 280 | if (len < 0) { |
6afdb158 QS |
281 | dm_warn("%s: Error while reading reg size (ret = %d)\n", |
282 | ofnode_get_name(node), len); | |
23d63267 | 283 | return len; |
2448f607 | 284 | } |
23d63267 | 285 | len /= sizeof(fdt32_t); |
6f98b750 | 286 | count = len / both_len; |
2448f607 | 287 | if (!count) { |
6afdb158 QS |
288 | dm_warn("%s: Not enough data in reg property\n", |
289 | ofnode_get_name(node)); | |
6f98b750 | 290 | return -EINVAL; |
2448f607 | 291 | } |
6f98b750 | 292 | |
8c1de5e0 | 293 | map = regmap_alloc(count); |
6f98b750 SG |
294 | if (!map) |
295 | return -ENOMEM; | |
296 | ||
8c1de5e0 | 297 | for (range = map->ranges, index = 0; count > 0; |
23d63267 | 298 | count--, range++, index++) { |
42a4ee8a | 299 | ret = init_range(node, range, addr_len, size_len, index); |
7f0e3669 | 300 | if (ret) |
42a4ee8a | 301 | goto err; |
6f98b750 SG |
302 | } |
303 | ||
9b77fe3b MS |
304 | if (ofnode_read_bool(node, "little-endian")) |
305 | map->endianness = REGMAP_LITTLE_ENDIAN; | |
306 | else if (ofnode_read_bool(node, "big-endian")) | |
307 | map->endianness = REGMAP_BIG_ENDIAN; | |
308 | else if (ofnode_read_bool(node, "native-endian")) | |
309 | map->endianness = REGMAP_NATIVE_ENDIAN; | |
310 | else /* Default: native endianness */ | |
311 | map->endianness = REGMAP_NATIVE_ENDIAN; | |
312 | ||
6f98b750 SG |
313 | *mapp = map; |
314 | ||
315 | return 0; | |
42a4ee8a FA |
316 | err: |
317 | regmap_uninit(map); | |
318 | ||
319 | return ret; | |
6f98b750 | 320 | } |
ffb22f6b JJH |
321 | |
322 | static void devm_regmap_release(struct udevice *dev, void *res) | |
323 | { | |
324 | regmap_uninit(*(struct regmap **)res); | |
325 | } | |
326 | ||
327 | struct regmap *devm_regmap_init(struct udevice *dev, | |
328 | const struct regmap_bus *bus, | |
329 | void *bus_context, | |
330 | const struct regmap_config *config) | |
331 | { | |
332 | int rc; | |
78aaedba | 333 | struct regmap **mapp, *map; |
ffb22f6b | 334 | |
37e79ee0 | 335 | /* this looks like a leak, but devres takes care of it */ |
ffb22f6b JJH |
336 | mapp = devres_alloc(devm_regmap_release, sizeof(struct regmap *), |
337 | __GFP_ZERO); | |
338 | if (unlikely(!mapp)) | |
339 | return ERR_PTR(-ENOMEM); | |
340 | ||
d8babb95 PY |
341 | if (config && config->r_size != 0) |
342 | rc = regmap_init_mem_range(dev_ofnode(dev), config->r_start, | |
343 | config->r_size, mapp); | |
344 | else | |
345 | rc = regmap_init_mem(dev_ofnode(dev), mapp); | |
ffb22f6b JJH |
346 | if (rc) |
347 | return ERR_PTR(rc); | |
348 | ||
78aaedba | 349 | map = *mapp; |
7aa5ddff | 350 | if (config) { |
78aaedba | 351 | map->width = config->width; |
7aa5ddff PY |
352 | map->reg_offset_shift = config->reg_offset_shift; |
353 | } | |
78aaedba | 354 | |
ffb22f6b JJH |
355 | devres_add(dev, mapp); |
356 | return *mapp; | |
357 | } | |
3b2a29e0 | 358 | #endif |
6f98b750 SG |
359 | |
360 | void *regmap_get_range(struct regmap *map, unsigned int range_num) | |
361 | { | |
362 | struct regmap_range *range; | |
363 | ||
364 | if (range_num >= map->range_count) | |
365 | return NULL; | |
8c1de5e0 | 366 | range = &map->ranges[range_num]; |
6f98b750 SG |
367 | |
368 | return map_sysmem(range->start, range->size); | |
369 | } | |
370 | ||
371 | int regmap_uninit(struct regmap *map) | |
372 | { | |
6f98b750 SG |
373 | free(map); |
374 | ||
375 | return 0; | |
376 | } | |
3bfb8cb4 | 377 | |
9b77fe3b MS |
378 | static inline u8 __read_8(u8 *addr, enum regmap_endianness_t endianness) |
379 | { | |
380 | return readb(addr); | |
381 | } | |
382 | ||
383 | static inline u16 __read_16(u16 *addr, enum regmap_endianness_t endianness) | |
384 | { | |
385 | switch (endianness) { | |
386 | case REGMAP_LITTLE_ENDIAN: | |
387 | return in_le16(addr); | |
388 | case REGMAP_BIG_ENDIAN: | |
389 | return in_be16(addr); | |
390 | case REGMAP_NATIVE_ENDIAN: | |
391 | return readw(addr); | |
392 | } | |
393 | ||
394 | return readw(addr); | |
395 | } | |
396 | ||
397 | static inline u32 __read_32(u32 *addr, enum regmap_endianness_t endianness) | |
398 | { | |
399 | switch (endianness) { | |
400 | case REGMAP_LITTLE_ENDIAN: | |
401 | return in_le32(addr); | |
402 | case REGMAP_BIG_ENDIAN: | |
403 | return in_be32(addr); | |
404 | case REGMAP_NATIVE_ENDIAN: | |
405 | return readl(addr); | |
406 | } | |
407 | ||
408 | return readl(addr); | |
409 | } | |
410 | ||
411 | #if defined(in_le64) && defined(in_be64) && defined(readq) | |
412 | static inline u64 __read_64(u64 *addr, enum regmap_endianness_t endianness) | |
413 | { | |
414 | switch (endianness) { | |
415 | case REGMAP_LITTLE_ENDIAN: | |
416 | return in_le64(addr); | |
417 | case REGMAP_BIG_ENDIAN: | |
418 | return in_be64(addr); | |
419 | case REGMAP_NATIVE_ENDIAN: | |
420 | return readq(addr); | |
421 | } | |
422 | ||
423 | return readq(addr); | |
424 | } | |
425 | #endif | |
426 | ||
d5c7bd98 MS |
427 | int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset, |
428 | void *valp, size_t val_len) | |
84ff8f62 | 429 | { |
d5c7bd98 | 430 | struct regmap_range *range; |
84ff8f62 MS |
431 | void *ptr; |
432 | ||
e21ec17d | 433 | if (do_range_check() && range_num >= map->range_count) { |
6afdb158 QS |
434 | dm_warn("%s: range index %d larger than range count\n", |
435 | __func__, range_num); | |
d5c7bd98 MS |
436 | return -ERANGE; |
437 | } | |
438 | range = &map->ranges[range_num]; | |
439 | ||
7aa5ddff | 440 | offset <<= map->reg_offset_shift; |
e21ec17d SG |
441 | if (do_range_check() && |
442 | (offset + val_len > range->size || offset + val_len < offset)) { | |
6afdb158 | 443 | dm_warn("%s: offset/size combination invalid\n", __func__); |
d5c7bd98 MS |
444 | return -ERANGE; |
445 | } | |
84ff8f62 | 446 | |
b59889bf PY |
447 | ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE); |
448 | ||
84ff8f62 MS |
449 | switch (val_len) { |
450 | case REGMAP_SIZE_8: | |
9b77fe3b | 451 | *((u8 *)valp) = __read_8(ptr, map->endianness); |
84ff8f62 MS |
452 | break; |
453 | case REGMAP_SIZE_16: | |
9b77fe3b | 454 | *((u16 *)valp) = __read_16(ptr, map->endianness); |
84ff8f62 MS |
455 | break; |
456 | case REGMAP_SIZE_32: | |
9b77fe3b | 457 | *((u32 *)valp) = __read_32(ptr, map->endianness); |
84ff8f62 | 458 | break; |
9b77fe3b | 459 | #if defined(in_le64) && defined(in_be64) && defined(readq) |
84ff8f62 | 460 | case REGMAP_SIZE_64: |
9b77fe3b | 461 | *((u64 *)valp) = __read_64(ptr, map->endianness); |
84ff8f62 MS |
462 | break; |
463 | #endif | |
464 | default: | |
6afdb158 | 465 | dm_warn("%s: regmap size %zu unknown\n", __func__, val_len); |
84ff8f62 MS |
466 | return -EINVAL; |
467 | } | |
d5c7bd98 | 468 | |
84ff8f62 MS |
469 | return 0; |
470 | } | |
471 | ||
d5c7bd98 MS |
472 | int regmap_raw_read(struct regmap *map, uint offset, void *valp, size_t val_len) |
473 | { | |
474 | return regmap_raw_read_range(map, 0, offset, valp, val_len); | |
475 | } | |
476 | ||
3bfb8cb4 PB |
477 | int regmap_read(struct regmap *map, uint offset, uint *valp) |
478 | { | |
364bef15 MB |
479 | union { |
480 | u8 v8; | |
481 | u16 v16; | |
482 | u32 v32; | |
483 | u64 v64; | |
484 | } u; | |
485 | int res; | |
486 | ||
487 | res = regmap_raw_read(map, offset, &u, map->width); | |
488 | if (res) | |
489 | return res; | |
490 | ||
491 | switch (map->width) { | |
492 | case REGMAP_SIZE_8: | |
493 | *valp = u.v8; | |
494 | break; | |
495 | case REGMAP_SIZE_16: | |
496 | *valp = u.v16; | |
497 | break; | |
498 | case REGMAP_SIZE_32: | |
499 | *valp = u.v32; | |
500 | break; | |
501 | case REGMAP_SIZE_64: | |
502 | *valp = u.v64; | |
503 | break; | |
504 | default: | |
505 | unreachable(); | |
506 | } | |
507 | ||
508 | return 0; | |
84ff8f62 | 509 | } |
3bfb8cb4 | 510 | |
9b77fe3b MS |
511 | static inline void __write_8(u8 *addr, const u8 *val, |
512 | enum regmap_endianness_t endianness) | |
513 | { | |
514 | writeb(*val, addr); | |
515 | } | |
516 | ||
517 | static inline void __write_16(u16 *addr, const u16 *val, | |
518 | enum regmap_endianness_t endianness) | |
519 | { | |
520 | switch (endianness) { | |
521 | case REGMAP_NATIVE_ENDIAN: | |
522 | writew(*val, addr); | |
523 | break; | |
524 | case REGMAP_LITTLE_ENDIAN: | |
525 | out_le16(addr, *val); | |
526 | break; | |
527 | case REGMAP_BIG_ENDIAN: | |
528 | out_be16(addr, *val); | |
529 | break; | |
530 | } | |
531 | } | |
532 | ||
533 | static inline void __write_32(u32 *addr, const u32 *val, | |
534 | enum regmap_endianness_t endianness) | |
535 | { | |
536 | switch (endianness) { | |
537 | case REGMAP_NATIVE_ENDIAN: | |
538 | writel(*val, addr); | |
539 | break; | |
540 | case REGMAP_LITTLE_ENDIAN: | |
541 | out_le32(addr, *val); | |
542 | break; | |
543 | case REGMAP_BIG_ENDIAN: | |
544 | out_be32(addr, *val); | |
545 | break; | |
546 | } | |
547 | } | |
548 | ||
549 | #if defined(out_le64) && defined(out_be64) && defined(writeq) | |
550 | static inline void __write_64(u64 *addr, const u64 *val, | |
551 | enum regmap_endianness_t endianness) | |
552 | { | |
553 | switch (endianness) { | |
554 | case REGMAP_NATIVE_ENDIAN: | |
555 | writeq(*val, addr); | |
556 | break; | |
557 | case REGMAP_LITTLE_ENDIAN: | |
558 | out_le64(addr, *val); | |
559 | break; | |
560 | case REGMAP_BIG_ENDIAN: | |
561 | out_be64(addr, *val); | |
562 | break; | |
563 | } | |
564 | } | |
565 | #endif | |
566 | ||
d5c7bd98 MS |
567 | int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset, |
568 | const void *val, size_t val_len) | |
84ff8f62 | 569 | { |
d5c7bd98 | 570 | struct regmap_range *range; |
84ff8f62 MS |
571 | void *ptr; |
572 | ||
d5c7bd98 | 573 | if (range_num >= map->range_count) { |
6afdb158 QS |
574 | dm_warn("%s: range index %d larger than range count\n", |
575 | __func__, range_num); | |
d5c7bd98 MS |
576 | return -ERANGE; |
577 | } | |
578 | range = &map->ranges[range_num]; | |
579 | ||
7aa5ddff | 580 | offset <<= map->reg_offset_shift; |
947d4f13 | 581 | if (offset + val_len > range->size || offset + val_len < offset) { |
6afdb158 | 582 | dm_warn("%s: offset/size combination invalid\n", __func__); |
d5c7bd98 MS |
583 | return -ERANGE; |
584 | } | |
84ff8f62 | 585 | |
b59889bf PY |
586 | ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE); |
587 | ||
84ff8f62 MS |
588 | switch (val_len) { |
589 | case REGMAP_SIZE_8: | |
9b77fe3b | 590 | __write_8(ptr, val, map->endianness); |
84ff8f62 MS |
591 | break; |
592 | case REGMAP_SIZE_16: | |
9b77fe3b | 593 | __write_16(ptr, val, map->endianness); |
84ff8f62 MS |
594 | break; |
595 | case REGMAP_SIZE_32: | |
9b77fe3b | 596 | __write_32(ptr, val, map->endianness); |
84ff8f62 | 597 | break; |
9b77fe3b | 598 | #if defined(out_le64) && defined(out_be64) && defined(writeq) |
84ff8f62 | 599 | case REGMAP_SIZE_64: |
9b77fe3b | 600 | __write_64(ptr, val, map->endianness); |
84ff8f62 MS |
601 | break; |
602 | #endif | |
603 | default: | |
6afdb158 | 604 | dm_warn("%s: regmap size %zu unknown\n", __func__, val_len); |
84ff8f62 MS |
605 | return -EINVAL; |
606 | } | |
3bfb8cb4 PB |
607 | |
608 | return 0; | |
609 | } | |
610 | ||
d5c7bd98 MS |
611 | int regmap_raw_write(struct regmap *map, uint offset, const void *val, |
612 | size_t val_len) | |
613 | { | |
614 | return regmap_raw_write_range(map, 0, offset, val, val_len); | |
615 | } | |
616 | ||
3bfb8cb4 PB |
617 | int regmap_write(struct regmap *map, uint offset, uint val) |
618 | { | |
364bef15 MB |
619 | union { |
620 | u8 v8; | |
621 | u16 v16; | |
622 | u32 v32; | |
623 | u64 v64; | |
624 | } u; | |
625 | ||
626 | switch (map->width) { | |
627 | case REGMAP_SIZE_8: | |
628 | u.v8 = val; | |
629 | break; | |
630 | case REGMAP_SIZE_16: | |
631 | u.v16 = val; | |
632 | break; | |
633 | case REGMAP_SIZE_32: | |
634 | u.v32 = val; | |
635 | break; | |
636 | case REGMAP_SIZE_64: | |
637 | u.v64 = val; | |
638 | break; | |
639 | default: | |
6afdb158 QS |
640 | dm_warn("%s: regmap size %zu unknown\n", __func__, |
641 | (size_t)map->width); | |
364bef15 MB |
642 | return -EINVAL; |
643 | } | |
644 | ||
645 | return regmap_raw_write(map, offset, &u, map->width); | |
3bfb8cb4 | 646 | } |
285cbcf9 NA |
647 | |
648 | int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val) | |
649 | { | |
650 | uint reg; | |
651 | int ret; | |
652 | ||
653 | ret = regmap_read(map, offset, ®); | |
654 | if (ret) | |
655 | return ret; | |
656 | ||
657 | reg &= ~mask; | |
658 | ||
5ca5ec1e | 659 | return regmap_write(map, offset, reg | (val & mask)); |
285cbcf9 | 660 | } |
1c4db59d JJH |
661 | |
662 | int regmap_field_read(struct regmap_field *field, unsigned int *val) | |
663 | { | |
664 | int ret; | |
665 | unsigned int reg_val; | |
666 | ||
667 | ret = regmap_read(field->regmap, field->reg, ®_val); | |
668 | if (ret != 0) | |
669 | return ret; | |
670 | ||
671 | reg_val &= field->mask; | |
672 | reg_val >>= field->shift; | |
673 | *val = reg_val; | |
674 | ||
675 | return ret; | |
676 | } | |
677 | ||
678 | int regmap_field_write(struct regmap_field *field, unsigned int val) | |
679 | { | |
680 | return regmap_update_bits(field->regmap, field->reg, field->mask, | |
681 | val << field->shift); | |
682 | } | |
683 | ||
684 | static void regmap_field_init(struct regmap_field *rm_field, | |
685 | struct regmap *regmap, | |
686 | struct reg_field reg_field) | |
687 | { | |
688 | rm_field->regmap = regmap; | |
689 | rm_field->reg = reg_field.reg; | |
690 | rm_field->shift = reg_field.lsb; | |
691 | rm_field->mask = GENMASK(reg_field.msb, reg_field.lsb); | |
692 | } | |
693 | ||
694 | struct regmap_field *devm_regmap_field_alloc(struct udevice *dev, | |
695 | struct regmap *regmap, | |
696 | struct reg_field reg_field) | |
697 | { | |
698 | struct regmap_field *rm_field = devm_kzalloc(dev, sizeof(*rm_field), | |
699 | GFP_KERNEL); | |
700 | if (!rm_field) | |
701 | return ERR_PTR(-ENOMEM); | |
702 | ||
703 | regmap_field_init(rm_field, regmap, reg_field); | |
704 | ||
705 | return rm_field; | |
706 | } | |
707 | ||
708 | void devm_regmap_field_free(struct udevice *dev, struct regmap_field *field) | |
709 | { | |
710 | devm_kfree(dev, field); | |
711 | } | |
712 | ||
713 | struct regmap_field *regmap_field_alloc(struct regmap *regmap, | |
714 | struct reg_field reg_field) | |
715 | { | |
716 | struct regmap_field *rm_field = kzalloc(sizeof(*rm_field), GFP_KERNEL); | |
717 | ||
718 | if (!rm_field) | |
719 | return ERR_PTR(-ENOMEM); | |
720 | ||
721 | regmap_field_init(rm_field, regmap, reg_field); | |
722 | ||
723 | return rm_field; | |
724 | } | |
725 | ||
726 | void regmap_field_free(struct regmap_field *field) | |
727 | { | |
728 | kfree(field); | |
729 | } |