regmap: Changes for v7.4

This is a relatively busy release, mostly due to the general cleanup
 work which people have been doing throughout the kernel - for regmap
 that's mostly been some work from Peng Fan.  I also added some more
 tests for the sparse caches to catch some gotchas people might run into
 trying to improve the maple cache.
 
  - Test very high register numbers to help people avoid a nasty gotcha
    trying to optimise the maple cache.
  - Make more use of scoped lock and allocation guards.
  - A couple of other smaller improvements and fixes.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQReagvpf3TrrA7BoYYv73Ows9A4iQUCatoASwAKCRAv73Ows9A4
 iSPBAP98TCBcSIIBzaeq0mYWqL9VwSQdDIfoltF8TiEEaq9lhAD+P+xTs2R1J4R1
 9sy2+oYEiZgrVwIn/xHi0oDtUD8bTwE=
 =5AGY
 -----END PGP SIGNATURE-----

Merge tag 'regmap-v7.4-2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap

Pull regmap updates from Mark Brown:
 "This is a relatively busy release, mostly due to the general cleanup
  work which people have been doing throughout the kernel - for regmap
  that's mostly been some work from Peng Fan.

  I also added some more tests for the sparse caches to catch some
  gotchas people might run into trying to improve the maple cache.

   - Test very high register numbers to help people avoid a nasty gotcha
     trying to optimise the maple cache

   - Make more use of scoped lock and allocation guards

   - A couple of other smaller improvements and fixes"

* tag 'regmap-v7.4-2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: debugfs: Use __free(kfree) for regmap_reg_ranges_read_file
  regmap: debugfs: Use guard to simplify code
  regmap: debugfs: Use __free(kfree) in regmap_name_read_file()
  regmap: fix potential double-free of map->reg_defaults on cache reinit
  regmap: use krealloc_array() in regmap_register_patch()
  regcache: simplify control flow and reduce nesting
  regcache: extract __regcache_sync() to deduplicate sync dispatch
  regcache: extract locked helpers to replace open-coded lock/unlock pairs
  regmap: debugfs: use the regmap scoped lock guard
  regcache: rbtree: use the regmap scoped lock guard
  regcache: use the regmap scoped lock guard
  regmap: convert lock/unlock to a scoped guard
  regmap: fix typo "asynchrnous" in comment
  regmap: Test rbtree and maple caches for very high register numbers
  regmap: Allow a base register to be specified for the RAM regmap
This commit is contained in:
Linus Torvalds 2026-10-22 07:23:27 -10:00
commit 9aa54fb74e
8 changed files with 314 additions and 289 deletions

View File

