]>
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 | { | |
171 | struct regmap *map; | |
172 | int addr_len, size_len; | |
173 | int ret; | |
174 | ||
175 | addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node)); | |
176 | if (addr_len < 0) { | |
6afdb158 QS |
177 | dm_warn("%s: Error while reading the addr length (ret = %d)\n", |
178 | ofnode_get_name(node), addr_len); | |
55a1a09b FA |
179 | return addr_len; |
180 | } | |
181 | ||
182 | size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node)); | |
183 | if (size_len < 0) { | |
6afdb158 QS |
184 | dm_warn("%s: Error while reading the size length: (ret = %d)\n", |
185 | ofnode_get_name(node), size_len); | |
55a1a09b FA |
186 | return size_len; |
187 | } | |
188 | ||
189 | map = regmap_alloc(1); | |
190 | if (!map) | |
191 | return -ENOMEM; | |
192 | ||
193 | ret = init_range(node, map->ranges, addr_len, size_len, index); | |
194 | if (ret) | |
42a4ee8a | 195 | goto err; |
55a1a09b FA |
196 | |
197 | if (ofnode_read_bool(node, "little-endian")) | |
198 | map->endianness = REGMAP_LITTLE_ENDIAN; | |
199 | else if (ofnode_read_bool(node, "big-endian")) | |
200 | map->endianness = REGMAP_BIG_ENDIAN; | |
201 | else if (ofnode_read_bool(node, "native-endian")) | |
202 | map->endianness = REGMAP_NATIVE_ENDIAN; | |
203 | else /* Default: native endianness */ | |
204 | map->endianness = REGMAP_NATIVE_ENDIAN; | |
205 | ||
206 | *mapp = map; | |
207 | ||
42a4ee8a FA |
208 | return 0; |
209 | err: | |
210 | regmap_uninit(map); | |
211 | ||
55a1a09b FA |
212 | return ret; |
213 | } | |
214 | ||
0e01a7c3 PY |
215 | int regmap_init_mem_range(ofnode node, ulong r_start, ulong r_size, |
216 | struct regmap **mapp) | |
217 | { | |
218 | struct regmap *map; | |
219 | struct regmap_range *range; | |
220 | ||
221 | map = regmap_alloc(1); | |
222 | if (!map) | |
223 | return -ENOMEM; | |
224 | ||
225 | range = &map->ranges[0]; | |
226 | range->start = r_start; | |
227 | range->size = r_size; | |
228 | ||
229 | if (ofnode_read_bool(node, "little-endian")) | |
230 | map->endianness = REGMAP_LITTLE_ENDIAN; | |
231 | else if (ofnode_read_bool(node, "big-endian")) | |
232 | map->endianness = REGMAP_BIG_ENDIAN; | |
233 | else if (ofnode_read_bool(node, "native-endian")) | |
234 | map->endianness = REGMAP_NATIVE_ENDIAN; | |
235 | else /* Default: native endianness */ | |
236 | map->endianness = REGMAP_NATIVE_ENDIAN; | |
237 | ||
238 | *mapp = map; | |
239 | return 0; | |
240 | } | |
241 | ||
d3581236 | 242 | int regmap_init_mem(ofnode node, struct regmap **mapp) |
6f98b750 | 243 | { |
6f98b750 | 244 | struct regmap_range *range; |
6f98b750 SG |
245 | struct regmap *map; |
246 | int count; | |
247 | int addr_len, size_len, both_len; | |
6f98b750 | 248 | int len; |
1804044f | 249 | int index; |
42a4ee8a | 250 | int ret; |
6f98b750 | 251 | |
d3581236 | 252 | addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node)); |
b6f58bfd | 253 | if (addr_len < 0) { |
6afdb158 QS |
254 | dm_warn("%s: Error while reading the addr length (ret = %d)\n", |
255 | ofnode_get_name(node), addr_len); | |
b6f58bfd MS |
256 | return addr_len; |
257 | } | |
258 | ||
d3581236 | 259 | size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node)); |
b6f58bfd | 260 | if (size_len < 0) { |
6afdb158 QS |
261 | dm_warn("%s: Error while reading the size length: (ret = %d)\n", |
262 | ofnode_get_name(node), size_len); | |
b6f58bfd MS |
263 | return size_len; |
264 | } | |
265 | ||
6f98b750 | 266 | both_len = addr_len + size_len; |
b6f58bfd | 267 | if (!both_len) { |
6afdb158 QS |
268 | dm_warn("%s: Both addr and size length are zero\n", |
269 | ofnode_get_name(node)); | |
b6f58bfd MS |
270 | return -EINVAL; |
271 | } | |
6f98b750 | 272 | |
d3581236 | 273 | len = ofnode_read_size(node, "reg"); |
2448f607 | 274 | if (len < 0) { |
6afdb158 QS |
275 | dm_warn("%s: Error while reading reg size (ret = %d)\n", |
276 | ofnode_get_name(node), len); | |
23d63267 | 277 | return len; |
2448f607 | 278 | } |
23d63267 | 279 | len /= sizeof(fdt32_t); |
6f98b750 | 280 | count = len / both_len; |
2448f607 | 281 | if (!count) { |
6afdb158 QS |
282 | dm_warn("%s: Not enough data in reg property\n", |
283 | ofnode_get_name(node)); | |
6f98b750 | 284 | return -EINVAL; |
2448f607 | 285 | } |
6f98b750 | 286 | |
8c1de5e0 | 287 | map = regmap_alloc(count); |
6f98b750 SG |
288 | if (!map) |
289 | return -ENOMEM; | |
290 | ||
8c1de5e0 | 291 | for (range = map->ranges, index = 0; count > 0; |
23d63267 | 292 | count--, range++, index++) { |
42a4ee8a | 293 | ret = init_range(node, range, addr_len, size_len, index); |
7f0e3669 | 294 | if (ret) |
42a4ee8a | 295 | goto err; |
6f98b750 SG |
296 | } |
297 | ||
9b77fe3b MS |
298 | if (ofnode_read_bool(node, "little-endian")) |
299 | map->endianness = REGMAP_LITTLE_ENDIAN; | |
300 | else if (ofnode_read_bool(node, "big-endian")) | |
301 | map->endianness = REGMAP_BIG_ENDIAN; | |
302 | else if (ofnode_read_bool(node, "native-endian")) | |
303 | map->endianness = REGMAP_NATIVE_ENDIAN; | |
304 | else /* Default: native endianness */ | |
305 | map->endianness = REGMAP_NATIVE_ENDIAN; | |
306 | ||
6f98b750 SG |
307 | *mapp = map; |
308 | ||
309 | return 0; | |
42a4ee8a FA |
310 | err: |
311 | regmap_uninit(map); | |
312 | ||
313 | return ret; | |
6f98b750 | 314 | } |
ffb22f6b JJH |
315 | |
316 | static void devm_regmap_release(struct udevice *dev, void *res) | |
317 | { | |
318 | regmap_uninit(*(struct regmap **)res); | |
319 | } | |
320 | ||
321 | struct regmap *devm_regmap_init(struct udevice *dev, | |
322 | const struct regmap_bus *bus, | |
323 | void *bus_context, | |
324 | const struct regmap_config *config) | |
325 | { | |
326 | int rc; | |
78aaedba | 327 | struct regmap **mapp, *map; |
ffb22f6b | 328 | |
37e79ee0 | 329 | /* this looks like a leak, but devres takes care of it */ |
ffb22f6b JJH |
330 | mapp = devres_alloc(devm_regmap_release, sizeof(struct regmap *), |
331 | __GFP_ZERO); | |
332 | if (unlikely(!mapp)) | |
333 | return ERR_PTR(-ENOMEM); | |
334 | ||
d8babb95 PY |
335 | if (config && config->r_size != 0) |
336 | rc = regmap_init_mem_range(dev_ofnode(dev), config->r_start, | |
337 | config->r_size, mapp); | |
338 | else | |
339 | rc = regmap_init_mem(dev_ofnode(dev), mapp); | |
ffb22f6b JJH |
340 | if (rc) |
341 | return ERR_PTR(rc); | |
342 | ||
78aaedba | 343 | map = *mapp; |
7aa5ddff | 344 | if (config) { |
78aaedba | 345 | map->width = config->width; |
7aa5ddff PY |
346 | map->reg_offset_shift = config->reg_offset_shift; |
347 | } | |
78aaedba | 348 | |
ffb22f6b JJH |
349 | devres_add(dev, mapp); |
350 | return *mapp; | |
351 | } | |
3b2a29e0 | 352 | #endif |
6f98b750 SG |
353 | |
354 | void *regmap_get_range(struct regmap *map, unsigned int range_num) | |
355 | { | |
356 | struct regmap_range *range; | |
357 | ||
358 | if (range_num >= map->range_count) | |
359 | return NULL; | |
8c1de5e0 | 360 | range = &map->ranges[range_num]; |
6f98b750 SG |
361 | |
362 | return map_sysmem(range->start, range->size); | |
363 | } | |
364 | ||
365 | int regmap_uninit(struct regmap *map) | |
366 | { | |
6f98b750 SG |
367 | free(map); |
368 | ||
369 | return 0; | |
370 | } | |
3bfb8cb4 | 371 | |
9b77fe3b MS |
372 | static inline u8 __read_8(u8 *addr, enum regmap_endianness_t endianness) |
373 | { | |
374 | return readb(addr); | |
375 | } | |
376 | ||
377 | static inline u16 __read_16(u16 *addr, enum regmap_endianness_t endianness) | |
378 | { | |
379 | switch (endianness) { | |
380 | case REGMAP_LITTLE_ENDIAN: | |
381 | return in_le16(addr); | |
382 | case REGMAP_BIG_ENDIAN: | |
383 | return in_be16(addr); | |
384 | case REGMAP_NATIVE_ENDIAN: | |
385 | return readw(addr); | |
386 | } | |
387 | ||
388 | return readw(addr); | |
389 | } | |
390 | ||
391 | static inline u32 __read_32(u32 *addr, enum regmap_endianness_t endianness) | |
392 | { | |
393 | switch (endianness) { | |
394 | case REGMAP_LITTLE_ENDIAN: | |
395 | return in_le32(addr); | |
396 | case REGMAP_BIG_ENDIAN: | |
397 | return in_be32(addr); | |
398 | case REGMAP_NATIVE_ENDIAN: | |
399 | return readl(addr); | |
400 | } | |
401 | ||
402 | return readl(addr); | |
403 | } | |
404 | ||
405 | #if defined(in_le64) && defined(in_be64) && defined(readq) | |
406 | static inline u64 __read_64(u64 *addr, enum regmap_endianness_t endianness) | |
407 | { | |
408 | switch (endianness) { | |
409 | case REGMAP_LITTLE_ENDIAN: | |
410 | return in_le64(addr); | |
411 | case REGMAP_BIG_ENDIAN: | |
412 | return in_be64(addr); | |
413 | case REGMAP_NATIVE_ENDIAN: | |
414 | return readq(addr); | |
415 | } | |
416 | ||
417 | return readq(addr); | |
418 | } | |
419 | #endif | |
420 | ||
d5c7bd98 MS |
421 | int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset, |
422 | void *valp, size_t val_len) | |
84ff8f62 | 423 | { |
d5c7bd98 | 424 | struct regmap_range *range; |
84ff8f62 MS |
425 | void *ptr; |
426 | ||
e21ec17d | 427 | if (do_range_check() && range_num >= map->range_count) { |
6afdb158 QS |
428 | dm_warn("%s: range index %d larger than range count\n", |
429 | __func__, range_num); | |
d5c7bd98 MS |
430 | return -ERANGE; |
431 | } | |
432 | range = &map->ranges[range_num]; | |
433 | ||
7aa5ddff | 434 | offset <<= map->reg_offset_shift; |
e21ec17d SG |
435 | if (do_range_check() && |
436 | (offset + val_len > range->size || offset + val_len < offset)) { | |
6afdb158 | 437 | dm_warn("%s: offset/size combination invalid\n", __func__); |
d5c7bd98 MS |
438 | return -ERANGE; |
439 | } | |
84ff8f62 | 440 | |
b59889bf PY |
441 | ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE); |
442 | ||
84ff8f62 MS |
443 | switch (val_len) { |
444 | case REGMAP_SIZE_8: | |
9b77fe3b | 445 | *((u8 *)valp) = __read_8(ptr, map->endianness); |
84ff8f62 MS |
446 | break; |
447 | case REGMAP_SIZE_16: | |
9b77fe3b | 448 | *((u16 *)valp) = __read_16(ptr, map->endianness); |
84ff8f62 MS |
449 | break; |
450 | case REGMAP_SIZE_32: | |
9b77fe3b | 451 | *((u32 *)valp) = __read_32(ptr, map->endianness); |
84ff8f62 | 452 | break; |
9b77fe3b | 453 | #if defined(in_le64) && defined(in_be64) && defined(readq) |
84ff8f62 | 454 | case REGMAP_SIZE_64: |
9b77fe3b | 455 | *((u64 *)valp) = __read_64(ptr, map->endianness); |
84ff8f62 MS |
456 | break; |
457 | #endif | |
458 | default: | |
6afdb158 | 459 | dm_warn("%s: regmap size %zu unknown\n", __func__, val_len); |
84ff8f62 MS |
460 | return -EINVAL; |
461 | } | |
d5c7bd98 | 462 | |
84ff8f62 MS |
463 | return 0; |
464 | } | |
465 | ||
d5c7bd98 MS |
466 | int regmap_raw_read(struct regmap *map, uint offset, void *valp, size_t val_len) |
467 | { | |
468 | return regmap_raw_read_range(map, 0, offset, valp, val_len); | |
469 | } | |
470 | ||
3bfb8cb4 PB |
471 | int regmap_read(struct regmap *map, uint offset, uint *valp) |
472 | { | |
364bef15 MB |
473 | union { |
474 | u8 v8; | |
475 | u16 v16; | |
476 | u32 v32; | |
477 | u64 v64; | |
478 | } u; | |
479 | int res; | |
480 | ||
481 | res = regmap_raw_read(map, offset, &u, map->width); | |
482 | if (res) | |
483 | return res; | |
484 | ||
485 | switch (map->width) { | |
486 | case REGMAP_SIZE_8: | |
487 | *valp = u.v8; | |
488 | break; | |
489 | case REGMAP_SIZE_16: | |
490 | *valp = u.v16; | |
491 | break; | |
492 | case REGMAP_SIZE_32: | |
493 | *valp = u.v32; | |
494 | break; | |
495 | case REGMAP_SIZE_64: | |
496 | *valp = u.v64; | |
497 | break; | |
498 | default: | |
499 | unreachable(); | |
500 | } | |
501 | ||
502 | return 0; | |
84ff8f62 | 503 | } |
3bfb8cb4 | 504 | |
9b77fe3b MS |
505 | static inline void __write_8(u8 *addr, const u8 *val, |
506 | enum regmap_endianness_t endianness) | |
507 | { | |
508 | writeb(*val, addr); | |
509 | } | |
510 | ||
511 | static inline void __write_16(u16 *addr, const u16 *val, | |
512 | enum regmap_endianness_t endianness) | |
513 | { | |
514 | switch (endianness) { | |
515 | case REGMAP_NATIVE_ENDIAN: | |
516 | writew(*val, addr); | |
517 | break; | |
518 | case REGMAP_LITTLE_ENDIAN: | |
519 | out_le16(addr, *val); | |
520 | break; | |
521 | case REGMAP_BIG_ENDIAN: | |
522 | out_be16(addr, *val); | |
523 | break; | |
524 | } | |
525 | } | |
526 | ||
527 | static inline void __write_32(u32 *addr, const u32 *val, | |
528 | enum regmap_endianness_t endianness) | |
529 | { | |
530 | switch (endianness) { | |
531 | case REGMAP_NATIVE_ENDIAN: | |
532 | writel(*val, addr); | |
533 | break; | |
534 | case REGMAP_LITTLE_ENDIAN: | |
535 | out_le32(addr, *val); | |
536 | break; | |
537 | case REGMAP_BIG_ENDIAN: | |
538 | out_be32(addr, *val); | |
539 | break; | |
540 | } | |
541 | } | |
542 | ||
543 | #if defined(out_le64) && defined(out_be64) && defined(writeq) | |
544 | static inline void __write_64(u64 *addr, const u64 *val, | |
545 | enum regmap_endianness_t endianness) | |
546 | { | |
547 | switch (endianness) { | |
548 | case REGMAP_NATIVE_ENDIAN: | |
549 | writeq(*val, addr); | |
550 | break; | |
551 | case REGMAP_LITTLE_ENDIAN: | |
552 | out_le64(addr, *val); | |
553 | break; | |
554 | case REGMAP_BIG_ENDIAN: | |
555 | out_be64(addr, *val); | |
556 | break; | |
557 | } | |
558 | } | |
559 | #endif | |
560 | ||
d5c7bd98 MS |
561 | int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset, |
562 | const void *val, size_t val_len) | |
84ff8f62 | 563 | { |
d5c7bd98 | 564 | struct regmap_range *range; |
84ff8f62 MS |
565 | void *ptr; |
566 | ||
d5c7bd98 | 567 | if (range_num >= map->range_count) { |
6afdb158 QS |
568 | dm_warn("%s: range index %d larger than range count\n", |
569 | __func__, range_num); | |
d5c7bd98 MS |
570 | return -ERANGE; |
571 | } | |
572 | range = &map->ranges[range_num]; | |
573 | ||
7aa5ddff | 574 | offset <<= map->reg_offset_shift; |
947d4f13 | 575 | if (offset + val_len > range->size || offset + val_len < offset) { |
6afdb158 | 576 | dm_warn("%s: offset/size combination invalid\n", __func__); |
d5c7bd98 MS |
577 | return -ERANGE; |
578 | } | |
84ff8f62 | 579 | |
b59889bf PY |
580 | ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE); |
581 | ||
84ff8f62 MS |
582 | switch (val_len) { |
583 | case REGMAP_SIZE_8: | |
9b77fe3b | 584 | __write_8(ptr, val, map->endianness); |
84ff8f62 MS |
585 | break; |
586 | case REGMAP_SIZE_16: | |
9b77fe3b | 587 | __write_16(ptr, val, map->endianness); |
84ff8f62 MS |
588 | break; |
589 | case REGMAP_SIZE_32: | |
9b77fe3b | 590 | __write_32(ptr, val, map->endianness); |
84ff8f62 | 591 | break; |
9b77fe3b | 592 | #if defined(out_le64) && defined(out_be64) && defined(writeq) |
84ff8f62 | 593 | case REGMAP_SIZE_64: |
9b77fe3b | 594 | __write_64(ptr, val, map->endianness); |
84ff8f62 MS |
595 | break; |
596 | #endif | |
597 | default: | |
6afdb158 | 598 | dm_warn("%s: regmap size %zu unknown\n", __func__, val_len); |
84ff8f62 MS |
599 | return -EINVAL; |
600 | } | |
3bfb8cb4 PB |
601 | |
602 | return 0; | |
603 | } | |
604 | ||
d5c7bd98 MS |
605 | int regmap_raw_write(struct regmap *map, uint offset, const void *val, |
606 | size_t val_len) | |
607 | { | |
608 | return regmap_raw_write_range(map, 0, offset, val, val_len); | |
609 | } | |
610 | ||
3bfb8cb4 PB |
611 | int regmap_write(struct regmap *map, uint offset, uint val) |
612 | { | |
364bef15 MB |
613 | union { |
614 | u8 v8; | |
615 | u16 v16; | |
616 | u32 v32; | |
617 | u64 v64; | |
618 | } u; | |
619 | ||
620 | switch (map->width) { | |
621 | case REGMAP_SIZE_8: | |
622 | u.v8 = val; | |
623 | break; | |
624 | case REGMAP_SIZE_16: | |
625 | u.v16 = val; | |
626 | break; | |
627 | case REGMAP_SIZE_32: | |
628 | u.v32 = val; | |
629 | break; | |
630 | case REGMAP_SIZE_64: | |
631 | u.v64 = val; | |
632 | break; | |
633 | default: | |
6afdb158 QS |
634 | dm_warn("%s: regmap size %zu unknown\n", __func__, |
635 | (size_t)map->width); | |
364bef15 MB |
636 | return -EINVAL; |
637 | } | |
638 | ||
639 | return regmap_raw_write(map, offset, &u, map->width); | |
3bfb8cb4 | 640 | } |
285cbcf9 NA |
641 | |
642 | int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val) | |
643 | { | |
644 | uint reg; | |
645 | int ret; | |
646 | ||
647 | ret = regmap_read(map, offset, ®); | |
648 | if (ret) | |
649 | return ret; | |
650 | ||
651 | reg &= ~mask; | |
652 | ||
5ca5ec1e | 653 | return regmap_write(map, offset, reg | (val & mask)); |
285cbcf9 | 654 | } |
1c4db59d JJH |
655 | |
656 | int regmap_field_read(struct regmap_field *field, unsigned int *val) | |
657 | { | |
658 | int ret; | |
659 | unsigned int reg_val; | |
660 | ||
661 | ret = regmap_read(field->regmap, field->reg, ®_val); | |
662 | if (ret != 0) | |
663 | return ret; | |
664 | ||
665 | reg_val &= field->mask; | |
666 | reg_val >>= field->shift; | |
667 | *val = reg_val; | |
668 | ||
669 | return ret; | |
670 | } | |
671 | ||
672 | int regmap_field_write(struct regmap_field *field, unsigned int val) | |
673 | { | |
674 | return regmap_update_bits(field->regmap, field->reg, field->mask, | |
675 | val << field->shift); | |
676 | } | |
677 | ||
678 | static void regmap_field_init(struct regmap_field *rm_field, | |
679 | struct regmap *regmap, | |
680 | struct reg_field reg_field) | |
681 | { | |
682 | rm_field->regmap = regmap; | |
683 | rm_field->reg = reg_field.reg; | |
684 | rm_field->shift = reg_field.lsb; | |
685 | rm_field->mask = GENMASK(reg_field.msb, reg_field.lsb); | |
686 | } | |
687 | ||
688 | struct regmap_field *devm_regmap_field_alloc(struct udevice *dev, | |
689 | struct regmap *regmap, | |
690 | struct reg_field reg_field) | |
691 | { | |
692 | struct regmap_field *rm_field = devm_kzalloc(dev, sizeof(*rm_field), | |
693 | GFP_KERNEL); | |
694 | if (!rm_field) | |
695 | return ERR_PTR(-ENOMEM); | |
696 | ||
697 | regmap_field_init(rm_field, regmap, reg_field); | |
698 | ||
699 | return rm_field; | |
700 | } | |
701 | ||
702 | void devm_regmap_field_free(struct udevice *dev, struct regmap_field *field) | |
703 | { | |
704 | devm_kfree(dev, field); | |
705 | } | |
706 | ||
707 | struct regmap_field *regmap_field_alloc(struct regmap *regmap, | |
708 | struct reg_field reg_field) | |
709 | { | |
710 | struct regmap_field *rm_field = kzalloc(sizeof(*rm_field), GFP_KERNEL); | |
711 | ||
712 | if (!rm_field) | |
713 | return ERR_PTR(-ENOMEM); | |
714 | ||
715 | regmap_field_init(rm_field, regmap, reg_field); | |
716 | ||
717 | return rm_field; | |
718 | } | |
719 | ||
720 | void regmap_field_free(struct regmap_field *field) | |
721 | { | |
722 | kfree(field); | |
723 | } |