File Index
Files and directories
Sign in
FILE

regcache-maple.c

File preview with syntax highlighting.

linux-6.19.14/drivers/base/regmap/regcache-maple.c C
  1// SPDX-License-Identifier: GPL-2.0
  2//
  3// Register cache access API - maple tree based cache
  4//
  5// Copyright 2023 Arm, Ltd
  6//
  7// Author: Mark Brown <broonie@kernel.org>
  8
  9#include <linux/debugfs.h>
 10#include <linux/device.h>
 11#include <linux/maple_tree.h>
 12#include <linux/slab.h>
 13
 14#include "internal.h"
 15
 16static int regcache_maple_read(struct regmap *map,
 17			       unsigned int reg, unsigned int *value)
 18{
 19	struct maple_tree *mt = map->cache;
 20	MA_STATE(mas, mt, reg, reg);
 21	unsigned long *entry;
 22
 23	rcu_read_lock();
 24
 25	entry = mas_walk(&mas);
 26	if (!entry) {
 27		rcu_read_unlock();
 28		return -ENOENT;
 29	}
 30
 31	*value = entry[reg - mas.index];
 32
 33	rcu_read_unlock();
 34
 35	return 0;
 36}
 37
 38static int regcache_maple_write(struct regmap *map, unsigned int reg,
 39				unsigned int val)
 40{
 41	struct maple_tree *mt = map->cache;
 42	MA_STATE(mas, mt, reg, reg);
 43	unsigned long *entry, *upper, *lower;
 44	unsigned long index, last;
 45	size_t lower_sz, upper_sz;
 46	int ret;
 47
 48	rcu_read_lock();
 49
 50	entry = mas_walk(&mas);
 51	if (entry) {
 52		entry[reg - mas.index] = val;
 53		rcu_read_unlock();
 54		return 0;
 55	}
 56
 57	/* Any adjacent entries to extend/merge? */
 58	mas_set_range(&mas, reg - 1, reg + 1);
 59	index = reg;
 60	last = reg;
 61
 62	lower = mas_find(&mas, reg - 1);
 63	if (lower) {
 64		index = mas.index;
 65		lower_sz = (mas.last - mas.index + 1) * sizeof(unsigned long);
 66	}
 67
 68	upper = mas_find(&mas, reg + 1);
 69	if (upper) {
 70		last = mas.last;
 71		upper_sz = (mas.last - mas.index + 1) * sizeof(unsigned long);
 72	}
 73
 74	rcu_read_unlock();
 75
 76	entry = kmalloc_array(last - index + 1, sizeof(*entry), map->alloc_flags);
 77	if (!entry)
 78		return -ENOMEM;
 79
 80	if (lower)
 81		memcpy(entry, lower, lower_sz);
 82	entry[reg - index] = val;
 83	if (upper)
 84		memcpy(&entry[reg - index + 1], upper, upper_sz);
 85
 86	/*
 87	 * This is safe because the regmap lock means the Maple lock
 88	 * is redundant, but we need to take it due to lockdep asserts
 89	 * in the maple tree code.
 90	 */
 91	mas_lock(&mas);
 92
 93	mas_set_range(&mas, index, last);
 94	ret = mas_store_gfp(&mas, entry, map->alloc_flags);
 95
 96	mas_unlock(&mas);
 97
 98	if (ret) {
 99		kfree(entry);
100		return ret;
101	}
102	kfree(lower);
103	kfree(upper);
104	return 0;
105}
106
107static int regcache_maple_drop(struct regmap *map, unsigned int min,
108			       unsigned int max)
109{
110	struct maple_tree *mt = map->cache;
111	MA_STATE(mas, mt, min, max);
112	unsigned long *entry, *lower, *upper;
113	/* initialized to work around false-positive -Wuninitialized warning */
114	unsigned long lower_index = 0, lower_last = 0;
115	unsigned long upper_index, upper_last;
116	int ret = 0;
117
118	lower = NULL;
119	upper = NULL;
120
121	mas_lock(&mas);
122
123	mas_for_each(&mas, entry, max) {
124		/*
125		 * This is safe because the regmap lock means the
126		 * Maple lock is redundant, but we need to take it due
127		 * to lockdep asserts in the maple tree code.
128		 */
129		mas_unlock(&mas);
130
131		/* Do we need to save any of this entry? */
132		if (mas.index < min) {
133			lower_index = mas.index;
134			lower_last = min -1;
135
136			lower = kmemdup_array(entry,
137					      min - mas.index, sizeof(*lower),
138					      map->alloc_flags);
139			if (!lower) {
140				ret = -ENOMEM;
141				goto out_unlocked;
142			}
143		}
144
145		if (mas.last > max) {
146			upper_index = max + 1;
147			upper_last = mas.last;
148
149			upper = kmemdup_array(&entry[max - mas.index + 1],
150					      mas.last - max, sizeof(*upper),
151					      map->alloc_flags);
152			if (!upper) {
153				ret = -ENOMEM;
154				goto out_unlocked;
155			}
156		}
157
158		kfree(entry);
159		mas_lock(&mas);
160		mas_erase(&mas);
161
162		/* Insert new nodes with the saved data */
163		if (lower) {
164			mas_set_range(&mas, lower_index, lower_last);
165			ret = mas_store_gfp(&mas, lower, map->alloc_flags);
166			if (ret != 0)
167				goto out;
168			lower = NULL;
169		}
170
171		if (upper) {
172			mas_set_range(&mas, upper_index, upper_last);
173			ret = mas_store_gfp(&mas, upper, map->alloc_flags);
174			if (ret != 0)
175				goto out;
176			upper = NULL;
177		}
178	}
179
180out:
181	mas_unlock(&mas);
182out_unlocked:
183	kfree(lower);
184	kfree(upper);
185
186	return ret;
187}
188
189static int regcache_maple_sync_block(struct regmap *map, unsigned long *entry,
190				     struct ma_state *mas,
191				     unsigned int min, unsigned int max)
192{
193	void *buf;
194	unsigned long r;
195	size_t val_bytes = map->format.val_bytes;
196	int ret = 0;
197
198	mas_pause(mas);
199	rcu_read_unlock();
200
201	/*
202	 * Use a raw write if writing more than one register to a
203	 * device that supports raw writes to reduce transaction
204	 * overheads.
205	 */
206	if (max - min > 1 && regmap_can_raw_write(map)) {
207		buf = kmalloc_array(max - min, val_bytes, map->alloc_flags);
208		if (!buf) {
209			ret = -ENOMEM;
210			goto out;
211		}
212
213		/* Render the data for a raw write */
214		for (r = min; r < max; r++) {
215			regcache_set_val(map, buf, r - min,
216					 entry[r - mas->index]);
217		}
218
219		ret = _regmap_raw_write(map, min, buf, (max - min) * val_bytes,
220					false);
221
222		kfree(buf);
223	} else {
224		for (r = min; r < max; r++) {
225			ret = _regmap_write(map, r,
226					    entry[r - mas->index]);
227			if (ret != 0)
228				goto out;
229		}
230	}
231
232out:
233	rcu_read_lock();
234
235	return ret;
236}
237
238static int regcache_maple_sync(struct regmap *map, unsigned int min,
239			       unsigned int max)
240{
241	struct maple_tree *mt = map->cache;
242	unsigned long *entry;
243	MA_STATE(mas, mt, min, max);
244	unsigned long lmin = min;
245	unsigned long lmax = max;
246	unsigned int r, v, sync_start;
247	int ret = 0;
248	bool sync_needed = false;
249
250	map->cache_bypass = true;
251
252	rcu_read_lock();
253
254	mas_for_each(&mas, entry, max) {
255		for (r = max(mas.index, lmin); r <= min(mas.last, lmax); r++) {
256			v = entry[r - mas.index];
257
258			if (regcache_reg_needs_sync(map, r, v)) {
259				if (!sync_needed) {
260					sync_start = r;
261					sync_needed = true;
262				}
263				continue;
264			}
265
266			if (!sync_needed)
267				continue;
268
269			ret = regcache_maple_sync_block(map, entry, &mas,
270							sync_start, r);
271			if (ret != 0)
272				goto out;
273			sync_needed = false;
274		}
275
276		if (sync_needed) {
277			ret = regcache_maple_sync_block(map, entry, &mas,
278							sync_start, r);
279			if (ret != 0)
280				goto out;
281			sync_needed = false;
282		}
283	}
284
285out:
286	rcu_read_unlock();
287
288	map->cache_bypass = false;
289
290	return ret;
291}
292
293static int regcache_maple_init(struct regmap *map)
294{
295	struct maple_tree *mt;
296
297	mt = kmalloc(sizeof(*mt), map->alloc_flags);
298	if (!mt)
299		return -ENOMEM;
300	map->cache = mt;
301
302	mt_init(mt);
303
304	if (!mt_external_lock(mt) && map->lock_key)
305		lockdep_set_class_and_subclass(&mt->ma_lock, map->lock_key, 1);
306
307	return 0;
308}
309
310static int regcache_maple_exit(struct regmap *map)
311{
312	struct maple_tree *mt = map->cache;
313	MA_STATE(mas, mt, 0, UINT_MAX);
314	unsigned int *entry;
315
316	/* if we've already been called then just return */
317	if (!mt)
318		return 0;
319
320	mas_lock(&mas);
321	mas_for_each(&mas, entry, UINT_MAX)
322		kfree(entry);
323	__mt_destroy(mt);
324	mas_unlock(&mas);
325
326	kfree(mt);
327	map->cache = NULL;
328
329	return 0;
330}
331
332static int regcache_maple_insert_block(struct regmap *map, int first,
333					int last)
334{
335	struct maple_tree *mt = map->cache;
336	MA_STATE(mas, mt, first, last);
337	unsigned long *entry;
338	int i, ret;
339
340	entry = kmalloc_array(last - first + 1, sizeof(*entry), map->alloc_flags);
341	if (!entry)
342		return -ENOMEM;
343
344	for (i = 0; i < last - first + 1; i++)
345		entry[i] = map->reg_defaults[first + i].def;
346
347	mas_lock(&mas);
348
349	mas_set_range(&mas, map->reg_defaults[first].reg,
350		      map->reg_defaults[last].reg);
351	ret = mas_store_gfp(&mas, entry, map->alloc_flags);
352
353	mas_unlock(&mas);
354
355	if (ret)
356		kfree(entry);
357
358	return ret;
359}
360
361static int regcache_maple_populate(struct regmap *map)
362{
363	int i;
364	int ret;
365	int range_start;
366
367	range_start = 0;
368
369	/* Scan for ranges of contiguous registers */
370	for (i = 1; i < map->num_reg_defaults; i++) {
371		if (map->reg_defaults[i].reg !=
372		    map->reg_defaults[i - 1].reg + 1) {
373			ret = regcache_maple_insert_block(map, range_start,
374							  i - 1);
375			if (ret != 0)
376				return ret;
377
378			range_start = i;
379		}
380	}
381
382	/* Add the last block */
383	return regcache_maple_insert_block(map, range_start, map->num_reg_defaults - 1);
384}
385
386struct regcache_ops regcache_maple_ops = {
387	.type = REGCACHE_MAPLE,
388	.name = "maple",
389	.init = regcache_maple_init,
390	.exit = regcache_maple_exit,
391	.populate = regcache_maple_populate,
392	.read = regcache_maple_read,
393	.write = regcache_maple_write,
394	.drop = regcache_maple_drop,
395	.sync = regcache_maple_sync,
396};
~/ Read-only file preview.