summaryrefslogtreecommitdiff
path: root/drivers/base/regmap/regcache-flat.c
blob: bacb7137092face879c572cb0e0dd0bfe69b69f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// SPDX-License-Identifier: GPL-2.0
//
// Register cache access API - flat caching support
//
// Copyright 2012 Wolfson Microelectronics plc
//
// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>

#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/limits.h>
#include <linux/overflow.h>
#include <linux/seq_file.h>
#include <linux/slab.h>

#include "internal.h"

static inline unsigned int regcache_flat_get_index(const struct regmap *map,
						   unsigned int reg)
{
	return regcache_get_index_by_order(map, reg);
}

struct regcache_flat_data {
	unsigned long *valid;
	unsigned int data[];
};

static int regcache_flat_init(struct regmap *map)
{
	int i;
	unsigned int cache_size;
	struct regcache_flat_data *cache;

	if (!map || map->reg_stride_order < 0 || !map->max_register_is_set)
		return -EINVAL;

	cache_size = regcache_flat_get_index(map, map->max_register) + 1;
	cache = kzalloc(struct_size(cache, data, cache_size), map->alloc_flags);
	if (!cache)
		return -ENOMEM;

	cache->valid = bitmap_zalloc(cache_size, map->alloc_flags);
	if (!cache->valid)
		goto err_free;

	map->cache = cache;

	for (i = 0; i < map->num_reg_defaults; i++) {
		unsigned int reg = map->reg_defaults[i].reg;
		unsigned int index = regcache_flat_get_index(map, reg);

		cache->data[index] = map->reg_defaults[i].def;
		__set_bit(index, cache->valid);
	}

	return 0;

err_free:
	kfree(cache);
	return -ENOMEM;
}

static int regcache_flat_exit(struct regmap *map)
{
	struct regcache_flat_data *cache = map->cache;

	if (cache)
		bitmap_free(cache->valid);

	kfree(cache);
	map->cache = NULL;

	return 0;
}

static int regcache_flat_read(struct regmap *map,
			      unsigned int reg, unsigned int *value)
{
	struct regcache_flat_data *cache = map->cache;
	unsigned int index = regcache_flat_get_index(map, reg);

	/* legacy behavior: ignore validity, but warn the user */
	if (unlikely(!test_bit(index, cache->valid)))
		dev_warn_once(map->dev,
			"using zero-initialized flat cache, this may cause unexpected behavior");

	*value = cache->data[index];

	return 0;
}

static int regcache_flat_sparse_read(struct regmap *map,
				     unsigned int reg, unsigned int *value)
{
	struct regcache_flat_data *cache = map->cache;
	unsigned int index = regcache_flat_get_index(map, reg);

	if (unlikely(!test_bit(index, cache->valid)))
		return -ENOENT;

	*value = cache->data[index];

	return 0;
}

static int regcache_flat_write(struct regmap *map, unsigned int reg,
			       unsigned int value)
{
	struct regcache_flat_data *cache = map->cache;
	unsigned int index = regcache_flat_get_index(map, reg);

	cache->data[index] = value;
	__set_bit(index, cache->valid);

	return 0;
}

static int regcache_flat_drop(struct regmap *map, unsigned int min,
			      unsigned int max)
{
	struct regcache_flat_data *cache = map->cache;
	unsigned int bitmap_min = regcache_flat_get_index(map, min);
	unsigned int bitmap_max = regcache_flat_get_index(map, max);

	bitmap_clear(cache->valid, bitmap_min, bitmap_max + 1 - bitmap_min);

	return 0;
}

struct regcache_ops regcache_flat_ops = {
	.type = REGCACHE_FLAT,
	.name = "flat",
	.init = regcache_flat_init,
	.exit = regcache_flat_exit,
	.read = regcache_flat_read,
	.write = regcache_flat_write,
};

struct regcache_ops regcache_flat_sparse_ops = {
	.type = REGCACHE_FLAT_S,
	.name = "flat-sparse",
	.init = regcache_flat_init,
	.exit = regcache_flat_exit,
	.read = regcache_flat_sparse_read,
	.write = regcache_flat_write,
	.drop = regcache_flat_drop,
};