From 9c7f7262bc1affb9b9acd2ec2fb1f6314d5d474c Mon Sep 17 00:00:00 2001 From: Sander Vanheule Date: Wed, 29 Oct 2025 09:12:47 +0100 Subject: [PATCH 1/9] 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 Link: https://patch.msgid.link/20251029081248.52607-2-sander@svanheule.net Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regcache-flat.c | 102 +++++++++++++++++++++++++--- drivers/base/regmap/regcache.c | 1 + drivers/base/regmap/regmap-kunit.c | 22 ++++++ include/linux/regmap.h | 17 +++-- 5 files changed, 126 insertions(+), 17 deletions(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 6f31240ee4a9..8d19a1414d5b 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -288,6 +288,7 @@ enum regmap_endian regmap_get_val_endian(struct device *dev, const struct regmap_bus *bus, const struct regmap_config *config); +extern struct regcache_ops regcache_flat_sparse_ops; extern struct regcache_ops regcache_rbtree_ops; extern struct regcache_ops regcache_maple_ops; extern struct regcache_ops regcache_flat_ops; diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c index f36d3618b67c..86f7679175b1 100644 --- a/drivers/base/regmap/regcache-flat.c +++ b/drivers/base/regmap/regcache-flat.c @@ -6,7 +6,11 @@ // // Author: Mark Brown +#include +#include #include +#include +#include #include #include @@ -18,34 +22,62 @@ static inline unsigned int regcache_flat_get_index(const struct regmap *map, return regcache_get_index_by_order(map, reg); } +struct regcache_flat_data { + unsigned long *valid; + unsigned int data[]; +}; + static int regcache_flat_init(struct regmap *map) { int i; - unsigned int *cache; + size_t cache_data_size; + unsigned int cache_size; + struct regcache_flat_data *cache; if (!map || map->reg_stride_order < 0 || !map->max_register_is_set) return -EINVAL; - map->cache = kcalloc(regcache_flat_get_index(map, map->max_register) - + 1, sizeof(unsigned int), map->alloc_flags); - if (!map->cache) + cache_size = regcache_flat_get_index(map, map->max_register) + 1; + cache_data_size = struct_size(cache, data, cache_size); + + if (cache_data_size == SIZE_MAX) { + dev_err(map->dev, "cannot allocate regmap cache"); + return -ENOMEM; + } + + cache = kzalloc(cache_data_size, map->alloc_flags); + if (!cache) return -ENOMEM; - cache = map->cache; + cache->valid = bitmap_zalloc(cache_size, map->alloc_flags); + if (!cache->valid) + goto err_free; + + map->cache = cache; for (i = 0; i < map->num_reg_defaults; i++) { unsigned int reg = map->reg_defaults[i].reg; unsigned int index = regcache_flat_get_index(map, reg); - cache[index] = map->reg_defaults[i].def; + cache->data[index] = map->reg_defaults[i].def; + __set_bit(index, cache->valid); } return 0; + +err_free: + kfree(cache); + return -ENOMEM; } static int regcache_flat_exit(struct regmap *map) { - kfree(map->cache); + struct regcache_flat_data *cache = map->cache; + + if (cache) + bitmap_free(cache->valid); + + kfree(cache); map->cache = NULL; return 0; @@ -54,10 +86,24 @@ static int regcache_flat_exit(struct regmap *map) static int regcache_flat_read(struct regmap *map, unsigned int reg, unsigned int *value) { - unsigned int *cache = map->cache; + struct regcache_flat_data *cache = map->cache; unsigned int index = regcache_flat_get_index(map, reg); - *value = cache[index]; + *value = cache->data[index]; + + return 0; +} + +static int regcache_flat_sparse_read(struct regmap *map, + unsigned int reg, unsigned int *value) +{ + struct regcache_flat_data *cache = map->cache; + unsigned int index = regcache_flat_get_index(map, reg); + + if (unlikely(!test_bit(index, cache->valid))) + return -ENOENT; + + *value = cache->data[index]; return 0; } @@ -65,10 +111,34 @@ static int regcache_flat_read(struct regmap *map, static int regcache_flat_write(struct regmap *map, unsigned int reg, unsigned int value) { - unsigned int *cache = map->cache; + struct regcache_flat_data *cache = map->cache; unsigned int index = regcache_flat_get_index(map, reg); - cache[index] = value; + cache->data[index] = value; + + return 0; +} + +static int regcache_flat_sparse_write(struct regmap *map, unsigned int reg, + unsigned int value) +{ + struct regcache_flat_data *cache = map->cache; + unsigned int index = regcache_flat_get_index(map, reg); + + cache->data[index] = value; + __set_bit(index, cache->valid); + + return 0; +} + +static int regcache_flat_drop(struct regmap *map, unsigned int min, + unsigned int max) +{ + struct regcache_flat_data *cache = map->cache; + unsigned int bitmap_min = regcache_flat_get_index(map, min); + unsigned int bitmap_max = regcache_flat_get_index(map, max); + + bitmap_clear(cache->valid, bitmap_min, bitmap_max + 1 - bitmap_min); return 0; } @@ -81,3 +151,13 @@ struct regcache_ops regcache_flat_ops = { .read = regcache_flat_read, .write = regcache_flat_write, }; + +struct regcache_ops regcache_flat_sparse_ops = { + .type = REGCACHE_FLAT_S, + .name = "flat-sparse", + .init = regcache_flat_init, + .exit = regcache_flat_exit, + .read = regcache_flat_sparse_read, + .write = regcache_flat_sparse_write, + .drop = regcache_flat_drop, +}; diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index c7650fa434ad..0392f5525cf3 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -16,6 +16,7 @@ #include "internal.h" static const struct regcache_ops *cache_types[] = { + ®cache_flat_sparse_ops, ®cache_rbtree_ops, ®cache_maple_ops, ®cache_flat_ops, diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regmap-kunit.c index 95c5bf2a78ee..f6fc5ed016da 100644 --- a/drivers/base/regmap/regmap-kunit.c +++ b/drivers/base/regmap/regmap-kunit.c @@ -54,6 +54,8 @@ static const char *regcache_type_name(enum regcache_type type) return "none"; case REGCACHE_FLAT: return "flat"; + case REGCACHE_FLAT_S: + return "flat-sparse"; case REGCACHE_RBTREE: return "rbtree"; case REGCACHE_MAPLE: @@ -93,6 +95,8 @@ static const struct regmap_test_param regcache_types_list[] = { { .cache = REGCACHE_NONE, .fast_io = true }, { .cache = REGCACHE_FLAT }, { .cache = REGCACHE_FLAT, .fast_io = true }, + { .cache = REGCACHE_FLAT_S }, + { .cache = REGCACHE_FLAT_S, .fast_io = true }, { .cache = REGCACHE_RBTREE }, { .cache = REGCACHE_RBTREE, .fast_io = true }, { .cache = REGCACHE_MAPLE }, @@ -104,6 +108,8 @@ KUNIT_ARRAY_PARAM(regcache_types, regcache_types_list, param_to_desc); static const struct regmap_test_param real_cache_types_only_list[] = { { .cache = REGCACHE_FLAT }, { .cache = REGCACHE_FLAT, .fast_io = true }, + { .cache = REGCACHE_FLAT_S }, + { .cache = REGCACHE_FLAT_S, .fast_io = true }, { .cache = REGCACHE_RBTREE }, { .cache = REGCACHE_RBTREE, .fast_io = true }, { .cache = REGCACHE_MAPLE }, @@ -119,6 +125,12 @@ static const struct regmap_test_param real_cache_types_list[] = { { .cache = REGCACHE_FLAT, .from_reg = 0x2002 }, { .cache = REGCACHE_FLAT, .from_reg = 0x2003 }, { .cache = REGCACHE_FLAT, .from_reg = 0x2004 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0, .fast_io = true }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2001 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2002 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2003 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2004 }, { .cache = REGCACHE_RBTREE, .from_reg = 0 }, { .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2001 }, @@ -136,6 +148,12 @@ static const struct regmap_test_param real_cache_types_list[] = { KUNIT_ARRAY_PARAM(real_cache_types, real_cache_types_list, param_to_desc); static const struct regmap_test_param sparse_cache_types_list[] = { + { .cache = REGCACHE_FLAT_S, .from_reg = 0 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0, .fast_io = true }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2001 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2002 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2003 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2004 }, { .cache = REGCACHE_RBTREE, .from_reg = 0 }, { .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2001 }, @@ -1597,6 +1615,8 @@ static const struct regmap_test_param raw_types_list[] = { { .cache = REGCACHE_NONE, .val_endian = REGMAP_ENDIAN_BIG }, { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_LITTLE }, { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_BIG }, + { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_LITTLE }, + { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_BIG }, { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_LITTLE }, { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_BIG }, { .cache = REGCACHE_MAPLE, .val_endian = REGMAP_ENDIAN_LITTLE }, @@ -1608,6 +1628,8 @@ KUNIT_ARRAY_PARAM(raw_test_types, raw_types_list, param_to_desc); static const struct regmap_test_param raw_cache_types_list[] = { { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_LITTLE }, { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_BIG }, + { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_LITTLE }, + { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_BIG }, { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_LITTLE }, { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_BIG }, { .cache = REGCACHE_MAPLE, .val_endian = REGMAP_ENDIAN_LITTLE }, diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 4e1ac1fbcec4..17bed25dc4e3 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -55,18 +55,23 @@ struct sdw_slave; #define REGMAP_DOWNSHIFT(s) (s) /* - * The supported cache types, the default is no cache. Any new caches - * should usually use the maple tree cache unless they specifically - * require that there are never any allocations at runtime and can't - * provide defaults in which case they should use the flat cache. The - * rbtree cache *may* have some performance advantage for very low end - * systems that make heavy use of cache syncs but is mainly legacy. + * The supported cache types, the default is no cache. Any new caches should + * usually use the maple tree cache unless they specifically require that there + * are never any allocations at runtime in which case they should use the sparse + * flat cache. The rbtree cache *may* have some performance advantage for very + * low end systems that make heavy use of cache syncs but is mainly legacy. + * These caches are sparse and entries will be initialized from hardware if no + * default has been provided. + * The non-sparse flat cache is provided for compatibility with existing users + * and will zero-initialize cache entries for which no defaults are provided. + * New users should use the sparse flat cache. */ enum regcache_type { REGCACHE_NONE, REGCACHE_RBTREE, REGCACHE_FLAT, REGCACHE_MAPLE, + REGCACHE_FLAT_S, }; /** From e062bdfdd6adbb2dee7751d054c1d8df63ddb8b8 Mon Sep 17 00:00:00 2001 From: Sander Vanheule Date: Wed, 29 Oct 2025 09:12:48 +0100 Subject: [PATCH 2/9] regmap: warn users about uninitialized flat cache The standard flat cache did not contain any validity info, so the cache was always considered to be entirely valid. Multiple mechanisms exist to initialize the cache on regmap init (defaults, raw defaults, HW init), but not all drivers are using one of these. As a result, their implementation might currently depend on the zero-initialized cache or contain other workarounds. When reading an uninitialized value from the flat cache, warn the user, but maintain the current behavior. This will allow developers to switch to a sparse (flat) cache independently. Signed-off-by: Sander Vanheule Link: https://patch.msgid.link/20251029081248.52607-3-sander@svanheule.net Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-flat.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c index 86f7679175b1..3b9235bb8313 100644 --- a/drivers/base/regmap/regcache-flat.c +++ b/drivers/base/regmap/regcache-flat.c @@ -89,6 +89,11 @@ static int regcache_flat_read(struct regmap *map, struct regcache_flat_data *cache = map->cache; unsigned int index = regcache_flat_get_index(map, reg); + /* legacy behavior: ignore validity, but warn the user */ + if (unlikely(!test_bit(index, cache->valid))) + dev_warn_once(map->dev, + "using zero-initialized flat cache, this may cause unexpected behavior"); + *value = cache->data[index]; return 0; @@ -114,17 +119,6 @@ static int regcache_flat_write(struct regmap *map, unsigned int reg, struct regcache_flat_data *cache = map->cache; unsigned int index = regcache_flat_get_index(map, reg); - cache->data[index] = value; - - return 0; -} - -static int regcache_flat_sparse_write(struct regmap *map, unsigned int reg, - unsigned int value) -{ - struct regcache_flat_data *cache = map->cache; - unsigned int index = regcache_flat_get_index(map, reg); - cache->data[index] = value; __set_bit(index, cache->valid); @@ -158,6 +152,6 @@ struct regcache_ops regcache_flat_sparse_ops = { .init = regcache_flat_init, .exit = regcache_flat_exit, .read = regcache_flat_sparse_read, - .write = regcache_flat_sparse_write, + .write = regcache_flat_write, .drop = regcache_flat_drop, }; From 94a3a95f03154d8d4c6206950a7f6ef9a30baec6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 31 Oct 2025 09:03:16 +0100 Subject: [PATCH 3/9] regcache: Add ->populate() callback to separate from ->init() In the future changes we would like to change the flow of the cache handling. Add ->populate() callback in order to prepare for that. Signed-off-by: Andy Shevchenko Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20251031080540.3970776-2-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regcache.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 8d19a1414d5b..1477329410ec 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -186,6 +186,7 @@ struct regcache_ops { enum regcache_type type; int (*init)(struct regmap *map); int (*exit)(struct regmap *map); + int (*populate)(struct regmap *map); #ifdef CONFIG_DEBUG_FS void (*debugfs_init)(struct regmap *map); #endif diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 0392f5525cf3..319c342bf5a0 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -222,8 +222,24 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) if (ret) goto err_free; } + + if (map->num_reg_defaults && map->cache_ops->populate) { + dev_dbg(map->dev, "Populating %s cache\n", map->cache_ops->name); + map->lock(map->lock_arg); + ret = map->cache_ops->populate(map); + map->unlock(map->lock_arg); + if (ret) + goto err_exit; + } return 0; +err_exit: + if (map->cache_ops->exit) { + dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name); + map->lock(map->lock_arg); + ret = map->cache_ops->exit(map); + map->unlock(map->lock_arg); + } err_free: kfree(map->reg_defaults); if (map->cache_free) From bda6f8749c8e0b10f083dc7a1edf169f349fb776 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 31 Oct 2025 09:03:17 +0100 Subject: [PATCH 4/9] regcache: rbtree: Split ->populate() from ->init() Split ->populate() implementation from ->init() code. This decoupling will help for the further changes. Signed-off-by: Andy Shevchenko Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20251031080540.3970776-3-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 31 +++++++++++++++------------ 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index a9d17f316e55..3344b82c3799 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -184,8 +184,6 @@ static void rbtree_debugfs_init(struct regmap *map) static int regcache_rbtree_init(struct regmap *map) { struct regcache_rbtree_ctx *rbtree_ctx; - int i; - int ret; map->cache = kmalloc(sizeof *rbtree_ctx, map->alloc_flags); if (!map->cache) @@ -195,19 +193,7 @@ static int regcache_rbtree_init(struct regmap *map) rbtree_ctx->root = RB_ROOT; rbtree_ctx->cached_rbnode = NULL; - for (i = 0; i < map->num_reg_defaults; i++) { - ret = regcache_rbtree_write(map, - map->reg_defaults[i].reg, - map->reg_defaults[i].def); - if (ret) - goto err; - } - return 0; - -err: - regcache_rbtree_exit(map); - return ret; } static int regcache_rbtree_exit(struct regmap *map) @@ -239,6 +225,22 @@ static int regcache_rbtree_exit(struct regmap *map) return 0; } +static int regcache_rbtree_populate(struct regmap *map) +{ + unsigned int i; + int ret; + + for (i = 0; i < map->num_reg_defaults; i++) { + ret = regcache_rbtree_write(map, + map->reg_defaults[i].reg, + map->reg_defaults[i].def); + if (ret) + return ret; + } + + return 0; +} + static int regcache_rbtree_read(struct regmap *map, unsigned int reg, unsigned int *value) { @@ -546,6 +548,7 @@ struct regcache_ops regcache_rbtree_ops = { .name = "rbtree", .init = regcache_rbtree_init, .exit = regcache_rbtree_exit, + .populate = regcache_rbtree_populate, #ifdef CONFIG_DEBUG_FS .debugfs_init = rbtree_debugfs_init, #endif From 27fef3048fe95934f6f2f87341eb33ef6581a075 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 31 Oct 2025 09:03:18 +0100 Subject: [PATCH 5/9] regcache: flat: Remove unneeded check and error message for -ENOMEM There is a convention in the kernel to avoid error messages in the cases of -ENOMEM errors. Besides that, the idea behind using struct_size() and other macros from overflow.h is to saturate the size that the following allocation call will definitely fail, hence the check and the error messaging added in regcache_flat_init() are redundant. Remove them. Acked-by: Sander Vanheule Signed-off-by: Andy Shevchenko Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20251031080540.3970776-4-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-flat.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c index 3b9235bb8313..bacb7137092f 100644 --- a/drivers/base/regmap/regcache-flat.c +++ b/drivers/base/regmap/regcache-flat.c @@ -30,7 +30,6 @@ struct regcache_flat_data { static int regcache_flat_init(struct regmap *map) { int i; - size_t cache_data_size; unsigned int cache_size; struct regcache_flat_data *cache; @@ -38,14 +37,7 @@ static int regcache_flat_init(struct regmap *map) return -EINVAL; cache_size = regcache_flat_get_index(map, map->max_register) + 1; - cache_data_size = struct_size(cache, data, cache_size); - - if (cache_data_size == SIZE_MAX) { - dev_err(map->dev, "cannot allocate regmap cache"); - return -ENOMEM; - } - - cache = kzalloc(cache_data_size, map->alloc_flags); + cache = kzalloc(struct_size(cache, data, cache_size), map->alloc_flags); if (!cache) return -ENOMEM; From 44c1a444b030647803d900e60f5a8af31a782f0e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 31 Oct 2025 09:03:19 +0100 Subject: [PATCH 6/9] regcache: flat: Split ->populate() from ->init() Split ->populate() implementation from ->init() code. This decoupling will help for the further changes. Signed-off-by: Andy Shevchenko Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20251031080540.3970776-5-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-flat.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c index bacb7137092f..53cc59c84e2f 100644 --- a/drivers/base/regmap/regcache-flat.c +++ b/drivers/base/regmap/regcache-flat.c @@ -29,7 +29,6 @@ struct regcache_flat_data { static int regcache_flat_init(struct regmap *map) { - int i; unsigned int cache_size; struct regcache_flat_data *cache; @@ -47,14 +46,6 @@ static int regcache_flat_init(struct regmap *map) map->cache = cache; - for (i = 0; i < map->num_reg_defaults; i++) { - unsigned int reg = map->reg_defaults[i].reg; - unsigned int index = regcache_flat_get_index(map, reg); - - cache->data[index] = map->reg_defaults[i].def; - __set_bit(index, cache->valid); - } - return 0; err_free: @@ -75,6 +66,22 @@ static int regcache_flat_exit(struct regmap *map) return 0; } +static int regcache_flat_populate(struct regmap *map) +{ + struct regcache_flat_data *cache = map->cache; + unsigned int i; + + for (i = 0; i < map->num_reg_defaults; i++) { + unsigned int reg = map->reg_defaults[i].reg; + unsigned int index = regcache_flat_get_index(map, reg); + + cache->data[index] = map->reg_defaults[i].def; + __set_bit(index, cache->valid); + } + + return 0; +} + static int regcache_flat_read(struct regmap *map, unsigned int reg, unsigned int *value) { @@ -134,6 +141,7 @@ struct regcache_ops regcache_flat_ops = { .name = "flat", .init = regcache_flat_init, .exit = regcache_flat_exit, + .populate = regcache_flat_populate, .read = regcache_flat_read, .write = regcache_flat_write, }; @@ -143,6 +151,7 @@ struct regcache_ops regcache_flat_sparse_ops = { .name = "flat-sparse", .init = regcache_flat_init, .exit = regcache_flat_exit, + .populate = regcache_flat_populate, .read = regcache_flat_sparse_read, .write = regcache_flat_write, .drop = regcache_flat_drop, From ed5d499b5c9cc11dd3edae1a7a55db7dfa4f1bdc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 31 Oct 2025 09:03:20 +0100 Subject: [PATCH 7/9] regcache: maple: Split ->populate() from ->init() Split ->populate() implementation from ->init() code. This decoupling will help for the further changes. Signed-off-by: Andy Shevchenko Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20251031080540.3970776-6-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-maple.c | 47 +++++++++++++--------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/drivers/base/regmap/regcache-maple.c b/drivers/base/regmap/regcache-maple.c index 2319c30283a6..ca1c72b68f31 100644 --- a/drivers/base/regmap/regcache-maple.c +++ b/drivers/base/regmap/regcache-maple.c @@ -289,6 +289,23 @@ out: return ret; } +static int regcache_maple_init(struct regmap *map) +{ + struct maple_tree *mt; + + mt = kmalloc(sizeof(*mt), map->alloc_flags); + if (!mt) + return -ENOMEM; + map->cache = mt; + + mt_init(mt); + + if (!mt_external_lock(mt) && map->lock_key) + lockdep_set_class_and_subclass(&mt->ma_lock, map->lock_key, 1); + + return 0; +} + static int regcache_maple_exit(struct regmap *map) { struct maple_tree *mt = map->cache; @@ -340,26 +357,12 @@ static int regcache_maple_insert_block(struct regmap *map, int first, return ret; } -static int regcache_maple_init(struct regmap *map) +static int regcache_maple_populate(struct regmap *map) { - struct maple_tree *mt; int i; int ret; int range_start; - mt = kmalloc(sizeof(*mt), map->alloc_flags); - if (!mt) - return -ENOMEM; - map->cache = mt; - - mt_init(mt); - - if (!mt_external_lock(mt) && map->lock_key) - lockdep_set_class_and_subclass(&mt->ma_lock, map->lock_key, 1); - - if (!map->num_reg_defaults) - return 0; - range_start = 0; /* Scan for ranges of contiguous registers */ @@ -369,23 +372,14 @@ static int regcache_maple_init(struct regmap *map) ret = regcache_maple_insert_block(map, range_start, i - 1); if (ret != 0) - goto err; + return ret; range_start = i; } } /* Add the last block */ - ret = regcache_maple_insert_block(map, range_start, - map->num_reg_defaults - 1); - if (ret != 0) - goto err; - - return 0; - -err: - regcache_maple_exit(map); - return ret; + return regcache_maple_insert_block(map, range_start, map->num_reg_defaults - 1); } struct regcache_ops regcache_maple_ops = { @@ -393,6 +387,7 @@ struct regcache_ops regcache_maple_ops = { .name = "maple", .init = regcache_maple_init, .exit = regcache_maple_exit, + .populate = regcache_maple_populate, .read = regcache_maple_read, .write = regcache_maple_write, .drop = regcache_maple_drop, From af9c8092d84244ca54ffb590435735f788e7a170 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 3 Nov 2025 19:09:46 +0100 Subject: [PATCH 8/9] regmap: i3c: Use ARRAY_SIZE() Use ARRAY_SIZE() instead of hard coded numbers to show the intention and make code robust against potential changes. Signed-off-by: Andy Shevchenko Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20251103180946.604127-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-i3c.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/base/regmap/regmap-i3c.c b/drivers/base/regmap/regmap-i3c.c index b5300b7c477e..6a0f6c826980 100644 --- a/drivers/base/regmap/regmap-i3c.c +++ b/drivers/base/regmap/regmap-i3c.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2018 Synopsys, Inc. and/or its affiliates. +#include #include #include #include @@ -18,7 +19,7 @@ static int regmap_i3c_write(void *context, const void *data, size_t count) }, }; - return i3c_device_do_priv_xfers(i3c, xfers, 1); + return i3c_device_do_priv_xfers(i3c, xfers, ARRAY_SIZE(xfers)); } static int regmap_i3c_read(void *context, @@ -37,7 +38,7 @@ static int regmap_i3c_read(void *context, xfers[1].len = val_size; xfers[1].data.in = val; - return i3c_device_do_priv_xfers(i3c, xfers, 2); + return i3c_device_do_priv_xfers(i3c, xfers, ARRAY_SIZE(xfers)); } static const struct regmap_bus regmap_i3c = { From 6985defd1d832f1dd9d1977a6a2cc2cef7632704 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 7 Nov 2025 10:45:51 +0000 Subject: [PATCH 9/9] regmap: sdw-mbq: Reorder regmap_mbq_context struct for better packing Avoid a hole in struct regmap_mbq_context by shuffling the members slightly. Pahole before: struct regmap_mbq_context { struct device * dev; /* 0 8 */ struct sdw_slave * sdw; /* 8 8 */ struct regmap_sdw_mbq_cfg cfg; /* 16 32 */ int val_size; /* 48 4 */ /* XXX 4 bytes hole, try to pack */ bool (*readable_reg)(struct device *, unsigned int); /* 56 8 */ /* size: 64, cachelines: 1, members: 5 */ /* sum members: 60, holes: 1, sum holes: 4 */ }; Pahole after: struct regmap_mbq_context { struct device * dev; /* 0 8 */ struct sdw_slave * sdw; /* 8 8 */ bool (*readable_reg)(struct device *, unsigned int); /* 16 8 */ struct regmap_sdw_mbq_cfg cfg; /* 24 32 */ int val_size; /* 56 4 */ /* size: 64, cachelines: 1, members: 5 */ /* padding: 4 */ }; Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20251107104551.1553526-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-sdw-mbq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap-sdw-mbq.c b/drivers/base/regmap/regmap-sdw-mbq.c index 86644bbd0710..225c158d7f9d 100644 --- a/drivers/base/regmap/regmap-sdw-mbq.c +++ b/drivers/base/regmap/regmap-sdw-mbq.c @@ -16,10 +16,11 @@ struct regmap_mbq_context { struct device *dev; + bool (*readable_reg)(struct device *dev, unsigned int reg); + struct regmap_sdw_mbq_cfg cfg; int val_size; - bool (*readable_reg)(struct device *dev, unsigned int reg); }; static int regmap_sdw_mbq_size(struct regmap_mbq_context *ctx, unsigned int reg)