[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20241231100256.194753-1-sander@svanheule.net>
Date: Tue, 31 Dec 2024 11:02:56 +0100
From: Sander Vanheule <sander@...nheule.net>
To: Mark Brown <broonie@...nel.org>,
Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
"Rafael J. Wysocki" <rafael@...nel.org>,
linux-kernel@...r.kernel.org
Cc: Sander Vanheule <sander@...nheule.net>
Subject: [PATCH] regmap: add cache validity to REGCACHE_FLAT
The flat regcache will always assume the data in the cache is valid.
Since the cache takes priority over hardware values, this may shadow the
actual state of the device. This is not the case with REGCACHE_RBTREE
for example, which makes these implementation behave differently.
Add a new containing cache structure with the flat data table and a
bitmap indicating cache validity. Use this to provide an implementation
for regcache_ops.drop.
Since this makes the flat cache behave more like other caches, a special
case in the read_writeonly unit test can also be dropped. This reverts
commit d0c99ffe2126 ("regmap: Allow reads from write only registers with
the flat cache").
Signed-off-by: Sander Vanheule <sander@...nheule.net>
---
drivers/base/regmap/regcache-flat.c | 70 ++++++++++++++++++++++++-----
drivers/base/regmap/regmap-kunit.c | 15 ++-----
2 files changed, 63 insertions(+), 22 deletions(-)
diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c
index f36d3618b67c..4bdf2e58ade1 100644
--- a/drivers/base/regmap/regcache-flat.c
+++ b/drivers/base/regmap/regcache-flat.c
@@ -6,6 +6,8 @@
//
// Author: Mark Brown <broonie@...nsource.wolfsonmicro.com>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
@@ -18,34 +20,65 @@ static inline unsigned int regcache_flat_get_index(const struct regmap *map,
return regcache_get_index_by_order(map, reg);
}
+struct regcache_flat_data {
+ unsigned int *data;
+ unsigned long *valid;
+};
+
static int regcache_flat_init(struct regmap *map)
{
int i;
- unsigned int *cache;
+ unsigned int cache_size;
+ struct regcache_flat_data *cache = NULL;
+ unsigned long *cache_valid = NULL;
+ unsigned int *cache_data = NULL;
if (!map || map->reg_stride_order < 0 || !map->max_register_is_set)
return -EINVAL;
- map->cache = kcalloc(regcache_flat_get_index(map, map->max_register)
- + 1, sizeof(unsigned int), map->alloc_flags);
- if (!map->cache)
+ cache_size = regcache_flat_get_index(map, map->max_register) + 1;
+ cache_data = kcalloc(cache_size, sizeof(unsigned int), map->alloc_flags);
+ if (!cache_data)
return -ENOMEM;
+ cache_valid = bitmap_zalloc(cache_size, map->alloc_flags);
+ if (!cache_valid)
+ goto err_free_valid;
+
+ map->cache = kmalloc(sizeof(*cache), map->alloc_flags);
+ if (!map->cache)
+ goto err_free;
+
cache = map->cache;
+ cache->valid = cache_valid;
+ cache->data = cache_data;
for (i = 0; i < map->num_reg_defaults; i++) {
unsigned int reg = map->reg_defaults[i].reg;
unsigned int index = regcache_flat_get_index(map, reg);
- cache[index] = map->reg_defaults[i].def;
+ cache->data[index] = map->reg_defaults[i].def;
+ __set_bit(index, cache->valid);
}
return 0;
+
+err_free:
+ kfree(cache_data);
+err_free_valid:
+ bitmap_free(cache_valid);
+ return -ENOMEM;
}
static int regcache_flat_exit(struct regmap *map)
{
- kfree(map->cache);
+ struct regcache_flat_data *cache = map->cache;
+
+ if (cache) {
+ bitmap_free(cache->valid);
+ kfree(cache->data);
+ }
+ kfree(cache);
map->cache = NULL;
return 0;
@@ -54,10 +87,13 @@ static int regcache_flat_exit(struct regmap *map)
static int regcache_flat_read(struct regmap *map,
unsigned int reg, unsigned int *value)
{
- unsigned int *cache = map->cache;
+ struct regcache_flat_data *cache = map->cache;
unsigned int index = regcache_flat_get_index(map, reg);
- *value = cache[index];
+ if (!test_bit(index, cache->valid))
+ return -ENOENT;
+
+ *value = cache->data[index];
return 0;
}
@@ -65,10 +101,23 @@ static int regcache_flat_read(struct regmap *map,
static int regcache_flat_write(struct regmap *map, unsigned int reg,
unsigned int value)
{
- unsigned int *cache = map->cache;
+ struct regcache_flat_data *cache = map->cache;
unsigned int index = regcache_flat_get_index(map, reg);
- cache[index] = value;
+ cache->data[index] = value;
+ __set_bit(index, cache->valid);
+
+ return 0;
+}
+
+static int regcache_flat_drop(struct regmap *map, unsigned int min,
+ unsigned int max)
+{
+ struct regcache_flat_data *cache = map->cache;
+ unsigned int index_min = regcache_flat_get_index(map, min);
+ unsigned int index_max = regcache_flat_get_index(map, max);
+
+ bitmap_clear(cache->valid, index_min, index_max - index_min);
return 0;
}
@@ -80,4 +129,5 @@ struct regcache_ops regcache_flat_ops = {
.exit = regcache_flat_exit,
.read = regcache_flat_read,
.write = regcache_flat_write,
+ .drop = regcache_flat_drop,
};
diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regmap-kunit.c
index 64ea340950b6..676f1a91c352 100644
--- a/drivers/base/regmap/regmap-kunit.c
+++ b/drivers/base/regmap/regmap-kunit.c
@@ -567,18 +567,9 @@ static void read_writeonly(struct kunit *test)
for (i = 0; i < BLOCK_TEST_SIZE; i++)
data->read[i] = false;
- /*
- * Try to read all the registers, the writeonly one should
- * fail if we aren't using the flat cache.
- */
- for (i = 0; i < BLOCK_TEST_SIZE; i++) {
- if (config.cache_type != REGCACHE_FLAT) {
- KUNIT_EXPECT_EQ(test, i != 5,
- regmap_read(map, i, &val) == 0);
- } else {
- KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &val));
- }
- }
+ /* Try to read all the registers, the writeonly one should fail */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, i != 5, regmap_read(map, i, &val) == 0);
/* Did we trigger a hardware access? */
KUNIT_EXPECT_FALSE(test, data->read[5]);
--
2.47.1
Powered by blists - more mailing lists