summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-04-15 14:27:51 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-04-15 14:27:51 -0700
commit8e258317dd01261331670877beafa3157bd61478 (patch)
tree00e70b3e411ae55b4efd8b14aaa70b2b58a59ba6 /drivers
parente41a25c53f96abe40edc5db1626d37a518852d84 (diff)
parent8ad7f3b265a87cd4e5052677545f90f14c855b10 (diff)
Merge tag 'regmap-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap
Pull regmap updates from Mark Brown: "This has been quite a busy release for regmap, the user visible changes are quite minor but there's some quite good work on internal code improvements: - Cleanup helper for __free()ing regmap_fields - Support non-devm I3C regmaps - A bunch of cleanup work, mostly from Andy Shevchenko - Fix for bootstrapping issues with hardware initialised regmaps, which was the main inspiration for some of the cleanups" * tag 'regmap-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap: regmap: i3c: Add non-devm regmap_init_i3c() helper regmap: debugfs: fix race condition in dummy name allocation regmap: Synchronize cache for the page selector regmap: Simplify devres handling regcache: Move HW readback after cache initialisation regcache: Allocate and free reg_defaults on the same level regcache: Move count check and cache_bypass assignment to the caller regcache: Factor out regcache_hw_exit() helper regcache: Amend printf() specifiers when printing registers regcache: Define iterator inside for-loop and align their types regmap: define cleanup helper for regmap_field regmap: sort header includes regcache: Split regcache_count_cacheable_registers() helper regcache: Remove duplicate check in regcache_hw_init()
Diffstat (limited to 'drivers')
-rw-r--r--drivers/base/regmap/internal.h3
-rw-r--r--drivers/base/regmap/regcache.c102
-rw-r--r--drivers/base/regmap/regmap-debugfs.c21
-rw-r--r--drivers/base/regmap/regmap-i3c.c10
-rw-r--r--drivers/base/regmap/regmap.c23
5 files changed, 92 insertions, 67 deletions
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index 5bf993165438..55273a6178f8 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -84,6 +84,7 @@ struct regmap {
bool debugfs_disable;
struct dentry *debugfs;
const char *debugfs_name;
+ int debugfs_dummy_id;
unsigned int debugfs_reg_len;
unsigned int debugfs_val_len;
@@ -162,7 +163,7 @@ struct regmap {
bool no_sync_defaults;
struct reg_sequence *patch;
- int patch_regs;
+ unsigned int patch_regs;
/* if set, the regmap core can sleep */
bool can_sleep;
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index a35f2b20298b..27616b05111c 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -42,33 +42,25 @@ void regcache_sort_defaults(struct reg_default *defaults, unsigned int ndefaults
}
EXPORT_SYMBOL_GPL(regcache_sort_defaults);
-static int regcache_hw_init(struct regmap *map)
+static int regcache_count_cacheable_registers(struct regmap *map)
{
- int i, j;
- int ret;
- int count;
- unsigned int reg, val;
- void *tmp_buf;
-
- if (!map->num_reg_defaults_raw)
- return -EINVAL;
+ unsigned int count;
/* calculate the size of reg_defaults */
- for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++)
+ count = 0;
+ for (unsigned int i = 0; i < map->num_reg_defaults_raw; i++)
if (regmap_readable(map, i * map->reg_stride) &&
!regmap_volatile(map, i * map->reg_stride))
count++;
- /* all registers are unreadable or volatile, so just bypass */
- if (!count) {
- map->cache_bypass = true;
- return 0;
- }
+ return count;
+}
- map->num_reg_defaults = count;
- map->reg_defaults = kmalloc_objs(struct reg_default, count);
- if (!map->reg_defaults)
- return -ENOMEM;
+static int regcache_hw_init(struct regmap *map)
+{
+ int ret;
+ unsigned int reg, val;
+ void *tmp_buf;
if (!map->reg_defaults_raw) {
bool cache_bypass = map->cache_bypass;
@@ -77,10 +69,8 @@ static int regcache_hw_init(struct regmap *map)
/* Bypass the cache access till data read from HW */
map->cache_bypass = true;
tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL);
- if (!tmp_buf) {
- ret = -ENOMEM;
- goto err_free;
- }
+ if (!tmp_buf)
+ return -ENOMEM;
ret = regmap_raw_read(map, 0, tmp_buf,
map->cache_size_raw);
map->cache_bypass = cache_bypass;
@@ -93,7 +83,7 @@ static int regcache_hw_init(struct regmap *map)
}
/* fill the reg_defaults */
- for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) {
+ for (unsigned int i = 0, j = 0; i < map->num_reg_defaults_raw; i++) {
reg = i * map->reg_stride;
if (!regmap_readable(map, reg))
@@ -111,9 +101,9 @@ static int regcache_hw_init(struct regmap *map)
ret = regmap_read(map, reg, &val);
map->cache_bypass = cache_bypass;
if (ret != 0) {
- dev_err(map->dev, "Failed to read %d: %d\n",
+ dev_err(map->dev, "Failed to read %x: %d\n",
reg, ret);
- goto err_free;
+ return ret;
}
}
@@ -123,15 +113,17 @@ static int regcache_hw_init(struct regmap *map)
}
return 0;
+}
-err_free:
- kfree(map->reg_defaults);
-
- return ret;
+static void regcache_hw_exit(struct regmap *map)
+{
+ if (map->cache_free)
+ kfree(map->reg_defaults_raw);
}
int regcache_init(struct regmap *map, const struct regmap_config *config)
{
+ int count = 0;
int ret;
int i;
void *tmp_buf;
@@ -196,15 +188,18 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
return -ENOMEM;
map->reg_defaults = tmp_buf;
} else if (map->num_reg_defaults_raw) {
- /* Some devices such as PMICs don't have cache defaults,
- * we cope with this by reading back the HW registers and
- * crafting the cache defaults by hand.
- */
- ret = regcache_hw_init(map);
- if (ret < 0)
- return ret;
+ count = regcache_count_cacheable_registers(map);
+ if (!count)
+ map->cache_bypass = true;
+
+ /* All registers are unreadable or volatile, so just bypass */
if (map->cache_bypass)
return 0;
+
+ map->num_reg_defaults = count;
+ map->reg_defaults = kmalloc_objs(struct reg_default, count);
+ if (!map->reg_defaults)
+ return -ENOMEM;
}
if (!map->max_register_is_set && map->num_reg_defaults_raw) {
@@ -219,7 +214,18 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
ret = map->cache_ops->init(map);
map->unlock(map->lock_arg);
if (ret)
- goto err_free;
+ goto err_free_reg_defaults;
+ }
+
+ /*
+ * Some devices such as PMICs don't have cache defaults,
+ * we cope with this by reading back the HW registers and
+ * crafting the cache defaults by hand.
+ */
+ if (count) {
+ ret = regcache_hw_init(map);
+ if (ret)
+ goto err_exit;
}
if (map->cache_ops->populate &&
@@ -229,10 +235,12 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
ret = map->cache_ops->populate(map);
map->unlock(map->lock_arg);
if (ret)
- goto err_exit;
+ goto err_free;
}
return 0;
+err_free:
+ regcache_hw_exit(map);
err_exit:
if (map->cache_ops->exit) {
dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name);
@@ -240,10 +248,8 @@ err_exit:
ret = map->cache_ops->exit(map);
map->unlock(map->lock_arg);
}
-err_free:
+err_free_reg_defaults:
kfree(map->reg_defaults);
- if (map->cache_free)
- kfree(map->reg_defaults_raw);
return ret;
}
@@ -255,9 +261,7 @@ void regcache_exit(struct regmap *map)
BUG_ON(!map->cache_ops);
- kfree(map->reg_defaults);
- if (map->cache_free)
- kfree(map->reg_defaults_raw);
+ regcache_hw_exit(map);
if (map->cache_ops->exit) {
dev_dbg(map->dev, "Destroying %s cache\n",
@@ -266,6 +270,8 @@ void regcache_exit(struct regmap *map)
map->cache_ops->exit(map);
map->unlock(map->lock_arg);
}
+
+ kfree(map->reg_defaults);
}
/**
@@ -504,7 +510,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
bypass = map->cache_bypass;
name = map->cache_ops->name;
- dev_dbg(map->dev, "Syncing %s cache from %d-%d\n", name, min, max);
+ dev_dbg(map->dev, "Syncing %s cache from %#x-%#x\n", name, min, max);
trace_regcache_sync(map, name, "start region");
@@ -835,13 +841,13 @@ static int regcache_sync_block_raw(struct regmap *map, void *block,
unsigned int block_base, unsigned int start,
unsigned int end)
{
- unsigned int i, val;
unsigned int regtmp = 0;
unsigned int base = 0;
const void *data = NULL;
+ unsigned int val;
int ret;
- for (i = start; i < end; i++) {
+ for (unsigned int i = start; i < end; i++) {
regtmp = block_base + (i * map->reg_stride);
if (!regcache_reg_present(cache_present, i) ||
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
index 5a46ce5fee72..18f1c60749fe 100644
--- a/drivers/base/regmap/regmap-debugfs.c
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -12,6 +12,7 @@
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/list.h>
+#include <linux/idr.h>
#include "internal.h"
@@ -20,7 +21,7 @@ struct regmap_debugfs_node {
struct list_head link;
};
-static unsigned int dummy_index;
+static DEFINE_IDA(dummy_ida);
static struct dentry *regmap_debugfs_root;
static LIST_HEAD(regmap_debugfs_early_list);
static DEFINE_MUTEX(regmap_debugfs_early_lock);
@@ -539,6 +540,7 @@ void regmap_debugfs_init(struct regmap *map)
struct regmap_range_node *range_node;
const char *devname = "dummy";
const char *name = map->name;
+ int id;
/*
* Userspace can initiate reads from the hardware over debugfs.
@@ -567,6 +569,7 @@ void regmap_debugfs_init(struct regmap *map)
INIT_LIST_HEAD(&map->debugfs_off_cache);
mutex_init(&map->cache_lock);
+ map->debugfs_dummy_id = -1;
if (map->dev)
devname = dev_name(map->dev);
@@ -585,12 +588,16 @@ void regmap_debugfs_init(struct regmap *map)
if (!strcmp(name, "dummy")) {
kfree(map->debugfs_name);
- map->debugfs_name = kasprintf(GFP_KERNEL, "dummy%d",
- dummy_index);
- if (!map->debugfs_name)
+ id = ida_alloc(&dummy_ida, GFP_KERNEL);
+ if (id < 0)
return;
+ map->debugfs_name = kasprintf(GFP_KERNEL, "dummy%d", id);
+ if (!map->debugfs_name) {
+ ida_free(&dummy_ida, id);
+ return;
+ }
+ map->debugfs_dummy_id = id;
name = map->debugfs_name;
- dummy_index++;
}
map->debugfs = debugfs_create_dir(name, regmap_debugfs_root);
@@ -660,6 +667,10 @@ void regmap_debugfs_exit(struct regmap *map)
mutex_lock(&map->cache_lock);
regmap_debugfs_free_dump_cache(map);
mutex_unlock(&map->cache_lock);
+ if (map->debugfs_dummy_id >= 0) {
+ ida_free(&dummy_ida, map->debugfs_dummy_id);
+ map->debugfs_dummy_id = -1;
+ }
kfree(map->debugfs_name);
map->debugfs_name = NULL;
} else {
diff --git a/drivers/base/regmap/regmap-i3c.c b/drivers/base/regmap/regmap-i3c.c
index 863b348704dc..5af583d472dd 100644
--- a/drivers/base/regmap/regmap-i3c.c
+++ b/drivers/base/regmap/regmap-i3c.c
@@ -46,6 +46,16 @@ static const struct regmap_bus regmap_i3c = {
.read = regmap_i3c_read,
};
+struct regmap *__regmap_init_i3c(struct i3c_device *i3c,
+ const struct regmap_config *config,
+ struct lock_class_key *lock_key,
+ const char *lock_name)
+{
+ return __regmap_init(&i3c->dev, &regmap_i3c, &i3c->dev, config,
+ lock_key, lock_name);
+}
+EXPORT_SYMBOL_GPL(__regmap_init_i3c);
+
struct regmap *__devm_regmap_init_i3c(struct i3c_device *i3c,
const struct regmap_config *config,
struct lock_class_key *lock_key,
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index e388b19850e3..b2b26f07f4e3 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -1182,9 +1182,9 @@ err:
}
EXPORT_SYMBOL_GPL(__regmap_init);
-static void devm_regmap_release(struct device *dev, void *res)
+static void devm_regmap_release(void *regmap)
{
- regmap_exit(*(struct regmap **)res);
+ regmap_exit(regmap);
}
struct regmap *__devm_regmap_init(struct device *dev,
@@ -1194,20 +1194,17 @@ struct regmap *__devm_regmap_init(struct device *dev,
struct lock_class_key *lock_key,
const char *lock_name)
{
- struct regmap **ptr, *regmap;
-
- ptr = devres_alloc(devm_regmap_release, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return ERR_PTR(-ENOMEM);
+ struct regmap *regmap;
+ int ret;
regmap = __regmap_init(dev, bus, bus_context, config,
lock_key, lock_name);
- if (!IS_ERR(regmap)) {
- *ptr = regmap;
- devres_add(dev, ptr);
- } else {
- devres_free(ptr);
- }
+ if (IS_ERR(regmap))
+ return regmap;
+
+ ret = devm_add_action_or_reset(dev, devm_regmap_release, regmap);
+ if (ret)
+ return ERR_PTR(ret);
return regmap;
}