regmap.c 6.8 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0+
2 3 4 5 6 7 8 9
/*
 * Copyright (c) 2015 Google, Inc
 * Written by Simon Glass <sjg@chromium.org>
 */

#include <common.h>
#include <dm.h>
#include <errno.h>
10
#include <linux/libfdt.h>
11 12 13
#include <malloc.h>
#include <mapmem.h>
#include <regmap.h>
14
#include <asm/io.h>
15 16
#include <dm/of_addr.h>
#include <linux/ioport.h>
17

18 19
DECLARE_GLOBAL_DATA_PTR;

M
Mario Six 已提交
20 21 22 23 24 25
/**
 * regmap_alloc() - Allocate a regmap with a given number of ranges.
 *
 * @count: Number of ranges to be allocated for the regmap.
 * Return: A pointer to the newly allocated regmap, or NULL on error.
 */
26
static struct regmap *regmap_alloc(int count)
27 28 29
{
	struct regmap *map;

30
	map = malloc(sizeof(*map) + sizeof(map->ranges[0]) * count);
31 32 33 34 35 36 37
	if (!map)
		return NULL;
	map->range_count = count;

	return map;
}

38
#if CONFIG_IS_ENABLED(OF_PLATDATA)
39
int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count,
40 41
			     struct regmap **mapp)
{
42 43 44
	struct regmap_range *range;
	struct regmap *map;

45
	map = regmap_alloc(count);
46 47 48
	if (!map)
		return -ENOMEM;

49
	for (range = map->ranges; count > 0; reg += 2, range++, count--) {
50 51 52 53 54 55
		range->start = *reg;
		range->size = reg[1];
	}

	*mapp = map;

56 57 58
	return 0;
}
#else
M
Mario Six 已提交
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
/**
 * init_range() - Initialize a single range of a regmap
 * @node:     Device node that will use the map in question
 * @range:    Pointer to a regmap_range structure that will be initialized
 * @addr_len: The length of the addr parts of the reg property
 * @size_len: The length of the size parts of the reg property
 * @index:    The index of the range to initialize
 *
 * This function will read the necessary 'reg' information from the device tree
 * (the 'addr' part, and the 'length' part), and initialize the range in
 * quesion.
 *
 * Return: 0 if OK, -ve on error
 */
static int init_range(ofnode node, struct regmap_range *range, int addr_len,
		      int size_len, int index)
{
	fdt_size_t sz;
	struct resource r;

	if (of_live_active()) {
		int ret;

		ret = of_address_to_resource(ofnode_to_np(node),
					     index, &r);
		if (ret) {
			debug("%s: Could not read resource of range %d (ret = %d)\n",
			      ofnode_get_name(node), index, ret);
			return ret;
		}

		range->start = r.start;
		range->size = r.end - r.start + 1;
	} else {
		int offset = ofnode_to_offset(node);

		range->start = fdtdec_get_addr_size_fixed(gd->fdt_blob, offset,
							  "reg", index,
							  addr_len, size_len,
							  &sz, true);
		if (range->start == FDT_ADDR_T_NONE) {
			debug("%s: Could not read start of range %d\n",
			      ofnode_get_name(node), index);
			return -EINVAL;
		}

		range->size = sz;
	}

	return 0;
}

111
int regmap_init_mem(ofnode node, struct regmap **mapp)
112 113 114 115 116 117
{
	struct regmap_range *range;
	struct regmap *map;
	int count;
	int addr_len, size_len, both_len;
	int len;
118
	int index;
119

120
	addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node));
M
Mario Six 已提交
121 122 123 124 125 126
	if (addr_len < 0) {
		debug("%s: Error while reading the addr length (ret = %d)\n",
		      ofnode_get_name(node), addr_len);
		return addr_len;
	}

127
	size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node));
M
Mario Six 已提交
128 129 130 131 132 133
	if (size_len < 0) {
		debug("%s: Error while reading the size length: (ret = %d)\n",
		      ofnode_get_name(node), size_len);
		return size_len;
	}

134
	both_len = addr_len + size_len;
M
Mario Six 已提交
135 136 137 138 139
	if (!both_len) {
		debug("%s: Both addr and size length are zero\n",
		      ofnode_get_name(node));
		return -EINVAL;
	}
140

141
	len = ofnode_read_size(node, "reg");
M
Mario Six 已提交
142 143 144
	if (len < 0) {
		debug("%s: Error while reading reg size (ret = %d)\n",
		      ofnode_get_name(node), len);
145
		return len;
M
Mario Six 已提交
146
	}
147
	len /= sizeof(fdt32_t);
148
	count = len / both_len;
