diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-15 14:27:51 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-15 14:27:51 -0700 |
| commit | 8e258317dd01261331670877beafa3157bd61478 (patch) | |
| tree | 00e70b3e411ae55b4efd8b14aaa70b2b58a59ba6 /drivers | |
| parent | e41a25c53f96abe40edc5db1626d37a518852d84 (diff) | |
| parent | 8ad7f3b265a87cd4e5052677545f90f14c855b10 (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.h | 3 | ||||
| -rw-r--r-- | drivers/base/regmap/regcache.c | 102 | ||||
| -rw-r--r-- | drivers/base/regmap/regmap-debugfs.c | 21 | ||||
| -rw-r--r-- | drivers/base/regmap/regmap-i3c.c | 10 | ||||
| -rw-r--r-- | drivers/base/regmap/regmap.c | 23 |
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, ®map_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; } |
