2222 lines · 65.3 KB · C
RawDownload
// 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.