xref: /openbmc/u-boot/drivers/core/regmap.c (revision 3ba98ed8)
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 }
425