regmap: Introduce init_range
[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                 return len;
144         len /= sizeof(fdt32_t);
145         count = len / both_len;
146         if (!count)
147                 return -EINVAL;
148
149         map = regmap_alloc(count);
150         if (!map)
151                 return -ENOMEM;
152
153         for (range = map->ranges, index = 0; count > 0;
154              count--, range++, index++) {
155                 int ret = init_range(node, range, addr_len, size_len, index);
156
157                 if (ret)
158                         return ret;
159         }
160
161         *mapp = map;
162
163         return 0;
164 }
165 #endif
166
167 void *regmap_get_range(struct regmap *map, unsigned int range_num)
168 {
169         struct regmap_range *range;
170
171         if (range_num >= map->range_count)
172                 return NULL;
173         range = &map->ranges[range_num];
174
175         return map_sysmem(range->start, range->size);
176 }
177
178 int regmap_uninit(struct regmap *map)
179 {
180         free(map);
181
182         return 0;
183 }
184
185 int regmap_read(struct regmap *map, uint offset, uint *valp)
186 {
187         u32 *ptr = map_physmem(map->ranges[0].start + offset, 4, MAP_NOCACHE);
188
189         *valp = le32_to_cpu(readl(ptr));
190
191         return 0;
192 }
193
194 int regmap_write(struct regmap *map, uint offset, uint val)
195 {
196         u32 *ptr = map_physmem(map->ranges[0].start + offset, 4, MAP_NOCACHE);
197
198         writel(cpu_to_le32(val), ptr);
199
200         return 0;
201 }
202
203 int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val)
204 {
205         uint reg;
206         int ret;
207
208         ret = regmap_read(map, offset, &reg);
209         if (ret)
210                 return ret;
211
212         reg &= ~mask;
213
214         return regmap_write(map, offset, reg | val);
215 }