regmap: Support reading from specific 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                 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         *mapp = map;
168
169         return 0;
170 }
171 #endif
172
173 void *regmap_get_range(struct regmap *map, unsigned int range_num)
174 {
175         struct regmap_range *range;
176
177         if (range_num >= map->range_count)
178                 return NULL;
179         range = &map->ranges[range_num];
180
181         return map_sysmem(range->start, range->size);
182 }
183
184 int regmap_uninit(struct regmap *map)
185 {
186         free(map);
187
188         return 0;
189 }
190
191 int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset,
192                           void *valp, size_t val_len)
193 {
194         struct regmap_range *range;
195         void *ptr;
196
197         if (range_num >= map->range_count) {
198                 debug("%s: range index %d larger than range count\n",
199                       __func__, range_num);
200                 return -ERANGE;
201         }
202         range = &map->ranges[range_num];
203
204         ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE);
205
206         if (offset + val_len > range->size) {
207                 debug("%s: offset/size combination invalid\n", __func__);
208                 return -ERANGE;
209         }
210
211         switch (val_len) {
212         case REGMAP_SIZE_8:
213                 *((u8 *)valp) = readb((u8 *)ptr);
214                 break;
215         case REGMAP_SIZE_16:
216                 *((u16 *)valp) = readw((u16 *)ptr);
217                 break;
218         case REGMAP_SIZE_32:
219                 *((u32 *)valp) = readl((u32 *)ptr);
220                 break;
221 #if defined(readq)
222         case REGMAP_SIZE_64:
223                 *((u64 *)valp) = readq((u64 *)ptr);
224                 break;
225 #endif
226         default:
227                 debug("%s: regmap size %zu unknown\n", __func__, val_len);
228                 return -EINVAL;
229         }
230
231         return 0;
232 }
233
234 int regmap_raw_read(struct regmap *map, uint offset, void *valp, size_t val_len)
235 {
236         return regmap_raw_read_range(map, 0, offset, valp, val_len);
237 }
238
239 int regmap_read(struct regmap *map, uint offset, uint *valp)
240 {
241         return regmap_raw_read(map, offset, valp, REGMAP_SIZE_32);
242 }
243
244 int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset,
245                            const void *val, size_t val_len)
246 {
247         struct regmap_range *range;
248         void *ptr;
249
250         if (range_num >= map->range_count) {
251                 debug("%s: range index %d larger than range count\n",
252                       __func__, range_num);
253                 return -ERANGE;
254         }
255         range = &map->ranges[range_num];
256
257         ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE);
258
259         if (offset + val_len > range->size) {
260                 debug("%s: offset/size combination invalid\n", __func__);
261                 return -ERANGE;
262         }
263
264         switch (val_len) {
265         case REGMAP_SIZE_8:
266                 writeb(*((u8 *)val), (u8 *)ptr);
267                 break;
268         case REGMAP_SIZE_16:
269                 writew(*((u16 *)val), (u16 *)ptr);
270                 break;
271         case REGMAP_SIZE_32:
272                 writel(*((u32 *)val), (u32 *)ptr);
273                 break;
274 #if defined(writeq)
275         case REGMAP_SIZE_64:
276                 writeq(*((u64 *)val), (u64 *)ptr);
277                 break;
278 #endif
279         default:
280                 debug("%s: regmap size %zu unknown\n", __func__, val_len);
281                 return -EINVAL;
282         }
283
284         return 0;
285 }
286
287 int regmap_raw_write(struct regmap *map, uint offset, const void *val,
288                      size_t val_len)
289 {
290         return regmap_raw_write_range(map, 0, offset, val, val_len);
291 }
292
293 int regmap_write(struct regmap *map, uint offset, uint val)
294 {
295         return regmap_raw_write(map, offset, &val, REGMAP_SIZE_32);
296 }
297
298 int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val)
299 {
300         uint reg;
301         int ret;
302
303         ret = regmap_read(map, offset, &reg);
304         if (ret)
305                 return ret;
306
307         reg &= ~mask;
308
309         return regmap_write(map, offset, reg | val);
310 }