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 
regcache_maple_read(struct regmap * map,unsigned int reg,unsigned int * value)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 
25fac79badSMark Brown 	entry = mas_walk(&mas);
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 
regcache_maple_write(struct regmap * map,unsigned int reg,unsigned int val)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 
50fac79badSMark Brown 	entry = mas_walk(&mas);
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),
77b0393e1fSGuenter Roeck 			map->alloc_flags);
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);
95b0393e1fSGuenter Roeck 	ret = mas_store_gfp(&mas, entry, map->alloc_flags);
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 
regcache_maple_drop(struct regmap * map,unsigned int min,unsigned int max)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;
115*fce7a547SRichard Fitzgerald 	int ret = 0;
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)),
137b0393e1fSGuenter Roeck 					map->alloc_flags);
138f033c26dSMark Brown 			if (!lower) {
139f033c26dSMark Brown 				ret = -ENOMEM;
140451941acSMark 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 
1483af6c5acSRichard Fitzgerald 			upper = kmemdup(&entry[max - mas.index + 1],
149f033c26dSMark Brown 					((mas.last - max) *
150f033c26dSMark Brown 					 sizeof(unsigned long)),
151b0393e1fSGuenter Roeck 					map->alloc_flags);
152f033c26dSMark Brown 			if (!upper) {
153f033c26dSMark Brown 				ret = -ENOMEM;
154451941acSMark 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);
165b0393e1fSGuenter Roeck 			ret = mas_store_gfp(&mas, lower, map->alloc_flags);
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);
173b0393e1fSGuenter Roeck 			ret = mas_store_gfp(&mas, upper, map->alloc_flags);
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);
182451941acSMark Brown out_unlocked:
183f033c26dSMark Brown 	kfree(lower);
184f033c26dSMark Brown 	kfree(upper);
185f033c26dSMark Brown 
186f033c26dSMark Brown 	return ret;
187f033c26dSMark Brown }
188f033c26dSMark Brown 
regcache_maple_sync_block(struct regmap * map,unsigned long * entry,struct ma_state * mas,unsigned int min,unsigned int max)189bfa0b38cSMark Brown static int regcache_maple_sync_block(struct regmap *map, unsigned long *entry,
190bfa0b38cSMark Brown 				     struct ma_state *mas,
191bfa0b38cSMark Brown 				     unsigned int min, unsigned int max)
192bfa0b38cSMark Brown {
193bfa0b38cSMark Brown 	void *buf;
194bfa0b38cSMark Brown 	unsigned long r;
195bfa0b38cSMark Brown 	size_t val_bytes = map->format.val_bytes;
196bfa0b38cSMark Brown 	int ret = 0;
197bfa0b38cSMark Brown 
198bfa0b38cSMark Brown 	mas_pause(mas);
199bfa0b38cSMark Brown 	rcu_read_unlock();
200bfa0b38cSMark Brown 
201bfa0b38cSMark Brown 	/*
202bfa0b38cSMark Brown 	 * Use a raw write if writing more than one register to a
203bfa0b38cSMark Brown 	 * device that supports raw writes to reduce transaction
204bfa0b38cSMark Brown 	 * overheads.
205bfa0b38cSMark Brown 	 */
206bfa0b38cSMark Brown 	if (max - min > 1 && regmap_can_raw_write(map)) {
207bfa0b38cSMark Brown 		buf = kmalloc(val_bytes * (max - min), map->alloc_flags);
208bfa0b38cSMark Brown 		if (!buf) {
209bfa0b38cSMark Brown 			ret = -ENOMEM;
210bfa0b38cSMark Brown 			goto out;
211bfa0b38cSMark Brown 		}
212bfa0b38cSMark Brown 
213bfa0b38cSMark Brown 		/* Render the data for a raw write */
214bfa0b38cSMark Brown 		for (r = min; r < max; r++) {
215bfa0b38cSMark Brown 			regcache_set_val(map, buf, r - min,
216bfa0b38cSMark Brown 					 entry[r - mas->index]);
217bfa0b38cSMark Brown 		}
218bfa0b38cSMark Brown 
219bfa0b38cSMark Brown 		ret = _regmap_raw_write(map, min, buf, (max - min) * val_bytes,
220bfa0b38cSMark Brown 					false);
221bfa0b38cSMark Brown 
222bfa0b38cSMark Brown 		kfree(buf);
223bfa0b38cSMark Brown 	} else {
224bfa0b38cSMark Brown 		for (r = min; r < max; r++) {
225bfa0b38cSMark Brown 			ret = _regmap_write(map, r,
226bfa0b38cSMark Brown 					    entry[r - mas->index]);
227bfa0b38cSMark Brown 			if (ret != 0)
228bfa0b38cSMark Brown 				goto out;
229bfa0b38cSMark Brown 		}
230bfa0b38cSMark Brown 	}
231bfa0b38cSMark Brown 
232bfa0b38cSMark Brown out:
233bfa0b38cSMark Brown 	rcu_read_lock();
234bfa0b38cSMark Brown 
235bfa0b38cSMark Brown 	return ret;
236bfa0b38cSMark Brown }
237bfa0b38cSMark Brown 
regcache_maple_sync(struct regmap * map,unsigned int min,unsigned int max)238f033c26dSMark Brown static int regcache_maple_sync(struct regmap *map, unsigned int min,
239f033c26dSMark Brown 			       unsigned int max)
240f033c26dSMark Brown {
241f033c26dSMark Brown 	struct maple_tree *mt = map->cache;
242f033c26dSMark Brown 	unsigned long *entry;
243f033c26dSMark Brown 	MA_STATE(mas, mt, min, max);
244f033c26dSMark Brown 	unsigned long lmin = min;
245f033c26dSMark Brown 	unsigned long lmax = max;
246bfa0b38cSMark Brown 	unsigned int r, v, sync_start;
247*fce7a547SRichard Fitzgerald 	int ret = 0;
248bfa0b38cSMark Brown 	bool sync_needed = false;
249f033c26dSMark Brown 
250f033c26dSMark Brown 	map->cache_bypass = true;
251f033c26dSMark Brown 
252f033c26dSMark Brown 	rcu_read_lock();
253f033c26dSMark Brown 
254f033c26dSMark Brown 	mas_for_each(&mas, entry, max) {
255f033c26dSMark Brown 		for (r = max(mas.index, lmin); r <= min(mas.last, lmax); r++) {
256bfa0b38cSMark Brown 			v = entry[r - mas.index];
257bfa0b38cSMark Brown 
258bfa0b38cSMark Brown 			if (regcache_reg_needs_sync(map, r, v)) {
259bfa0b38cSMark Brown 				if (!sync_needed) {
260bfa0b38cSMark Brown 					sync_start = r;
261bfa0b38cSMark Brown 					sync_needed = true;
262bfa0b38cSMark Brown 				}
263bfa0b38cSMark Brown 				continue;
264bfa0b38cSMark Brown 			}
265bfa0b38cSMark Brown 
266bfa0b38cSMark Brown 			if (!sync_needed)
267bfa0b38cSMark Brown 				continue;
268bfa0b38cSMark Brown 
269bfa0b38cSMark Brown 			ret = regcache_maple_sync_block(map, entry, &mas,
270bfa0b38cSMark Brown 							sync_start, r);
271f033c26dSMark Brown 			if (ret != 0)
272f033c26dSMark Brown 				goto out;
273bfa0b38cSMark Brown 			sync_needed = false;
274f033c26dSMark Brown 		}
275f033c26dSMark Brown 
276bfa0b38cSMark Brown 		if (sync_needed) {
277bfa0b38cSMark Brown 			ret = regcache_maple_sync_block(map, entry, &mas,
278bfa0b38cSMark Brown 							sync_start, r);
279bfa0b38cSMark Brown 			if (ret != 0)
280bfa0b38cSMark Brown 				goto out;
281bfa0b38cSMark Brown 			sync_needed = false;
282bfa0b38cSMark Brown 		}
283bfa0b38cSMark Brown 	}
284f033c26dSMark Brown 
2850cc65780SMark Brown out:
286bfa0b38cSMark Brown 	rcu_read_unlock();
287bfa0b38cSMark Brown 
288f033c26dSMark Brown 	map->cache_bypass = false;
289f033c26dSMark Brown 
290f033c26dSMark Brown 	return ret;
291f033c26dSMark Brown }
292f033c26dSMark Brown 
regcache_maple_exit(struct regmap * map)293f033c26dSMark Brown static int regcache_maple_exit(struct regmap *map)
294f033c26dSMark Brown {
295f033c26dSMark Brown 	struct maple_tree *mt = map->cache;
296f033c26dSMark Brown 	MA_STATE(mas, mt, 0, UINT_MAX);
297f033c26dSMark Brown 	unsigned int *entry;;
298f033c26dSMark Brown 
299f033c26dSMark Brown 	/* if we've already been called then just return */
300f033c26dSMark Brown 	if (!mt)
301f033c26dSMark Brown 		return 0;
302f033c26dSMark Brown 
303f033c26dSMark Brown 	mas_lock(&mas);
304f033c26dSMark Brown 	mas_for_each(&mas, entry, UINT_MAX)
305f033c26dSMark Brown 		kfree(entry);
306f033c26dSMark Brown 	__mt_destroy(mt);
307f033c26dSMark Brown 	mas_unlock(&mas);
308f033c26dSMark Brown 
309f033c26dSMark Brown 	kfree(mt);
310f033c26dSMark Brown 	map->cache = NULL;
311f033c26dSMark Brown 
312f033c26dSMark Brown 	return 0;
313f033c26dSMark Brown }
314f033c26dSMark Brown 
regcache_maple_insert_block(struct regmap * map,int first,int last)3153a48d212SMark Brown static int regcache_maple_insert_block(struct regmap *map, int first,
3163a48d212SMark Brown 					int last)
3173a48d212SMark Brown {
3183a48d212SMark Brown 	struct maple_tree *mt = map->cache;
3193a48d212SMark Brown 	MA_STATE(mas, mt, first, last);
3203a48d212SMark Brown 	unsigned long *entry;
3213a48d212SMark Brown 	int i, ret;
3223a48d212SMark Brown 
323b0393e1fSGuenter Roeck 	entry = kcalloc(last - first + 1, sizeof(unsigned long), map->alloc_flags);
3243a48d212SMark Brown 	if (!entry)
3253a48d212SMark Brown 		return -ENOMEM;
3263a48d212SMark Brown 
3273a48d212SMark Brown 	for (i = 0; i < last - first + 1; i++)
3283a48d212SMark Brown 		entry[i] = map->reg_defaults[first + i].def;
3293a48d212SMark Brown 
3303a48d212SMark Brown 	mas_lock(&mas);
3313a48d212SMark Brown 
3323a48d212SMark Brown 	mas_set_range(&mas, map->reg_defaults[first].reg,
3333a48d212SMark Brown 		      map->reg_defaults[last].reg);
334b0393e1fSGuenter Roeck 	ret = mas_store_gfp(&mas, entry, map->alloc_flags);
3353a48d212SMark Brown 
3363a48d212SMark Brown 	mas_unlock(&mas);
3373a48d212SMark Brown 
3383a48d212SMark Brown 	if (ret)
3393a48d212SMark Brown 		kfree(entry);
3403a48d212SMark Brown 
3413a48d212SMark Brown 	return ret;
3423a48d212SMark Brown }
3433a48d212SMark Brown 
regcache_maple_init(struct regmap * map)344f033c26dSMark Brown static int regcache_maple_init(struct regmap *map)
345f033c26dSMark Brown {
346f033c26dSMark Brown 	struct maple_tree *mt;
347f033c26dSMark Brown 	int i;
348f033c26dSMark Brown 	int ret;
3493a48d212SMark Brown 	int range_start;
350f033c26dSMark Brown 
351f033c26dSMark Brown 	mt = kmalloc(sizeof(*mt), GFP_KERNEL);
352f033c26dSMark Brown 	if (!mt)
353f033c26dSMark Brown 		return -ENOMEM;
354f033c26dSMark Brown 	map->cache = mt;
355f033c26dSMark Brown 
356f033c26dSMark Brown 	mt_init(mt);
357f033c26dSMark Brown 
3583a48d212SMark Brown 	if (!map->num_reg_defaults)
3593a48d212SMark Brown 		return 0;
3603a48d212SMark Brown 
3613a48d212SMark Brown 	range_start = 0;
3623a48d212SMark Brown 
3633a48d212SMark Brown 	/* Scan for ranges of contiguous registers */
3643a48d212SMark Brown 	for (i = 1; i < map->num_reg_defaults; i++) {
3653a48d212SMark Brown 		if (map->reg_defaults[i].reg !=
3663a48d212SMark Brown 		    map->reg_defaults[i - 1].reg + 1) {
3673a48d212SMark Brown 			ret = regcache_maple_insert_block(map, range_start,
3683a48d212SMark Brown 							  i - 1);
3693a48d212SMark Brown 			if (ret != 0)
370f033c26dSMark Brown 				goto err;
3713a48d212SMark Brown 
3723a48d212SMark Brown 			range_start = i;
373f033c26dSMark Brown 		}
3743a48d212SMark Brown 	}
3753a48d212SMark Brown 
3763a48d212SMark Brown 	/* Add the last block */
3773a48d212SMark Brown 	ret = regcache_maple_insert_block(map, range_start,
3783a48d212SMark Brown 					  map->num_reg_defaults - 1);
3793a48d212SMark Brown 	if (ret != 0)
3803a48d212SMark Brown 		goto err;
381f033c26dSMark Brown 
382f033c26dSMark Brown 	return 0;
383f033c26dSMark Brown 
384f033c26dSMark Brown err:
385f033c26dSMark Brown 	regcache_maple_exit(map);
386f033c26dSMark Brown 	return ret;
387f033c26dSMark Brown }
388f033c26dSMark Brown 
389f033c26dSMark Brown struct regcache_ops regcache_maple_ops = {
390f033c26dSMark Brown 	.type = REGCACHE_MAPLE,
391f033c26dSMark Brown 	.name = "maple",
392f033c26dSMark Brown 	.init = regcache_maple_init,
393f033c26dSMark Brown 	.exit = regcache_maple_exit,
394f033c26dSMark Brown 	.read = regcache_maple_read,
395f033c26dSMark Brown 	.write = regcache_maple_write,
396f033c26dSMark Brown 	.drop = regcache_maple_drop,
397f033c26dSMark Brown 	.sync = regcache_maple_sync,
398f033c26dSMark Brown };
399