@ -10,6 +10,7 @@
#ifndef _REGMAP_INTERNAL_H
#define _REGMAP_INTERNAL_H
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/regmap.h>
#include <linux/fs.h>
@ -185,6 +186,16 @@ struct regmap {
struct hwspinlock *hwlock;
};
/*
* Scoped guard for the regmap lock. The lock/unlock callbacks are selected
* at init time (mutex, spinlock, raw spinlock, hwspinlock or none) and never
* fail, so an unconditional guard is sufficient. Use with guard(regmap)(map)
* or scoped_guard(regmap, map) { ... }.
*/
DEFINE_GUARD(regmap, struct regmap *,
_T->lock(_T->lock_arg),
_T->unlock(_T->lock_arg))
struct regcache_ops {
const char *name;
enum regcache_type type;
@ -322,6 +333,7 @@ static inline unsigned int regcache_get_index_by_order(const struct regmap *map,
}
struct regmap_ram_data {
unsigned int base_reg;
unsigned int *vals; /* Allocatd by caller */
bool *read;
bool *written;

View File

@ -141,7 +141,7 @@ static int rbtree_show(struct seq_file *s, void *ignored)
int registers = 0;
int this_registers, average;
map->lock(map->lock_arg);
guard(regmap)(map);
mem_size = sizeof(*rbtree_ctx);
@ -168,8 +168,6 @@ static int rbtree_show(struct seq_file *s, void *ignored)
seq_printf(s, "%d nodes, %d registers, average %d registers, used %zu bytes\n",
nodes, registers, average, mem_size);
map->unlock(map->lock_arg);
return 0;
}

View File

@ -22,6 +22,8 @@ static const struct regcache_ops *cache_types[] = {
&regcache_flat_ops,
};
static int regcache_default_sync(struct regmap *map, unsigned int min, unsigned int max);
static int regcache_defaults_cmp(const void *a, const void *b)
{
const struct reg_default *x = a;
@ -56,12 +58,15 @@ static int regcache_count_cacheable_registers(struct regmap *map)
return count;
}
static int regcache_hw_init(struct regmap *map)
static int regcache_hw_init(struct regmap *map, int count)
{
int ret;
unsigned int reg, val;
void *tmp_buf;
if (!count)
return 0;
if (!map->reg_defaults_raw) {
bool cache_bypass = map->cache_bypass;
dev_dbg(map->dev, "No cache defaults, reading back from HW\n");
@ -121,6 +126,29 @@ static void regcache_hw_exit(struct regmap *map)
kfree(map->reg_defaults_raw);
}
static int regcache_locked_op(struct regmap *map,
int (*op)(struct regmap *map),
const char *action)
{
if (!op)
return 0;
dev_dbg(map->dev, "%s %s cache\n", action, map->cache_ops->name);
guard(regmap)(map);
return op(map);
}
static int __regcache_sync(struct regmap *map, unsigned int min, unsigned int max)
{
if (!map->cache_dirty)
return 0;
if (map->cache_ops->sync)
return map->cache_ops->sync(map, min, max);
return regcache_default_sync(map, min, max);
}
int regcache_init(struct regmap *map, const struct regmap_config *config)
{
bool sort_defaults = false;
@ -139,15 +167,8 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
return 0;
}
if (config->reg_defaults && !config->num_reg_defaults) {
dev_err(map->dev,
"Register defaults are set without the number!\n");
return -EINVAL;
}
if (config->num_reg_defaults && !config->reg_defaults) {
dev_err(map->dev,
"Register defaults number are set without the reg!\n");
if (!!config->reg_defaults != !!config->num_reg_defaults) {
dev_err(map->dev, "reg_defaults and num_reg_defaults must both be specified\n");
return -EINVAL;
}
@ -180,9 +201,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
map->cache = NULL;
map->cache_ops = cache_types[i];
if (!map->cache_ops->read ||
!map->cache_ops->write ||
!map->cache_ops->name)
if (!map->cache_ops->read || !map->cache_ops->write || !map->cache_ops->name)
return -EINVAL;
/* We still need to ensure that the reg_defaults
@ -235,21 +254,17 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
* 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;
}
ret = regcache_hw_init(map, count);
if (ret)
goto err_exit;
if (!map->num_reg_defaults && !map->reg_default_cb)
return 0;
ret = regcache_locked_op(map, map->cache_ops->populate, "Populating");
if (ret)
goto err_free;
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);
map->unlock(map->lock_arg);
if (ret)
goto err_free;
}
return 0;
err_free:
@ -273,7 +288,6 @@ void regcache_exit(struct regmap *map)
BUG_ON(!map->cache_ops);
regcache_hw_exit(map);
if (map->cache_ops->exit) {
dev_dbg(map->dev, "Destroying %s cache\n",
map->cache_ops->name);
@ -281,6 +295,7 @@ void regcache_exit(struct regmap *map)
}
kfree(map->reg_defaults);
map->reg_defaults = NULL;
}
/**
@ -455,11 +470,7 @@ int regcache_sync(struct regmap *map)
}
map->cache_bypass = false;
if (map->cache_ops->sync)
sync_ret = map->cache_ops->sync(map, 0, map->max_register);
else
sync_ret = regcache_default_sync(map, 0, map->max_register);
sync_ret = __regcache_sync(map, 0, map->max_register);
if (sync_ret == 0)
map->cache_dirty = false;
@ -539,17 +550,10 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
trace_regcache_sync(map, name, "start region");
if (!map->cache_dirty)
goto out;
map->async = true;
if (map->cache_ops->sync)
ret = map->cache_ops->sync(map, min, max);
else
ret = regcache_default_sync(map, min, max);
ret = __regcache_sync(map, min, max);
out:
/* Restore the bypass state */
map->cache_bypass = bypass;
map->async = false;
@ -578,20 +582,14 @@ EXPORT_SYMBOL_GPL(regcache_sync_region);
int regcache_drop_region(struct regmap *map, unsigned int min,
unsigned int max)
{
int ret = 0;
if (!map->cache_ops || !map->cache_ops->drop)
return -EINVAL;
map->lock(map->lock_arg);
guard(regmap)(map);
trace_regcache_drop_region(map, min, max);
ret = map->cache_ops->drop(map, min, max);
map->unlock(map->lock_arg);
return ret;
return map->cache_ops->drop(map, min, max);
}
EXPORT_SYMBOL_GPL(regcache_drop_region);
@ -609,12 +607,11 @@ EXPORT_SYMBOL_GPL(regcache_drop_region);
*/
void regcache_cache_only(struct regmap *map, bool enable)
{
map->lock(map->lock_arg);
guard(regmap)(map);
WARN_ON(map->cache_type != REGCACHE_NONE &&
map->cache_bypass && enable);
map->cache_only = enable;
trace_regmap_cache_only(map, enable);
map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_cache_only);
@ -633,10 +630,9 @@ EXPORT_SYMBOL_GPL(regcache_cache_only);
*/
void regcache_mark_dirty(struct regmap *map)
{
map->lock(map->lock_arg);
guard(regmap)(map);
map->cache_dirty = true;
map->no_sync_defaults = true;
map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_mark_dirty);
@ -653,11 +649,10 @@ EXPORT_SYMBOL_GPL(regcache_mark_dirty);
*/
void regcache_cache_bypass(struct regmap *map, bool enable)
{
map->lock(map->lock_arg);
guard(regmap)(map);
WARN_ON(map->cache_only && enable);
map->cache_bypass = enable;
trace_regmap_cache_bypass(map, enable);
map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_cache_bypass);
@ -674,12 +669,10 @@ bool regcache_reg_cached(struct regmap *map, unsigned int reg)
unsigned int val;
int ret;
map->lock(map->lock_arg);
guard(regmap)(map);
ret = regcache_read(map, reg, &val);
map->unlock(map->lock_arg);
return ret == 0;
}
EXPORT_SYMBOL_GPL(regcache_reg_cached);
@ -765,8 +758,8 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg)
if (r)
return r - map->reg_defaults;
else
return -ENOENT;
return -ENOENT;
}
static bool regcache_reg_present(unsigned long *cache_present, unsigned int idx)
@ -903,7 +896,7 @@ int regcache_sync_block(struct regmap *map, void *block,
if (regmap_can_raw_write(map) && !map->use_single_write)
return regcache_sync_block_raw(map, block, cache_present,
block_base, start, end);
else
return regcache_sync_block_single(map, block, cache_present,
block_base, start, end);
return regcache_sync_block_single(map, block, cache_present,
block_base, start, end);
}

