From nobody Mon May 6 04:27:14 2024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 724B4C6FD1C for ; Sun, 26 Mar 2023 14:11:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232110AbjCZOLz (ORCPT ); Sun, 26 Mar 2023 10:11:55 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47640 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232153AbjCZOLt (ORCPT ); Sun, 26 Mar 2023 10:11:49 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 280575261; Sun, 26 Mar 2023 07:11:23 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id B1FFE60EE2; Sun, 26 Mar 2023 14:11:22 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id B97EAC4339B; Sun, 26 Mar 2023 14:11:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1679839882; bh=wyNzs9+T4TyTUveC2AG8JrzhOfbyVA0mVx0XwJQWyR8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=mOBWG4oR2HdfkPs+wysroZGZPHjfE43qPQxJRaRL1gc6aEc16v55HYY1tsNaappkO suJ4y0M1R3WvwlvZj5i/hH1F2jJZc7OjylVATjq+2Sd2IA0lm9CjqpDj3KkehdV3TJ x4nPnTF4dXCXCNzy1q4SBu+MMDSEg4DyAtYOD/ZAyVvJTohvwE8PAAJeYrl2Smnvwx sM6XkPnj+LnbSHOVD+LTFgKXn6InFSKURAV8vzaSr12frs5xhFMqwjhyj5tNW9O724 v24dsWdWNd3GJPBYdwrhJDzL7rsI5phmHzAJHZ8l+TcV7OudNN1sSUaQTUOaqh7UaW YDQ+spVq4fFJg== From: Mark Brown Date: Sun, 26 Mar 2023 15:11:12 +0100 Subject: [PATCH v2 1/2] regmap: Add RAM backed register map MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20230324-regmap-kunit-v2-1-b208801dc2c8@kernel.org> References: <20230324-regmap-kunit-v2-0-b208801dc2c8@kernel.org> In-Reply-To: <20230324-regmap-kunit-v2-0-b208801dc2c8@kernel.org> To: David Gow , Brendan Higgins Cc: linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, Mark Brown X-Mailer: b4 0.13-dev-2eb1a X-Developer-Signature: v=1; a=openpgp-sha256; l=4600; i=broonie@kernel.org; h=from:subject:message-id; bh=wyNzs9+T4TyTUveC2AG8JrzhOfbyVA0mVx0XwJQWyR8=; b=owEBbQGS/pANAwAKASTWi3JdVIfQAcsmYgBkIFKEfs8DakGv8m2gHvEgZHUGczL8LasApU+xSdde NhD8DBuJATMEAAEKAB0WIQSt5miqZ1cYtZ/in+ok1otyXVSH0AUCZCBShAAKCRAk1otyXVSH0NVTB/ 9079L6liiE2csSBm7kECRkk1ck//NL81FNsKMEgAWua/PkcJwPL1JyRLSW3jbMhmYG8CIuE+/S+EG/ IaVZ1N+NG21KExCMkbdUtNxr/AqCaoWkw67n8Kj1HYKmy6mE6kT80TDnQOazX2yRziDzXaol/+413Q APx5ylO5B6225xJv/XTZpPnmNBUTT8lGy9VrVq5o3ldX0IhYeeupJGFg9tJrGdWXhm1Hj4GeQ3+IRX qC2rPw3OrFDcEubpe8px+CEhz3g7NPzQx7bNw/DmrWtp6XuUcUgA05hEWvMBniQSfX1pqOS5gRrZb3 BscZ7pOXNiukdxAv1PLzC/masIVWwr X-Developer-Key: i=broonie@kernel.org; a=openpgp; fpr=3F2568AAC26998F9E813A1C5C3F436CA30F5D8EB Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add a register map that is a simple array of memory, for use in KUnit testing of the framework. This is not exposed in regmap.h since I can't think of a non-test use case, it is purely for use internally. To facilitate testing we track if registers have been read or written to. Signed-off-by: Mark Brown --- drivers/base/regmap/Kconfig | 3 ++ drivers/base/regmap/Makefile | 1 + drivers/base/regmap/internal.h | 19 +++++++++ drivers/base/regmap/regmap-ram.c | 85 ++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 108 insertions(+) diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index cd4bb642b9de..65ce888d7c04 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -46,6 +46,9 @@ config REGMAP_MMIO config REGMAP_IRQ bool =20 +config REGMAP_RAM + tristate + config REGMAP_SOUNDWIRE tristate depends on SOUNDWIRE diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index 6990de7ca9a9..5ef6f129497c 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_REGCACHE_COMPRESSED) +=3D regcache-lzo.o obj-$(CONFIG_DEBUG_FS) +=3D regmap-debugfs.o obj-$(CONFIG_REGMAP_AC97) +=3D regmap-ac97.o obj-$(CONFIG_REGMAP_I2C) +=3D regmap-i2c.o +obj-$(CONFIG_REGMAP_RAM) +=3D regmap-ram.o obj-$(CONFIG_REGMAP_SLIMBUS) +=3D regmap-slimbus.o obj-$(CONFIG_REGMAP_SPI) +=3D regmap-spi.o obj-$(CONFIG_REGMAP_SPMI) +=3D regmap-spmi.o diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index da8996e7a1f1..aa98a5284fb3 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -307,4 +307,23 @@ static inline unsigned int regcache_get_index_by_order= (const struct regmap *map, return reg >> map->reg_stride_order; } =20 +struct regmap_ram_data { + unsigned int *vals; /* Allocatd by caller */ + bool *read; + bool *written; +}; + +/* + * Create a test register map with data stored in RAM, not intended + * for practical use. + */ +struct regmap *__regmap_init_ram(const struct regmap_config *config, + struct regmap_ram_data *data, + struct lock_class_key *lock_key, + const char *lock_name); + +#define regmap_init_ram(config, data) \ + __regmap_lockdep_wrapper(__regmap_init_ram, #config, config, data) + + #endif diff --git a/drivers/base/regmap/regmap-ram.c b/drivers/base/regmap/regmap-= ram.c new file mode 100644 index 000000000000..85f34a5dee04 --- /dev/null +++ b/drivers/base/regmap/regmap-ram.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Register map access API - Memory region +// +// This is intended for testing only +// +// Copyright (c) 2023, Arm Ltd + +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +static int regmap_ram_write(void *context, unsigned int reg, unsigned int = val) +{ + struct regmap_ram_data *data =3D context; + + data->vals[reg] =3D val; + data->written[reg] =3D true; + + return 0; +} + +static int regmap_ram_read(void *context, unsigned int reg, unsigned int *= val) +{ + struct regmap_ram_data *data =3D context; + + *val =3D data->vals[reg]; + data->read[reg] =3D true; + + return 0; +} + +static void regmap_ram_free_context(void *context) +{ + struct regmap_ram_data *data =3D context; + + kfree(data->vals); + kfree(data->read); + kfree(data->written); + kfree(data); +} + +static const struct regmap_bus regmap_ram =3D { + .fast_io =3D true, + .reg_write =3D regmap_ram_write, + .reg_read =3D regmap_ram_read, + .free_context =3D regmap_ram_free_context, +}; + +struct regmap *__regmap_init_ram(const struct regmap_config *config, + struct regmap_ram_data *data, + struct lock_class_key *lock_key, + const char *lock_name) +{ + struct regmap *map; + + if (!config->max_register) { + pr_crit("No max_register specified for RAM regmap\n"); + return ERR_PTR(-EINVAL); + } + + data->read =3D kcalloc(sizeof(bool), config->max_register + 1, + GFP_KERNEL); + if (!data->read) + return ERR_PTR(-ENOMEM); + + data->written =3D kcalloc(sizeof(bool), config->max_register + 1, + GFP_KERNEL); + if (!data->written) + return ERR_PTR(-ENOMEM); + + map =3D __regmap_init(NULL, ®map_ram, data, config, + lock_key, lock_name); + + return map; +} +EXPORT_SYMBOL_GPL(__regmap_init_ram); + +MODULE_LICENSE("GPL v2"); --=20 2.34.1 From nobody Mon May 6 04:27:14 2024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0467DC6FD1C for ; Sun, 26 Mar 2023 14:12:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232245AbjCZOL7 (ORCPT ); Sun, 26 Mar 2023 10:11:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47140 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232152AbjCZOLw (ORCPT ); Sun, 26 Mar 2023 10:11:52 -0400 Received: from ams.source.kernel.org (ams.source.kernel.org [145.40.68.75]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E4EEA83E6; Sun, 26 Mar 2023 07:11:26 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id 7A287B80CA4; Sun, 26 Mar 2023 14:11:25 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9502BC433D2; Sun, 26 Mar 2023 14:11:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1679839884; bh=dIc8AwB8B4dHyw7iTyCt8sSZAE3CfczeXDK1RN/Ym/E=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=U+C4+IEbv1rgMibrvtrDxJfB8weHwvgGMqyPK7k6eGWx5AhS80C4penwzLnIzmAp3 W4B0gwF/JwK51Q9QldSJAsNxPKnfN+WJQhNikP53OQq607G4BZjzCqz7rjlRQ2HpKY zc12BftJgBwWFvuFzVAH02iVayoD7xuadzmzeqoul81vk18KMjL6NKsq4OahXbYLRt qmPASCHedxXl/yl7/mLadXTpJ/+B1nmxrwmoKSZd9P+AG3LzGprGYqlo0+XhPyyZ/2 roPstLWH1CMfJ3MGy8OQh+rIomDTxhgOVs5SNDXMsnD4CCKbhLFZ1qNXVE7LISMqzO WNK0GKcAYNkzg== From: Mark Brown Date: Sun, 26 Mar 2023 15:11:13 +0100 Subject: [PATCH v2 2/2] regmap: Add some basic kunit tests MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20230324-regmap-kunit-v2-2-b208801dc2c8@kernel.org> References: <20230324-regmap-kunit-v2-0-b208801dc2c8@kernel.org> In-Reply-To: <20230324-regmap-kunit-v2-0-b208801dc2c8@kernel.org> To: David Gow , Brendan Higgins Cc: linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, Mark Brown X-Mailer: b4 0.13-dev-2eb1a X-Developer-Signature: v=1; a=openpgp-sha256; l=24489; i=broonie@kernel.org; h=from:subject:message-id; bh=dIc8AwB8B4dHyw7iTyCt8sSZAE3CfczeXDK1RN/Ym/E=; b=owEBbQGS/pANAwAKASTWi3JdVIfQAcsmYgBkIFKES+2fUa7NFhdkZ2gxH7nJoUpFKBrfWEOSX4ql 8HHjdA6JATMEAAEKAB0WIQSt5miqZ1cYtZ/in+ok1otyXVSH0AUCZCBShAAKCRAk1otyXVSH0EAsB/ 0UmygSepc34CgZ1Ajza7kU9z4WZ1kdJLZILDwV+1pxydN1Ffd27WzfVAaBMGVHFKv1UOcNEWdfVqsY amQhNhaRqamkqWr6wlKrbnf22ocEivHCS/9vsrm1fmkUiihPsw0/kFGiG/IVVw5WuzeTYHGg75r7Wu a7vfZ8crEC52zjvfFoJ/RkhzRiNWvYVt7c5LRkMB9AroYkIlGpERsFuup7wqcOTpi7UqYSicIElSUf JstBrBDUMLxKhgQ46Qu7WqtqRSuVjrqDsHS/+IImwRGP0LgY/X+iatSdG9jb7fGKqJp6gONT9w44Ov UneUSBjP74wEKCg9c4TqfzjHTryE+g X-Developer-Key: i=broonie@kernel.org; a=openpgp; fpr=3F2568AAC26998F9E813A1C5C3F436CA30F5D8EB Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On the theory that it's better to make a start let's add some KUnit tests for regmap. Currently this is a bit of a mess but it passes and hopefully will at some point help catch problems. We provide very basic cover for most of the core functionality that operates at the register level, repeating each test for each cache type in order to exercise the caches. There is no coverage of anything to do with the bulk operations at the bus level or formatting for byte stream buses yet. Each test creates it's own regmap since the cache structures are built incrementally, meaning we gain coverage from the different access patterns, and some of the tests cover different init scenarios. Signed-off-by: Mark Brown --- drivers/base/regmap/Kconfig | 7 + drivers/base/regmap/Makefile | 1 + drivers/base/regmap/regmap-kunit.c | 736 +++++++++++++++++++++++++++++++++= ++++ 3 files changed, 744 insertions(+) diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index 65ce888d7c04..6e77bf96569c 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -14,6 +14,13 @@ config REGCACHE_COMPRESSED select LZO_DECOMPRESS bool =20 +config REGMAP_KUNIT + tristate "KUnit tests for regmap" + depends on KUNIT + default KUNIT_ALL_TESTS + select REGMAP + select REGMAP_RAM + config REGMAP_AC97 tristate =20 diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index 5ef6f129497c..fe2775fc76c6 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_REGMAP) +=3D regmap.o regcache.o obj-$(CONFIG_REGMAP) +=3D regcache-rbtree.o regcache-flat.o obj-$(CONFIG_REGCACHE_COMPRESSED) +=3D regcache-lzo.o obj-$(CONFIG_DEBUG_FS) +=3D regmap-debugfs.o +obj-$(CONFIG_REGMAP_KUNIT) +=3D regmap-kunit.o obj-$(CONFIG_REGMAP_AC97) +=3D regmap-ac97.o obj-$(CONFIG_REGMAP_I2C) +=3D regmap-i2c.o obj-$(CONFIG_REGMAP_RAM) +=3D regmap-ram.o diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regma= p-kunit.c new file mode 100644 index 000000000000..c78f45cf9a8d --- /dev/null +++ b/drivers/base/regmap/regmap-kunit.c @@ -0,0 +1,736 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// regmap KUnit tests +// +// Copyright 2023 Arm Ltd + +#include +#include "internal.h" + +#define BLOCK_TEST_SIZE 12 + +static const struct regmap_config test_regmap_config =3D { + .max_register =3D BLOCK_TEST_SIZE, + .reg_stride =3D 1, + .val_bits =3D sizeof(unsigned int) * 8, +}; + +struct regcache_types { + enum regcache_type type; + const char *name; +}; + +static void case_to_desc(const struct regcache_types *t, char *desc) +{ + strcpy(desc, t->name); +} + +static const struct regcache_types regcache_types_list[] =3D { + { REGCACHE_NONE, "none" }, + { REGCACHE_FLAT, "flat" }, + { REGCACHE_RBTREE, "rbtree" }, +}; + +KUNIT_ARRAY_PARAM(regcache_types, regcache_types_list, case_to_desc); + +static const struct regcache_types real_cache_types_list[] =3D { + { REGCACHE_FLAT, "flat" }, + { REGCACHE_RBTREE, "rbtree" }, +}; + +KUNIT_ARRAY_PARAM(real_cache_types, real_cache_types_list, case_to_desc); + +static const struct regcache_types sparse_cache_types_list[] =3D { + { REGCACHE_RBTREE, "rbtree" }, +}; + +KUNIT_ARRAY_PARAM(sparse_cache_types, sparse_cache_types_list, case_to_des= c); + +static struct regmap *gen_regmap(struct regmap_config *config, + struct regmap_ram_data **data) +{ + unsigned int *buf; + struct regmap *ret; + size_t size =3D (config->max_register + 1) * sizeof(unsigned int); + int i; + struct reg_default *defaults; + + buf =3D kmalloc(size, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + get_random_bytes(buf, size); + + *data =3D kzalloc(sizeof(**data), GFP_KERNEL); + if (!(*data)) + return ERR_PTR(-ENOMEM); + (*data)->vals =3D buf; + + if (config->num_reg_defaults) { + defaults =3D kcalloc(config->num_reg_defaults, + sizeof(struct reg_default), + GFP_KERNEL); + if (!defaults) + return ERR_PTR(-ENOMEM); + config->reg_defaults =3D defaults; + + for (i =3D 0; i < config->num_reg_defaults; i++) { + defaults[i].reg =3D i * config->reg_stride; + defaults[i].def =3D buf[i * config->reg_stride]; + } + } + + ret =3D regmap_init_ram(config, *data); + if (IS_ERR(ret)) { + kfree(buf); + kfree(*data); + } + + return ret; +} + +static void basic_read_write(struct kunit *test) +{ + struct regcache_types *t =3D (struct regcache_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int val, rval; + + config =3D test_regmap_config; + config.cache_type =3D t->type; + + map =3D gen_regmap(&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, t->type =3D=3D REGCACHE_NONE, data->read[0]); + + regmap_exit(map); +} + +static void bulk_write(struct kunit *test) +{ + struct regcache_types *t =3D (struct regcache_types *)test->param_value; + 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 =3D test_regmap_config; + config.cache_type =3D t->type; + + map =3D gen_regmap(&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 =3D 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 =3D 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, t->type =3D=3D REGCACHE_NONE, data->read[i]); + + regmap_exit(map); +} + +static void bulk_read(struct kunit *test) +{ + struct regcache_types *t =3D (struct regcache_types *)test->param_value; + 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 =3D test_regmap_config; + config.cache_type =3D t->type; + + map =3D gen_regmap(&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 =3D 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 =3D 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, t->type =3D=3D REGCACHE_NONE, data->read[i]); + + regmap_exit(map); +} + +static void reg_defaults(struct kunit *test) +{ + struct regcache_types *t =3D (struct regcache_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int rval[BLOCK_TEST_SIZE]; + int i; + + config =3D test_regmap_config; + config.cache_type =3D t->type; + config.num_reg_defaults =3D BLOCK_TEST_SIZE; + + map =3D gen_regmap(&config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + /* Read back the expected default data */ + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval, + BLOCK_TEST_SIZE)); + KUNIT_EXPECT_MEMEQ(test, data->vals, rval, sizeof(rval)); + + /* The data should have been read from cache if there was one */ + for (i =3D 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, t->type =3D=3D REGCACHE_NONE, data->read[i]); +} + +static void reg_defaults_read_dev(struct kunit *test) +{ + struct regcache_types *t =3D (struct regcache_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int rval[BLOCK_TEST_SIZE]; + int i; + + config =3D test_regmap_config; + config.cache_type =3D t->type; + config.num_reg_defaults_raw =3D BLOCK_TEST_SIZE; + + map =3D gen_regmap(&config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + /* We should have read the cache defaults back from the map */ + for (i =3D 0; i < BLOCK_TEST_SIZE; i++) { + KUNIT_EXPECT_EQ(test, t->type !=3D REGCACHE_NONE, data->read[i]); + data->read[i] =3D false; + } + + /* Read back the expected default data */ + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval, + BLOCK_TEST_SIZE)); + KUNIT_EXPECT_MEMEQ(test, data->vals, rval, sizeof(rval)); + + /* The data should have been read from cache if there was one */ + for (i =3D 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, t->type =3D=3D REGCACHE_NONE, data->read[i]); +} + +static void register_patch(struct kunit *test) +{ + struct regcache_types *t =3D (struct regcache_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + struct reg_sequence patch[2]; + unsigned int rval[BLOCK_TEST_SIZE]; + int i; + + /* We need defaults so readback works */ + config =3D test_regmap_config; + config.cache_type =3D t->type; + config.num_reg_defaults =3D BLOCK_TEST_SIZE; + + map =3D gen_regmap(&config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + /* Stash the original values */ + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval, + BLOCK_TEST_SIZE)); + + /* Patch a couple of values */ + patch[0].reg =3D 2; + patch[0].def =3D rval[2] + 1; + patch[0].delay_us =3D 0; + patch[1].reg =3D 5; + patch[1].def =3D rval[5] + 1; + patch[1].delay_us =3D 0; + KUNIT_EXPECT_EQ(test, 0, regmap_register_patch(map, patch, + ARRAY_SIZE(patch))); + + /* Only the patched registers are written */ + for (i =3D 0; i < BLOCK_TEST_SIZE; i++) { + switch (i) { + case 2: + case 5: + KUNIT_EXPECT_TRUE(test, data->written[i]); + KUNIT_EXPECT_EQ(test, data->vals[i], rval[i] + 1); + break; + default: + KUNIT_EXPECT_FALSE(test, data->written[i]); + KUNIT_EXPECT_EQ(test, data->vals[i], rval[i]); + break; + } + } + + regmap_exit(map); +} + +static void stride(struct kunit *test) +{ + struct regcache_types *t =3D (struct regcache_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int rval; + int i; + + config =3D test_regmap_config; + config.cache_type =3D t->type; + config.reg_stride =3D 2; + config.num_reg_defaults =3D BLOCK_TEST_SIZE / 2; + + map =3D gen_regmap(&config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + /* Only even registers can be accessed, try both read and write */ + for (i =3D 0; i < BLOCK_TEST_SIZE; i++) { + data->read[i] =3D false; + data->written[i] =3D false; + + if (i % 2) { + KUNIT_EXPECT_NE(test, 0, regmap_read(map, i, &rval)); + KUNIT_EXPECT_NE(test, 0, regmap_write(map, i, rval)); + KUNIT_EXPECT_FALSE(test, data->read[i]); + KUNIT_EXPECT_FALSE(test, data->written[i]); + } else { + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval)); + KUNIT_EXPECT_EQ(test, data->vals[i], rval); + KUNIT_EXPECT_EQ(test, t->type =3D=3D REGCACHE_NONE, + data->read[i]); + + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, rval)); + KUNIT_EXPECT_TRUE(test, data->written[i]); + } + } + + regmap_exit(map); +} + +static struct regmap_range_cfg test_range =3D { + .selector_reg =3D 1, + .selector_mask =3D 0xff, + + .window_start =3D 4, + .window_len =3D 10, + + .range_min =3D 20, + .range_max =3D 40, +}; + +static bool test_range_volatile(struct device *dev, unsigned int reg) +{ + if (reg >=3D test_range.window_start && + reg <=3D test_range.selector_reg + test_range.window_len) + return true; + + if (reg >=3D test_range.range_min && reg <=3D test_range.range_max) + return true; + + return false; +} + +static void basic_ranges(struct kunit *test) +{ + struct regcache_types *t =3D (struct regcache_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int val; + int i; + + config =3D test_regmap_config; + config.cache_type =3D t->type; + config.volatile_reg =3D test_range_volatile; + config.ranges =3D &test_range; + config.num_ranges =3D 1; + config.max_register =3D test_range.range_max; + + map =3D gen_regmap(&config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + for (i =3D test_range.range_min; i < test_range.range_max; i++) { + data->read[i] =3D false; + data->written[i] =3D false; + } + + /* Reset the page to a non-zero value to trigger a change */ + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, test_range.selector_reg, + test_range.range_max)); + + /* Check we set the page and use the window for writes */ + data->written[test_range.selector_reg] =3D false; + data->written[test_range.window_start] =3D false; + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, test_range.range_min, 0)); + KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]); + KUNIT_EXPECT_TRUE(test, data->written[test_range.window_start]); + + data->written[test_range.selector_reg] =3D false; + data->written[test_range.window_start] =3D false; + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, + test_range.range_min + + test_range.window_len, + 0)); + KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]); + KUNIT_EXPECT_TRUE(test, data->written[test_range.window_start]); + + /* Same for reads */ + data->written[test_range.selector_reg] =3D false; + data->read[test_range.window_start] =3D false; + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, test_range.range_min, &val)); + KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]); + KUNIT_EXPECT_TRUE(test, data->read[test_range.window_start]); + + data->written[test_range.selector_reg] =3D false; + data->read[test_range.window_start] =3D false; + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, + test_range.range_min + + test_range.window_len, + &val)); + KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]); + KUNIT_EXPECT_TRUE(test, data->read[test_range.window_start]); + + /* No physical access triggered in the virtual range */ + for (i =3D test_range.range_min; i < test_range.range_max; i++) { + KUNIT_EXPECT_FALSE(test, data->read[i]); + KUNIT_EXPECT_FALSE(test, data->written[i]); + } + + regmap_exit(map); +} + +/* Try to stress dynamic creation of cache data structures */ +static void stress_insert(struct kunit *test) +{ + struct regcache_types *t =3D (struct regcache_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int rval, *vals; + size_t buf_sz; + int i; + + config =3D test_regmap_config; + config.cache_type =3D t->type; + config.max_register =3D 300; + + map =3D gen_regmap(&config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + vals =3D kunit_kcalloc(test, sizeof(unsigned long), config.max_register, + GFP_KERNEL); + KUNIT_ASSERT_FALSE(test, vals =3D=3D NULL); + buf_sz =3D sizeof(unsigned long) * config.max_register; + + get_random_bytes(vals, buf_sz); + + /* Write data into the map/cache in ever decreasing strides */ + for (i =3D 0; i < config.max_register; i +=3D 100) + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i])); + for (i =3D 0; i < config.max_register; i +=3D 50) + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i])); + for (i =3D 0; i < config.max_register; i +=3D 25) + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i])); + for (i =3D 0; i < config.max_register; i +=3D 10) + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i])); + for (i =3D 0; i < config.max_register; i +=3D 5) + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i])); + for (i =3D 0; i < config.max_register; i +=3D 3) + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i])); + for (i =3D 0; i < config.max_register; i +=3D 2) + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i])); + for (i =3D 0; i < config.max_register; i++) + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i])); + + /* Do reads from the cache (if there is one) match? */ + for (i =3D 0; i < config.max_register; i ++) { + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval)); + KUNIT_EXPECT_EQ(test, rval, vals[i]); + KUNIT_EXPECT_EQ(test, t->type =3D=3D REGCACHE_NONE, data->read[i]); + } + + regmap_exit(map); +} + +static void cache_bypass(struct kunit *test) +{ + struct regcache_types *t =3D (struct regcache_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int val, rval; + + config =3D test_regmap_config; + config.cache_type =3D t->type; + + map =3D gen_regmap(&config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + get_random_bytes(&val, sizeof(val)); + + /* Ensure the cache has a value in it */ + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 0, val)); + + /* Bypass then write a different value */ + regcache_cache_bypass(map, true); + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 0, val + 1)); + + /* Read the bypassed value */ + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 0, &rval)); + KUNIT_EXPECT_EQ(test, val + 1, rval); + KUNIT_EXPECT_EQ(test, data->vals[0], rval); + + /* Disable bypass, the cache should still return the original value */ + regcache_cache_bypass(map, false); + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 0, &rval)); + KUNIT_EXPECT_EQ(test, val, rval); + + regmap_exit(map); +} + +static void cache_sync(struct kunit *test) +{ + struct regcache_types *t =3D (struct regcache_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int val[BLOCK_TEST_SIZE]; + int i; + + config =3D test_regmap_config; + config.cache_type =3D t->type; + + map =3D gen_regmap(&config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + get_random_bytes(&val, sizeof(val)); + + /* Put some data into the cache */ + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, 0, val, + BLOCK_TEST_SIZE)); + for (i =3D 0; i < BLOCK_TEST_SIZE; i++) + data->written[i] =3D false; + + /* Trash the data on the device itself then resync */ + regcache_mark_dirty(map); + memset(data->vals, 0, sizeof(val)); + KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); + + /* Did we just write the correct data out? */ + KUNIT_EXPECT_MEMEQ(test, data->vals, val, sizeof(val)); + for (i =3D 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, true, data->written[i]); + + regmap_exit(map); +} + +static void cache_sync_defaults(struct kunit *test) +{ + struct regcache_types *t =3D (struct regcache_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int val; + int i; + + config =3D test_regmap_config; + config.cache_type =3D t->type; + config.num_reg_defaults =3D BLOCK_TEST_SIZE; + + map =3D gen_regmap(&config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + get_random_bytes(&val, sizeof(val)); + + /* Change the value of one register */ + KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 2, val)); + + /* Resync */ + regcache_mark_dirty(map); + for (i =3D 0; i < BLOCK_TEST_SIZE; i++) + data->written[i] =3D false; + KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); + + /* Did we just sync the one register we touched? */ + for (i =3D 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, i =3D=3D 2, data->written[i]); + + regmap_exit(map); +} + +static void cache_sync_patch(struct kunit *test) +{ + struct regcache_types *t =3D (struct regcache_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + struct reg_sequence patch[2]; + unsigned int rval[BLOCK_TEST_SIZE], val; + int i; + + /* We need defaults so readback works */ + config =3D test_regmap_config; + config.cache_type =3D t->type; + config.num_reg_defaults =3D BLOCK_TEST_SIZE; + + map =3D gen_regmap(&config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + /* Stash the original values */ + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval, + BLOCK_TEST_SIZE)); + + /* Patch a couple of values */ + patch[0].reg =3D 2; + patch[0].def =3D rval[2] + 1; + patch[0].delay_us =3D 0; + patch[1].reg =3D 5; + patch[1].def =3D rval[5] + 1; + patch[1].delay_us =3D 0; + KUNIT_EXPECT_EQ(test, 0, regmap_register_patch(map, patch, + ARRAY_SIZE(patch))); + + /* Sync the cache */ + regcache_mark_dirty(map); + for (i =3D 0; i < BLOCK_TEST_SIZE; i++) + data->written[i] =3D false; + KUNIT_EXPECT_EQ(test, 0, regcache_sync(map)); + + /* The patch should be on the device but not in the cache */ + for (i =3D 0; i < BLOCK_TEST_SIZE; i++) { + KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &val)); + KUNIT_EXPECT_EQ(test, val, rval[i]); + + switch (i) { + case 2: + case 5: + KUNIT_EXPECT_EQ(test, true, data->written[i]); + KUNIT_EXPECT_EQ(test, data->vals[i], rval[i] + 1); + break; + default: + KUNIT_EXPECT_EQ(test, false, data->written[i]); + KUNIT_EXPECT_EQ(test, data->vals[i], rval[i]); + break; + } + } + + regmap_exit(map); +} + +static void cache_drop(struct kunit *test) +{ + struct regcache_types *t =3D (struct regcache_types *)test->param_value; + struct regmap *map; + struct regmap_config config; + struct regmap_ram_data *data; + unsigned int rval[BLOCK_TEST_SIZE]; + int i; + + config =3D test_regmap_config; + config.cache_type =3D t->type; + config.num_reg_defaults =3D BLOCK_TEST_SIZE; + + map =3D gen_regmap(&config, &data); + KUNIT_ASSERT_FALSE(test, IS_ERR(map)); + if (IS_ERR(map)) + return; + + /* Ensure the data is read from the cache */ + for (i =3D 0; i < BLOCK_TEST_SIZE; i++) + data->read[i] =3D false; + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval, + BLOCK_TEST_SIZE)); + for (i =3D 0; i < BLOCK_TEST_SIZE; i++) { + KUNIT_EXPECT_FALSE(test, data->read[i]); + data->read[i] =3D false; + } + KUNIT_EXPECT_MEMEQ(test, data->vals, rval, sizeof(rval)); + + /* Drop some registers */ + KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, 3, 5)); + + /* Reread and check only the dropped registers hit the device. */ + KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval, + BLOCK_TEST_SIZE)); + for (i =3D 0; i < BLOCK_TEST_SIZE; i++) + KUNIT_EXPECT_EQ(test, data->read[i], i >=3D 3 && i <=3D 5); + KUNIT_EXPECT_MEMEQ(test, data->vals, rval, sizeof(rval)); + + regmap_exit(map); +} + +static struct kunit_case regmap_test_cases[] =3D { + KUNIT_CASE_PARAM(basic_read_write, regcache_types_gen_params), + KUNIT_CASE_PARAM(bulk_write, regcache_types_gen_params), + KUNIT_CASE_PARAM(bulk_read, regcache_types_gen_params), + KUNIT_CASE_PARAM(reg_defaults, regcache_types_gen_params), + KUNIT_CASE_PARAM(reg_defaults_read_dev, regcache_types_gen_params), + KUNIT_CASE_PARAM(register_patch, regcache_types_gen_params), + KUNIT_CASE_PARAM(stride, regcache_types_gen_params), + KUNIT_CASE_PARAM(basic_ranges, regcache_types_gen_params), + KUNIT_CASE_PARAM(stress_insert, regcache_types_gen_params), + KUNIT_CASE_PARAM(cache_bypass, real_cache_types_gen_params), + KUNIT_CASE_PARAM(cache_sync, real_cache_types_gen_params), + 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), + {} +}; + +static struct kunit_suite regmap_test_suite =3D { + .name =3D "regmap", + .test_cases =3D regmap_test_cases, +}; +kunit_test_suite(regmap_test_suite); + +MODULE_LICENSE("GPL v2"); --=20 2.34.1