Merge branch 'master' of git://git.denx.de/u-boot-sh
[platform/kernel/u-boot.git] / drivers / core / regmap.c
1 /*
2  * Copyright (c) 2015 Google, Inc
3  * Written by Simon Glass <sjg@chromium.org>
4  *
5  * SPDX-License-Identifier:     GPL-2.0+
6  */
7
8 #include <common.h>
9 #include <dm.h>
10 #include <errno.h>
11 #include <libfdt.h>
12 #include <malloc.h>
13 #include <mapmem.h>
14 #include <regmap.h>
15
16 DECLARE_GLOBAL_DATA_PTR;
17
18 static struct regmap *regmap_alloc_count(int count)
19 {
20         struct regmap *map;
21
22         map = malloc(sizeof(struct regmap));
23         if (!map)
24                 return NULL;
25         if (count <= 1) {
26                 map->range = &map->base_range;
27         } else {
28                 map->range = malloc(count * sizeof(struct regmap_range));
29                 if (!map->range) {
30                         free(map);
31                         return NULL;
32                 }
33         }
34         map->range_count = count;
35
36         return map;
37 }
38
39 #if CONFIG_IS_ENABLED(OF_PLATDATA)
40 int regmap_init_mem_platdata(struct udevice *dev, u32 *reg, int count,
41                              struct regmap **mapp)
42 {
43         struct regmap_range *range;
44         struct regmap *map;
45
46         map = regmap_alloc_count(count);
47         if (!map)
48                 return -ENOMEM;
49
50         map->base = *reg;
51         for (range = map->range; count > 0; reg += 2, range++, count--) {
52                 range->start = *reg;
53                 range->size = reg[1];
54         }
55
56         *mapp = map;
57
58         return 0;
59 }
60 #else
61 int regmap_init_mem(struct udevice *dev, struct regmap **mapp)
62 {
63         const void *blob = gd->fdt_blob;
64         struct regmap_range *range;
65         const fdt32_t *cell;
66         struct regmap *map;
67         int count;
68         int addr_len, size_len, both_len;
69         int parent;
70         int len;
71
72         parent = dev->parent->of_offset;
73         addr_len = fdt_address_cells(blob, parent);
74         size_len = fdt_size_cells(blob, parent);
75         both_len = addr_len + size_len;
76
77         cell = fdt_getprop(blob, dev->of_offset, "reg", &len);
78         len /= sizeof(*cell);
79         count = len / both_len;
80         if (!cell || !count)
81                 return -EINVAL;
82
83         map = regmap_alloc_count(count);
84         if (!map)
85                 return -ENOMEM;
86
87         map->base = fdtdec_get_number(cell, addr_len);
88
89         for (range = map->range; count > 0;
90              count--, cell += both_len, range++) {
91                 range->start = fdtdec_get_number(cell, addr_len);
92                 range->size = fdtdec_get_number(cell + addr_len, size_len);
93         }
94
95         *mapp = map;
96
97         return 0;
98 }
99 #endif
100
101 void *regmap_get_range(struct regmap *map, unsigned int range_num)
102 {
103         struct regmap_range *range;
104
105         if (range_num >= map->range_count)
106                 return NULL;
107         range = &map->range[range_num];
108
109         return map_sysmem(range->start, range->size);
110 }
111
112 int regmap_uninit(struct regmap *map)
113 {
114         if (map->range_count > 1)
115                 free(map->range);
116         free(map);
117
118         return 0;
119 }