// SPDX-License-Identifier: GPL-2.0 // // Register cache access API - maple tree based cache // // Copyright 2023 Arm, Ltd // // Author: Mark Brown #include #include #include #include #include "internal.h" static int regcache_maple_read(struct regmap *map, unsigned int reg, unsigned int *value) { struct maple_tree *mt = map->cache; MA_STATE(mas, mt, reg, reg); unsigned long *entry; rcu_read_lock(); entry = mas_walk(&mas); if (!entry) { rcu_read_unlock(); return -ENOENT; } *value = entry[reg - mas.index]; rcu_read_unlock(); return 0; } static int regcache_maple_write(struct regmap *map, unsigned int reg, unsigned int val) { struct maple_tree *mt = map->cache; MA_STATE(mas, mt, reg, reg); unsigned long *entry, *upper, *lower; unsigned long index, last; size_t lower_sz, upper_sz; int ret; rcu_read_lock(); entry = mas_walk(&mas); if (entry) { entry[reg - mas.index] = val; rcu_read_unlock(); return 0; } /* Any adjacent entries to extend/merge? */ mas_set_range(&mas, reg - 1, reg + 1); index = reg; last = reg; lower = mas_find(&mas, reg - 1); if (lower) { index = mas.index; lower_sz = (mas.last - mas.index + 1) * sizeof(unsigned long); } upper = mas_find(&mas, reg + 1); if (upper) { last = mas.last; upper_sz = (mas.last - mas.index + 1) * sizeof(unsigned long); } rcu_read_unlock(); entry = kmalloc((last - index + 1) * sizeof(unsigned long), GFP_KERNEL); if (!entry) return -ENOMEM; if (lower) memcpy(entry, lower, lower_sz); entry[reg - index] = val; if (upper) memcpy(&entry[reg - index + 1], upper, upper_sz); /* * This is safe because the regmap lock means the Maple lock * is redundant, but we need to take it due to lockdep asserts * in the maple tree code. */ mas_lock(&mas); mas_set_range(&mas, index, last); ret = mas_store_gfp(&mas, entry, GFP_KERNEL); mas_unlock(&mas); if (ret == 0) { kfree(lower); kfree(upper); } return ret; } static int regcache_maple_drop(struct regmap *map, unsigned int min, unsigned int max) { struct maple_tree *mt = map->cache; MA_STATE(mas, mt, min, max); unsigned long *entry, *lower, *upper; unsigned long lower_index, lower_last; unsigned long upper_index, upper_last; int ret; lower = NULL; upper = NULL; mas_lock(&mas); mas_for_each(&mas, entry, max) { /* * This is safe because the regmap lock means the * Maple lock is redundant, but we need to take it due * to lockdep asserts in the maple tree code. */ mas_unlock(&mas); /* Do we need to save any of this entry? */ if (mas.index < min) { lower_index = mas.index; lower_last = min -1; lower = kmemdup(entry, ((min - mas.index) * sizeof(unsigned long)), GFP_KERNEL); if (!lower) { ret = -ENOMEM; goto out_unlocked; } } if (mas.last > max) { upper_index = max + 1; upper_last = mas.last; upper = kmemdup(&entry[max + 1], ((mas.last - max) * sizeof(unsigned long)), GFP_KERNEL); if (!upper) { ret = -ENOMEM; goto out_unlocked; } } kfree(entry); mas_lock(&mas); mas_erase(&mas); /* Insert new nodes with the saved data */ if (lower) { mas_set_range(&mas, lower_index, lower_last); ret = mas_store_gfp(&mas, lower, GFP_KERNEL); if (ret != 0) goto out; lower = NULL; } if (upper) { mas_set_range(&mas, upper_index, upper_last); ret = mas_store_gfp(&mas, upper, GFP_KERNEL); if (ret != 0) goto out; upper = NULL; } } out: mas_unlock(&mas); out_unlocked: kfree(lower); kfree(upper); return ret; } static int regcache_maple_sync(struct regmap *map, unsigned int min, unsigned int max) { struct maple_tree *mt = map->cache; unsigned long *entry; MA_STATE(mas, mt, min, max); unsigned long lmin = min; unsigned long lmax = max; unsigned int r; int ret; map->cache_bypass = true; rcu_read_lock(); mas_for_each(&mas, entry, max) { for (r = max(mas.index, lmin); r <= min(mas.last, lmax); r++) { mas_pause(&mas); rcu_read_unlock(); ret = regcache_sync_val(map, r, entry[r - mas.index]); if (ret != 0) goto out; rcu_read_lock(); } } rcu_read_unlock(); out: map->cache_bypass = false; return ret; } static int regcache_maple_exit(struct regmap *map) { struct maple_tree *mt = map->cache; MA_STATE(mas, mt, 0, UINT_MAX); unsigned int *entry;; /* if we've already been called then just return */ if (!mt) return 0; mas_lock(&mas); mas_for_each(&mas, entry, UINT_MAX) kfree(entry); __mt_destroy(mt); mas_unlock(&mas); kfree(mt); map->cache = NULL; return 0; } static int regcache_maple_init(struct regmap *map) { struct maple_tree *mt; int i; int ret; mt = kmalloc(sizeof(*mt), GFP_KERNEL); if (!mt) return -ENOMEM; map->cache = mt; mt_init(mt); for (i = 0; i < map->num_reg_defaults; i++) { ret = regcache_maple_write(map, map->reg_defaults[i].reg, map->reg_defaults[i].def); if (ret) goto err; } return 0; err: regcache_maple_exit(map); return ret; } struct regcache_ops regcache_maple_ops = { .type = REGCACHE_MAPLE, .name = "maple", .init = regcache_maple_init, .exit = regcache_maple_exit, .read = regcache_maple_read, .write = regcache_maple_write, .drop = regcache_maple_drop, .sync = regcache_maple_sync, };