regmap: Updates for v6.11

There's one new feature here, a regmap_multi_reg_read() matching the
 existing write function which has some IIO users coming.  This allows
 atomic reads from multiple registers without the need to wrap a higher
 level lock in the client driver just for regmap (which already has locks
 anyway).  We also have one fix for the KUnit tests, and a bunch of
 cleanups.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmaVJRsACgkQJNaLcl1U
 h9CQcAf+PhHwUoxBextaJzy2HQe9CFV71/I4GEj0MTrtpqKDhmO8AQnsI4dKXr/9
 hZPUPR7+6qw7N12NdZQcT6hIX9CnhdiaNALo4ANvG5KlxFljhFbv2sBg1QAgb+Ks
 2gLapB4HY0jso+6urNEN5rvucFpf4Mxlz5ilrk3kuuJaC/xf6/QXoU5/9PjPyD5s
 ZuCBMg5G0BIIdwc9SFGJPv1UUJ0H6Aj/1ZQNo/JVtIj8uGt8TuvnUrmOxdnHRN1U
 b5AMjLnSYMtwRg7Nd/DCINmwPN+I0X79gNE4SG1LAOlQIChhW+47HuGWyCM40jGO
 p4X8o/zID9hw5q3y6CpwYvrJ5GRZbA==
 =ETId
 -----END PGP SIGNATURE-----

Merge tag 'regmap-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap

Pull regmap updates from Mark Brown:
 "There's one new feature here, a regmap_multi_reg_read() matching the
  existing write function which has some IIO users coming.

  This allows atomic reads from multiple registers without the need to
  wrap a higher level lock in the client driver just for regmap (which
  already has locks anyway).

  We also have one fix for the KUnit tests, and a bunch of cleanups"

* tag 'regmap-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: kunit: Add test cases for regmap_multi_reg_(read,write}()
  regmap: Implement regmap_multi_reg_read()
  regmap-irq: handle const struct regmap_irq_sub_irq_map
  const_structs.checkpatch: add regmap structs
  regmap: add missing MODULE_DESCRIPTION() macros
  regmap-i2c: add missing MODULE_DESCRIPTION() macro
  regmap: kunit: Use array_size() and sizeof(*ptr) consistently
  regmap: maple: Switch to use kmemdup_array()
  regmap: cache: Switch to use kmemdup_array()
  regmap: cache: Use correct type of the rb_for_each() parameter
  regmap: Switch to use kmemdup_array()
  regmap: kunit: add missing MODULE_DESCRIPTION()
  regmap: kunit: Fix memory leaks in gen_regmap() and gen_raw_regmap()
This commit is contained in:
Linus Torvalds 2024-07-15 17:56:11 -07:00
commit 25617a5c45
16 changed files with 226 additions and 81 deletions

View File