M
Mario Six 已提交
149 150 151
	if (!count) {
		debug("%s: Not enough data in reg property\n",
		      ofnode_get_name(node));
152
		return -EINVAL;
M
Mario Six 已提交
153
	}
154

155
	map = regmap_alloc(count);
156 157 158
	if (!map)
		return -ENOMEM;

159
	for (range = map->ranges, index = 0; count > 0;
160
	     count--, range++, index++) {
M
Mario Six 已提交
161 162 163 164
		int ret = init_range(node, range, addr_len, size_len, index);

		if (ret)
			return ret;
165 166 167 168 169 170
	}

	*mapp = map;

	return 0;
}
171
#endif
172 173 174 175 176 177 178

void *regmap_get_range(struct regmap *map, unsigned int range_num)
{
	struct regmap_range *range;

	if (range_num >= map->range_count)
		return NULL;
179
	range = &map->ranges[range_num];
180 181 182 183 184 185 186 187 188 189

	return map_sysmem(range->start, range->size);
}

int regmap_uninit(struct regmap *map)
{
	free(map);

	return 0;
}
190

191 192
int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset,
			  void *valp, size_t val_len)
M
Mario Six 已提交
193
{
194
	struct regmap_range *range;
M
Mario Six 已提交
195 196
	void *ptr;

197 198 199 200 201 202 203 204 205 206 207 208 209
	if (range_num >= map->range_count) {
		debug("%s: range index %d larger than range count\n",
		      __func__, range_num);
		return -ERANGE;
	}
	range = &map->ranges[range_num];

	ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE);

	if (offset + val_len > range->size) {
		debug("%s: offset/size combination invalid\n", __func__);
		return -ERANGE;
	}
M
Mario Six 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229

	switch (val_len) {
	case REGMAP_SIZE_8:
		*((u8 *)valp) = readb((u8 *)ptr);
		break;
	case REGMAP_SIZE_16:
		*((u16 *)valp) = readw((u16 *)ptr);
		break;
	case REGMAP_SIZE_32:
		*((u32 *)valp) = readl((u32 *)ptr);
		break;
#if defined(readq)
	case REGMAP_SIZE_64:
		*((u64 *)valp) = readq((u64 *)ptr);
		break;
#endif
	default:
		debug("%s: regmap size %zu unknown\n", __func__, val_len);
		return -EINVAL;
	}
230

M
Mario Six 已提交
231 232 233
	return 0;
}

234 235 236 237 238
int regmap_raw_read(struct regmap *map, uint offset, void *valp, size_t val_len)
{
	return regmap_raw_read_range(map, 0, offset, valp, val_len);
}

239 240
int regmap_read(struct regmap *map, uint offset, uint *valp)
{
M
Mario Six 已提交
241 242
	return regmap_raw_read(map, offset, valp, REGMAP_SIZE_32);
}
243

244 245
int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset,
			   const void *val, size_t val_len)
M
Mario Six 已提交
246
{
247
	struct regmap_range *range;
M
Mario Six 已提交
248 249
	void *ptr;

250 251 252 253 254 255 256 257 258 259 260 261 262
	if (range_num >= map->range_count) {
		debug("%s: range index %d larger than range count\n",
		      __func__, range_num);
		return -ERANGE;
	}
	range = &map->ranges[range_num];

	ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE);

	if (offset + val_len > range->size) {
		debug("%s: offset/size combination invalid\n", __func__);
		return -ERANGE;
	}
M
Mario Six 已提交
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282

	switch (val_len) {
	case REGMAP_SIZE_8:
		writeb(*((u8 *)val), (u8 *)ptr);
		break;
	case REGMAP_SIZE_16:
		writew(*((u16 *)val), (u16 *)ptr);
		break;
	case REGMAP_SIZE_32:
		writel(*((u32 *)val), (u32 *)ptr);
		break;
#if defined(writeq)
	case REGMAP_SIZE_64:
		writeq(*((u64 *)val), (u64 *)ptr);
		break;
#endif
	default:
		debug("%s: regmap size %zu unknown\n", __func__, val_len);
		return -EINVAL;
	}
283 284 285 286

	return 0;
}

287 288 289 290 291 292
int regmap_raw_write(struct regmap *map, uint offset, const void *val,
		     size_t val_len)
{
	return regmap_raw_write_range(map, 0, offset, val, val_len);
}

293 294
int regmap_write(struct regmap *map, uint offset, uint val)
{
M
Mario Six 已提交
295
	return regmap_raw_write(map, offset, &val, REGMAP_SIZE_32);
296
}
297 298 299 300 301 302 303 304 305 306 307 308 309 310

int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val)
{
	uint reg;
	int ret;

	ret = regmap_read(map, offset, &reg);
	if (ret)
		return ret;

	reg &= ~mask;

	return regmap_write(map, offset, reg | val);
}