blob: d1d12eef385f5757bd327cb01d2027bbe3bfb662 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass6f98b752015-06-23 15:38:42 -06002/*
3 * Copyright (c) 2015 Google, Inc
4 * Written by Simon Glass <sjg@chromium.org>
Simon Glass6f98b752015-06-23 15:38:42 -06005 */
6
7#include <common.h>
8#include <dm.h>
9#include <errno.h>
Masahiro Yamadab08c8c42018-03-05 01:20:11 +090010#include <linux/libfdt.h>
Simon Glass6f98b752015-06-23 15:38:42 -060011#include <malloc.h>
12#include <mapmem.h>
13#include <regmap.h>
Paul Burton3bfb8cb2016-09-08 07:47:35 +010014#include <asm/io.h>
Simon Glass23d63262017-05-18 20:09:10 -060015#include <dm/of_addr.h>
16#include <linux/ioport.h>
Paul Burton3bfb8cb2016-09-08 07:47:35 +010017
Simon Glass6f98b752015-06-23 15:38:42 -060018DECLARE_GLOBAL_DATA_PTR;
19
Mario Six9b076092018-10-04 09:00:42 +020020/**
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 */
Masahiro Yamada8c1de5e2018-04-19 12:14:01 +090026static struct regmap *regmap_alloc(int count)
Simon Glassa9514312016-07-04 11:58:21 -060027{
28 struct regmap *map;
29
Masahiro Yamada8c1de5e2018-04-19 12:14:01 +090030 map = malloc(sizeof(*map) + sizeof(map->ranges[0]) * count);
Simon Glassa9514312016-07-04 11:58:21 -060031 if (!map)
32 return NULL;
Simon Glassa9514312016-07-04 11:58:21 -060033 map->range_count = count;
34
35 return map;
36}
37
Simon Glass3b2a29e2016-07-04 11:57:59 -060038#if CONFIG_IS_ENABLED(OF_PLATDATA)
Simon Glassc20ee0e2017-08-29 14:15:50 -060039int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count,
Simon Glass3b2a29e2016-07-04 11:57:59 -060040 struct regmap **mapp)
41{
Simon Glass1e6ca1a2016-07-04 11:58:22 -060042 struct regmap_range *range;
43 struct regmap *map;
44
Masahiro Yamada8c1de5e2018-04-19 12:14:01 +090045 map = regmap_alloc(count);
Simon Glass1e6ca1a2016-07-04 11:58:22 -060046 if (!map)
47 return -ENOMEM;
48
Masahiro Yamada8c1de5e2018-04-19 12:14:01 +090049 for (range = map->ranges; count > 0; reg += 2, range++, count--) {
Simon Glass1e6ca1a2016-07-04 11:58:22 -060050 range->start = *reg;
51 range->size = reg[1];
52 }
53
54 *mapp = map;
55
Simon Glass3b2a29e2016-07-04 11:57:59 -060056 return 0;
57}
58#else
Mario Six7f0e3662018-10-15 09:24:07 +020059/**
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 */
73static 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
Faiz Abbas55a1a092019-06-11 00:43:33 +0530111int 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
Masahiro Yamadad3581232018-04-19 12:14:03 +0900153int regmap_init_mem(ofnode node, struct regmap **mapp)
Simon Glass6f98b752015-06-23 15:38:42 -0600154{
Simon Glass6f98b752015-06-23 15:38:42 -0600155 struct regmap_range *range;
Simon Glass6f98b752015-06-23 15:38:42 -0600156 struct regmap *map;
157 int count;
158 int addr_len, size_len, both_len;
Simon Glass6f98b752015-06-23 15:38:42 -0600159 int len;
Jean-Jacques Hiblot18040442017-02-13 16:17:48 +0100160 int index;
Simon Glass6f98b752015-06-23 15:38:42 -0600161
Masahiro Yamadad3581232018-04-19 12:14:03 +0900162 addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node));
Mario Sixb6f58bf2018-10-04 09:00:43 +0200163 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
Masahiro Yamadad3581232018-04-19 12:14:03 +0900169 size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node));
Mario Sixb6f58bf2018-10-04 09:00:43 +0200170 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
Simon Glass6f98b752015-06-23 15:38:42 -0600176 both_len = addr_len + size_len;
Mario Sixb6f58bf2018-10-04 09:00:43 +0200177 if (!both_len) {
178 debug("%s: Both addr and size length are zero\n",
179 ofnode_get_name(node));
180 return -EINVAL;
181 }
Simon Glass6f98b752015-06-23 15:38:42 -0600182
Masahiro Yamadad3581232018-04-19 12:14:03 +0900183 len = ofnode_read_size(node, "reg");
Mario Six2448f602018-10-15 09:24:08 +0200184 if (len < 0) {
185 debug("%s: Error while reading reg size (ret = %d)\n",
186 ofnode_get_name(node), len);
Simon Glass23d63262017-05-18 20:09:10 -0600187 return len;
Mario Six2448f602018-10-15 09:24:08 +0200188 }
Simon Glass23d63262017-05-18 20:09:10 -0600189 len /= sizeof(fdt32_t);
Simon Glass6f98b752015-06-23 15:38:42 -0600190 count = len / both_len;
Mario Six2448f602018-10-15 09:24:08 +0200191 if (!count) {
192 debug("%s: Not enough data in reg property\n",
193 ofnode_get_name(node));
Simon Glass6f98b752015-06-23 15:38:42 -0600194 return -EINVAL;
Mario Six2448f602018-10-15 09:24:08 +0200195 }
Simon Glass6f98b752015-06-23 15:38:42 -0600196
Masahiro Yamada8c1de5e2018-04-19 12:14:01 +0900197 map = regmap_alloc(count);
Simon Glass6f98b752015-06-23 15:38:42 -0600198 if (!map)
199 return -ENOMEM;
200
Masahiro Yamada8c1de5e2018-04-19 12:14:01 +0900201 for (range = map->ranges, index = 0; count > 0;
Simon Glass23d63262017-05-18 20:09:10 -0600202 count--, range++, index++) {
Mario Six7f0e3662018-10-15 09:24:07 +0200203 int ret = init_range(node, range, addr_len, size_len, index);
204
205 if (ret)
206 return ret;
Simon Glass6f98b752015-06-23 15:38:42 -0600207 }
208
Mario Six9b77fe32018-10-15 09:24:14 +0200209 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
Simon Glass6f98b752015-06-23 15:38:42 -0600218 *mapp = map;
219
220 return 0;
221}
Simon Glass3b2a29e2016-07-04 11:57:59 -0600222#endif
Simon Glass6f98b752015-06-23 15:38:42 -0600223
224void *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;
Masahiro Yamada8c1de5e2018-04-19 12:14:01 +0900230 range = &map->ranges[range_num];
Simon Glass6f98b752015-06-23 15:38:42 -0600231
232 return map_sysmem(range->start, range->size);
233}
234
235int regmap_uninit(struct regmap *map)
236{
Simon Glass6f98b752015-06-23 15:38:42 -0600237 free(map);
238
239 return 0;
240}
Paul Burton3bfb8cb2016-09-08 07:47:35 +0100241
Mario Six9b77fe32018-10-15 09:24:14 +0200242static inline u8 __read_8(u8 *addr, enum regmap_endianness_t endianness)
243{
244 return readb(addr);
245}
246
247static 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
261static 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)
276static 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
Mario Sixd5c7bd92018-10-15 09:24:11 +0200291int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset,
292 void *valp, size_t val_len)
Mario Six84ff8f62018-10-15 09:24:10 +0200293{
Mario Sixd5c7bd92018-10-15 09:24:11 +0200294 struct regmap_range *range;
Mario Six84ff8f62018-10-15 09:24:10 +0200295 void *ptr;
296
Mario Sixd5c7bd92018-10-15 09:24:11 +0200297 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 }
Mario Six84ff8f62018-10-15 09:24:10 +0200310
311 switch (val_len) {
312 case REGMAP_SIZE_8:
Mario Six9b77fe32018-10-15 09:24:14 +0200313 *((u8 *)valp) = __read_8(ptr, map->endianness);
Mario Six84ff8f62018-10-15 09:24:10 +0200314 break;
315 case REGMAP_SIZE_16:
Mario Six9b77fe32018-10-15 09:24:14 +0200316 *((u16 *)valp) = __read_16(ptr, map->endianness);
Mario Six84ff8f62018-10-15 09:24:10 +0200317 break;
318 case REGMAP_SIZE_32:
Mario Six9b77fe32018-10-15 09:24:14 +0200319 *((u32 *)valp) = __read_32(ptr, map->endianness);
Mario Six84ff8f62018-10-15 09:24:10 +0200320 break;
Mario Six9b77fe32018-10-15 09:24:14 +0200321#if defined(in_le64) && defined(in_be64) && defined(readq)
Mario Six84ff8f62018-10-15 09:24:10 +0200322 case REGMAP_SIZE_64:
Mario Six9b77fe32018-10-15 09:24:14 +0200323 *((u64 *)valp) = __read_64(ptr, map->endianness);
Mario Six84ff8f62018-10-15 09:24:10 +0200324 break;
325#endif
326 default:
327 debug("%s: regmap size %zu unknown\n", __func__, val_len);
328 return -EINVAL;
329 }
Mario Sixd5c7bd92018-10-15 09:24:11 +0200330
Mario Six84ff8f62018-10-15 09:24:10 +0200331 return 0;
332}
333
Mario Sixd5c7bd92018-10-15 09:24:11 +0200334int 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
Paul Burton3bfb8cb2016-09-08 07:47:35 +0100339int regmap_read(struct regmap *map, uint offset, uint *valp)
340{
Mario Six84ff8f62018-10-15 09:24:10 +0200341 return regmap_raw_read(map, offset, valp, REGMAP_SIZE_32);
342}
Paul Burton3bfb8cb2016-09-08 07:47:35 +0100343
Mario Six9b77fe32018-10-15 09:24:14 +0200344static inline void __write_8(u8 *addr, const u8 *val,
345 enum regmap_endianness_t endianness)
346{
347 writeb(*val, addr);
348}
349
350static 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
366static 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)
383static 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
Mario Sixd5c7bd92018-10-15 09:24:11 +0200400int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset,
401 const void *val, size_t val_len)
Mario Six84ff8f62018-10-15 09:24:10 +0200402{
Mario Sixd5c7bd92018-10-15 09:24:11 +0200403 struct regmap_range *range;
Mario Six84ff8f62018-10-15 09:24:10 +0200404 void *ptr;
405
Mario Sixd5c7bd92018-10-15 09:24:11 +0200406 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 }
Mario Six84ff8f62018-10-15 09:24:10 +0200419
420 switch (val_len) {
421 case REGMAP_SIZE_8:
Mario Six9b77fe32018-10-15 09:24:14 +0200422 __write_8(ptr, val, map->endianness);
Mario Six84ff8f62018-10-15 09:24:10 +0200423 break;
424 case REGMAP_SIZE_16:
Mario Six9b77fe32018-10-15 09:24:14 +0200425 __write_16(ptr, val, map->endianness);
Mario Six84ff8f62018-10-15 09:24:10 +0200426 break;
427 case REGMAP_SIZE_32:
Mario Six9b77fe32018-10-15 09:24:14 +0200428 __write_32(ptr, val, map->endianness);
Mario Six84ff8f62018-10-15 09:24:10 +0200429 break;
Mario Six9b77fe32018-10-15 09:24:14 +0200430#if defined(out_le64) && defined(out_be64) && defined(writeq)
Mario Six84ff8f62018-10-15 09:24:10 +0200431 case REGMAP_SIZE_64:
Mario Six9b77fe32018-10-15 09:24:14 +0200432 __write_64(ptr, val, map->endianness);
Mario Six84ff8f62018-10-15 09:24:10 +0200433 break;
434#endif
435 default:
436 debug("%s: regmap size %zu unknown\n", __func__, val_len);
437 return -EINVAL;
438 }
Paul Burton3bfb8cb2016-09-08 07:47:35 +0100439
440 return 0;
441}
442
Mario Sixd5c7bd92018-10-15 09:24:11 +0200443int 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
Paul Burton3bfb8cb2016-09-08 07:47:35 +0100449int regmap_write(struct regmap *map, uint offset, uint val)
450{
Mario Six84ff8f62018-10-15 09:24:10 +0200451 return regmap_raw_write(map, offset, &val, REGMAP_SIZE_32);
Paul Burton3bfb8cb2016-09-08 07:47:35 +0100452}
Neil Armstrong285cbcf2018-04-27 11:56:14 +0200453
454int 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}