mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
regmap: Updates for v7.1
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.
-----BEGIN PGP SIGNATURE-----
iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmnc3gIACgkQJNaLcl1U
h9Ay2Af/XhDtVKTtE0H60Z1OESswXFKw/pLyOBBfMqME3OcXksCWZhybr0uB2boD
nz5EQWvitIfGrR03C4CwVwHF/aCcBYLb6Zo8X9hTMq7Zjlvno2jVM/mmmpQnEZFh
vrzJIAu8+ZqlxtjtNPJ2FsoTTosA4Y58jmVKHLhLtcPExEQyXWkokM50lZAFgY3i
R8FdkRI5rHQM+gnKVupT0eI+rBqQxkyp3wtJ42d/2FMduiEbptuZuGppLM79oIlm
ICMLvPC4ZuXQ7rsUccSqSi06XD5JKpICY2Re7pq+L3cToitkiuWqObOWxLKXswJP
eU/EaqIKnZJgxKoRuNx44bp7LTOo2g==
=DnAo
-----END PGP SIGNATURE-----
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()
This commit is contained in:
commit
8e258317dd
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
|
|||
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) ||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -1182,9 +1182,9 @@ struct regmap *__regmap_init(struct device *dev,
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,16 @@
|
|||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/fwnode.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/rbtree.h>
|
||||
|
||||
struct module;
|
||||
struct clk;
|
||||
|
|
@ -692,6 +693,10 @@ struct regmap *__regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw,
|
|||
const struct regmap_sdw_mbq_cfg *mbq_config,
|
||||
struct lock_class_key *lock_key,
|
||||
const char *lock_name);
|
||||
struct regmap *__regmap_init_i3c(struct i3c_device *i3c,
|
||||
const struct regmap_config *config,
|
||||
struct lock_class_key *lock_key,
|
||||
const char *lock_name);
|
||||
struct regmap *__regmap_init_spi_avmm(struct spi_device *spi,
|
||||
const struct regmap_config *config,
|
||||
struct lock_class_key *lock_key,
|
||||
|
|
@ -998,6 +1003,19 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
|
|||
__regmap_lockdep_wrapper(__regmap_init_sdw_mbq, #config, \
|
||||
dev, sdw, config, mbq_config)
|
||||
|
||||
/**
|
||||
* regmap_init_i3c() - Initialise register map
|
||||
*
|
||||
* @i3c: Device that will be interacted with
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
* The return value will be an ERR_PTR() on error or a valid pointer to
|
||||
* a struct regmap.
|
||||
*/
|
||||
#define regmap_init_i3c(i3c, config) \
|
||||
__regmap_lockdep_wrapper(__regmap_init_i3c, #config, \
|
||||
i3c, config)
|
||||
|
||||
/**
|
||||
* regmap_init_spi_avmm() - Initialize register map for Intel SPI Slave
|
||||
* to AVMM Bus Bridge
|
||||
|
|
@ -1460,6 +1478,8 @@ struct regmap_field *regmap_field_alloc(struct regmap *regmap,
|
|||
struct reg_field reg_field);
|
||||
void regmap_field_free(struct regmap_field *field);
|
||||
|
||||
DEFINE_FREE(regmap_field, struct regmap_field *, if (_T) regmap_field_free(_T))
|
||||
|
||||
struct regmap_field *devm_regmap_field_alloc(struct device *dev,
|
||||
struct regmap *regmap, struct reg_field reg_field);
|
||||
void devm_regmap_field_free(struct device *dev, struct regmap_field *field);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user