Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

regmap: add flat cache with sparse validity

The flat regcache will always assume the data in the cache is valid.
Since the cache is preferred over hardware access, this may shadow the
actual state of the device.

Add a new containing cache structure with the flat data table and a
bitmap indicating cache validity. REGCACHE_FLAT will still behave as
before, as the validity is ignored.

Define new cache type REGCACHE_FLAT_S: a flat cache with sparse
validity. The sparse validity is used to determine if a hardware access
should occur to initialize the cache on the fly, vs. at regmap init for
REGCACHE_FLAT. Contrary to REGCACHE_FLAT, this allows us to implement
regcache_ops.drop.

Signed-off-by: Sander Vanheule <sander@svanheule.net>
Link: https://patch.msgid.link/20251029081248.52607-2-sander@svanheule.net
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Sander Vanheule and committed by
Mark Brown
9c7f7262 dcb6fa37

+126 -17
+1
drivers/base/regmap/internal.h
··· 288 288 const struct regmap_bus *bus, 289 289 const struct regmap_config *config); 290 290 291 + extern struct regcache_ops regcache_flat_sparse_ops; 291 292 extern struct regcache_ops regcache_rbtree_ops; 292 293 extern struct regcache_ops regcache_maple_ops; 293 294 extern struct regcache_ops regcache_flat_ops;
+91 -11
drivers/base/regmap/regcache-flat.c
··· 6 6 // 7 7 // Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 8 8 9 + #include <linux/bitmap.h> 10 + #include <linux/bitops.h> 9 11 #include <linux/device.h> 12 + #include <linux/limits.h> 13 + #include <linux/overflow.h> 10 14 #include <linux/seq_file.h> 11 15 #include <linux/slab.h> 12 16 ··· 22 18 return regcache_get_index_by_order(map, reg); 23 19 } 24 20 21 + struct regcache_flat_data { 22 + unsigned long *valid; 23 + unsigned int data[]; 24 + }; 25 + 25 26 static int regcache_flat_init(struct regmap *map) 26 27 { 27 28 int i; 28 - unsigned int *cache; 29 + size_t cache_data_size; 30 + unsigned int cache_size; 31 + struct regcache_flat_data *cache; 29 32 30 33 if (!map || map->reg_stride_order < 0 || !map->max_register_is_set) 31 34 return -EINVAL; 32 35 33 - map->cache = kcalloc(regcache_flat_get_index(map, map->max_register) 34 - + 1, sizeof(unsigned int), map->alloc_flags); 35 - if (!map->cache) 36 + cache_size = regcache_flat_get_index(map, map->max_register) + 1; 37 + cache_data_size = struct_size(cache, data, cache_size); 38 + 39 + if (cache_data_size == SIZE_MAX) { 40 + dev_err(map->dev, "cannot allocate regmap cache"); 41 + return -ENOMEM; 42 + } 43 + 44 + cache = kzalloc(cache_data_size, map->alloc_flags); 45 + if (!cache) 36 46 return -ENOMEM; 37 47 38 - cache = map->cache; 48 + cache->valid = bitmap_zalloc(cache_size, map->alloc_flags); 49 + if (!cache->valid) 50 + goto err_free; 51 + 52 + map->cache = cache; 39 53 40 54 for (i = 0; i < map->num_reg_defaults; i++) { 41 55 unsigned int reg = map->reg_defaults[i].reg; 42 56 unsigned int index = regcache_flat_get_index(map, reg); 43 57 44 - cache[index] = map->reg_defaults[i].def; 58 + cache->data[index] = map->reg_defaults[i].def; 59 + __set_bit(index, cache->valid); 45 60 } 46 61 47 62 return 0; 63 + 64 + err_free: 65 + kfree(cache); 66 + return -ENOMEM; 48 67 } 49 68 50 69 static int regcache_flat_exit(struct regmap *map) 51 70 { 52 - kfree(map->cache); 71 + struct regcache_flat_data *cache = map->cache; 72 + 73 + if (cache) 74 + bitmap_free(cache->valid); 75 + 76 + kfree(cache); 53 77 map->cache = NULL; 54 78 55 79 return 0; ··· 86 54 static int regcache_flat_read(struct regmap *map, 87 55 unsigned int reg, unsigned int *value) 88 56 { 89 - unsigned int *cache = map->cache; 57 + struct regcache_flat_data *cache = map->cache; 90 58 unsigned int index = regcache_flat_get_index(map, reg); 91 59 92 - *value = cache[index]; 60 + *value = cache->data[index]; 61 + 62 + return 0; 63 + } 64 + 65 + static int regcache_flat_sparse_read(struct regmap *map, 66 + unsigned int reg, unsigned int *value) 67 + { 68 + struct regcache_flat_data *cache = map->cache; 69 + unsigned int index = regcache_flat_get_index(map, reg); 70 + 71 + if (unlikely(!test_bit(index, cache->valid))) 72 + return -ENOENT; 73 + 74 + *value = cache->data[index]; 93 75 94 76 return 0; 95 77 } ··· 111 65 static int regcache_flat_write(struct regmap *map, unsigned int reg, 112 66 unsigned int value) 113 67 { 114 - unsigned int *cache = map->cache; 68 + struct regcache_flat_data *cache = map->cache; 115 69 unsigned int index = regcache_flat_get_index(map, reg); 116 70 117 - cache[index] = value; 71 + cache->data[index] = value; 72 + 73 + return 0; 74 + } 75 + 76 + static int regcache_flat_sparse_write(struct regmap *map, unsigned int reg, 77 + unsigned int value) 78 + { 79 + struct regcache_flat_data *cache = map->cache; 80 + unsigned int index = regcache_flat_get_index(map, reg); 81 + 82 + cache->data[index] = value; 83 + __set_bit(index, cache->valid); 84 + 85 + return 0; 86 + } 87 + 88 + static int regcache_flat_drop(struct regmap *map, unsigned int min, 89 + unsigned int max) 90 + { 91 + struct regcache_flat_data *cache = map->cache; 92 + unsigned int bitmap_min = regcache_flat_get_index(map, min); 93 + unsigned int bitmap_max = regcache_flat_get_index(map, max); 94 + 95 + bitmap_clear(cache->valid, bitmap_min, bitmap_max + 1 - bitmap_min); 118 96 119 97 return 0; 120 98 } ··· 150 80 .exit = regcache_flat_exit, 151 81 .read = regcache_flat_read, 152 82 .write = regcache_flat_write, 83 + }; 84 + 85 + struct regcache_ops regcache_flat_sparse_ops = { 86 + .type = REGCACHE_FLAT_S, 87 + .name = "flat-sparse", 88 + .init = regcache_flat_init, 89 + .exit = regcache_flat_exit, 90 + .read = regcache_flat_sparse_read, 91 + .write = regcache_flat_sparse_write, 92 + .drop = regcache_flat_drop, 153 93 };
+1
drivers/base/regmap/regcache.c
··· 16 16 #include "internal.h" 17 17 18 18 static const struct regcache_ops *cache_types[] = { 19 + &regcache_flat_sparse_ops, 19 20 &regcache_rbtree_ops, 20 21 &regcache_maple_ops, 21 22 &regcache_flat_ops,
+22
drivers/base/regmap/regmap-kunit.c
··· 54 54 return "none"; 55 55 case REGCACHE_FLAT: 56 56 return "flat"; 57 + case REGCACHE_FLAT_S: 58 + return "flat-sparse"; 57 59 case REGCACHE_RBTREE: 58 60 return "rbtree"; 59 61 case REGCACHE_MAPLE: ··· 95 93 { .cache = REGCACHE_NONE, .fast_io = true }, 96 94 { .cache = REGCACHE_FLAT }, 97 95 { .cache = REGCACHE_FLAT, .fast_io = true }, 96 + { .cache = REGCACHE_FLAT_S }, 97 + { .cache = REGCACHE_FLAT_S, .fast_io = true }, 98 98 { .cache = REGCACHE_RBTREE }, 99 99 { .cache = REGCACHE_RBTREE, .fast_io = true }, 100 100 { .cache = REGCACHE_MAPLE }, ··· 108 104 static const struct regmap_test_param real_cache_types_only_list[] = { 109 105 { .cache = REGCACHE_FLAT }, 110 106 { .cache = REGCACHE_FLAT, .fast_io = true }, 107 + { .cache = REGCACHE_FLAT_S }, 108 + { .cache = REGCACHE_FLAT_S, .fast_io = true }, 111 109 { .cache = REGCACHE_RBTREE }, 112 110 { .cache = REGCACHE_RBTREE, .fast_io = true }, 113 111 { .cache = REGCACHE_MAPLE }, ··· 125 119 { .cache = REGCACHE_FLAT, .from_reg = 0x2002 }, 126 120 { .cache = REGCACHE_FLAT, .from_reg = 0x2003 }, 127 121 { .cache = REGCACHE_FLAT, .from_reg = 0x2004 }, 122 + { .cache = REGCACHE_FLAT_S, .from_reg = 0 }, 123 + { .cache = REGCACHE_FLAT_S, .from_reg = 0, .fast_io = true }, 124 + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2001 }, 125 + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2002 }, 126 + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2003 }, 127 + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2004 }, 128 128 { .cache = REGCACHE_RBTREE, .from_reg = 0 }, 129 129 { .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true }, 130 130 { .cache = REGCACHE_RBTREE, .from_reg = 0x2001 }, ··· 148 136 KUNIT_ARRAY_PARAM(real_cache_types, real_cache_types_list, param_to_desc); 149 137 150 138 static const struct regmap_test_param sparse_cache_types_list[] = { 139 + { .cache = REGCACHE_FLAT_S, .from_reg = 0 }, 140 + { .cache = REGCACHE_FLAT_S, .from_reg = 0, .fast_io = true }, 141 + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2001 }, 142 + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2002 }, 143 + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2003 }, 144 + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2004 }, 151 145 { .cache = REGCACHE_RBTREE, .from_reg = 0 }, 152 146 { .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true }, 153 147 { .cache = REGCACHE_RBTREE, .from_reg = 0x2001 }, ··· 1615 1597 { .cache = REGCACHE_NONE, .val_endian = REGMAP_ENDIAN_BIG }, 1616 1598 { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_LITTLE }, 1617 1599 { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_BIG }, 1600 + { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_LITTLE }, 1601 + { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_BIG }, 1618 1602 { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_LITTLE }, 1619 1603 { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_BIG }, 1620 1604 { .cache = REGCACHE_MAPLE, .val_endian = REGMAP_ENDIAN_LITTLE }, ··· 1628 1608 static const struct regmap_test_param raw_cache_types_list[] = { 1629 1609 { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_LITTLE }, 1630 1610 { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_BIG }, 1611 + { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_LITTLE }, 1612 + { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_BIG }, 1631 1613 { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_LITTLE }, 1632 1614 { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_BIG }, 1633 1615 { .cache = REGCACHE_MAPLE, .val_endian = REGMAP_ENDIAN_LITTLE },
+11 -6
include/linux/regmap.h
··· 55 55 #define REGMAP_DOWNSHIFT(s) (s) 56 56 57 57 /* 58 - * The supported cache types, the default is no cache. Any new caches 59 - * should usually use the maple tree cache unless they specifically 60 - * require that there are never any allocations at runtime and can't 61 - * provide defaults in which case they should use the flat cache. The 62 - * rbtree cache *may* have some performance advantage for very low end 63 - * systems that make heavy use of cache syncs but is mainly legacy. 58 + * The supported cache types, the default is no cache. Any new caches should 59 + * usually use the maple tree cache unless they specifically require that there 60 + * are never any allocations at runtime in which case they should use the sparse 61 + * flat cache. The rbtree cache *may* have some performance advantage for very 62 + * low end systems that make heavy use of cache syncs but is mainly legacy. 63 + * These caches are sparse and entries will be initialized from hardware if no 64 + * default has been provided. 65 + * The non-sparse flat cache is provided for compatibility with existing users 66 + * and will zero-initialize cache entries for which no defaults are provided. 67 + * New users should use the sparse flat cache. 64 68 */ 65 69 enum regcache_type { 66 70 REGCACHE_NONE, 67 71 REGCACHE_RBTREE, 68 72 REGCACHE_FLAT, 69 73 REGCACHE_MAPLE, 74 + REGCACHE_FLAT_S, 70 75 }; 71 76 72 77 /**