configs: Add sam9x60ek_mmc_defconfig
[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_index(ofnode node, struct regmap **mapp, int index)
112 {
113         struct regmap *map;
114         int addr_len, size_len;
115         int ret;
116
117         addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node));
118         if (addr_len < 0) {
119                 debug("%s: Error while reading the addr length (ret = %d)\n",
120                       ofnode_get_name(node), addr_len);
121                 return addr_len;
122         }
123
124         size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node));
125         if (size_len < 0) {
126                 debug("%s: Error while reading the size length: (ret = %d)\n",
127                       ofnode_get_name(node), size_len);
128                 return size_len;
129         }
130
131         map = regmap_alloc(1);
132         if (!map)
133                 return -ENOMEM;
134
135         ret = init_range(node, map->ranges, addr_len, size_len, index);
136         if (ret)
137                 return ret;
138
139         if (ofnode_read_bool(node, "little-endian"))
140                 map->endianness = REGMAP_LITTLE_ENDIAN;
141         else if (ofnode_read_bool(node, "big-endian"))
142                 map->endianness = REGMAP_BIG_ENDIAN;
143         else if (ofnode_read_bool(node, "native-endian"))
144                 map->endianness = REGMAP_NATIVE_ENDIAN;
145         else /* Default: native endianness */
146                 map->endianness = REGMAP_NATIVE_ENDIAN;
147
148         *mapp = map;
149
150         return ret;
151 }
152
153 int regmap_init_mem(ofnode node, struct regmap **mapp)
154 {
155         struct regmap_range *range;
156         struct regmap *map;
157         int count;
158         int addr_len, size_len, both_len;
159         int len;
160         int index;
161
162         addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node));
163         if (addr_len < 0) {
164                 debug("%s: Error while reading the addr length (ret = %d)\n",
165                       ofnode_get_name(node), addr_len);
166                 return addr_len;
167         }
168
169         size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node));
170         if (size_len < 0) {
171                 debug("%s: Error while reading the size length: (ret = %d)\n",
172                       ofnode_get_name(node), size_len);
173                 return size_len;
174         }
175
176         both_len = addr_len + size_len;
177         if (!both_len) {
178                 debug("%s: Both addr and size length are zero\n",
179                       ofnode_get_name(node));
180                 return -EINVAL;
181         }
182
183         len = ofnode_read_size(node, "reg");
184         if (len < 0) {
185                 debug("%s: Error while reading reg size (ret = %d)\n",
186                       ofnode_get_name(node), len);
187                 return len;
188         }
189         len /= sizeof(fdt32_t);
190         count = len / both_len;
191         if (!count) {
192                 debug("%s: Not enough data in reg property\n",
193                       ofnode_get_name(node));
194                 return -EINVAL;
195         }
196
197         map = regmap_alloc(count);
198         if (!map)
199                 return -ENOMEM;
200
201         for (range = map->ranges, index = 0; count > 0;
202              count--, range++, index++) {
203                 int ret = init_range(node, range, addr_len, size_len, index);
204
205                 if (ret)
206                         return ret;
207         }
208
209         if (ofnode_read_bool(node, "little-endian"))
210                 map->endianness = REGMAP_LITTLE_ENDIAN;
211         else if (ofnode_read_bool(node, "big-endian"))
212                 map->endianness = REGMAP_BIG_ENDIAN;
213         else if (ofnode_read_bool(node, "native-endian"))
214                 map->endianness = REGMAP_NATIVE_ENDIAN;
215         else /* Default: native endianness */
216                 map->endianness = REGMAP_NATIVE_ENDIAN;
217
218         *mapp = map;
219
220         return 0;
221 }
222 #endif
223
224 void *regmap_get_range(struct regmap *map, unsigned int range_num)
225 {
226         struct regmap_range *range;
227
228         if (range_num >= map->range_count)
229                 return NULL;
230         range = &map->ranges[range_num];
231
232         return map_sysmem(range->start, range->size);
233 }
234
235 int regmap_uninit(struct regmap *map)
236 {
237         free(map);
238
239         return 0;
240 }
241
242 static inline u8 __read_8(u8 *addr, enum regmap_endianness_t endianness)
243 {
244         return readb(addr);
245 }
246
247 static inline u16 __read_16(u16 *addr, enum regmap_endianness_t endianness)
248 {
249         switch (endianness) {
250         case REGMAP_LITTLE_ENDIAN:
251                 return in_le16(addr);
252         case REGMAP_BIG_ENDIAN:
253                 return in_be16(addr);
254         case REGMAP_NATIVE_ENDIAN:
255                 return readw(addr);
256         }
257
258         return readw(addr);
259 }
260
261 static inline u32 __read_32(u32 *addr, enum regmap_endianness_t endianness)
262 {
263         switch (endianness) {
264         case REGMAP_LITTLE_ENDIAN:
265                 return in_le32(addr);
266         case REGMAP_BIG_ENDIAN:
267                 return in_be32(addr);
268         case REGMAP_NATIVE_ENDIAN:
269                 return readl(addr);
270         }
271
272         return readl(addr);
273 }
274
275 #if defined(in_le64) && defined(in_be64) && defined(readq)
276 static inline u64 __read_64(u64 *addr, enum regmap_endianness_t endianness)
277 {
278         switch (endianness) {
279         case REGMAP_LITTLE_ENDIAN:
280                 return in_le64(addr);
281         case REGMAP_BIG_ENDIAN:
282                 return in_be64(addr);
283         case REGMAP_NATIVE_ENDIAN:
284                 return readq(addr);
285         }
286
287         return readq(addr);
288 }
289 #endif
290
291 int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset,
292                           void *valp, size_t val_len)
293 {
294         struct regmap_range *range;
295         void *ptr;
296
297         if (range_num >= map->range_count) {
298                 debug("%s: range index %d larger than range count\n",
299                       __func__, range_num);
300                 return -ERANGE;
301         }
302         range = &map->ranges[range_num];
303
304         ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE);
305
306         if (offset + val_len > range->size) {
307                 debug("%s: offset/size combination invalid\n", __func__);
308                 return -ERANGE;
309         }
310
311         switch (val_len) {
312         case REGMAP_SIZE_8:
313                 *((u8 *)valp) = __read_8(ptr, map->endianness);
314                 break;
315         case REGMAP_SIZE_16:
316                 *((u16 *)valp) = __read_16(ptr, map->endianness);
317                 break;
318         case REGMAP_SIZE_32:
319                 *((u32 *)valp) = __read_32(ptr, map->endianness);
320                 break;
321 #if defined(in_le64) && defined(in_be64) && defined(readq)
322         case REGMAP_SIZE_64:
323                 *((u64 *)valp) = __read_64(ptr, map->endianness);
324                 break;
325 #endif
326         default:
327                 debug("%s: regmap size %zu unknown\n", __func__, val_len);
328                 return -EINVAL;
329         }
330
331         return 0;
332 }
333
334 int regmap_raw_read(struct regmap *map, uint offset, void *valp, size_t val_len)
335 {
336         return regmap_raw_read_range(map, 0, offset, valp, val_len);
337 }
338
339 int regmap_read(struct regmap *map, uint offset, uint *valp)
340 {
341         return regmap_raw_read(map, offset, valp, REGMAP_SIZE_32);
342 }
343
344 static inline void __write_8(u8 *addr, const u8 *val,
345                              enum regmap_endianness_t endianness)
346 {
347         writeb(*val, addr);
348 }
349
350 static inline void __write_16(u16 *addr, const u16 *val,
351                               enum regmap_endianness_t endianness)
352 {
353         switch (endianness) {
354         case REGMAP_NATIVE_ENDIAN:
355                 writew(*val, addr);
356                 break;
357         case REGMAP_LITTLE_ENDIAN:
358                 out_le16(addr, *val);
359                 break;
360         case REGMAP_BIG_ENDIAN:
361                 out_be16(addr, *val);
362                 break;
363         }
364 }
365
366 static inline void __write_32(u32 *addr, const u32 *val,
367                               enum regmap_endianness_t endianness)
368 {
369         switch (endianness) {
370         case REGMAP_NATIVE_ENDIAN:
371                 writel(*val, addr);
372                 break;
373         case REGMAP_LITTLE_ENDIAN:
374                 out_le32(addr, *val);
375                 break;
376         case REGMAP_BIG_ENDIAN:
377                 out_be32(addr, *val);
378                 break;
379         }
380 }
381
382 #if defined(out_le64) && defined(out_be64) && defined(writeq)
383 static inline void __write_64(u64 *addr, const u64 *val,
384                               enum regmap_endianness_t endianness)
385 {
386         switch (endianness) {
387         case REGMAP_NATIVE_ENDIAN:
388                 writeq(*val, addr);
389                 break;
390         case REGMAP_LITTLE_ENDIAN:
391                 out_le64(addr, *val);
392                 break;
393         case REGMAP_BIG_ENDIAN:
394                 out_be64(addr, *val);
395                 break;
396         }
397 }
398 #endif
399
400 int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset,
401                            const void *val, size_t val_len)
402 {
403         struct regmap_range *range;
404         void *ptr;
405
406         if (range_num >= map->range_count) {
407                 debug("%s: range index %d larger than range count\n",
408                       __func__, range_num);
409                 return -ERANGE;
410         }
411         range = &map->ranges[range_num];
412
413         ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE);
414
415         if (offset + val_len > range->size) {
416                 debug("%s: offset/size combination invalid\n", __func__);
417                 return -ERANGE;
418         }
419
420         switch (val_len) {
421         case REGMAP_SIZE_8:
422                 __write_8(ptr, val, map->endianness);
423                 break;
424         case REGMAP_SIZE_16:
425                 __write_16(ptr, val, map->endianness);
426                 break;
427         case REGMAP_SIZE_32:
428                 __write_32(ptr, val, map->endianness);
429                 break;
430 #if defined(out_le64) && defined(out_be64) && defined(writeq)
431         case REGMAP_SIZE_64:
432                 __write_64(ptr, val, map->endianness);
433                 break;
434 #endif
435         default:
436                 debug("%s: regmap size %zu unknown\n", __func__, val_len);
437                 return -EINVAL;
438         }
439
440         return 0;
441 }
442
443 int regmap_raw_write(struct regmap *map, uint offset, const void *val,
444                      size_t val_len)
445 {
446         return regmap_raw_write_range(map, 0, offset, val, val_len);
447 }
448
449 int regmap_write(struct regmap *map, uint offset, uint val)
450 {
451         return regmap_raw_write(map, offset, &val, REGMAP_SIZE_32);
452 }
453
454 int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val)
455 {
456         uint reg;
457         int ret;
458
459         ret = regmap_read(map, offset, &reg);
460         if (ret)
461                 return ret;
462
463         reg &= ~mask;
464
465         return regmap_write(map, offset, reg | val);
466 }