dm: core: Introduce dev_read_alias_highest_id()
[oweals/u-boot.git] / drivers / core / regmap.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2015 Google, Inc
4  * Written by Simon Glass <sjg@chromium.org>
5  */
6
7 #include <common.h>
8 #include <dm.h>
9 #include <errno.h>
10 #include <linux/libfdt.h>
11 #include <malloc.h>
12 #include <mapmem.h>
13 #include <regmap.h>
14 #include <asm/io.h>
15 #include <dm/of_addr.h>
16 #include <linux/ioport.h>
17
18 DECLARE_GLOBAL_DATA_PTR;
19
20 /**
21  * regmap_alloc() - Allocate a regmap with a given number of ranges.
22  *
23  * @count: Number of ranges to be allocated for the regmap.
24  * Return: A pointer to the newly allocated regmap, or NULL on error.
25  */
26 static struct regmap *regmap_alloc(int count)
27 {
28         struct regmap *map;
29
30         map = malloc(sizeof(*map) + sizeof(map->ranges[0]) * count);
31         if (!map)
32                 return NULL;
33         map->range_count = count;
34
35         return map;
36 }
37
38 #if CONFIG_IS_ENABLED(OF_PLATDATA)
39 int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count,
40                              struct regmap **mapp)
41 {
42         struct regmap_range *range;
43         struct regmap *map;
44
45         map = regmap_alloc(count);
46         if (!map)
47                 return -ENOMEM;
48
49         for (range = map->ranges; count > 0; reg += 2, range++, count--) {
50                 range->start = *reg;
51                 range->size = reg[1];
52         }
53
54         *mapp = map;
55
56         return 0;
57 }
58 #else
59 /**
60  * init_range() - Initialize a single range of a regmap
61  * @node:     Device node that will use the map in question
62  * @range:    Pointer to a regmap_range structure that will be initialized
63  * @addr_len: The length of the addr parts of the reg property
64  * @size_len: The length of the size parts of the reg property
65  * @index:    The index of the range to initialize
66  *
67  * This function will read the necessary 'reg' information from the device tree
68  * (the 'addr' part, and the 'length' part), and initialize the range in
69  * quesion.
70  *
71  * Return: 0 if OK, -ve on error
72  */
73 static int init_range(ofnode node, struct regmap_range *range, int addr_len,
74                       int size_len, int index)
75 {
76         fdt_size_t sz;
77         struct resource r;
78
79         if (of_live_active()) {
80                 int ret;
81
82                 ret = of_address_to_resource(ofnode_to_np(node),
83                                              index, &r);
84                 if (ret) {
85                         debug("%s: Could not read resource of range %d (ret = %d)\n",
86                               ofnode_get_name(node), index, ret);
87                         return ret;
88                 }
89
90                 range->start = r.start;
91                 range->size = r.end - r.start + 1;
92         } else {
93                 int offset = ofnode_to_offset(node);
94
95                 range->start = fdtdec_get_addr_size_fixed(gd->fdt_blob, offset,
96                                                           "reg", index,
97                                                           addr_len, size_len,
98                                                           &sz, true);
99                 if (range->start == FDT_ADDR_T_NONE) {
100                         debug("%s: Could not read start of range %d\n",
101                               ofnode_get_name(node), index);
102                         return -EINVAL;
103                 }
104
105                 range->size = sz;
106         }
107
108         return 0;
109 }
110
111 int regmap_init_mem(ofnode node, struct regmap **mapp)
112 {
113         struct regmap_range *range;
114         struct regmap *map;
115         int count;
116         int addr_len, size_len, both_len;
117         int len;
118         int index;
119
120         addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node));
121         if (addr_len < 0) {
122                 debug("%s: Error while reading the addr length (ret = %d)\n",
123                       ofnode_get_name(node), addr_len);
124                 return addr_len;
125         }
126
127         size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node));
128         if (size_len < 0) {
129                 debug("%s: Error while reading the size length: (ret = %d)\n",
130                       ofnode_get_name(node), size_len);
131                 return size_len;
132         }
133
134         both_len = addr_len + size_len;
135         if (!both_len) {
136                 debug("%s: Both addr and size length are zero\n",
137                       ofnode_get_name(node));
138                 return -EINVAL;
139         }
140
141         len = ofnode_read_size(node, "reg");
142         if (len < 0) {
143                 debug("%s: Error while reading reg size (ret = %d)\n",
144                       ofnode_get_name(node), len);
145                 return len;
146         }
147         len /= sizeof(fdt32_t);
148         count = len / both_len;
149         if (!count) {
150                 debug("%s: Not enough data in reg property\n",
151                       ofnode_get_name(node));
152                 return -EINVAL;
153         }
154
155         map = regmap_alloc(count);
156         if (!map)
157                 return -ENOMEM;
158
159         for (range = map->ranges, index = 0; count > 0;
160              count--, range++, index++) {
161                 int ret = init_range(node, range, addr_len, size_len, index);
162
163                 if (ret)
164                         return ret;
165         }
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
176         *mapp = map;
177
178         return 0;
179 }
180 #endif
181
182 void *regmap_get_range(struct regmap *map, unsigned int range_num)
183 {
184         struct regmap_range *range;
185
186         if (range_num >= map->range_count)
187                 return NULL;
188         range = &map->ranges[range_num];
189
190         return map_sysmem(range->start, range->size);
191 }
192
193 int regmap_uninit(struct regmap *map)
194 {
195         free(map);
196
197         return 0;
198 }
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
249 int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset,
250                           void *valp, size_t val_len)
251 {
252         struct regmap_range *range;
253         void *ptr;
254
255         if (range_num >= map->range_count) {
256                 debug("%s: range index %d larger than range count\n",
257                       __func__, range_num);
258                 return -ERANGE;
259         }
260         range = &map->ranges[range_num];
261
262         ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE);
263
264         if (offset + val_len > range->size) {
265                 debug("%s: offset/size combination invalid\n", __func__);
266                 return -ERANGE;
267         }
268
269         switch (val_len) {
270         case REGMAP_SIZE_8:
271                 *((u8 *)valp) = __read_8(ptr, map->endianness);
272                 break;
273         case REGMAP_SIZE_16:
274                 *((u16 *)valp) = __read_16(ptr, map->endianness);
275                 break;
276         case REGMAP_SIZE_32:
277                 *((u32 *)valp) = __read_32(ptr, map->endianness);
278                 break;
279 #if defined(in_le64) && defined(in_be64) && defined(readq)
280         case REGMAP_SIZE_64:
281                 *((u64 *)valp) = __read_64(ptr, map->endianness);
282                 break;
283 #endif
284         default:
285                 debug("%s: regmap size %zu unknown\n", __func__, val_len);
286                 return -EINVAL;
287         }
288
289         return 0;
290 }
291
292 int regmap_raw_read(struct regmap *map, uint offset, void *valp, size_t val_len)
293 {
294         return regmap_raw_read_range(map, 0, offset, valp, val_len);
295 }
296
297 int regmap_read(struct regmap *map, uint offset, uint *valp)
298 {
299         return regmap_raw_read(map, offset, valp, REGMAP_SIZE_32);
300 }
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
358 int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset,
359                            const void *val, size_t val_len)
360 {
361         struct regmap_range *range;
362         void *ptr;
363
364         if (range_num >= map->range_count) {
365                 debug("%s: range index %d larger than range count\n",
366                       __func__, range_num);
367                 return -ERANGE;
368         }
369         range = &map->ranges[range_num];
370
371         ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE);
372
373         if (offset + val_len > range->size) {
374                 debug("%s: offset/size combination invalid\n", __func__);
375                 return -ERANGE;
376         }
377
378         switch (val_len) {
379         case REGMAP_SIZE_8:
380                 __write_8(ptr, val, map->endianness);
381                 break;
382         case REGMAP_SIZE_16:
383                 __write_16(ptr, val, map->endianness);
384                 break;
385         case REGMAP_SIZE_32:
386                 __write_32(ptr, val, map->endianness);
387                 break;
388 #if defined(out_le64) && defined(out_be64) && defined(writeq)
389         case REGMAP_SIZE_64:
390                 __write_64(ptr, val, map->endianness);
391                 break;
392 #endif
393         default:
394                 debug("%s: regmap size %zu unknown\n", __func__, val_len);
395                 return -EINVAL;
396         }
397
398         return 0;
399 }
400
401 int regmap_raw_write(struct regmap *map, uint offset, const void *val,
402                      size_t val_len)
403 {
404         return regmap_raw_write_range(map, 0, offset, val, val_len);
405 }
406
407 int regmap_write(struct regmap *map, uint offset, uint val)
408 {
409         return regmap_raw_write(map, offset, &val, REGMAP_SIZE_32);
410 }
411
412 int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val)
413 {
414         uint reg;
415         int ret;
416
417         ret = regmap_read(map, offset, &reg);
418         if (ret)
419                 return ret;
420
421         reg &= ~mask;
422
423         return regmap_write(map, offset, reg | val);
424 }