// SPDX-License-Identifier: GPL-2.0//// regmap KUnit tests//// Copyright 2023 Arm Ltd #include <kunit/device.h>#include <kunit/resource.h>#include <kunit/test.h>#include "internal.h" #define BLOCK_TEST_SIZE 12 KUNIT_DEFINE_ACTION_WRAPPER(regmap_exit_action, regmap_exit, struct regmap *); struct regmap_test_priv { struct device *dev; bool *reg_default_called; unsigned int reg_default_max;}; struct regmap_test_param { enum regcache_type cache; enum regmap_endian val_endian; unsigned int from_reg; bool fast_io;}; static void get_changed_bytes(void *orig, void *new, size_t size){ char *o = orig; char *n = new; int i; get_random_bytes(new, size); /* * This could be nicer and more efficient but we shouldn't * super care. */ for (i = 0; i < size; i++) while (n[i] == o[i]) get_random_bytes(&n[i], 1);} static const struct regmap_config test_regmap_config = { .reg_stride = 1, .val_bits = sizeof(unsigned int) * 8,}; static const char *regcache_type_name(enum regcache_type type){ switch (type) { case REGCACHE_NONE: return "none"; case REGCACHE_FLAT: return "flat"; case REGCACHE_FLAT_S: return "flat-sparse"; case REGCACHE_RBTREE: return "rbtree"; case REGCACHE_MAPLE: return "maple"; default: return NULL; }} static const char *regmap_endian_name(enum regmap_endian endian){ switch (endian) { case REGMAP_ENDIAN_BIG: return "big"; case REGMAP_ENDIAN_LITTLE: return "little"; case REGMAP_ENDIAN_DEFAULT: return "default"; case REGMAP_ENDIAN_NATIVE: return "native"; default: return NULL; }} static void param_to_desc(const struct regmap_test_param *param, char *desc){ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s-%s%s @%#x", regcache_type_name(param->cache), regmap_endian_name(param->val_endian), param->fast_io ? " fast I/O" : "", param->from_reg);} static const struct regmap_test_param regcache_types_list[] = { { .cache = REGCACHE_NONE }, { .cache = REGCACHE_NONE, .fast_io = true }, { .cache = REGCACHE_FLAT }, { .cache = REGCACHE_FLAT, .fast_io = true }, { .cache = REGCACHE_FLAT_S }, { .cache = REGCACHE_FLAT_S, .fast_io = true }, { .cache = REGCACHE_RBTREE }, { .cache = REGCACHE_RBTREE, .fast_io = true }, { .cache = REGCACHE_MAPLE }, { .cache = REGCACHE_MAPLE, .fast_io = true },}; KUNIT_ARRAY_PARAM(regcache_types, regcache_types_list, param_to_desc); static const struct regmap_test_param real_cache_types_only_list[] = { { .cache = REGCACHE_FLAT }, { .cache = REGCACHE_FLAT, .fast_io = true }, { .cache = REGCACHE_FLAT_S }, { .cache = REGCACHE_FLAT_S, .fast_io = true }, { .cache = REGCACHE_RBTREE }, { .cache = REGCACHE_RBTREE, .fast_io = true }, { .cache = REGCACHE_MAPLE }, { .cache = REGCACHE_MAPLE, .fast_io = true },}; KUNIT_ARRAY_PARAM(real_cache_types_only, real_cache_types_only_list, param_to_desc); static const struct regmap_test_param flat_cache_types_list[] = { { .cache = REGCACHE_FLAT, .from_reg = 0 }, { .cache = REGCACHE_FLAT, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_FLAT, .from_reg = 0x2001 },}; KUNIT_ARRAY_PARAM(flat_cache_types, flat_cache_types_list, param_to_desc); static const struct regmap_test_param real_cache_types_list[] = { { .cache = REGCACHE_FLAT, .from_reg = 0 }, { .cache = REGCACHE_FLAT, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_FLAT, .from_reg = 0x2001 }, { .cache = REGCACHE_FLAT, .from_reg = 0x2002 }, { .cache = REGCACHE_FLAT, .from_reg = 0x2003 }, { .cache = REGCACHE_FLAT, .from_reg = 0x2004 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_FLAT_S, .from_reg = 0x2001 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0x2002 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0x2003 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0x2004 }, { .cache = REGCACHE_RBTREE, .from_reg = 0 }, { .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2001 }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2002 }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2003 }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2004 }, { .cache = REGCACHE_MAPLE, .from_reg = 0 }, { .cache = REGCACHE_MAPLE, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2001 }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2002 }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2003 }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2004 },}; KUNIT_ARRAY_PARAM(real_cache_types, real_cache_types_list, param_to_desc); static const struct regmap_test_param sparse_cache_types_list[] = { { .cache = REGCACHE_FLAT_S, .from_reg = 0 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_FLAT_S, .from_reg = 0x2001 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0x2002 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0x2003 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0x2004 }, { .cache = REGCACHE_RBTREE, .from_reg = 0 }, { .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2001 }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2002 }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2003 }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2004 }, { .cache = REGCACHE_MAPLE, .from_reg = 0 }, { .cache = REGCACHE_MAPLE, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2001 }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2002 }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2003 }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2004 },}; KUNIT_ARRAY_PARAM(sparse_cache_types, sparse_cache_types_list, param_to_desc); static struct regmap *gen_regmap(struct kunit *test, struct regmap_config *config, struct regmap_ram_data **data){ const struct regmap_test_param *param = test->param_value; struct regmap_test_priv *priv = test->priv; unsigned int *buf; struct regmap *ret = ERR_PTR(-ENOMEM); size_t size; int i, error; struct reg_default *defaults; config->cache_type = param->cache; config->fast_io = param->fast_io; if (config->max_register == 0) { config->max_register = param->from_reg; if (config->num_reg_defaults) config->max_register += (config->num_reg_defaults - 1) * config->reg_stride; else config->max_register += (BLOCK_TEST_SIZE * config->reg_stride); } size = array_size(config->max_register + 1, sizeof(*buf)); buf = kmalloc(size, GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM); get_random_bytes(buf, size); *data = kzalloc_obj(**data); if (!(*data)) goto out_free; (*data)->vals = buf; if (config->num_reg_defaults) { defaults = kunit_kcalloc(test, config->num_reg_defaults, sizeof(struct reg_default), GFP_KERNEL); if (!defaults) goto out_free; 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)]; } } ret = regmap_init_ram(priv->dev, config, *data); 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;} static bool reg_5_false(struct device *dev, unsigned int reg){ struct kunit *test = dev_get_drvdata(dev); const struct regmap_test_param *param = test->param_value; return reg != (param->from_reg + 5);} static unsigned int reg_default_expected(unsigned int reg){ return 0x5a5a0000 | (reg & 0xffff);} static int reg_default_test_cb(struct device *dev, unsigned int reg, unsigned int *def){ struct kunit *test = dev_get_drvdata(dev); struct regmap_test_priv *priv = test->priv; if (priv && priv->reg_default_called && reg <= priv->reg_default_max) priv->reg_default_called[reg] = true; *def = reg_default_expected(reg); return 0;} static void expect_reg_default_value(struct kunit *test, struct regmap *map, struct regmap_ram_data *data, struct regmap_test_priv *priv, unsigned int reg){ unsigned int val; KUNIT_EXPECT_TRUE(test, priv->reg_default_called[reg]); KUNIT_EXPECT_EQ(test, 0, regmap_read(map, reg, &val)); KUNIT_EXPECT_EQ(test, reg_default_expected(reg), val); KUNIT_EXPECT_FALSE(test, data->read[reg]);} static void basic_read_write(struct kunit *test){ struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; unsigned int val, rval; 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)); /* If we write a value to a register we can read it back */ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 0, val)); KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 0, &rval)); KUNIT_EXPECT_EQ(test, val, rval); /* If using a cache the cache satisfied the read */ KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[0]);} static void bulk_write(struct kunit *test){ struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; 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 bulk API can be read back with single * reads. */ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, 0, val, 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 bulk_read(struct kunit *test){ struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; 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 bulk API */ for (i = 0; i < BLOCK_TEST_SIZE; i++) KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, val[i])); KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, 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 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; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; 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; KUNIT_EXPECT_FALSE(test, map->cache_bypass); get_random_bytes(&val, sizeof(val)); /* Write some test values */ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, param->from_reg, val, ARRAY_SIZE(val))); regcache_cache_only(map, true); /* * While in cache-only regmap_read_bypassed() should return the register * value and leave the map in cache-only. */ for (i = 0; i < ARRAY_SIZE(val); i++) { /* Put inverted bits in rval to prove we really read the value */ rval = ~val[i]; KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i, &rval)); KUNIT_EXPECT_EQ(test, val[i], rval); rval = ~val[i]; KUNIT_EXPECT_EQ(test, 0, regmap_read_bypassed(map, param->from_reg + i, &rval)); KUNIT_EXPECT_EQ(test, val[i], rval); KUNIT_EXPECT_TRUE(test, map->cache_only); KUNIT_EXPECT_FALSE(test, map->cache_bypass); } /* * Change the underlying register values to prove it is returning * real values not cached values. */ for (i = 0; i < ARRAY_SIZE(val); i++) { val[i] = ~val[i]; data->vals[param->from_reg + i] = val[i]; } Lines 1–500 of 2222. Load more
// SPDX-License-Identifier: GPL-2.0 // // regmap KUnit tests // // Copyright 2023 Arm Ltd #include <kunit/device.h> #include <kunit/resource.h> #include <kunit/test.h> #include "internal.h" #define BLOCK_TEST_SIZE 12 KUNIT_DEFINE_ACTION_WRAPPER(regmap_exit_action, regmap_exit, struct regmap *); struct regmap_test_priv { struct device *dev; bool *reg_default_called; unsigned int reg_default_max; }; struct regmap_test_param { enum regcache_type cache; enum regmap_endian val_endian; unsigned int from_reg; bool fast_io; }; static void get_changed_bytes(void *orig, void *new, size_t size) { char *o = orig; char *n = new; int i; get_random_bytes(new, size); /* * This could be nicer and more efficient but we shouldn't * super care. */ for (i = 0; i < size; i++) while (n[i] == o[i]) get_random_bytes(&n[i], 1); } static const struct regmap_config test_regmap_config = { .reg_stride = 1, .val_bits = sizeof(unsigned int) * 8, }; static const char *regcache_type_name(enum regcache_type type) { switch (type) { case REGCACHE_NONE: return "none"; case REGCACHE_FLAT: return "flat"; case REGCACHE_FLAT_S: return "flat-sparse"; case REGCACHE_RBTREE: return "rbtree"; case REGCACHE_MAPLE: return "maple"; default: return NULL; } } static const char *regmap_endian_name(enum regmap_endian endian) { switch (endian) { case REGMAP_ENDIAN_BIG: return "big"; case REGMAP_ENDIAN_LITTLE: return "little"; case REGMAP_ENDIAN_DEFAULT: return "default"; case REGMAP_ENDIAN_NATIVE: return "native"; default: return NULL; } } static void param_to_desc(const struct regmap_test_param *param, char *desc) { snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s-%s%s @%#x", regcache_type_name(param->cache), regmap_endian_name(param->val_endian), param->fast_io ? " fast I/O" : "", param->from_reg); } static const struct regmap_test_param regcache_types_list[] = { { .cache = REGCACHE_NONE }, { .cache = REGCACHE_NONE, .fast_io = true }, { .cache = REGCACHE_FLAT }, { .cache = REGCACHE_FLAT, .fast_io = true }, { .cache = REGCACHE_FLAT_S }, { .cache = REGCACHE_FLAT_S, .fast_io = true }, { .cache = REGCACHE_RBTREE }, { .cache = REGCACHE_RBTREE, .fast_io = true }, { .cache = REGCACHE_MAPLE }, { .cache = REGCACHE_MAPLE, .fast_io = true }, }; KUNIT_ARRAY_PARAM(regcache_types, regcache_types_list, param_to_desc); static const struct regmap_test_param real_cache_types_only_list[] = { { .cache = REGCACHE_FLAT }, { .cache = REGCACHE_FLAT, .fast_io = true }, { .cache = REGCACHE_FLAT_S }, { .cache = REGCACHE_FLAT_S, .fast_io = true }, { .cache = REGCACHE_RBTREE }, { .cache = REGCACHE_RBTREE, .fast_io = true }, { .cache = REGCACHE_MAPLE }, { .cache = REGCACHE_MAPLE, .fast_io = true }, }; KUNIT_ARRAY_PARAM(real_cache_types_only, real_cache_types_only_list, param_to_desc); static const struct regmap_test_param flat_cache_types_list[] = { { .cache = REGCACHE_FLAT, .from_reg = 0 }, { .cache = REGCACHE_FLAT, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_FLAT, .from_reg = 0x2001 }, }; KUNIT_ARRAY_PARAM(flat_cache_types, flat_cache_types_list, param_to_desc); static const struct regmap_test_param real_cache_types_list[] = { { .cache = REGCACHE_FLAT, .from_reg = 0 }, { .cache = REGCACHE_FLAT, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_FLAT, .from_reg = 0x2001 }, { .cache = REGCACHE_FLAT, .from_reg = 0x2002 }, { .cache = REGCACHE_FLAT, .from_reg = 0x2003 }, { .cache = REGCACHE_FLAT, .from_reg = 0x2004 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_FLAT_S, .from_reg = 0x2001 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0x2002 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0x2003 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0x2004 }, { .cache = REGCACHE_RBTREE, .from_reg = 0 }, { .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2001 }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2002 }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2003 }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2004 }, { .cache = REGCACHE_MAPLE, .from_reg = 0 }, { .cache = REGCACHE_MAPLE, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2001 }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2002 }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2003 }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2004 }, }; KUNIT_ARRAY_PARAM(real_cache_types, real_cache_types_list, param_to_desc); static const struct regmap_test_param sparse_cache_types_list[] = { { .cache = REGCACHE_FLAT_S, .from_reg = 0 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_FLAT_S, .from_reg = 0x2001 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0x2002 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0x2003 }, { .cache = REGCACHE_FLAT_S, .from_reg = 0x2004 }, { .cache = REGCACHE_RBTREE, .from_reg = 0 }, { .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2001 }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2002 }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2003 }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2004 }, { .cache = REGCACHE_MAPLE, .from_reg = 0 }, { .cache = REGCACHE_MAPLE, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2001 }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2002 }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2003 }, { .cache = REGCACHE_MAPLE, .from_reg = 0x2004 }, }; KUNIT_ARRAY_PARAM(sparse_cache_types, sparse_cache_types_list, param_to_desc); static struct regmap *gen_regmap(struct kunit *test, struct regmap_config *config, struct regmap_ram_data **data) { const struct regmap_test_param *param = test->param_value; struct regmap_test_priv *priv = test->priv; unsigned int *buf; struct regmap *ret = ERR_PTR(-ENOMEM); size_t size; int i, error; struct reg_default *defaults; config->cache_type = param->cache; config->fast_io = param->fast_io; if (config->max_register == 0) { config->max_register = param->from_reg; if (config->num_reg_defaults) config->max_register += (config->num_reg_defaults - 1) * config->reg_stride; else config->max_register += (BLOCK_TEST_SIZE * config->reg_stride); } size = array_size(config->max_register + 1, sizeof(*buf)); buf = kmalloc(size, GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM); get_random_bytes(buf, size); *data = kzalloc_obj(**data); if (!(*data)) goto out_free; (*data)->vals = buf; if (config->num_reg_defaults) { defaults = kunit_kcalloc(test, config->num_reg_defaults, sizeof(struct reg_default), GFP_KERNEL); if (!defaults) goto out_free; 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)]; } } ret = regmap_init_ram(priv->dev, config, *data); 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; } static bool reg_5_false(struct device *dev, unsigned int reg) { struct kunit *test = dev_get_drvdata(dev); const struct regmap_test_param *param = test->param_value; return reg != (param->from_reg + 5); } static unsigned int reg_default_expected(unsigned int reg) { return 0x5a5a0000 | (reg & 0xffff); } static int reg_default_test_cb(struct device *dev, unsigned int reg, unsigned int *def) { struct kunit *test = dev_get_drvdata(dev); struct regmap_test_priv *priv = test->priv; if (priv && priv->reg_default_called && reg <= priv->reg_default_max) priv->reg_default_called[reg] = true; *def = reg_default_expected(reg); return 0; } static void expect_reg_default_value(struct kunit *test, struct regmap *map, struct regmap_ram_data *data, struct regmap_test_priv *priv, unsigned int reg) { unsigned int val; KUNIT_EXPECT_TRUE(test, priv->reg_default_called[reg]); KUNIT_EXPECT_EQ(test, 0, regmap_read(map, reg, &val)); KUNIT_EXPECT_EQ(test, reg_default_expected(reg), val); KUNIT_EXPECT_FALSE(test, data->read[reg]); } static void basic_read_write(struct kunit *test) { struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; unsigned int val, rval; 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)); /* If we write a value to a register we can read it back */ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 0, val)); KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 0, &rval)); KUNIT_EXPECT_EQ(test, val, rval); /* If using a cache the cache satisfied the read */ KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[0]); } static void bulk_write(struct kunit *test) { struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; 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 bulk API can be read back with single * reads. */ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, 0, val, 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 bulk_read(struct kunit *test) { struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; 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 bulk API */ for (i = 0; i < BLOCK_TEST_SIZE; i++) KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, val[i])); KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, 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 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; struct regmap *map; struct regmap_config config; struct regmap_ram_data *data; 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; KUNIT_EXPECT_FALSE(test, map->cache_bypass); get_random_bytes(&val, sizeof(val)); /* Write some test values */ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, param->from_reg, val, ARRAY_SIZE(val))); regcache_cache_only(map, true); /* * While in cache-only regmap_read_bypassed() should return the register * value and leave the map in cache-only. */ for (i = 0; i < ARRAY_SIZE(val); i++) { /* Put inverted bits in rval to prove we really read the value */ rval = ~val[i]; KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i, &rval)); KUNIT_EXPECT_EQ(test, val[i], rval); rval = ~val[i]; KUNIT_EXPECT_EQ(test, 0, regmap_read_bypassed(map, param->from_reg + i, &rval)); KUNIT_EXPECT_EQ(test, val[i], rval); KUNIT_EXPECT_TRUE(test, map->cache_only); KUNIT_EXPECT_FALSE(test, map->cache_bypass); } /* * Change the underlying register values to prove it is returning * real values not cached values. */ for (i = 0; i < ARRAY_SIZE(val); i++) { val[i] = ~val[i]; data->vals[param->from_reg + i] = val[i]; }