View File

@ -38,10 +38,9 @@ static ssize_t regmap_name_read_file(struct file *file,
{
struct regmap *map = file->private_data;
const char *name = "nodev";
char *buf __free(kfree) = kmalloc(PAGE_SIZE, GFP_KERNEL);
int ret;
char *buf;
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@ -49,14 +48,10 @@ static ssize_t regmap_name_read_file(struct file *file,
name = map->dev->driver->name;
ret = snprintf(buf, PAGE_SIZE, "%s\n", name);
if (ret >= PAGE_SIZE) {
kfree(buf);
if (ret >= PAGE_SIZE)
return ret;
}
ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
kfree(buf);
return ret;
return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
}
static const struct file_operations regmap_name_fops = {
@ -112,7 +107,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
* If we don't have a cache build one so we don't have to do a
* linear scan each time.
*/
mutex_lock(&map->cache_lock);
guard(mutex)(&map->cache_lock);
i = base;
if (list_empty(&map->debugfs_off_cache)) {
for (; i <= map->max_register; i += map->reg_stride) {
@ -134,7 +129,6 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
c = kzalloc_obj(*c);
if (!c) {
regmap_debugfs_free_dump_cache(map);
mutex_unlock(&map->cache_lock);
return base;
}
c->min = p;
@ -167,14 +161,12 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
fpos_offset = from - c->min;
reg_offset = fpos_offset / map->debugfs_tot_len;
*pos = c->min + (reg_offset * map->debugfs_tot_len);
mutex_unlock(&map->cache_lock);
return c->base_reg + (reg_offset * map->reg_stride);
}
*pos = c->max;
ret = c->max_reg;
}
mutex_unlock(&map->cache_lock);
return ret;
}
@ -196,10 +188,10 @@ static int regmap_next_readable_reg(struct regmap *map, int reg)
struct regmap_debugfs_off_cache *c;
int ret = -EINVAL;
if (regmap_printable(map, reg + map->reg_stride)) {
ret = reg + map->reg_stride;
} else {
mutex_lock(&map->cache_lock);
if (regmap_printable(map, reg + map->reg_stride))
return reg + map->reg_stride;
scoped_guard(mutex, &map->cache_lock) {
list_for_each_entry(c, &map->debugfs_off_cache, list) {
if (reg > c->max_reg)
continue;
@ -208,8 +200,8 @@ static int regmap_next_readable_reg(struct regmap *map, int reg)
break;
}
}
mutex_unlock(&map->cache_lock);
}
return ret;
}
@ -366,8 +358,6 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file,
struct regmap_debugfs_off_cache *c;
loff_t p = 0;
size_t buf_pos = 0;
char *buf;
char *entry;
int ret;
unsigned int entry_len;
@ -377,15 +367,13 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file,
if (count > (PAGE_SIZE << MAX_PAGE_ORDER))
count = PAGE_SIZE << MAX_PAGE_ORDER;
buf = kmalloc(count, GFP_KERNEL);
char *buf __free(kfree) = kmalloc(count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
entry = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!entry) {
kfree(buf);
char *entry __free(kfree) = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!entry)
return -ENOMEM;
}
/* While we are at it, build the register dump cache
* now so the read() operation on the `registers' file
@ -398,31 +386,27 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file,
/* Reset file pointer as the fixed-format of the `registers'
* file is not compatible with the `range' file */
p = 0;
mutex_lock(&map->cache_lock);
list_for_each_entry(c, &map->debugfs_off_cache, list) {
entry_len = snprintf(entry, PAGE_SIZE, "%x-%x\n",
c->base_reg, c->max_reg);
if (p >= *ppos) {
if (buf_pos + entry_len > count)
break;
memcpy(buf + buf_pos, entry, entry_len);
buf_pos += entry_len;
scoped_guard(mutex, &map->cache_lock) {
list_for_each_entry(c, &map->debugfs_off_cache, list) {
entry_len = snprintf(entry, PAGE_SIZE, "%x-%x\n",
c->base_reg, c->max_reg);
if (p >= *ppos) {
if (buf_pos + entry_len > count)
break;
memcpy(buf + buf_pos, entry, entry_len);
buf_pos += entry_len;
}
p += entry_len;
}
p += entry_len;
}
mutex_unlock(&map->cache_lock);
kfree(entry);
ret = buf_pos;
if (copy_to_user(user_buf, buf, buf_pos)) {
ret = -EFAULT;
goto out_buf;
}
if (copy_to_user(user_buf, buf, buf_pos))
return -EFAULT;
*ppos += buf_pos;
out_buf:
kfree(buf);
return ret;
}
@ -471,18 +455,16 @@ static ssize_t regmap_cache_only_write_file(struct file *file,
if (err)
return count;
map->lock(map->lock_arg);
if (new_val && !map->cache_only) {
dev_warn(map->dev, "debugfs cache_only=Y forced\n");
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
} else if (!new_val && map->cache_only) {
dev_warn(map->dev, "debugfs cache_only=N forced: syncing cache\n");
require_sync = true;
scoped_guard(regmap, map) {
if (new_val && !map->cache_only) {
dev_warn(map->dev, "debugfs cache_only=Y forced\n");
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
} else if (!new_val && map->cache_only) {
dev_warn(map->dev, "debugfs cache_only=N forced: syncing cache\n");
require_sync = true;
}
map->cache_only = new_val;
}
map->cache_only = new_val;
map->unlock(map->lock_arg);
if (require_sync) {
err = regcache_sync(map);
@ -513,7 +495,7 @@ static ssize_t regmap_cache_bypass_write_file(struct file *file,
if (err)
return count;
map->lock(map->lock_arg);
guard(regmap)(map);
if (new_val && !map->cache_bypass) {
dev_warn(map->dev, "debugfs cache_bypass=Y forced\n");
@ -523,8 +505,6 @@ static ssize_t regmap_cache_bypass_write_file(struct file *file,
}
map->cache_bypass = new_val;
map->unlock(map->lock_arg);
return count;
}
@ -561,9 +541,8 @@ void regmap_debugfs_init(struct regmap *map)
if (!node)
return;
node->map = map;
mutex_lock(&regmap_debugfs_early_lock);
list_add(&node->link, &regmap_debugfs_early_list);
mutex_unlock(&regmap_debugfs_early_lock);
scoped_guard(mutex, &regmap_debugfs_early_lock)
list_add(&node->link, &regmap_debugfs_early_list);
return;
}
@ -662,30 +641,30 @@ void regmap_debugfs_init(struct regmap *map)
void regmap_debugfs_exit(struct regmap *map)
{
struct regmap_debugfs_node *node, *tmp;
if (map->debugfs) {
debugfs_remove_recursive(map->debugfs);
map->debugfs = NULL;
mutex_lock(&map->cache_lock);
regmap_debugfs_free_dump_cache(map);
mutex_unlock(&map->cache_lock);
scoped_guard(mutex, &map->cache_lock)
regmap_debugfs_free_dump_cache(map);
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 {
struct regmap_debugfs_node *node, *tmp;
mutex_lock(&regmap_debugfs_early_lock);
list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list,
link) {
return;
}
scoped_guard(mutex, &regmap_debugfs_early_lock) {
list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list, link) {
if (node->map == map) {
list_del(&node->link);
kfree(node);
}
}
mutex_unlock(&regmap_debugfs_early_lock);
}
}
@ -695,11 +674,10 @@ void regmap_debugfs_initcall(void)
regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
mutex_lock(&regmap_debugfs_early_lock);
guard(mutex)(&regmap_debugfs_early_lock);
list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list, link) {
regmap_debugfs_init(node->map);
list_del(&node->link);
kfree(node);
}
mutex_unlock(&regmap_debugfs_early_lock);
}

View File

@ -11,6 +11,8 @@
#define BLOCK_TEST_SIZE 12
#define HIGH_BIT_REG ((UINT_MAX / 2) - 3)
KUNIT_DEFINE_ACTION_WRAPPER(regmap_exit_action, regmap_exit, struct regmap *);
struct regmap_test_priv {
@ -23,6 +25,7 @@ struct regmap_test_param {
enum regcache_type cache;
enum regmap_endian val_endian;
unsigned int base_reg;
unsigned int from_reg;
bool fast_io;
};
@ -204,7 +207,8 @@ static struct regmap *gen_regmap(struct kunit *test,
config->max_register += (BLOCK_TEST_SIZE * config->reg_stride);
}
size = array_size(config->max_register + 1, sizeof(*buf));
size = array_size(config->max_register - param->base_reg + 1,
sizeof(*buf));
buf = kmalloc(size, GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
@ -215,6 +219,7 @@ static struct regmap *gen_regmap(struct kunit *test,
if (!(*data))
goto out_free;
(*data)->vals = buf;
(*data)->base_reg = param->base_reg;
if (config->num_reg_defaults) {
defaults = kunit_kcalloc(test,
@ -227,8 +232,8 @@ static struct regmap *gen_regmap(struct kunit *test,
config->reg_defaults = defaults;
for (i = 0; i < config->num_reg_defaults; i++) {
defaults[i].reg = param->from_reg + (i * config->reg_stride);
defaults[i].def = buf[param->from_reg + (i * config->reg_stride)];
defaults[i].reg = param->from_reg - param->base_reg + (i * config->reg_stride);
defaults[i].def = buf[param->from_reg - param->base_reg + (i * config->reg_stride)];
}
}
@ -1648,6 +1653,92 @@ static void cache_write_zero(struct kunit *test)
KUNIT_ASSERT_FALSE(test, regcache_reg_cached(map, 1));
}
static const struct regmap_test_param high_bit_cache_types_list[] = {
{ .cache = REGCACHE_RBTREE, .from_reg = HIGH_BIT_REG,
.base_reg = HIGH_BIT_REG },
{ .cache = REGCACHE_MAPLE, .from_reg = HIGH_BIT_REG,
.base_reg = HIGH_BIT_REG },
};
KUNIT_ARRAY_PARAM(high_bit_cache_types, high_bit_cache_types_list, param_to_desc);
/*
* Dynamically allocated cache types should support registers with the
* high bit set, the backend will return an error for any operation on
* a register below the expected window.
*/
static void cache_high_bit_reg(struct kunit *test)
{
const struct regmap_test_param *param = test->param_value;
struct regmap_ram_data *data;
struct regmap_config config;
struct regmap *map;
unsigned int val[BLOCK_TEST_SIZE], rval;
int i;
config = test_regmap_config;
map = gen_regmap(test, &config, &data);
KUNIT_ASSERT_FALSE(test, IS_ERR(map));
if (IS_ERR(map))
return;
get_random_bytes(&val, sizeof(val));
/* No defaults so no registers cached */
for (i = 0; i < BLOCK_TEST_SIZE; i++)
KUNIT_EXPECT_FALSE(test, regcache_reg_cached(map, param->from_reg + i));
/* Writes should reach the device */
for (i = 0; i < BLOCK_TEST_SIZE; i++)
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg + i,
val[i]));
for (i = 0; i < BLOCK_TEST_SIZE; i++) {
KUNIT_EXPECT_TRUE(test, data->written[i]);
KUNIT_EXPECT_EQ(test, val[i], data->vals[i]);
}
/* Reads should be satisfied from the cache */
for (i = 0; i < BLOCK_TEST_SIZE; i++) {
KUNIT_EXPECT_TRUE(test, regcache_reg_cached(map, param->from_reg + i));
KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i,
&rval));
KUNIT_EXPECT_EQ(test, val[i], rval);
KUNIT_EXPECT_FALSE(test, data->read[i]);
}
/* Trash the data on the device then resync */
regcache_mark_dirty(map);
for (i = 0; i < BLOCK_TEST_SIZE; i++) {
data->vals[i] = 0;
data->written[i] = false;
}
KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
/* Did we just write the correct data out? */
for (i = 0; i < BLOCK_TEST_SIZE; i++) {
KUNIT_EXPECT_TRUE(test, data->written[i]);
KUNIT_EXPECT_EQ(test, val[i], data->vals[i]);
}
/* Drop some registers */
KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, param->from_reg + 3,
param->from_reg + 5));
for (i = 0; i < BLOCK_TEST_SIZE; i++)
KUNIT_EXPECT_EQ(test, i < 3 || i > 5,
regcache_reg_cached(map, param->from_reg + i));
/* Reread and check only the dropped registers hit the device */
for (i = 0; i < BLOCK_TEST_SIZE; i++)
data->read[i] = false;
for (i = 0; i < BLOCK_TEST_SIZE; i++) {
KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i,
&rval));
KUNIT_EXPECT_EQ(test, val[i], rval);
KUNIT_EXPECT_EQ(test, i >= 3 && i <= 5, data->read[i]);
}
}
/* Check that caching the window register works with sync */
static void cache_range_window_reg(struct kunit *test)
{
@ -2168,6 +2259,7 @@ static struct kunit_case regmap_test_cases[] = {
KUNIT_CASE_PARAM(cache_drop_all_and_sync_has_defaults, sparse_cache_types_gen_params),
KUNIT_CASE_PARAM(cache_present, sparse_cache_types_gen_params),
KUNIT_CASE_PARAM(cache_write_zero, sparse_cache_types_gen_params),
KUNIT_CASE_PARAM(cache_high_bit_reg, high_bit_cache_types_gen_params),
KUNIT_CASE_PARAM(cache_range_window_reg, real_cache_types_only_gen_params),
KUNIT_CASE_PARAM(raw_read_defaults_single, raw_test_types_gen_params),

View File

@ -20,8 +20,11 @@ static int regmap_ram_write(void *context, unsigned int reg, unsigned int val)
{
struct regmap_ram_data *data = context;
data->vals[reg] = val;
data->written[reg] = true;
if (reg < data->base_reg)
return -EINVAL;
data->vals[reg - data->base_reg] = val;
data->written[reg - data->base_reg] = true;
return 0;
}
@ -30,8 +33,11 @@ static int regmap_ram_read(void *context, unsigned int reg, unsigned int *val)
{
struct regmap_ram_data *data = context;
*val = data->vals[reg];
data->read[reg] = true;
if (reg < data->base_reg)
return -EINVAL;
*val = data->vals[reg - data->base_reg];
data->read[reg - data->base_reg] = true;
return 0;
}
@ -60,17 +66,25 @@ struct regmap *__regmap_init_ram(struct device *dev,
const char *lock_name)
{
struct regmap *map;
unsigned int num_regs;
if (!config->max_register) {
pr_crit("No max_register specified for RAM regmap\n");
return ERR_PTR(-EINVAL);
}
data->read = kzalloc_objs(bool, config->max_register + 1);
if (data->base_reg > config->max_register) {
pr_crit("base_reg above max_register for RAM regmap\n");
return ERR_PTR(-EINVAL);
}
num_regs = config->max_register - data->base_reg + 1;
data->read = kzalloc_objs(bool, num_regs);
if (!data->read)
return ERR_PTR(-ENOMEM);
data->written = kzalloc_objs(bool, config->max_register + 1);
data->written = kzalloc_objs(bool, num_regs);
if (!data->written) {
kfree(data->read);
return ERR_PTR(-ENOMEM);

View File

@ -16,14 +16,17 @@
#include "internal.h"
static unsigned int decode_reg(enum regmap_endian endian, const void *reg)
static unsigned int decode_reg(struct regmap_ram_data *data, const void *reg)
{
const u16 *r = reg;
unsigned int decode;
if (endian == REGMAP_ENDIAN_BIG)
return be16_to_cpu(*r);
if (data->reg_endian == REGMAP_ENDIAN_BIG)
decode = be16_to_cpu(*r);
else
return le16_to_cpu(*r);
decode = le16_to_cpu(*r);
return decode - data->base_reg;
}
static int regmap_raw_ram_gather_write(void *context,
@ -40,7 +43,7 @@ static int regmap_raw_ram_gather_write(void *context,
if (val_len % 2)
return -EINVAL;
r = decode_reg(data->reg_endian, reg);
r = decode_reg(data, reg);
if (data->noinc_reg && data->noinc_reg(data, r)) {
memcpy(&our_buf[r], val + val_len - 2, 2);
data->written[r] = true;
@ -74,7 +77,7 @@ static int regmap_raw_ram_read(void *context,
if (val_len % 2)
return -EINVAL;
r = decode_reg(data->reg_endian, reg);
r = decode_reg(data, reg);
if (data->noinc_reg && data->noinc_reg(data, r)) {
for (i = 0; i < val_len; i += 2)
memcpy(val + i, &our_buf[r], 2);
@ -114,6 +117,7 @@ struct regmap *__regmap_init_raw_ram(struct device *dev,
const char *lock_name)
{
struct regmap *map;
unsigned int regs;
if (config->reg_bits != 16)
return ERR_PTR(-EINVAL);
@ -123,11 +127,12 @@ struct regmap *__regmap_init_raw_ram(struct device *dev,
return ERR_PTR(-EINVAL);
}
data->read = kzalloc_objs(bool, config->max_register + 1);
regs = config->max_register - data->base_reg + 1;
data->read = kzalloc_objs(bool, regs);
if (!data->read)
return ERR_PTR(-ENOMEM);
data->written = kzalloc_objs(bool, config->max_register + 1);
data->written = kzalloc_objs(bool, regs);
if (!data->written)
return ERR_PTR(-ENOMEM);

View File

@ -115,9 +115,8 @@ bool regmap_cached(struct regmap *map, unsigned int reg)
if (map->max_register_is_set && reg > map->max_register)
return false;
map->lock(map->lock_arg);
ret = regcache_read(map, reg, &val);
map->unlock(map->lock_arg);
scoped_guard(regmap, map)
ret = regcache_read(map, reg, &val);
if (ret)
return false;
@ -1980,18 +1979,12 @@ int _regmap_write(struct regmap *map, unsigned int reg,
*/
int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
{
int ret;
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
guard(regmap)(map);
ret = _regmap_write(map, reg, val);
map->unlock(map->lock_arg);
return ret;
return _regmap_write(map, reg, val);
}
EXPORT_SYMBOL_GPL(regmap_write);
@ -2012,7 +2005,7 @@ int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val)
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
guard(regmap)(map);
map->async = true;
@ -2020,8 +2013,6 @@ int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val)
map->async = false;
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_write_async);
@ -2083,20 +2074,14 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,
int regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len)
{
int ret;
if (!regmap_can_raw_write(map))
return -EINVAL;
if (val_len % map->format.val_bytes)
return -EINVAL;
map->lock(map->lock_arg);
guard(regmap)(map);
ret = _regmap_raw_write(map, reg, val, val_len, false);
map->unlock(map->lock_arg);
return ret;
return _regmap_raw_write(map, reg, val, val_len, false);
}
EXPORT_SYMBOL_GPL(regmap_raw_write);
@ -2214,21 +2199,17 @@ int regmap_noinc_write(struct regmap *map, unsigned int reg,
if (val_len == 0)
return -EINVAL;
map->lock(map->lock_arg);
guard(regmap)(map);
if (!regmap_volatile(map, reg) || !regmap_writeable_noinc(map, reg)) {
ret = -EINVAL;
goto out_unlock;
}
if (!regmap_volatile(map, reg) || !regmap_writeable_noinc(map, reg))
return -EINVAL;
/*
* Use the accelerated operation if we can. The val drops the const
* typing in order to facilitate code reuse in regmap_noinc_readwrite().
*/
if (map->bus->reg_noinc_write) {
ret = regmap_noinc_readwrite(map, reg, (void *)val, val_len, true);
goto out_unlock;
}
if (map->bus->reg_noinc_write)
return regmap_noinc_readwrite(map, reg, (void *)val, val_len, true);
while (val_len) {
if (map->max_raw_write && map->max_raw_write < val_len)
@ -2237,14 +2218,12 @@ int regmap_noinc_write(struct regmap *map, unsigned int reg,
write_len = val_len;
ret = _regmap_raw_write(map, reg, val, write_len, true);
if (ret)
goto out_unlock;
return ret;
val = ((u8 *)val) + write_len;
val_len -= write_len;
}
out_unlock:
map->unlock(map->lock_arg);
return ret;
return 0;
}
EXPORT_SYMBOL_GPL(regmap_noinc_write);
@ -2360,7 +2339,8 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
* single write operations.
*/
if (!map->write || !map->format.parse_inplace) {
map->lock(map->lock_arg);
guard(regmap)(map);
for (i = 0; i < val_count; i++) {
unsigned int ival;
@ -2375,18 +2355,15 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
ival = *(u32 *)(val + (i * val_bytes));
break;
default:
ret = -EINVAL;
goto out;
return -EINVAL;
}
ret = _regmap_write(map,
reg + regmap_get_offset(map, i),
ival);
if (ret != 0)
goto out;
return ret;
}
out:
map->unlock(map->lock_arg);
} else {
void *wval;
@ -2656,15 +2633,9 @@ static int _regmap_multi_reg_write(struct regmap *map,
int regmap_multi_reg_write(struct regmap *map, const struct reg_sequence *regs,
int num_regs)
{
int ret;
guard(regmap)(map);
map->lock(map->lock_arg);
ret = _regmap_multi_reg_write(map, regs, num_regs);
map->unlock(map->lock_arg);
return ret;
return _regmap_multi_reg_write(map, regs, num_regs);
}
EXPORT_SYMBOL_GPL(regmap_multi_reg_write);
@ -2693,7 +2664,7 @@ int regmap_multi_reg_write_bypassed(struct regmap *map,
int ret;
bool bypass;
map->lock(map->lock_arg);
guard(regmap)(map);
bypass = map->cache_bypass;
map->cache_bypass = true;
@ -2702,8 +2673,6 @@ int regmap_multi_reg_write_bypassed(struct regmap *map,
map->cache_bypass = bypass;
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_multi_reg_write_bypassed);
@ -2725,7 +2694,7 @@ EXPORT_SYMBOL_GPL(regmap_multi_reg_write_bypassed);
* If supported by the underlying bus the write will be scheduled
* asynchronously, helping maximise I/O speed on higher speed buses
* like SPI. regmap_async_complete() can be called to ensure that all
* asynchrnous writes have been completed.
* asynchronous writes have been completed.
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
@ -2740,7 +2709,7 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg,
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
guard(regmap)(map);
map->async = true;
@ -2748,8 +2717,6 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg,
map->async = false;
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_raw_write_async);
@ -2866,18 +2833,12 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
*/
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
{
int ret;
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
guard(regmap)(map);
ret = _regmap_read(map, reg, val);
map->unlock(map->lock_arg);
return ret;
return _regmap_read(map, reg, val);
}
EXPORT_SYMBOL_GPL(regmap_read);
@ -2900,7 +2861,7 @@ int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
guard(regmap)(map);
bypass = map->cache_bypass;
cache_only = map->cache_only;
@ -2912,8 +2873,6 @@ int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val
map->cache_bypass = bypass;
map->cache_only = cache_only;
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_read_bypassed);
@ -2944,22 +2903,18 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
if (val_count == 0)
return -EINVAL;
map->lock(map->lock_arg);
guard(regmap)(map);
if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass ||
map->cache_type == REGCACHE_NONE) {
size_t chunk_count, chunk_bytes;
size_t chunk_regs = val_count;
if (!map->cache_bypass && map->cache_only) {
ret = -EBUSY;
goto out;
}
if (!map->cache_bypass && map->cache_only)
return -EBUSY;
if (!map->read) {
ret = -ENOTSUPP;
goto out;
}
if (!map->read)
return -ENOTSUPP;
if (map->use_single_read)
chunk_regs = 1;
@ -2973,7 +2928,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
for (i = 0; i < chunk_count; i++) {
ret = _regmap_raw_read(map, reg, val, chunk_bytes, false);
if (ret != 0)
goto out;
return ret;
reg += regmap_get_offset(map, chunk_regs);
val += chunk_bytes;
@ -2984,7 +2939,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
if (val_len) {
ret = _regmap_raw_read(map, reg, val, val_len, false);
if (ret != 0)
goto out;
return ret;
}
} else {
/* Otherwise go word by word for the cache; should be low
@ -2994,16 +2949,13 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
ret = _regmap_read(map, reg + regmap_get_offset(map, i),
&v);
if (ret != 0)
goto out;
return ret;
map->format.format_val(val + (i * val_bytes), v, 0);
}
}
out:
map->unlock(map->lock_arg);
return ret;
return 0;
}
EXPORT_SYMBOL_GPL(regmap_raw_read);
@ -3044,12 +2996,10 @@ int regmap_noinc_read(struct regmap *map, unsigned int reg,
if (val_len == 0)
return -EINVAL;
map->lock(map->lock_arg);
guard(regmap)(map);
if (!regmap_volatile(map, reg) || !regmap_readable_noinc(map, reg)) {
ret = -EINVAL;
goto out_unlock;
}
if (!regmap_volatile(map, reg) || !regmap_readable_noinc(map, reg))
return -EINVAL;
/*
* We have not defined the FIFO semantics for cache, as the
@ -3057,16 +3007,12 @@ int regmap_noinc_read(struct regmap *map, unsigned int reg,
* written value? Just avoid this by always reading the FIFO
* even when using cache. Cache only will not work.
*/
if (!map->cache_bypass && map->cache_only) {
ret = -EBUSY;
goto out_unlock;
}
if (!map->cache_bypass && map->cache_only)
return -EBUSY;
/* Use the accelerated operation if we can */
if (map->bus->reg_noinc_read) {
ret = regmap_noinc_readwrite(map, reg, val, val_len, false);
goto out_unlock;
}
if (map->bus->reg_noinc_read)
return regmap_noinc_readwrite(map, reg, val, val_len, false);
while (val_len) {
if (map->max_raw_read && map->max_raw_read < val_len)
@ -3075,14 +3021,12 @@ int regmap_noinc_read(struct regmap *map, unsigned int reg,
read_len = val_len;
ret = _regmap_raw_read(map, reg, val, read_len, true);
if (ret)
goto out_unlock;
return ret;
val = ((u8 *)val) + read_len;
val_len -= read_len;
}
out_unlock:
map->unlock(map->lock_arg);
return ret;
return 0;
}
EXPORT_SYMBOL_GPL(regmap_noinc_read);
@ -3152,22 +3096,20 @@ static int _regmap_bulk_read(struct regmap *map, unsigned int reg,
u8 *u8 = val;
int ret, i;
map->lock(map->lock_arg);
guard(regmap)(map);
for (i = 0; i < val_count; i++) {
unsigned int ival;
if (regs) {
if (!IS_ALIGNED(regs[i], map->reg_stride)) {
ret = -EINVAL;
goto out;
}
if (!IS_ALIGNED(regs[i], map->reg_stride))
return -EINVAL;
ret = _regmap_read(map, regs[i], &ival);
} else {
ret = _regmap_read(map, reg + regmap_get_offset(map, i), &ival);
}
if (ret != 0)
goto out;
return ret;
switch (map->format.val_bytes) {
case 4:
@ -3180,13 +3122,11 @@ static int _regmap_bulk_read(struct regmap *map, unsigned int reg,
u8[i] = ival;
break;
default:
ret = -EINVAL;
goto out;
return -EINVAL;
}
}
out:
map->unlock(map->lock_arg);
return ret;
return 0;
}
/**
@ -3313,7 +3253,7 @@ int regmap_update_bits_base(struct regmap *map, unsigned int reg,
{
int ret;
map->lock(map->lock_arg);
guard(regmap)(map);
map->async = async;
@ -3321,8 +3261,6 @@ int regmap_update_bits_base(struct regmap *map, unsigned int reg,
map->async = false;
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_update_bits_base);
@ -3444,31 +3382,26 @@ int regmap_register_patch(struct regmap *map, const struct reg_sequence *regs,
num_regs))
return 0;
p = krealloc(map->patch,
sizeof(struct reg_sequence) * (map->patch_regs + num_regs),
GFP_KERNEL);
if (p) {
memcpy(p + map->patch_regs, regs, num_regs * sizeof(*regs));
map->patch = p;
map->patch_regs += num_regs;
} else {
p = krealloc_array(map->patch, map->patch_regs + num_regs, sizeof(*p), GFP_KERNEL);
if (!p)
return -ENOMEM;
memcpy(p + map->patch_regs, regs, num_regs * sizeof(*regs));
map->patch = p;
map->patch_regs += num_regs;
scoped_guard(regmap, map) {
bypass = map->cache_bypass;
map->cache_bypass = true;
map->async = true;
ret = _regmap_multi_reg_write(map, regs, num_regs);
map->async = false;
map->cache_bypass = bypass;
}
map->lock(map->lock_arg);
bypass = map->cache_bypass;
map->cache_bypass = true;
map->async = true;
ret = _regmap_multi_reg_write(map, regs, num_regs);
map->async = false;
map->cache_bypass = bypass;
map->unlock(map->lock_arg);
regmap_async_complete(map);
return ret;