diff --git a/drivers/base/regmap/regcache-maple.c b/drivers/base/regmap/regcache-maple.c index e42433404854..f0df2da6d522 100644 --- a/drivers/base/regmap/regcache-maple.c +++ b/drivers/base/regmap/regcache-maple.c @@ -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; diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 2e41cb12b8e2..7ec1ec605335 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -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); diff --git a/drivers/base/regmap/regmap-ac97.c b/drivers/base/regmap/regmap-ac97.c index b9f76bdf74a9..a561971c459c 100644 --- a/drivers/base/regmap/regmap-ac97.c +++ b/drivers/base/regmap/regmap-ac97.c @@ -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"); diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index a905e955bbfc..c9b39a02278e 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -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"); diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 45fd13ef13fc..d3ec1345b5b5 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -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; diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regmap-kunit.c index be32cd4e84da..d790c7df5cac 100644 --- a/drivers/base/regmap/regmap-kunit.c +++ b/drivers/base/regmap/regmap-kunit.c @@ -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"); diff --git a/drivers/base/regmap/regmap-ram.c b/drivers/base/regmap/regmap-ram.c index 5b4cbf982a11..4e5b4518ce4d 100644 --- a/drivers/base/regmap/regmap-ram.c +++ b/drivers/base/regmap/regmap-ram.c @@ -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"); diff --git a/drivers/base/regmap/regmap-raw-ram.c b/drivers/base/regmap/regmap-raw-ram.c index 69eabfb89eda..76c98814fb8a 100644 --- a/drivers/base/regmap/regmap-raw-ram.c +++ b/drivers/base/regmap/regmap-raw-ram.c @@ -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"); diff --git a/drivers/base/regmap/regmap-sccb.c b/drivers/base/regmap/regmap-sccb.c index 986af26d88c2..12bbbb03e5f2 100644 --- a/drivers/base/regmap/regmap-sccb.c +++ b/drivers/base/regmap/regmap-sccb.c @@ -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"); diff --git a/drivers/base/regmap/regmap-slimbus.c b/drivers/base/regmap/regmap-slimbus.c index 8075db788b39..54eb7d227cf4 100644 --- a/drivers/base/regmap/regmap-slimbus.c +++ b/drivers/base/regmap/regmap-slimbus.c @@ -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"); diff --git a/drivers/base/regmap/regmap-spi-avmm.c b/drivers/base/regmap/regmap-spi-avmm.c index 4c2b94b3e30b..d86a06cadcdb 100644 --- a/drivers/base/regmap/regmap-spi-avmm.c +++ b/drivers/base/regmap/regmap-spi-avmm.c @@ -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"); diff --git a/drivers/base/regmap/regmap-spmi.c b/drivers/base/regmap/regmap-spmi.c index cdf12d2aa3a1..347bfe9544ce 100644 --- a/drivers/base/regmap/regmap-spmi.c +++ b/drivers/base/regmap/regmap-spmi.c @@ -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"); diff --git a/drivers/base/regmap/regmap-w1.c b/drivers/base/regmap/regmap-w1.c index 3a8b402db852..29fd24f9c7ed 100644 --- a/drivers/base/regmap/regmap-w1.c +++ b/drivers/base/regmap/regmap-w1.c @@ -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"); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 0a34dd3c4f38..bfc6bc1eb3a4 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -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) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a6bc2980a98b..122e38161acb 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -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; diff --git a/scripts/const_structs.checkpatch b/scripts/const_structs.checkpatch index 52e5bfb61fd0..cc62980cfa6e 100644 --- a/scripts/const_structs.checkpatch +++ b/scripts/const_structs.checkpatch @@ -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