summaryrefslogtreecommitdiff
path: root/drivers/base
diff options
context:
space:
mode:
authorPeter Ujfalusi <peter.ujfalusi@linux.intel.com>2026-08-05 16:22:50 +0300
committerMark Brown <broonie@kernel.org>2026-08-06 13:01:39 +0100
commit4b05ccb17f92268384d483221a577fccfc291c7a (patch)
tree6266d7433d2dc83393b5d6afcede9e2bf3dcb414 /drivers/base
parent9ed3d974a26644ad57b3d4d067e279188b2038b6 (diff)
regcache: Sort the local copy of an unsorted reg_defaults array
regcache_lookup_reg() bsearch()es the reg_defaults array, which requires it to be sorted by ascending register address. Entries following a descending step are never found, so regcache_reg_needs_sync() reports that they need a sync and they are written to the device on every regcache_sync() even when they were never touched. Detect the misordering while reg_defaults is validated against the register stride and sort the local copy. The check needs no new loop and sort() only runs for the affected drivers, which are also warned about. Note that sort() is not stable, so for arrays with duplicated register addresses it remains unspecified which entry is found. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com> Reviewed-by: Charles Keepax <ckeepax@opensource.cirrus.com> Tested-by: Charles Keepax <ckeepax@opensource.cirrus.com> Link: https://patch.msgid.link/20260805132250.2637-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/regmap/regcache.c17
1 files changed, 16 insertions, 1 deletions
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 480bc76f9a02..623db7a1f3bd 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -123,6 +123,8 @@ static void regcache_hw_exit(struct regmap *map)
int regcache_init(struct regmap *map, const struct regmap_config *config)
{
+ bool sort_defaults = false;
+ unsigned int reg_prev = 0;
int count = 0;
int ret;
int i;
@@ -149,10 +151,16 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
return -EINVAL;
}
- for (i = 0; i < config->num_reg_defaults; i++)
+ for (i = 0; i < config->num_reg_defaults; i++) {
if (config->reg_defaults[i].reg % map->reg_stride)
return -EINVAL;
+ if (reg_prev > config->reg_defaults[i].reg)
+ sort_defaults = true;
+
+ reg_prev = config->reg_defaults[i].reg;
+ }
+
for (i = 0; i < ARRAY_SIZE(cache_types); i++)
if (cache_types[i]->type == map->cache_type)
break;
@@ -186,6 +194,13 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
sizeof(*map->reg_defaults), GFP_KERNEL);
if (!tmp_buf)
return -ENOMEM;
+
+ /* regcache_lookup_reg() bsearch()es this array */
+ if (sort_defaults) {
+ dev_warn(map->dev,
+ "Driver needs fixing: Unsorted reg_defaults, sorting the copy\n");
+ regcache_sort_defaults(tmp_buf, map->num_reg_defaults);
+ }
map->reg_defaults = tmp_buf;
} else if (map->num_reg_defaults_raw) {
count = regcache_count_cacheable_registers(map);