]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
regmap: Add reg_default_cb callback for flat cache defaults
authorSheetal <sheetal@nvidia.com>
Fri, 23 Jan 2026 09:53:44 +0000 (15:23 +0530)
committerMark Brown <broonie@kernel.org>
Tue, 27 Jan 2026 12:46:10 +0000 (12:46 +0000)
Commit e062bdfdd6ad ("regmap: warn users about uninitialized flat cache")
warns when REGCACHE_FLAT is used without full defaults. This causes
false positives on hardware where many registers reset to zero but are
not listed in reg_defaults, forcing drivers to maintain large tables
just to silence the warning.

Add a reg_default_cb() hook so drivers can supply defaults for registers
not present in reg_defaults when populating REGCACHE_FLAT. This keeps
the warning quiet for known zero-reset registers without bloating
tables. Provide a generic regmap_default_zero_cb() helper for drivers
that need zero defaults.

The hook is only used for REGCACHE_FLAT; the core does not
check readable/writeable access, so drivers must provide readable_reg/
writeable_reg callbacks and handle holes in the register map.

Signed-off-by: Sheetal <sheetal@nvidia.com>
Link: https://patch.msgid.link/20260123095346.1258556-3-sheetal@nvidia.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/base/regmap/internal.h
drivers/base/regmap/regcache-flat.c
drivers/base/regmap/regcache.c
drivers/base/regmap/regmap.c
include/linux/regmap.h

index 1477329410ec445663bcd855602a132dbc0d20d5..5bf9931654387215da8f34f031a4f412fbc8b6c0 100644 (file)
@@ -117,6 +117,9 @@ struct regmap {
                    void *val_buf, size_t val_size);
        int (*write)(void *context, const void *data, size_t count);
 
+       int (*reg_default_cb)(struct device *dev, unsigned int reg,
+                             unsigned int *val);
+
        unsigned long read_flag_mask;
        unsigned long write_flag_mask;
 
index 53cc59c84e2f0117bd2cf85cee880968ea939a52..c924817e19b1c3e7d22bb6c558da795d1b1683e9 100644 (file)
@@ -79,6 +79,25 @@ static int regcache_flat_populate(struct regmap *map)
                __set_bit(index, cache->valid);
        }
 
+       if (map->reg_default_cb) {
+               dev_dbg(map->dev,
+                       "Populating regcache_flat using reg_default_cb callback\n");
+
+               for (i = 0; i <= map->max_register; i += map->reg_stride) {
+                       unsigned int index = regcache_flat_get_index(map, i);
+                       unsigned int value;
+
+                       if (test_bit(index, cache->valid))
+                               continue;
+
+                       if (map->reg_default_cb(map->dev, i, &value))
+                               continue;
+
+                       cache->data[index] = value;
+                       __set_bit(index, cache->valid);
+               }
+       }
+
        return 0;
 }
 
index 319c342bf5a064b1a5069ff83b602bb262fbb8d4..31bdbf37dbed5db798aba3a9307928b76f129c74 100644 (file)
@@ -223,7 +223,8 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
                        goto err_free;
        }
 
-       if (map->num_reg_defaults && map->cache_ops->populate) {
+       if (map->cache_ops->populate &&
+           (map->num_reg_defaults || map->reg_default_cb)) {
                dev_dbg(map->dev, "Populating %s cache\n", map->cache_ops->name);
                map->lock(map->lock_arg);
                ret = map->cache_ops->populate(map);
index ae2215d4e61c3e53aa280bf5a555361815079a65..4231e9d4b8ff5fc251ab016a13fbcfb31fc0207d 100644 (file)
@@ -813,6 +813,7 @@ struct regmap *__regmap_init(struct device *dev,
        map->precious_reg = config->precious_reg;
        map->writeable_noinc_reg = config->writeable_noinc_reg;
        map->readable_noinc_reg = config->readable_noinc_reg;
+       map->reg_default_cb = config->reg_default_cb;
        map->cache_type = config->cache_type;
 
        spin_lock_init(&map->async_lock);
@@ -1435,6 +1436,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
        map->precious_reg = config->precious_reg;
        map->writeable_noinc_reg = config->writeable_noinc_reg;
        map->readable_noinc_reg = config->readable_noinc_reg;
+       map->reg_default_cb = config->reg_default_cb;
        map->cache_type = config->cache_type;
 
        ret = regmap_set_name(map, config);
index b0b9be750d939cd753616899aa0c9adba74bfa24..caff2240bdab7922288cd74546fda1d92cacbb60 100644 (file)
@@ -359,6 +359,10 @@ typedef void (*regmap_unlock)(void *);
  * @reg_defaults: Power on reset values for registers (for use with
  *                register cache support).
  * @num_reg_defaults: Number of elements in reg_defaults.
+ * @reg_default_cb: Optional callback to return default values for registers
+ *                  not listed in reg_defaults. This is only used for
+ *                  REGCACHE_FLAT population; drivers must ensure the readable_reg/
+ *                  writeable_reg callbacks are defined to handle holes.
  *
  * @read_flag_mask: Mask to be set in the top bytes of the register when doing
  *                  a read.
@@ -449,6 +453,8 @@ struct regmap_config {
        const struct regmap_access_table *rd_noinc_table;
        const struct reg_default *reg_defaults;
        unsigned int num_reg_defaults;
+       int (*reg_default_cb)(struct device *dev, unsigned int reg,
+                             unsigned int *def);
        enum regcache_type cache_type;
        const void *reg_defaults_raw;
        unsigned int num_reg_defaults_raw;
@@ -1349,6 +1355,14 @@ static inline int regmap_write_bits(struct regmap *map, unsigned int reg,
        return regmap_update_bits_base(map, reg, mask, val, NULL, false, true);
 }
 
+static inline int regmap_default_zero_cb(struct device *dev,
+                                        unsigned int reg,
+                                        unsigned int *def)
+{
+       *def = 0;
+       return 0;
+}
+
 int regmap_get_val_bytes(struct regmap *map);
 int regmap_get_max_register(struct regmap *map);
 int regmap_get_reg_stride(struct regmap *map);