1f033c26dSMark Brown // SPDX-License-Identifier: GPL-2.0
2f033c26dSMark Brown //
3f033c26dSMark Brown // Register cache access API - maple tree based cache
4f033c26dSMark Brown //
5f033c26dSMark Brown // Copyright 2023 Arm, Ltd
6f033c26dSMark Brown //
7f033c26dSMark Brown // Author: Mark Brown <broonie@kernel.org>
8f033c26dSMark Brown 
9f033c26dSMark Brown #include <linux/debugfs.h>
10f033c26dSMark Brown #include <linux/device.h>
11f033c26dSMark Brown #include <linux/maple_tree.h>
12f033c26dSMark Brown #include <linux/slab.h>
13f033c26dSMark Brown 
14f033c26dSMark Brown #include "internal.h"
15f033c26dSMark Brown 
16f033c26dSMark Brown static int regcache_maple_read(struct regmap *map,
17f033c26dSMark Brown 			       unsigned int reg, unsigned int *value)
18f033c26dSMark Brown {
19f033c26dSMark Brown 	struct maple_tree *mt = map->cache;
20f033c26dSMark Brown 	MA_STATE(mas, mt, reg, reg);
21f033c26dSMark Brown 	unsigned long *entry;
22f033c26dSMark Brown 
23f033c26dSMark Brown 	rcu_read_lock();
24f033c26dSMark Brown 
25f033c26dSMark Brown 	entry = mas_find(&mas, reg);
26f033c26dSMark Brown 	if (!entry) {
27f033c26dSMark Brown 		rcu_read_unlock();
28f033c26dSMark Brown 		return -ENOENT;
29f033c26dSMark Brown 	}
30f033c26dSMark Brown 
31f033c26dSMark Brown 	*value = entry[reg - mas.index];
32f033c26dSMark Brown 
33f033c26dSMark Brown 	rcu_read_unlock();
34f033c26dSMark Brown 
35f033c26dSMark Brown 	return 0;
36f033c26dSMark Brown }
37f033c26dSMark Brown 
38f033c26dSMark Brown static int regcache_maple_write(struct regmap *map, unsigned int reg,
39f033c26dSMark Brown 				unsigned int val)
40f033c26dSMark Brown {
41f033c26dSMark Brown 	struct maple_tree *mt = map->cache;
42f033c26dSMark Brown 	MA_STATE(mas, mt, reg, reg);
43f033c26dSMark Brown 	unsigned long *entry, *upper, *lower;
44f033c26dSMark Brown 	unsigned long index, last;
45f033c26dSMark Brown 	size_t lower_sz, upper_sz;
46f033c26dSMark Brown 	int ret;
47f033c26dSMark Brown 
48f033c26dSMark Brown 	rcu_read_lock();
49f033c26dSMark Brown 
50f033c26dSMark Brown 	entry = mas_find(&mas, reg);
51f033c26dSMark Brown 	if (entry) {
52f033c26dSMark Brown 		entry[reg - mas.index] = val;
53f033c26dSMark Brown 		rcu_read_unlock();
54f033c26dSMark Brown 		return 0;
55f033c26dSMark Brown 	}
56f033c26dSMark Brown 
57f033c26dSMark Brown 	/* Any adjacent entries to extend/merge? */
58f033c26dSMark Brown 	mas_set_range(&mas, reg - 1, reg + 1);
59f033c26dSMark Brown 	index = reg;
60f033c26dSMark Brown 	last = reg;
61f033c26dSMark Brown 
62f033c26dSMark Brown 	lower = mas_find(&mas, reg - 1);
63f033c26dSMark Brown 	if (lower) {
64f033c26dSMark Brown 		index = mas.index;
65f033c26dSMark Brown 		lower_sz = (mas.last - mas.index + 1) * sizeof(unsigned long);
66f033c26dSMark Brown 	}
67f033c26dSMark Brown 
68f033c26dSMark Brown 	upper = mas_find(&mas, reg + 1);
69f033c26dSMark Brown 	if (upper) {
70f033c26dSMark Brown 		last = mas.last;
71f033c26dSMark Brown 		upper_sz = (mas.last - mas.index + 1) * sizeof(unsigned long);
72f033c26dSMark Brown 	}
73f033c26dSMark Brown 
74f033c26dSMark Brown 	rcu_read_unlock();
75f033c26dSMark Brown 
76f033c26dSMark Brown 	entry = kmalloc((last - index + 1) * sizeof(unsigned long),
77f033c26dSMark Brown 			GFP_KERNEL);
78f033c26dSMark Brown 	if (!entry)
79f033c26dSMark Brown 		return -ENOMEM;
80f033c26dSMark Brown 
81f033c26dSMark Brown 	if (lower)
82f033c26dSMark Brown 		memcpy(entry, lower, lower_sz);
83f033c26dSMark Brown 	entry[reg - index] = val;
84f033c26dSMark Brown 	if (upper)
85f033c26dSMark Brown 		memcpy(&entry[reg - index + 1], upper, upper_sz);
86f033c26dSMark Brown 
87f033c26dSMark Brown 	/*
88f033c26dSMark Brown 	 * This is safe because the regmap lock means the Maple lock
89f033c26dSMark Brown 	 * is redundant, but we need to take it due to lockdep asserts
90f033c26dSMark Brown 	 * in the maple tree code.
91f033c26dSMark Brown 	 */
92f033c26dSMark Brown 	mas_lock(&mas);
93f033c26dSMark Brown 
94f033c26dSMark Brown 	mas_set_range(&mas, index, last);
95f033c26dSMark Brown 	ret = mas_store_gfp(&mas, entry, GFP_KERNEL);
96f033c26dSMark Brown 
97f033c26dSMark Brown 	mas_unlock(&mas);
98f033c26dSMark Brown 
99f033c26dSMark Brown 	if (ret == 0) {
100f033c26dSMark Brown 		kfree(lower);
101f033c26dSMark Brown 		kfree(upper);
102f033c26dSMark Brown 	}
103f033c26dSMark Brown 
104f033c26dSMark Brown 	return ret;
105f033c26dSMark Brown }
106f033c26dSMark Brown 
107f033c26dSMark Brown static int regcache_maple_drop(struct regmap *map, unsigned int min,
108f033c26dSMark Brown 			       unsigned int max)
109f033c26dSMark Brown {
110f033c26dSMark Brown 	struct maple_tree *mt = map->cache;
111f033c26dSMark Brown 	MA_STATE(mas, mt, min, max);
112f033c26dSMark Brown 	unsigned long *entry, *lower, *upper;
113f033c26dSMark Brown 	unsigned long lower_index, lower_last;
114f033c26dSMark Brown 	unsigned long upper_index, upper_last;
115f033c26dSMark Brown 	int ret;
116f033c26dSMark Brown 
117f033c26dSMark Brown 	lower = NULL;
118f033c26dSMark Brown 	upper = NULL;
119f033c26dSMark Brown 
120f033c26dSMark Brown 	mas_lock(&mas);
121f033c26dSMark Brown 
122f033c26dSMark Brown 	mas_for_each(&mas, entry, max) {
123f033c26dSMark Brown 		/*
124f033c26dSMark Brown 		 * This is safe because the regmap lock means the
125f033c26dSMark Brown 		 * Maple lock is redundant, but we need to take it due
126f033c26dSMark Brown 		 * to lockdep asserts in the maple tree code.
127f033c26dSMark Brown 		 */
128f033c26dSMark Brown 		mas_unlock(&mas);
129f033c26dSMark Brown 
130f033c26dSMark Brown 		/* Do we need to save any of this entry? */
131f033c26dSMark Brown 		if (mas.index < min) {
132f033c26dSMark Brown 			lower_index = mas.index;
133f033c26dSMark Brown 			lower_last = min -1;
134f033c26dSMark Brown 
135f033c26dSMark Brown 			lower = kmemdup(entry, ((min - mas.index) *
136f033c26dSMark Brown 						sizeof(unsigned long)),
137f033c26dSMark Brown 					GFP_KERNEL);
138f033c26dSMark Brown 			if (!lower) {
139f033c26dSMark Brown 				ret = -ENOMEM;
140*451941acSMark Brown 				goto out_unlocked;
141f033c26dSMark Brown 			}
142f033c26dSMark Brown 		}
143f033c26dSMark Brown 
144f033c26dSMark Brown 		if (mas.last > max) {
145f033c26dSMark Brown 			upper_index = max + 1;
146f033c26dSMark Brown 			upper_last = mas.last;
147f033c26dSMark Brown 
148f033c26dSMark Brown 			upper = kmemdup(&entry[max + 1],
149f033c26dSMark Brown 					((mas.last - max) *
150f033c26dSMark Brown 					 sizeof(unsigned long)),
151f033c26dSMark Brown 					GFP_KERNEL);
152f033c26dSMark Brown 			if (!upper) {
153f033c26dSMark Brown 				ret = -ENOMEM;
154*451941acSMark Brown 				goto out_unlocked;
155f033c26dSMark Brown 			}
156f033c26dSMark Brown 		}
157f033c26dSMark Brown 
158f033c26dSMark Brown 		kfree(entry);
159f033c26dSMark Brown 		mas_lock(&mas);
160f033c26dSMark Brown 		mas_erase(&mas);
161f033c26dSMark Brown 
162f033c26dSMark Brown 		/* Insert new nodes with the saved data */
163f033c26dSMark Brown 		if (lower) {
164f033c26dSMark Brown 			mas_set_range(&mas, lower_index, lower_last);
165f033c26dSMark Brown 			ret = mas_store_gfp(&mas, lower, GFP_KERNEL);
166f033c26dSMark Brown 			if (ret != 0)
167f033c26dSMark Brown 				goto out;
168f033c26dSMark Brown 			lower = NULL;
169f033c26dSMark Brown 		}
170f033c26dSMark Brown 
171f033c26dSMark Brown 		if (upper) {
172f033c26dSMark Brown 			mas_set_range(&mas, upper_index, upper_last);
173f033c26dSMark Brown 			ret = mas_store_gfp(&mas, upper, GFP_KERNEL);
174f033c26dSMark Brown 			if (ret != 0)
175f033c26dSMark Brown 				goto out;
176f033c26dSMark Brown 			upper = NULL;
177f033c26dSMark Brown 		}
178f033c26dSMark Brown 	}
179f033c26dSMark Brown 
180f033c26dSMark Brown out:
181f033c26dSMark Brown 	mas_unlock(&mas);
182*451941acSMark Brown out_unlocked:
183f033c26dSMark Brown 	kfree(lower);
184f033c26dSMark Brown 	kfree(upper);
185f033c26dSMark Brown 
186f033c26dSMark Brown 	return ret;
187f033c26dSMark Brown }
188f033c26dSMark Brown 
189f033c26dSMark Brown static int regcache_maple_sync(struct regmap *map, unsigned int min,
190f033c26dSMark Brown 			       unsigned int max)
191f033c26dSMark Brown {
192f033c26dSMark Brown 	struct maple_tree *mt = map->cache;
193f033c26dSMark Brown 	unsigned long *entry;
194f033c26dSMark Brown 	MA_STATE(mas, mt, min, max);
195f033c26dSMark Brown 	unsigned long lmin = min;
196f033c26dSMark Brown 	unsigned long lmax = max;
197f033c26dSMark Brown 	unsigned int r;
198f033c26dSMark Brown 	int ret;
199f033c26dSMark Brown 
200f033c26dSMark Brown 	map->cache_bypass = true;
201f033c26dSMark Brown 
202f033c26dSMark Brown 	rcu_read_lock();
203f033c26dSMark Brown 
204f033c26dSMark Brown 	mas_for_each(&mas, entry, max) {
205f033c26dSMark Brown 		for (r = max(mas.index, lmin); r <= min(mas.last, lmax); r++) {
206f033c26dSMark Brown 			ret = regcache_sync_val(map, r, entry[r - mas.index]);
207f033c26dSMark Brown 			if (ret != 0)
208f033c26dSMark Brown 				goto out;
209f033c26dSMark Brown 		}
210f033c26dSMark Brown 	}
211f033c26dSMark Brown 
212f033c26dSMark Brown out:
213f033c26dSMark Brown 	rcu_read_unlock();
214f033c26dSMark Brown 
215f033c26dSMark Brown 	map->cache_bypass = false;
216f033c26dSMark Brown 
217f033c26dSMark Brown 	return ret;
218f033c26dSMark Brown }
219f033c26dSMark Brown 
220f033c26dSMark Brown static int regcache_maple_exit(struct regmap *map)
221f033c26dSMark Brown {
222f033c26dSMark Brown 	struct maple_tree *mt = map->cache;
223f033c26dSMark Brown 	MA_STATE(mas, mt, 0, UINT_MAX);
224f033c26dSMark Brown 	unsigned int *entry;;
225f033c26dSMark Brown 
226f033c26dSMark Brown 	/* if we've already been called then just return */
227f033c26dSMark Brown 	if (!mt)
228f033c26dSMark Brown 		return 0;
229f033c26dSMark Brown 
230f033c26dSMark Brown 	mas_lock(&mas);
231f033c26dSMark Brown 	mas_for_each(&mas, entry, UINT_MAX)
232f033c26dSMark Brown 		kfree(entry);
233f033c26dSMark Brown 	__mt_destroy(mt);
234f033c26dSMark Brown 	mas_unlock(&mas);
235f033c26dSMark Brown 
236f033c26dSMark Brown 	kfree(mt);
237f033c26dSMark Brown 	map->cache = NULL;
238f033c26dSMark Brown 
239f033c26dSMark Brown 	return 0;
240f033c26dSMark Brown }
241f033c26dSMark Brown 
242f033c26dSMark Brown static int regcache_maple_init(struct regmap *map)
243f033c26dSMark Brown {
244f033c26dSMark Brown 	struct maple_tree *mt;
245f033c26dSMark Brown 	int i;
246f033c26dSMark Brown 	int ret;
247f033c26dSMark Brown 
248f033c26dSMark Brown 	mt = kmalloc(sizeof(*mt), GFP_KERNEL);
249f033c26dSMark Brown 	if (!mt)
250f033c26dSMark Brown 		return -ENOMEM;
251f033c26dSMark Brown 	map->cache = mt;
252f033c26dSMark Brown 
253f033c26dSMark Brown 	mt_init(mt);
254f033c26dSMark Brown 
255f033c26dSMark Brown 	for (i = 0; i < map->num_reg_defaults; i++) {
256f033c26dSMark Brown 		ret = regcache_maple_write(map,
257f033c26dSMark Brown 					   map->reg_defaults[i].reg,
258f033c26dSMark Brown 					   map->reg_defaults[i].def);
259f033c26dSMark Brown 		if (ret)
260f033c26dSMark Brown 			goto err;
261f033c26dSMark Brown 	}
262f033c26dSMark Brown 
263f033c26dSMark Brown 	return 0;
264f033c26dSMark Brown 
265f033c26dSMark Brown err:
266f033c26dSMark Brown 	regcache_maple_exit(map);
267f033c26dSMark Brown 	return ret;
268f033c26dSMark Brown }
269f033c26dSMark Brown 
270f033c26dSMark Brown struct regcache_ops regcache_maple_ops = {
271f033c26dSMark Brown 	.type = REGCACHE_MAPLE,
272f033c26dSMark Brown 	.name = "maple",
273f033c26dSMark Brown 	.init = regcache_maple_init,
274f033c26dSMark Brown 	.exit = regcache_maple_exit,
275f033c26dSMark Brown 	.read = regcache_maple_read,
276f033c26dSMark Brown 	.write = regcache_maple_write,
277f033c26dSMark Brown 	.drop = regcache_maple_drop,
278f033c26dSMark Brown 	.sync = regcache_maple_sync,
279f033c26dSMark Brown };
280