blob: e33bb9d798d7e131532145a2cf9a07fb36f23d63 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glassad8f8ab2015-06-23 15:38:42 -06002/*
3 * Copyright (c) 2015 Google, Inc
4 * Written by Simon Glass <sjg@chromium.org>
Simon Glassad8f8ab2015-06-23 15:38:42 -06005 */
6
Simon Glass9e0a0902022-10-15 08:08:54 -06007#define LOG_CATEGORY LOGC_DM
8
Simon Glassad8f8ab2015-06-23 15:38:42 -06009#include <common.h>
10#include <dm.h>
11#include <errno.h>
Simon Glass0f2af882020-05-10 11:40:05 -060012#include <log.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060013#include <asm/global_data.h>
Masahiro Yamada75f82d02018-03-05 01:20:11 +090014#include <linux/libfdt.h>
Simon Glassad8f8ab2015-06-23 15:38:42 -060015#include <malloc.h>
16#include <mapmem.h>
17#include <regmap.h>
Paul Burton39776032016-09-08 07:47:35 +010018#include <asm/io.h>
Simon Glasseeeb5192017-05-18 20:09:10 -060019#include <dm/of_addr.h>
Jean-Jacques Hiblotde03d122020-09-24 10:04:10 +053020#include <dm/devres.h>
Simon Glasseeeb5192017-05-18 20:09:10 -060021#include <linux/ioport.h>
Jean-Jacques Hiblotde03d122020-09-24 10:04:10 +053022#include <linux/compat.h>
23#include <linux/err.h>
Jean-Jacques Hiblotb3bbbcf2020-09-24 10:04:16 +053024#include <linux/bitops.h>
25
26/*
27 * Internal representation of a regmap field. Instead of storing the MSB and
28 * LSB, store the shift and mask. This makes the code a bit cleaner and faster
29 * because the shift and mask don't have to be calculated every time.
30 */
31struct regmap_field {
32 struct regmap *regmap;
33 unsigned int mask;
34 /* lsb */
35 unsigned int shift;
36 unsigned int reg;
37};
Paul Burton39776032016-09-08 07:47:35 +010038
Simon Glassad8f8ab2015-06-23 15:38:42 -060039DECLARE_GLOBAL_DATA_PTR;
40
Mario Six0aa52992018-10-04 09:00:42 +020041/**
Simon Glass9e0a0902022-10-15 08:08:54 -060042 * do_range_check() - Control whether range checks are done
43 *
44 * Returns: true to do range checks, false to skip
45 *
46 * This is used to reduce code size on SPL where range checks are known not to
47 * be needed
48 *
49 * Add this to the top of the file to enable them: #define LOG_DEBUG
50 */
51static inline bool do_range_check(void)
52{
53 return _LOG_DEBUG || !IS_ENABLED(CONFIG_SPL);
54
55}
56
57/**
Mario Six0aa52992018-10-04 09:00:42 +020058 * regmap_alloc() - Allocate a regmap with a given number of ranges.
59 *
60 * @count: Number of ranges to be allocated for the regmap.
Pratyush Yadav1c9867c2020-09-24 10:04:12 +053061 *
62 * The default regmap width is set to REGMAP_SIZE_32. Callers can override it
63 * if they need.
64 *
Mario Six0aa52992018-10-04 09:00:42 +020065 * Return: A pointer to the newly allocated regmap, or NULL on error.
66 */
Masahiro Yamada54c5ecb2018-04-19 12:14:01 +090067static struct regmap *regmap_alloc(int count)
Simon Glass30d73e82016-07-04 11:58:21 -060068{
69 struct regmap *map;
Pratyush Yadav279cad82020-09-24 10:04:11 +053070 size_t size = sizeof(*map) + sizeof(map->ranges[0]) * count;
Simon Glass30d73e82016-07-04 11:58:21 -060071
Pratyush Yadav279cad82020-09-24 10:04:11 +053072 map = calloc(1, size);
Simon Glass30d73e82016-07-04 11:58:21 -060073 if (!map)
74 return NULL;
Simon Glass30d73e82016-07-04 11:58:21 -060075 map->range_count = count;
Pratyush Yadav1c9867c2020-09-24 10:04:12 +053076 map->width = REGMAP_SIZE_32;
Simon Glass30d73e82016-07-04 11:58:21 -060077
78 return map;
79}
80
Simon Glassb9443452016-07-04 11:57:59 -060081#if CONFIG_IS_ENABLED(OF_PLATDATA)
Simon Glassb75b15b2020-12-03 16:55:23 -070082int regmap_init_mem_plat(struct udevice *dev, fdt_val_t *reg, int count,
83 struct regmap **mapp)
Simon Glassb9443452016-07-04 11:57:59 -060084{
Simon Glassb6114332016-07-04 11:58:22 -060085 struct regmap_range *range;
86 struct regmap *map;
87
Masahiro Yamada54c5ecb2018-04-19 12:14:01 +090088 map = regmap_alloc(count);
Simon Glassb6114332016-07-04 11:58:22 -060089 if (!map)
90 return -ENOMEM;
91
Masahiro Yamada54c5ecb2018-04-19 12:14:01 +090092 for (range = map->ranges; count > 0; reg += 2, range++, count--) {
Simon Glassb6114332016-07-04 11:58:22 -060093 range->start = *reg;
94 range->size = reg[1];
95 }
96
97 *mapp = map;
98
Simon Glassb9443452016-07-04 11:57:59 -060099 return 0;
100}
101#else
Mario Six5159c0c2018-10-15 09:24:07 +0200102/**
103 * init_range() - Initialize a single range of a regmap
104 * @node: Device node that will use the map in question
105 * @range: Pointer to a regmap_range structure that will be initialized
106 * @addr_len: The length of the addr parts of the reg property
107 * @size_len: The length of the size parts of the reg property
108 * @index: The index of the range to initialize
109 *
110 * This function will read the necessary 'reg' information from the device tree
111 * (the 'addr' part, and the 'length' part), and initialize the range in
112 * quesion.
113 *
114 * Return: 0 if OK, -ve on error
115 */
116static int init_range(ofnode node, struct regmap_range *range, int addr_len,
117 int size_len, int index)
118{
119 fdt_size_t sz;
120 struct resource r;
121
122 if (of_live_active()) {
123 int ret;
124
125 ret = of_address_to_resource(ofnode_to_np(node),
126 index, &r);
127 if (ret) {
128 debug("%s: Could not read resource of range %d (ret = %d)\n",
129 ofnode_get_name(node), index, ret);
130 return ret;
131 }
132
133 range->start = r.start;
134 range->size = r.end - r.start + 1;
135 } else {
136 int offset = ofnode_to_offset(node);
137
138 range->start = fdtdec_get_addr_size_fixed(gd->fdt_blob, offset,
139 "reg", index,
140 addr_len, size_len,
141 &sz, true);
142 if (range->start == FDT_ADDR_T_NONE) {
143 debug("%s: Could not read start of range %d\n",
144 ofnode_get_name(node), index);
145 return -EINVAL;
146 }
147
148 range->size = sz;
149 }
150
151 return 0;
152}
153
Faiz Abbas52742f42019-06-11 00:43:33 +0530154int regmap_init_mem_index(ofnode node, struct regmap **mapp, int index)
155{
156 struct regmap *map;
157 int addr_len, size_len;
158 int ret;
159
160 addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node));
161 if (addr_len < 0) {
162 debug("%s: Error while reading the addr length (ret = %d)\n",
163 ofnode_get_name(node), addr_len);
164 return addr_len;
165 }
166
167 size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node));
168 if (size_len < 0) {
169 debug("%s: Error while reading the size length: (ret = %d)\n",
170 ofnode_get_name(node), size_len);
171 return size_len;
172 }
173
174 map = regmap_alloc(1);
175 if (!map)
176 return -ENOMEM;
177
178 ret = init_range(node, map->ranges, addr_len, size_len, index);
179 if (ret)
Faiz Abbas4bba4132019-11-11 15:29:05 +0530180 goto err;
Faiz Abbas52742f42019-06-11 00:43:33 +0530181
182 if (ofnode_read_bool(node, "little-endian"))
183 map->endianness = REGMAP_LITTLE_ENDIAN;
184 else if (ofnode_read_bool(node, "big-endian"))
185 map->endianness = REGMAP_BIG_ENDIAN;
186 else if (ofnode_read_bool(node, "native-endian"))
187 map->endianness = REGMAP_NATIVE_ENDIAN;
188 else /* Default: native endianness */
189 map->endianness = REGMAP_NATIVE_ENDIAN;
190
191 *mapp = map;
192
Faiz Abbas4bba4132019-11-11 15:29:05 +0530193 return 0;
194err:
195 regmap_uninit(map);
196
Faiz Abbas52742f42019-06-11 00:43:33 +0530197 return ret;
198}
199
Pratyush Yadav7eb24762020-09-24 10:04:14 +0530200int regmap_init_mem_range(ofnode node, ulong r_start, ulong r_size,
201 struct regmap **mapp)
202{
203 struct regmap *map;
204 struct regmap_range *range;
205
206 map = regmap_alloc(1);
207 if (!map)
208 return -ENOMEM;
209
210 range = &map->ranges[0];
211 range->start = r_start;
212 range->size = r_size;
213
214 if (ofnode_read_bool(node, "little-endian"))
215 map->endianness = REGMAP_LITTLE_ENDIAN;
216 else if (ofnode_read_bool(node, "big-endian"))
217 map->endianness = REGMAP_BIG_ENDIAN;
218 else if (ofnode_read_bool(node, "native-endian"))
219 map->endianness = REGMAP_NATIVE_ENDIAN;
220 else /* Default: native endianness */
221 map->endianness = REGMAP_NATIVE_ENDIAN;
222
223 *mapp = map;
224 return 0;
225}
226
Masahiro Yamadae4873e32018-04-19 12:14:03 +0900227int regmap_init_mem(ofnode node, struct regmap **mapp)
Simon Glassad8f8ab2015-06-23 15:38:42 -0600228{
Simon Glassad8f8ab2015-06-23 15:38:42 -0600229 struct regmap_range *range;
Simon Glassad8f8ab2015-06-23 15:38:42 -0600230 struct regmap *map;
231 int count;
232 int addr_len, size_len, both_len;
Simon Glassad8f8ab2015-06-23 15:38:42 -0600233 int len;
Jean-Jacques Hiblot024611b2017-02-13 16:17:48 +0100234 int index;
Faiz Abbas4bba4132019-11-11 15:29:05 +0530235 int ret;
Simon Glassad8f8ab2015-06-23 15:38:42 -0600236
Masahiro Yamadae4873e32018-04-19 12:14:03 +0900237 addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node));
Mario Six12321162018-10-04 09:00:43 +0200238 if (addr_len < 0) {
239 debug("%s: Error while reading the addr length (ret = %d)\n",
240 ofnode_get_name(node), addr_len);
241 return addr_len;
242 }
243
Masahiro Yamadae4873e32018-04-19 12:14:03 +0900244 size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node));
Mario Six12321162018-10-04 09:00:43 +0200245 if (size_len < 0) {
246 debug("%s: Error while reading the size length: (ret = %d)\n",
247 ofnode_get_name(node), size_len);
248 return size_len;
249 }
250
Simon Glassad8f8ab2015-06-23 15:38:42 -0600251 both_len = addr_len + size_len;
Mario Six12321162018-10-04 09:00:43 +0200252 if (!both_len) {
253 debug("%s: Both addr and size length are zero\n",
254 ofnode_get_name(node));
255 return -EINVAL;
256 }
Simon Glassad8f8ab2015-06-23 15:38:42 -0600257
Masahiro Yamadae4873e32018-04-19 12:14:03 +0900258 len = ofnode_read_size(node, "reg");
Mario Six6e96ba22018-10-15 09:24:08 +0200259 if (len < 0) {
260 debug("%s: Error while reading reg size (ret = %d)\n",
261 ofnode_get_name(node), len);
Simon Glasseeeb5192017-05-18 20:09:10 -0600262 return len;
Mario Six6e96ba22018-10-15 09:24:08 +0200263 }
Simon Glasseeeb5192017-05-18 20:09:10 -0600264 len /= sizeof(fdt32_t);
Simon Glassad8f8ab2015-06-23 15:38:42 -0600265 count = len / both_len;
Mario Six6e96ba22018-10-15 09:24:08 +0200266 if (!count) {
267 debug("%s: Not enough data in reg property\n",
268 ofnode_get_name(node));
Simon Glassad8f8ab2015-06-23 15:38:42 -0600269 return -EINVAL;
Mario Six6e96ba22018-10-15 09:24:08 +0200270 }
Simon Glassad8f8ab2015-06-23 15:38:42 -0600271
Masahiro Yamada54c5ecb2018-04-19 12:14:01 +0900272 map = regmap_alloc(count);
Simon Glassad8f8ab2015-06-23 15:38:42 -0600273 if (!map)
274 return -ENOMEM;
275
Masahiro Yamada54c5ecb2018-04-19 12:14:01 +0900276 for (range = map->ranges, index = 0; count > 0;
Simon Glasseeeb5192017-05-18 20:09:10 -0600277 count--, range++, index++) {
Faiz Abbas4bba4132019-11-11 15:29:05 +0530278 ret = init_range(node, range, addr_len, size_len, index);
Mario Six5159c0c2018-10-15 09:24:07 +0200279 if (ret)
Faiz Abbas4bba4132019-11-11 15:29:05 +0530280 goto err;
Simon Glassad8f8ab2015-06-23 15:38:42 -0600281 }
282
Mario Sixe5a3d5b2018-10-15 09:24:14 +0200283 if (ofnode_read_bool(node, "little-endian"))
284 map->endianness = REGMAP_LITTLE_ENDIAN;
285 else if (ofnode_read_bool(node, "big-endian"))
286 map->endianness = REGMAP_BIG_ENDIAN;
287 else if (ofnode_read_bool(node, "native-endian"))
288 map->endianness = REGMAP_NATIVE_ENDIAN;
289 else /* Default: native endianness */
290 map->endianness = REGMAP_NATIVE_ENDIAN;
291
Simon Glassad8f8ab2015-06-23 15:38:42 -0600292 *mapp = map;
293
294 return 0;
Faiz Abbas4bba4132019-11-11 15:29:05 +0530295err:
296 regmap_uninit(map);
297
298 return ret;
Simon Glassad8f8ab2015-06-23 15:38:42 -0600299}
Jean-Jacques Hiblotde03d122020-09-24 10:04:10 +0530300
301static void devm_regmap_release(struct udevice *dev, void *res)
302{
303 regmap_uninit(*(struct regmap **)res);
304}
305
306struct regmap *devm_regmap_init(struct udevice *dev,
307 const struct regmap_bus *bus,
308 void *bus_context,
309 const struct regmap_config *config)
310{
311 int rc;
Pratyush Yadav1c9867c2020-09-24 10:04:12 +0530312 struct regmap **mapp, *map;
Jean-Jacques Hiblotde03d122020-09-24 10:04:10 +0530313
Simon Glasse0ea9792021-05-13 19:39:22 -0600314 /* this looks like a leak, but devres takes care of it */
Jean-Jacques Hiblotde03d122020-09-24 10:04:10 +0530315 mapp = devres_alloc(devm_regmap_release, sizeof(struct regmap *),
316 __GFP_ZERO);
317 if (unlikely(!mapp))
318 return ERR_PTR(-ENOMEM);
319
Pratyush Yadav3e4e50a2020-09-24 10:04:15 +0530320 if (config && config->r_size != 0)
321 rc = regmap_init_mem_range(dev_ofnode(dev), config->r_start,
322 config->r_size, mapp);
323 else
324 rc = regmap_init_mem(dev_ofnode(dev), mapp);
Jean-Jacques Hiblotde03d122020-09-24 10:04:10 +0530325 if (rc)
326 return ERR_PTR(rc);
327
Pratyush Yadav1c9867c2020-09-24 10:04:12 +0530328 map = *mapp;
Pratyush Yadav3b94e5d2020-09-24 10:04:13 +0530329 if (config) {
Pratyush Yadav1c9867c2020-09-24 10:04:12 +0530330 map->width = config->width;
Pratyush Yadav3b94e5d2020-09-24 10:04:13 +0530331 map->reg_offset_shift = config->reg_offset_shift;
332 }
Pratyush Yadav1c9867c2020-09-24 10:04:12 +0530333
Jean-Jacques Hiblotde03d122020-09-24 10:04:10 +0530334 devres_add(dev, mapp);
335 return *mapp;
336}
Simon Glassb9443452016-07-04 11:57:59 -0600337#endif
Simon Glassad8f8ab2015-06-23 15:38:42 -0600338
339void *regmap_get_range(struct regmap *map, unsigned int range_num)
340{
341 struct regmap_range *range;
342
343 if (range_num >= map->range_count)
344 return NULL;
Masahiro Yamada54c5ecb2018-04-19 12:14:01 +0900345 range = &map->ranges[range_num];
Simon Glassad8f8ab2015-06-23 15:38:42 -0600346
347 return map_sysmem(range->start, range->size);
348}
349
350int regmap_uninit(struct regmap *map)
351{
Simon Glassad8f8ab2015-06-23 15:38:42 -0600352 free(map);
353
354 return 0;
355}
Paul Burton39776032016-09-08 07:47:35 +0100356
Mario Sixe5a3d5b2018-10-15 09:24:14 +0200357static inline u8 __read_8(u8 *addr, enum regmap_endianness_t endianness)
358{
359 return readb(addr);
360}
361
362static inline u16 __read_16(u16 *addr, enum regmap_endianness_t endianness)
363{
364 switch (endianness) {
365 case REGMAP_LITTLE_ENDIAN:
366 return in_le16(addr);
367 case REGMAP_BIG_ENDIAN:
368 return in_be16(addr);
369 case REGMAP_NATIVE_ENDIAN:
370 return readw(addr);
371 }
372
373 return readw(addr);
374}
375
376static inline u32 __read_32(u32 *addr, enum regmap_endianness_t endianness)
377{
378 switch (endianness) {
379 case REGMAP_LITTLE_ENDIAN:
380 return in_le32(addr);
381 case REGMAP_BIG_ENDIAN:
382 return in_be32(addr);
383 case REGMAP_NATIVE_ENDIAN:
384 return readl(addr);
385 }
386
387 return readl(addr);
388}
389
390#if defined(in_le64) && defined(in_be64) && defined(readq)
391static inline u64 __read_64(u64 *addr, enum regmap_endianness_t endianness)
392{
393 switch (endianness) {
394 case REGMAP_LITTLE_ENDIAN:
395 return in_le64(addr);
396 case REGMAP_BIG_ENDIAN:
397 return in_be64(addr);
398 case REGMAP_NATIVE_ENDIAN:
399 return readq(addr);
400 }
401
402 return readq(addr);
403}
404#endif
405
Mario Six2f009972018-10-15 09:24:11 +0200406int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset,
407 void *valp, size_t val_len)
Paul Burton39776032016-09-08 07:47:35 +0100408{
Mario Six2f009972018-10-15 09:24:11 +0200409 struct regmap_range *range;
Mario Sixa4fd59e2018-10-15 09:24:10 +0200410 void *ptr;
Paul Burton39776032016-09-08 07:47:35 +0100411
Simon Glass9e0a0902022-10-15 08:08:54 -0600412 if (do_range_check() && range_num >= map->range_count) {
Mario Six2f009972018-10-15 09:24:11 +0200413 debug("%s: range index %d larger than range count\n",
414 __func__, range_num);
415 return -ERANGE;
416 }
417 range = &map->ranges[range_num];
418
Pratyush Yadav3b94e5d2020-09-24 10:04:13 +0530419 offset <<= map->reg_offset_shift;
Simon Glass9e0a0902022-10-15 08:08:54 -0600420 if (do_range_check() &&
421 (offset + val_len > range->size || offset + val_len < offset)) {
Mario Six2f009972018-10-15 09:24:11 +0200422 debug("%s: offset/size combination invalid\n", __func__);
423 return -ERANGE;
424 }
Paul Burton39776032016-09-08 07:47:35 +0100425
Pratyush Yadav5c42d5d2020-05-26 17:35:57 +0530426 ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE);
427
Mario Sixa4fd59e2018-10-15 09:24:10 +0200428 switch (val_len) {
429 case REGMAP_SIZE_8:
Mario Sixe5a3d5b2018-10-15 09:24:14 +0200430 *((u8 *)valp) = __read_8(ptr, map->endianness);
Mario Sixa4fd59e2018-10-15 09:24:10 +0200431 break;
432 case REGMAP_SIZE_16:
Mario Sixe5a3d5b2018-10-15 09:24:14 +0200433 *((u16 *)valp) = __read_16(ptr, map->endianness);
Mario Sixa4fd59e2018-10-15 09:24:10 +0200434 break;
435 case REGMAP_SIZE_32:
Mario Sixe5a3d5b2018-10-15 09:24:14 +0200436 *((u32 *)valp) = __read_32(ptr, map->endianness);
Mario Sixa4fd59e2018-10-15 09:24:10 +0200437 break;
Mario Sixe5a3d5b2018-10-15 09:24:14 +0200438#if defined(in_le64) && defined(in_be64) && defined(readq)
Mario Sixa4fd59e2018-10-15 09:24:10 +0200439 case REGMAP_SIZE_64:
Mario Sixe5a3d5b2018-10-15 09:24:14 +0200440 *((u64 *)valp) = __read_64(ptr, map->endianness);
Mario Sixa4fd59e2018-10-15 09:24:10 +0200441 break;
442#endif
443 default:
444 debug("%s: regmap size %zu unknown\n", __func__, val_len);
445 return -EINVAL;
446 }
Mario Six2f009972018-10-15 09:24:11 +0200447
Paul Burton39776032016-09-08 07:47:35 +0100448 return 0;
449}
450
Mario Six2f009972018-10-15 09:24:11 +0200451int regmap_raw_read(struct regmap *map, uint offset, void *valp, size_t val_len)
452{
453 return regmap_raw_read_range(map, 0, offset, valp, val_len);
454}
455
Mario Sixa4fd59e2018-10-15 09:24:10 +0200456int regmap_read(struct regmap *map, uint offset, uint *valp)
Paul Burton39776032016-09-08 07:47:35 +0100457{
Marek BehĂșn10448862021-05-20 13:23:50 +0200458 union {
459 u8 v8;
460 u16 v16;
461 u32 v32;
462 u64 v64;
463 } u;
464 int res;
465
466 res = regmap_raw_read(map, offset, &u, map->width);
467 if (res)
468 return res;
469
470 switch (map->width) {
471 case REGMAP_SIZE_8:
472 *valp = u.v8;
473 break;
474 case REGMAP_SIZE_16:
475 *valp = u.v16;
476 break;
477 case REGMAP_SIZE_32:
478 *valp = u.v32;
479 break;
480 case REGMAP_SIZE_64:
481 *valp = u.v64;
482 break;
483 default:
484 unreachable();
485 }
486
487 return 0;
Mario Sixa4fd59e2018-10-15 09:24:10 +0200488}
489
Mario Sixe5a3d5b2018-10-15 09:24:14 +0200490static inline void __write_8(u8 *addr, const u8 *val,
491 enum regmap_endianness_t endianness)
492{
493 writeb(*val, addr);
494}
495
496static inline void __write_16(u16 *addr, const u16 *val,
497 enum regmap_endianness_t endianness)
498{
499 switch (endianness) {
500 case REGMAP_NATIVE_ENDIAN:
501 writew(*val, addr);
502 break;
503 case REGMAP_LITTLE_ENDIAN:
504 out_le16(addr, *val);
505 break;
506 case REGMAP_BIG_ENDIAN:
507 out_be16(addr, *val);
508 break;
509 }
510}
511
512static inline void __write_32(u32 *addr, const u32 *val,
513 enum regmap_endianness_t endianness)
514{
515 switch (endianness) {
516 case REGMAP_NATIVE_ENDIAN:
517 writel(*val, addr);
518 break;
519 case REGMAP_LITTLE_ENDIAN:
520 out_le32(addr, *val);
521 break;
522 case REGMAP_BIG_ENDIAN:
523 out_be32(addr, *val);
524 break;
525 }
526}
527
528#if defined(out_le64) && defined(out_be64) && defined(writeq)
529static inline void __write_64(u64 *addr, const u64 *val,
530 enum regmap_endianness_t endianness)
531{
532 switch (endianness) {
533 case REGMAP_NATIVE_ENDIAN:
534 writeq(*val, addr);
535 break;
536 case REGMAP_LITTLE_ENDIAN:
537 out_le64(addr, *val);
538 break;
539 case REGMAP_BIG_ENDIAN:
540 out_be64(addr, *val);
541 break;
542 }
543}
544#endif
545
Mario Six2f009972018-10-15 09:24:11 +0200546int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset,
547 const void *val, size_t val_len)
Mario Sixa4fd59e2018-10-15 09:24:10 +0200548{
Mario Six2f009972018-10-15 09:24:11 +0200549 struct regmap_range *range;
Mario Sixa4fd59e2018-10-15 09:24:10 +0200550 void *ptr;
Paul Burton39776032016-09-08 07:47:35 +0100551
Mario Six2f009972018-10-15 09:24:11 +0200552 if (range_num >= map->range_count) {
553 debug("%s: range index %d larger than range count\n",
554 __func__, range_num);
555 return -ERANGE;
556 }
557 range = &map->ranges[range_num];
558
Pratyush Yadav3b94e5d2020-09-24 10:04:13 +0530559 offset <<= map->reg_offset_shift;
Heinrich Schuchardt9aaf70e2022-09-29 22:27:06 +0000560 if (offset + val_len > range->size || offset + val_len < offset) {
Mario Six2f009972018-10-15 09:24:11 +0200561 debug("%s: offset/size combination invalid\n", __func__);
562 return -ERANGE;
563 }
Mario Sixa4fd59e2018-10-15 09:24:10 +0200564
Pratyush Yadav5c42d5d2020-05-26 17:35:57 +0530565 ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE);
566
Mario Sixa4fd59e2018-10-15 09:24:10 +0200567 switch (val_len) {
568 case REGMAP_SIZE_8:
Mario Sixe5a3d5b2018-10-15 09:24:14 +0200569 __write_8(ptr, val, map->endianness);
Mario Sixa4fd59e2018-10-15 09:24:10 +0200570 break;
571 case REGMAP_SIZE_16:
Mario Sixe5a3d5b2018-10-15 09:24:14 +0200572 __write_16(ptr, val, map->endianness);
Mario Sixa4fd59e2018-10-15 09:24:10 +0200573 break;
574 case REGMAP_SIZE_32:
Mario Sixe5a3d5b2018-10-15 09:24:14 +0200575 __write_32(ptr, val, map->endianness);
Mario Sixa4fd59e2018-10-15 09:24:10 +0200576 break;
Mario Sixe5a3d5b2018-10-15 09:24:14 +0200577#if defined(out_le64) && defined(out_be64) && defined(writeq)
Mario Sixa4fd59e2018-10-15 09:24:10 +0200578 case REGMAP_SIZE_64:
Mario Sixe5a3d5b2018-10-15 09:24:14 +0200579 __write_64(ptr, val, map->endianness);
Mario Sixa4fd59e2018-10-15 09:24:10 +0200580 break;
581#endif
582 default:
583 debug("%s: regmap size %zu unknown\n", __func__, val_len);
584 return -EINVAL;
585 }
Paul Burton39776032016-09-08 07:47:35 +0100586
587 return 0;
588}
Neil Armstrong5444ec62018-04-27 11:56:14 +0200589
Mario Six2f009972018-10-15 09:24:11 +0200590int regmap_raw_write(struct regmap *map, uint offset, const void *val,
591 size_t val_len)
592{
593 return regmap_raw_write_range(map, 0, offset, val, val_len);
594}
595
Mario Sixa4fd59e2018-10-15 09:24:10 +0200596int regmap_write(struct regmap *map, uint offset, uint val)
597{
Marek BehĂșn10448862021-05-20 13:23:50 +0200598 union {
599 u8 v8;
600 u16 v16;
601 u32 v32;
602 u64 v64;
603 } u;
604
605 switch (map->width) {
606 case REGMAP_SIZE_8:
607 u.v8 = val;
608 break;
609 case REGMAP_SIZE_16:
610 u.v16 = val;
611 break;
612 case REGMAP_SIZE_32:
613 u.v32 = val;
614 break;
615 case REGMAP_SIZE_64:
616 u.v64 = val;
617 break;
618 default:
619 debug("%s: regmap size %zu unknown\n", __func__,
620 (size_t)map->width);
621 return -EINVAL;
622 }
623
624 return regmap_raw_write(map, offset, &u, map->width);
Mario Sixa4fd59e2018-10-15 09:24:10 +0200625}
626
Neil Armstrong5444ec62018-04-27 11:56:14 +0200627int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val)
628{
629 uint reg;
630 int ret;
631
632 ret = regmap_read(map, offset, &reg);
633 if (ret)
634 return ret;
635
636 reg &= ~mask;
637
Simon Glass56bed2a2019-10-11 16:16:49 -0600638 return regmap_write(map, offset, reg | (val & mask));
Neil Armstrong5444ec62018-04-27 11:56:14 +0200639}
Jean-Jacques Hiblotb3bbbcf2020-09-24 10:04:16 +0530640
641int regmap_field_read(struct regmap_field *field, unsigned int *val)
642{
643 int ret;
644 unsigned int reg_val;
645
646 ret = regmap_read(field->regmap, field->reg, &reg_val);
647 if (ret != 0)
648 return ret;
649
650 reg_val &= field->mask;
651 reg_val >>= field->shift;
652 *val = reg_val;
653
654 return ret;
655}
656
657int regmap_field_write(struct regmap_field *field, unsigned int val)
658{
659 return regmap_update_bits(field->regmap, field->reg, field->mask,
660 val << field->shift);
661}
662
663static void regmap_field_init(struct regmap_field *rm_field,
664 struct regmap *regmap,
665 struct reg_field reg_field)
666{
667 rm_field->regmap = regmap;
668 rm_field->reg = reg_field.reg;
669 rm_field->shift = reg_field.lsb;
670 rm_field->mask = GENMASK(reg_field.msb, reg_field.lsb);
671}
672
673struct regmap_field *devm_regmap_field_alloc(struct udevice *dev,
674 struct regmap *regmap,
675 struct reg_field reg_field)
676{
677 struct regmap_field *rm_field = devm_kzalloc(dev, sizeof(*rm_field),
678 GFP_KERNEL);
679 if (!rm_field)
680 return ERR_PTR(-ENOMEM);
681
682 regmap_field_init(rm_field, regmap, reg_field);
683
684 return rm_field;
685}
686
687void devm_regmap_field_free(struct udevice *dev, struct regmap_field *field)
688{
689 devm_kfree(dev, field);
690}
691
692struct regmap_field *regmap_field_alloc(struct regmap *regmap,
693 struct reg_field reg_field)
694{
695 struct regmap_field *rm_field = kzalloc(sizeof(*rm_field), GFP_KERNEL);
696
697 if (!rm_field)
698 return ERR_PTR(-ENOMEM);
699
700 regmap_field_init(rm_field, regmap, reg_field);
701
702 return rm_field;
703}
704
705void regmap_field_free(struct regmap_field *field)
706{
707 kfree(field);
708}