[RFC PATCH v3 2/4] regmap: Add reg_default_cb callback for flat cache defaults

Sheetal . posted 4 patches 2 weeks, 2 days ago
[RFC PATCH v3 2/4] regmap: Add reg_default_cb callback for flat cache defaults
Posted by Sheetal . 2 weeks, 2 days ago
From: Sheetal <sheetal@nvidia.com>

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>
---
 drivers/base/regmap/internal.h      |  3 +++
 drivers/base/regmap/regcache-flat.c | 19 +++++++++++++++++++
 drivers/base/regmap/regcache.c      |  3 ++-
 drivers/base/regmap/regmap.c        |  2 ++
 include/linux/regmap.h              | 14 ++++++++++++++
 5 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index 1477329410ec..5bf993165438 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -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;
 
diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c
index 53cc59c84e2f..c924817e19b1 100644
--- a/drivers/base/regmap/regcache-flat.c
+++ b/drivers/base/regmap/regcache-flat.c
@@ -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;
 }
 
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 319c342bf5a0..31bdbf37dbed 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -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);
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index ce9be3989a21..57c5551044ed 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -811,6 +811,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);
@@ -1433,6 +1434,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);
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index b0b9be750d93..51940eeff872 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -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);
-- 
2.34.1
Re: [RFC PATCH v3 2/4] regmap: Add reg_default_cb callback for flat cache defaults
Posted by Jon Hunter 1 week, 4 days ago
On 23/01/2026 09:53, Sheetal . wrote:
> From: Sheetal <sheetal@nvidia.com>
> 
> 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>
> ---
>   drivers/base/regmap/internal.h      |  3 +++
>   drivers/base/regmap/regcache-flat.c | 19 +++++++++++++++++++
>   drivers/base/regmap/regcache.c      |  3 ++-
>   drivers/base/regmap/regmap.c        |  2 ++
>   include/linux/regmap.h              | 14 ++++++++++++++
>   5 files changed, 40 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
> index 1477329410ec..5bf993165438 100644
> --- a/drivers/base/regmap/internal.h
> +++ b/drivers/base/regmap/internal.h
> @@ -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;
>   
> diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c
> index 53cc59c84e2f..c924817e19b1 100644
> --- a/drivers/base/regmap/regcache-flat.c
> +++ b/drivers/base/regmap/regcache-flat.c
> @@ -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;
>   }
>   
> diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
> index 319c342bf5a0..31bdbf37dbed 100644
> --- a/drivers/base/regmap/regcache.c
> +++ b/drivers/base/regmap/regcache.c
> @@ -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);
> diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
> index ce9be3989a21..57c5551044ed 100644
> --- a/drivers/base/regmap/regmap.c
> +++ b/drivers/base/regmap/regmap.c
> @@ -811,6 +811,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);
> @@ -1433,6 +1434,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);
> diff --git a/include/linux/regmap.h b/include/linux/regmap.h
> index b0b9be750d93..51940eeff872 100644
> --- a/include/linux/regmap.h
> +++ b/include/linux/regmap.h
> @@ -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;

It might be worth ...

     if (!def)
         return -EINVAL;

     *def = 0;

Otherwise ...

Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Tested-by: Jon Hunter <jonathanh@nvidia.com>

Thanks
Jon

-- 
nvpublic