diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regmap-kunit.c index 2999d9c185d5..c78772b660d6 100644 --- a/drivers/base/regmap/regmap-kunit.c +++ b/drivers/base/regmap/regmap-kunit.c @@ -11,6 +11,8 @@ #define BLOCK_TEST_SIZE 12 +#define HIGH_BIT_REG ((UINT_MAX / 2) - 3) + KUNIT_DEFINE_ACTION_WRAPPER(regmap_exit_action, regmap_exit, struct regmap *); struct regmap_test_priv { @@ -23,6 +25,7 @@ struct regmap_test_param { enum regcache_type cache; enum regmap_endian val_endian; + unsigned int base_reg; unsigned int from_reg; bool fast_io; }; @@ -204,7 +207,8 @@ static struct regmap *gen_regmap(struct kunit *test, config->max_register += (BLOCK_TEST_SIZE * config->reg_stride); } - size = array_size(config->max_register + 1, sizeof(*buf)); + size = array_size(config->max_register - param->base_reg + 1, + sizeof(*buf)); buf = kmalloc(size, GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM); @@ -215,6 +219,7 @@ static struct regmap *gen_regmap(struct kunit *test, if (!(*data)) goto out_free; (*data)->vals = buf; + (*data)->base_reg = param->base_reg; if (config->num_reg_defaults) { defaults = kunit_kcalloc(test, @@ -227,8 +232,8 @@ static struct regmap *gen_regmap(struct kunit *test, 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)]; + defaults[i].reg = param->from_reg - param->base_reg + (i * config->reg_stride); + defaults[i].def = buf[param->from_reg - param->base_reg + (i * config->reg_stride)]; } } @@ -1648,6 +1653,92 @@ static void cache_write_zero(struct kunit *test) KUNIT_ASSERT_FALSE(test, regcache_reg_cached(map, 1)); } +static const struct regmap_test_param high_bit_cache_types_list[] = { + { .cache = REGCACHE_RBTREE, .from_reg = HIGH_BIT_REG, + .base_reg = HIGH_BIT_REG }, + { .cache = REGCACHE_MAPLE, .from_reg = HIGH_BIT_REG, + .base_reg = HIGH_BIT_REG }, +}; + +KUNIT_ARRAY_PARAM(high_bit_cache_types, high_bit_cache_types_list, param_to_desc); + +/* + * Dynamically allocated cache types should support registers with the + * high bit set, the backend will return an error for any operation on + * a register below the expected window. + */ +static void cache_high_bit_reg(struct kunit *test) +{ + const struct regmap_test_param *param = test->param_value; + struct regmap_ram_data *data; + struct regmap_config config; + struct regmap *map; + 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; + + get_random_bytes(&val, sizeof(val)); + + /* No defaults so no registers cached */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_FALSE(test, regcache_reg_cached(map, param->from_reg + i)); + + /* Writes should reach the device */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg + i, + val[i])); + for (i = 0; i < BLOCK_TEST_SIZE; i++) { + KUNIT_EXPECT_TRUE(test, data->written[i]); + KUNIT_EXPECT_EQ(test, val[i], data->vals[i]); + } + + /* Reads should be satisfied from the cache */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) { + KUNIT_EXPECT_TRUE(test, regcache_reg_cached(map, param->from_reg + i)); + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i, + &rval)); + KUNIT_EXPECT_EQ(test, val[i], rval); + KUNIT_EXPECT_FALSE(test, data->read[i]); + } + + /* Trash the data on the device then resync */ + regcache_mark_dirty(map); + for (i = 0; i < BLOCK_TEST_SIZE; i++) { + data->vals[i] = 0; + data->written[i] = false; + } + KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); + + /* Did we just write the correct data out? */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) { + KUNIT_EXPECT_TRUE(test, data->written[i]); + KUNIT_EXPECT_EQ(test, val[i], data->vals[i]); + } + + /* Drop some registers */ + KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, param->from_reg + 3, + param->from_reg + 5)); + for (i = 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, i < 3 || i > 5, + regcache_reg_cached(map, param->from_reg + i)); + + /* Reread and check only the dropped registers hit the device */ + for (i = 0; i < BLOCK_TEST_SIZE; i++) + data->read[i] = false; + for (i = 0; i < BLOCK_TEST_SIZE; i++) { + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i, + &rval)); + KUNIT_EXPECT_EQ(test, val[i], rval); + KUNIT_EXPECT_EQ(test, i >= 3 && i <= 5, data->read[i]); + } +} + /* Check that caching the window register works with sync */ static void cache_range_window_reg(struct kunit *test) { @@ -2168,6 +2259,7 @@ static struct kunit_case regmap_test_cases[] = { KUNIT_CASE_PARAM(cache_drop_all_and_sync_has_defaults, sparse_cache_types_gen_params), KUNIT_CASE_PARAM(cache_present, sparse_cache_types_gen_params), KUNIT_CASE_PARAM(cache_write_zero, sparse_cache_types_gen_params), + KUNIT_CASE_PARAM(cache_high_bit_reg, high_bit_cache_types_gen_params), KUNIT_CASE_PARAM(cache_range_window_reg, real_cache_types_only_gen_params), KUNIT_CASE_PARAM(raw_read_defaults_single, raw_test_types_gen_params),