From: Mark Brown Date: Sun, 11 Jun 2023 13:25:03 +0000 (+0100) Subject: regmap: Provide basic KUnit coverage for the raw register I/O X-Git-Tag: v6.6.7~2545^2~5^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=155a6bd6375b584c8bdbf963b8ddef672ff9aca3;p=platform%2Fkernel%2Flinux-starfive.git regmap: Provide basic KUnit coverage for the raw register I/O Simple tests that cover basic raw I/O, plus basic coverage of cache sync since the caches generate bulk I/O with raw register maps. This could be more comprehensive but it is good for testing generic code. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20230610-regcache-raw-kunit-v1-2-583112cd28ac@kernel.org Signed-off-by: Mark Brown --- diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regmap-kunit.c index f76d416..4ec5cbf 100644 --- a/drivers/base/regmap/regmap-kunit.c +++ b/drivers/base/regmap/regmap-kunit.c @@ -712,6 +712,327 @@ static void cache_drop(struct kunit *test) regmap_exit(map); } +struct raw_test_types { + const char *name; + + enum regcache_type cache_type; + enum regmap_endian val_endian; +}; + +static void raw_to_desc(const struct raw_test_types *t, char *desc) +{ + strcpy(desc, t->name); +} + +static const struct raw_test_types raw_types_list[] = { + { "none-little", REGCACHE_NONE, REGMAP_ENDIAN_LITTLE }, + { "none-big", REGCACHE_NONE, REGMAP_ENDIAN_BIG }, + { "flat-little", REGCACHE_FLAT, REGMAP_ENDIAN_LITTLE }, + { "flat-big", REGCACHE_FLAT, REGMAP_ENDIAN_BIG }, + { "rbtree-little", REGCACHE_RBTREE, REGMAP_ENDIAN_LITTLE }, + { "rbtree-big", REGCACHE_RBTREE, REGMAP_ENDIAN_BIG }, + { "maple-little", REGCACHE_MAPLE, REGMAP_ENDIAN_LITTLE }, + { "maple-big", REGCACHE_MAPLE, REGMAP_ENDIAN_BIG }, +}; + +KUNIT_ARRAY_PARAM(raw_test_types, raw_types_list, raw_to_desc); + +static const struct raw_test_types raw_cache_types_list[] = { + { "flat-little", REGCACHE_FLAT, REGMAP_ENDIAN_LITTLE }, + { "flat-big", REGCACHE_FLAT, REGMAP_ENDIAN_BIG }, + { "rbtree-little", REGCACHE_RBTREE, REGMAP_ENDIAN_LITTLE }, + { "rbtree-big", REGCACHE_RBTREE, REGMAP_ENDIAN_BIG }, + { "maple-little", REGCACHE_MAPLE, REGMAP_ENDIAN_LITTLE }, + { "maple-big", REGCACHE_MAPLE, REGMAP_ENDIAN_BIG }, +}; + +KUNIT_ARRAY_PARAM(raw_test_cache_types, raw_cache_types_list, raw_to_desc); + +static const struct regmap_config raw_regmap_config = { + .max_register = BLOCK_TEST_SIZE, + + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .reg_bits = 16, + .val_bits = 16, +}; + +static struct regmap *gen_raw_regmap(struct regmap_config *config, + struct raw_test_types *test_type, + struct regmap_ram_data **data) +{ + u16 *buf; + struct regmap *ret; + size_t size = (config->max_register + 1) * config->reg_bits / 8; + int i; + struct reg_default *defaults; + + config->cache_type = test_type->cache_type; + config->val_format_endian = test_type->val_endian; + + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + get_random_bytes(buf, size); + + *data = kzalloc(sizeof(**data), GFP_KERNEL); + if (!(*data)) + return ERR_PTR(-ENOMEM); + (*data)->vals = (void *)buf; + + config->num_reg_defaults = config->max_register + 1; + defaults = kcalloc(config->num_reg_defaults, + sizeof(struct reg_default), + GFP_KERNEL); + if (!defaults) + return ERR_PTR(-ENOMEM); + config->reg_defaults = defaults; + + for (i = 0; i < config->num_reg_defaults; i++) { + defaults[i].reg = i; + switch (test_type->val_endian) { + case REGMAP_ENDIAN_LITTLE: + defaults[i].def = le16_to_cpu(buf[i]); + break; + case REGMAP_ENDIAN_BIG: + defaults[i].def = be16_to_cpu(buf[i]); + break; + default: + return ERR_PTR(-EINVAL); + } + } + + /* + * We use the defaults in the tests but they don't make sense + * to the core if there's no cache. + */ + if (config->cache_type == REGCACHE_NONE) + config->num_reg_defaults = 0; + + ret = regmap_init_raw_ram(config, *data); + if (IS_ERR(ret)) { + kfree(buf); + kfree(*data); + } + + return ret; +} + +static void raw_read_defaults_single(struct kunit *test) +{ + struct raw_test_types *t = (struct raw_test_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int rval; + int i; + + config = raw_regmap_config; + + map = gen_raw_regmap(&config, t, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + /* Check that we can read the defaults via the API */ + for (i = 0; i < config.max_register + 1; i++) { + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval)); + KUNIT_EXPECT_EQ(test, config.reg_defaults[i].def, rval); + } + + regmap_exit(map); +} + +static void raw_read_defaults(struct kunit *test) +{ + struct raw_test_types *t = (struct raw_test_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + u16 *rval; + u16 def; + size_t val_len; + int i; + + config = raw_regmap_config; + + map = gen_raw_regmap(&config, t, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + val_len = sizeof(*rval) * (config.max_register + 1); + rval = kmalloc(val_len, GFP_KERNEL); + KUNIT_ASSERT_TRUE(test, rval != NULL); + if (!rval) + return; + + /* Check that we can read the defaults via the API */ + KUNIT_EXPECT_EQ(test, 0, regmap_raw_read(map, 0, rval, val_len)); + for (i = 0; i < config.max_register + 1; i++) { + def = config.reg_defaults[i].def; + if (config.val_format_endian == REGMAP_ENDIAN_BIG) { + KUNIT_EXPECT_EQ(test, def, be16_to_cpu(rval[i])); + } else { + KUNIT_EXPECT_EQ(test, def, le16_to_cpu(rval[i])); + } + } + + kfree(rval); + regmap_exit(map); +} + +static void raw_write_read_single(struct kunit *test) +{ + struct raw_test_types *t = (struct raw_test_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + u16 val; + unsigned int rval; + + config = raw_regmap_config; + + map = gen_raw_regmap(&config, t, &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); + + regmap_exit(map); +} + +static void raw_write(struct kunit *test) +{ + struct raw_test_types *t = (struct raw_test_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + u16 *hw_buf; + u16 val[2]; + unsigned int rval; + int i; + + config = raw_regmap_config; + + map = gen_raw_regmap(&config, t, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + hw_buf = (u16 *)data->vals; + + get_random_bytes(&val, sizeof(val)); + + /* Do a raw write */ + KUNIT_EXPECT_EQ(test, 0, regmap_raw_write(map, 2, val, sizeof(val))); + + /* We should read back the new values, and defaults for the rest */ + for (i = 0; i < config.max_register + 1; i++) { + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval)); + + switch (i) { + case 2: + case 3: + if (config.val_format_endian == REGMAP_ENDIAN_BIG) { + KUNIT_EXPECT_EQ(test, rval, + be16_to_cpu(val[i % 2])); + } else { + KUNIT_EXPECT_EQ(test, rval, + le16_to_cpu(val[i % 2])); + } + break; + default: + KUNIT_EXPECT_EQ(test, config.reg_defaults[i].def, rval); + break; + } + } + + /* The values should appear in the "hardware" */ + KUNIT_EXPECT_MEMEQ(test, &hw_buf[2], val, sizeof(val)); + + regmap_exit(map); +} + +static void raw_sync(struct kunit *test) +{ + struct raw_test_types *t = (struct raw_test_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + u16 val[2]; + u16 *hw_buf; + unsigned int rval; + int i; + + config = raw_regmap_config; + + map = gen_raw_regmap(&config, t, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + hw_buf = (u16 *)data->vals; + + get_random_bytes(&val, sizeof(val)); + + /* Do a regular write and a raw write in cache only mode */ + regcache_cache_only(map, true); + KUNIT_EXPECT_EQ(test, 0, regmap_raw_write(map, 2, val, sizeof(val))); + if (config.val_format_endian == REGMAP_ENDIAN_BIG) + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 6, + be16_to_cpu(val[0]))); + else + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 6, + le16_to_cpu(val[0]))); + + /* We should read back the new values, and defaults for the rest */ + for (i = 0; i < config.max_register + 1; i++) { + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval)); + + switch (i) { + case 2: + case 3: + case 6: + if (config.val_format_endian == REGMAP_ENDIAN_BIG) { + KUNIT_EXPECT_EQ(test, rval, + be16_to_cpu(val[i % 2])); + } else { + KUNIT_EXPECT_EQ(test, rval, + le16_to_cpu(val[i % 2])); + } + break; + default: + KUNIT_EXPECT_EQ(test, config.reg_defaults[i].def, rval); + break; + } + } + + /* The values should not appear in the "hardware" */ + KUNIT_EXPECT_MEMNEQ(test, &hw_buf[2], val, sizeof(val)); + KUNIT_EXPECT_MEMNEQ(test, &hw_buf[6], val, sizeof(u16)); + + for (i = 0; i < config.max_register + 1; i++) + data->written[i] = false; + + /* Do the sync */ + regcache_cache_only(map, false); + regcache_mark_dirty(map); + KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); + + /* The values should now appear in the "hardware" */ + KUNIT_EXPECT_MEMEQ(test, &hw_buf[2], val, sizeof(val)); + KUNIT_EXPECT_MEMEQ(test, &hw_buf[6], val, sizeof(u16)); + + regmap_exit(map); +} + static struct kunit_case regmap_test_cases[] = { KUNIT_CASE_PARAM(basic_read_write, regcache_types_gen_params), KUNIT_CASE_PARAM(bulk_write, regcache_types_gen_params), @@ -727,6 +1048,12 @@ static struct kunit_case regmap_test_cases[] = { KUNIT_CASE_PARAM(cache_sync_defaults, real_cache_types_gen_params), KUNIT_CASE_PARAM(cache_sync_patch, real_cache_types_gen_params), KUNIT_CASE_PARAM(cache_drop, sparse_cache_types_gen_params), + + KUNIT_CASE_PARAM(raw_read_defaults_single, raw_test_types_gen_params), + KUNIT_CASE_PARAM(raw_read_defaults, raw_test_types_gen_params), + KUNIT_CASE_PARAM(raw_write_read_single, raw_test_types_gen_params), + KUNIT_CASE_PARAM(raw_write, raw_test_types_gen_params), + KUNIT_CASE_PARAM(raw_sync, raw_test_cache_types_gen_params), {} };