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>
This commit is contained in:
Peter Ujfalusi 2026-08-05 16:22:50 +03:00 committed by Mark Brown
parent 9ed3d974a2
commit 4b05ccb17f
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0

View File

@ -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);