@ -132,9 +132,9 @@ static int regcache_maple_drop(struct regmap *map, unsigned int min,
lower_index = mas.index;
lower_last = min -1;
lower = kmemdup(entry, ((min - mas.index) *
sizeof(unsigned long)),
map->alloc_flags);
lower = kmemdup_array(entry,
min - mas.index, sizeof(*lower),
map->alloc_flags);
if (!lower) {
ret = -ENOMEM;
goto out_unlocked;
@ -145,10 +145,9 @@ static int regcache_maple_drop(struct regmap *map, unsigned int min,
upper_index = max + 1;
upper_last = mas.last;
upper = kmemdup(&entry[max - mas.index + 1],
((mas.last - max) *
sizeof(unsigned long)),
map->alloc_flags);
upper = kmemdup_array(&entry[max - mas.index + 1],
mas.last - max, sizeof(*upper),
map->alloc_flags);
if (!upper) {
ret = -ENOMEM;
goto out_unlocked;

View File

@ -170,8 +170,8 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
* a copy of it.
*/
if (config->reg_defaults) {
tmp_buf = kmemdup(config->reg_defaults, map->num_reg_defaults *
sizeof(struct reg_default), GFP_KERNEL);
tmp_buf = kmemdup_array(config->reg_defaults, map->num_reg_defaults,
sizeof(*map->reg_defaults), GFP_KERNEL);
if (!tmp_buf)
return -ENOMEM;
map->reg_defaults = tmp_buf;
@ -407,7 +407,7 @@ out:
* have gone out of sync, force writes of all the paging
* registers.
*/
rb_for_each(node, 0, &map->range_tree, rbtree_all) {
rb_for_each(node, NULL, &map->range_tree, rbtree_all) {
struct regmap_range_node *this =
rb_entry(node, struct regmap_range_node, node);

View File

@ -86,4 +86,5 @@ struct regmap *__devm_regmap_init_ac97(struct snd_ac97 *ac97,
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_ac97);
MODULE_DESCRIPTION("Register map access API - AC'97 support");
MODULE_LICENSE("GPL v2");

View File

@ -397,4 +397,5 @@ struct regmap *__devm_regmap_init_i2c(struct i2c_client *i2c,
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_i2c);
MODULE_DESCRIPTION("Register map access API - I2C support");
MODULE_LICENSE("GPL");

View File

@ -305,8 +305,8 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
unsigned int b)
{
const struct regmap_irq_chip *chip = data->chip;
const struct regmap_irq_sub_irq_map *subreg;
struct regmap *map = data->map;
struct regmap_irq_sub_irq_map *subreg;
unsigned int reg;
int i, ret = 0;

View File

@ -145,9 +145,9 @@ static struct regmap *gen_regmap(struct kunit *test,
const struct regmap_test_param *param = test->param_value;
struct regmap_test_priv *priv = test->priv;
unsigned int *buf;
struct regmap *ret;
struct regmap *ret = ERR_PTR(-ENOMEM);
size_t size;
int i;
int i, error;
struct reg_default *defaults;
config->cache_type = param->cache;
@ -163,7 +163,7 @@ static struct regmap *gen_regmap(struct kunit *test,
config->max_register += (BLOCK_TEST_SIZE * config->reg_stride);
}
size = (config->max_register + 1) * sizeof(unsigned int);
size = array_size(config->max_register + 1, sizeof(*buf));
buf = kmalloc(size, GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
@ -172,15 +172,17 @@ static struct regmap *gen_regmap(struct kunit *test,
*data = kzalloc(sizeof(**data), GFP_KERNEL);
if (!(*data))
return ERR_PTR(-ENOMEM);
goto out_free;
(*data)->vals = buf;
if (config->num_reg_defaults) {
defaults = kcalloc(config->num_reg_defaults,
sizeof(struct reg_default),
GFP_KERNEL);
defaults = kunit_kcalloc(test,
config->num_reg_defaults,
sizeof(struct reg_default),
GFP_KERNEL);
if (!defaults)
return ERR_PTR(-ENOMEM);
goto out_free;
config->reg_defaults = defaults;
for (i = 0; i < config->num_reg_defaults; i++) {
@ -190,12 +192,19 @@ static struct regmap *gen_regmap(struct kunit *test,
}
ret = regmap_init_ram(priv->dev, config, *data);
if (IS_ERR(ret)) {
kfree(buf);
kfree(*data);
} else {
kunit_add_action(test, regmap_exit_action, ret);
}
if (IS_ERR(ret))
goto out_free;
/* This calls regmap_exit() on failure, which frees buf and *data */
error = kunit_add_action_or_reset(test, regmap_exit_action, ret);
if (error)
ret = ERR_PTR(error);
return ret;
out_free:
kfree(buf);
kfree(*data);
return ret;
}
@ -295,6 +304,77 @@ static void bulk_read(struct kunit *test)
KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]);
}
static void multi_write(struct kunit *test)
{
struct regmap *map;
struct regmap_config config;
struct regmap_ram_data *data;
struct reg_sequence sequence[BLOCK_TEST_SIZE];
unsigned int val[BLOCK_TEST_SIZE], rval[BLOCK_TEST_SIZE];
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));
/*
* Data written via the multi API can be read back with single
* reads.
*/
for (i = 0; i < BLOCK_TEST_SIZE; i++) {
sequence[i].reg = i;
sequence[i].def = val[i];
sequence[i].delay_us = 0;
}
KUNIT_EXPECT_EQ(test, 0,
regmap_multi_reg_write(map, sequence, BLOCK_TEST_SIZE));
for (i = 0; i < BLOCK_TEST_SIZE; i++)
KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval[i]));
KUNIT_EXPECT_MEMEQ(test, val, rval, sizeof(val));
/* If using a cache the cache satisfied the read */
for (i = 0; i < BLOCK_TEST_SIZE; i++)
KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]);
}
static void multi_read(struct kunit *test)
{
struct regmap *map;
struct regmap_config config;
struct regmap_ram_data *data;
unsigned int regs[BLOCK_TEST_SIZE];
unsigned int val[BLOCK_TEST_SIZE], rval[BLOCK_TEST_SIZE];
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));
/* Data written as single writes can be read via the multi API */
for (i = 0; i < BLOCK_TEST_SIZE; i++) {
regs[i] = i;
KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, val[i]));
}
KUNIT_EXPECT_EQ(test, 0,
regmap_multi_reg_read(map, regs, rval, BLOCK_TEST_SIZE));
KUNIT_EXPECT_MEMEQ(test, val, rval, sizeof(val));
/* If using a cache the cache satisfied the read */
for (i = 0; i < BLOCK_TEST_SIZE; i++)
KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]);
}
static void read_bypassed(struct kunit *test)
{
const struct regmap_test_param *param = test->param_value;
@ -759,10 +839,9 @@ static void stress_insert(struct kunit *test)
if (IS_ERR(map))
return;
vals = kunit_kcalloc(test, sizeof(unsigned long), config.max_register,
GFP_KERNEL);
buf_sz = array_size(sizeof(*vals), config.max_register);
vals = kunit_kmalloc(test, buf_sz, GFP_KERNEL);
KUNIT_ASSERT_FALSE(test, vals == NULL);
buf_sz = sizeof(unsigned long) * config.max_register;
get_random_bytes(vals, buf_sz);
@ -1497,16 +1576,17 @@ static struct regmap *gen_raw_regmap(struct kunit *test,
struct regmap_test_priv *priv = test->priv;
const struct regmap_test_param *param = test->param_value;
u16 *buf;
struct regmap *ret;
size_t size = (config->max_register + 1) * config->reg_bits / 8;
int i;
struct regmap *ret = ERR_PTR(-ENOMEM);
int i, error;
struct reg_default *defaults;
size_t size;
config->cache_type = param->cache;
config->val_format_endian = param->val_endian;
config->disable_locking = config->cache_type == REGCACHE_RBTREE ||
config->cache_type == REGCACHE_MAPLE;
size = array_size(config->max_register + 1, BITS_TO_BYTES(config->reg_bits));
buf = kmalloc(size, GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
@ -1515,15 +1595,16 @@ static struct regmap *gen_raw_regmap(struct kunit *test,
*data = kzalloc(sizeof(**data), GFP_KERNEL);
if (!(*data))
return ERR_PTR(-ENOMEM);
goto out_free;
(*data)->vals = (void *)buf;
config->num_reg_defaults = config->max_register + 1;
defaults = kcalloc(config->num_reg_defaults,
sizeof(struct reg_default),
GFP_KERNEL);
defaults = kunit_kcalloc(test,
config->num_reg_defaults,
sizeof(struct reg_default),
GFP_KERNEL);
if (!defaults)
return ERR_PTR(-ENOMEM);
goto out_free;
config->reg_defaults = defaults;
for (i = 0; i < config->num_reg_defaults; i++) {
@ -1536,7 +1617,8 @@ static struct regmap *gen_raw_regmap(struct kunit *test,
defaults[i].def = be16_to_cpu(buf[i]);
break;
default:
return ERR_PTR(-EINVAL);
ret = ERR_PTR(-EINVAL);
goto out_free;
}
}
@ -1548,12 +1630,19 @@ static struct regmap *gen_raw_regmap(struct kunit *test,
config->num_reg_defaults = 0;
ret = regmap_init_raw_ram(priv->dev, config, *data);
if (IS_ERR(ret)) {
kfree(buf);
kfree(*data);
} else {
kunit_add_action(test, regmap_exit_action, ret);
}
if (IS_ERR(ret))
goto out_free;
/* This calls regmap_exit() on failure, which frees buf and *data */
error = kunit_add_action_or_reset(test, regmap_exit_action, ret);
if (error)
ret = ERR_PTR(error);
return ret;
out_free:
kfree(buf);
kfree(*data);
return ret;
}
@ -1597,7 +1686,7 @@ static void raw_read_defaults(struct kunit *test)
if (IS_ERR(map))
return;
val_len = sizeof(*rval) * (config.max_register + 1);
val_len = array_size(sizeof(*rval), config.max_register + 1);
rval = kunit_kmalloc(test, val_len, GFP_KERNEL);
KUNIT_ASSERT_TRUE(test, rval != NULL);
if (!rval)
@ -1887,6 +1976,8 @@ static struct kunit_case regmap_test_cases[] = {
KUNIT_CASE_PARAM(read_bypassed_volatile, real_cache_types_gen_params),
KUNIT_CASE_PARAM(bulk_write, regcache_types_gen_params),
KUNIT_CASE_PARAM(bulk_read, regcache_types_gen_params),
KUNIT_CASE_PARAM(multi_write, regcache_types_gen_params),
KUNIT_CASE_PARAM(multi_read, regcache_types_gen_params),
KUNIT_CASE_PARAM(write_readonly, regcache_types_gen_params),
KUNIT_CASE_PARAM(read_writeonly, regcache_types_gen_params),
KUNIT_CASE_PARAM(reg_defaults, regcache_types_gen_params),
@ -1958,4 +2049,5 @@ static struct kunit_suite regmap_test_suite = {
};
kunit_test_suite(regmap_test_suite);
MODULE_DESCRIPTION("Regmap KUnit tests");
MODULE_LICENSE("GPL v2");

View File

@ -83,4 +83,5 @@ struct regmap *__regmap_init_ram(struct device *dev,
}
EXPORT_SYMBOL_GPL(__regmap_init_ram);
MODULE_DESCRIPTION("Register map access API - Memory region");
MODULE_LICENSE("GPL v2");

View File

@ -142,4 +142,5 @@ struct regmap *__regmap_init_raw_ram(struct device *dev,
}
EXPORT_SYMBOL_GPL(__regmap_init_raw_ram);
MODULE_DESCRIPTION("Register map access API - Memory region with raw access");
MODULE_LICENSE("GPL v2");

View File

@ -125,4 +125,5 @@ struct regmap *__devm_regmap_init_sccb(struct i2c_client *i2c,
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_sccb);
MODULE_DESCRIPTION("Register map access API - SCCB support");
MODULE_LICENSE("GPL v2");

View File

@ -68,4 +68,5 @@ struct regmap *__devm_regmap_init_slimbus(struct slim_device *slimbus,
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_slimbus);
MODULE_DESCRIPTION("Register map access API - SLIMbus support");
MODULE_LICENSE("GPL v2");

View File

@ -710,4 +710,5 @@ struct regmap *__devm_regmap_init_spi_avmm(struct spi_device *spi,
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_spi_avmm);
MODULE_DESCRIPTION("Register map access API - SPI AVMM support");
MODULE_LICENSE("GPL v2");

View File

@ -222,4 +222,5 @@ struct regmap *__devm_regmap_init_spmi_ext(struct spmi_device *sdev,
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_ext);
MODULE_DESCRIPTION("Register map access API - SPMI support");
MODULE_LICENSE("GPL");

View File

@ -234,4 +234,5 @@ struct regmap *__devm_regmap_init_w1(struct device *w1_dev,
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_w1);
MODULE_DESCRIPTION("Register map access API - W1 (1-Wire) support");
MODULE_LICENSE("GPL");

View File

@ -2347,7 +2347,7 @@ out:
} else {
void *wval;
wval = kmemdup(val, val_count * val_bytes, map->alloc_flags);
wval = kmemdup_array(val, val_count, val_bytes, map->alloc_flags);
if (!wval)
return -ENOMEM;
@ -3101,8 +3101,53 @@ int regmap_fields_read(struct regmap_field *field, unsigned int id,
}
EXPORT_SYMBOL_GPL(regmap_fields_read);
static int _regmap_bulk_read(struct regmap *map, unsigned int reg,
unsigned int *regs, void *val, size_t val_count)
{
u32 *u32 = val;
u16 *u16 = val;
u8 *u8 = val;
int ret, i;
map->lock(map->lock_arg);
for (i = 0; i < val_count; i++) {
unsigned int ival;
if (regs) {
if (!IS_ALIGNED(regs[i], map->reg_stride)) {
ret = -EINVAL;
goto out;
}
ret = _regmap_read(map, regs[i], &ival);
} else {
ret = _regmap_read(map, reg + regmap_get_offset(map, i), &ival);
}
if (ret != 0)
goto out;
switch (map->format.val_bytes) {
case 4:
u32[i] = ival;
break;
case 2:
u16[i] = ival;
break;
case 1:
u8[i] = ival;
break;
default:
ret = -EINVAL;
goto out;
}
}
out:
map->unlock(map->lock_arg);
return ret;
}
/**
* regmap_bulk_read() - Read multiple registers from the device
* regmap_bulk_read() - Read multiple sequential registers from the device
*
* @map: Register map to read from
* @reg: First register to be read from
@ -3132,47 +3177,35 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
for (i = 0; i < val_count * val_bytes; i += val_bytes)
map->format.parse_inplace(val + i);
} else {
u32 *u32 = val;
u16 *u16 = val;
u8 *u8 = val;
map->lock(map->lock_arg);
for (i = 0; i < val_count; i++) {
unsigned int ival;
ret = _regmap_read(map, reg + regmap_get_offset(map, i),
&ival);
if (ret != 0)
goto out;
switch (map->format.val_bytes) {
case 4:
u32[i] = ival;
break;
case 2:
u16[i] = ival;
break;
case 1:
u8[i] = ival;
break;
default:
ret = -EINVAL;
goto out;
}
}
out:
map->unlock(map->lock_arg);
ret = _regmap_bulk_read(map, reg, NULL, val, val_count);
}
if (!ret)
trace_regmap_bulk_read(map, reg, val, val_bytes * val_count);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_bulk_read);
/**
* regmap_multi_reg_read() - Read multiple non-sequential registers from the device
*
* @map: Register map to read from
* @regs: Array of registers to read from
* @val: Pointer to store read value, in native register size for device
* @val_count: Number of registers to read
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_multi_reg_read(struct regmap *map, unsigned int *regs, void *val,
size_t val_count)
{
if (val_count == 0)
return -EINVAL;
return _regmap_bulk_read(map, 0, regs, val, val_count);
}
EXPORT_SYMBOL_GPL(regmap_multi_reg_read);
static int _regmap_update_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change, bool force_write)

View File

@ -1237,6 +1237,8 @@ int regmap_noinc_read(struct regmap *map, unsigned int reg,
void *val, size_t val_len);
int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
size_t val_count);
int regmap_multi_reg_read(struct regmap *map, unsigned int *reg, void *val,
size_t val_count);
int regmap_update_bits_base(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change, bool async, bool force);
@ -1607,7 +1609,7 @@ struct regmap_irq_chip {
unsigned int main_status;
unsigned int num_main_status_bits;
struct regmap_irq_sub_irq_map *sub_reg_offsets;
const struct regmap_irq_sub_irq_map *sub_reg_offsets;
int num_main_regs;
unsigned int status_base;

View File

@ -64,7 +64,17 @@ platform_suspend_ops
proc_ops
proto_ops
pwm_ops
reg_default
reg_field
reg_sequence
regmap_access_table
regmap_bus
regmap_config
regmap_irq
regmap_irq_chip
regmap_irq_sub_irq_map
regmap_range
regmap_range_cfg
regulator_ops
reset_control_ops
rpc_pipe_ops