mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
commit
9aa54fb74e
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ static const struct regcache_ops *cache_types[] = {
|
|||
®cache_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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(®map_debugfs_early_lock);
|
||||
list_add(&node->link, ®map_debugfs_early_list);
|
||||
mutex_unlock(®map_debugfs_early_lock);
|
||||
scoped_guard(mutex, ®map_debugfs_early_lock)
|
||||
list_add(&node->link, ®map_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(®map_debugfs_early_lock);
|
||||
list_for_each_entry_safe(node, tmp, ®map_debugfs_early_list,
|
||||
link) {
|
||||
return;
|
||||
}
|
||||
|
||||
scoped_guard(mutex, ®map_debugfs_early_lock) {
|
||||
list_for_each_entry_safe(node, tmp, ®map_debugfs_early_list, link) {
|
||||
if (node->map == map) {
|
||||
list_del(&node->link);
|
||||
kfree(node);
|
||||
}
|
||||
}
|
||||
mutex_unlock(®map_debugfs_early_lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -695,11 +674,10 @@ void regmap_debugfs_initcall(void)
|
|||
|
||||
regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
|
||||
|
||||
mutex_lock(®map_debugfs_early_lock);
|
||||
guard(mutex)(®map_debugfs_early_lock);
|
||||
list_for_each_entry_safe(node, tmp, ®map_debugfs_early_list, link) {
|
||||
regmap_debugfs_init(node->map);
|
||||
list_del(&node->link);
|
||||
kfree(node);
|
||||
}
|
||||
mutex_unlock(®map_debugfs_early_lock);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user