Skip to content

Commit 4b05ccb

Browse files
ujfalusibroonie
authored andcommitted
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>
1 parent 9ed3d97 commit 4b05ccb

1 file changed

Lines changed: 16 additions & 1 deletion

File tree

drivers/base/regmap/regcache.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ static void regcache_hw_exit(struct regmap *map)
123123

124124
int regcache_init(struct regmap *map, const struct regmap_config *config)
125125
{
126+
bool sort_defaults = false;
127+
unsigned int reg_prev = 0;
126128
int count = 0;
127129
int ret;
128130
int i;
@@ -149,10 +151,16 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
149151
return -EINVAL;
150152
}
151153

152-
for (i = 0; i < config->num_reg_defaults; i++)
154+
for (i = 0; i < config->num_reg_defaults; i++) {
153155
if (config->reg_defaults[i].reg % map->reg_stride)
154156
return -EINVAL;
155157

158+
if (reg_prev > config->reg_defaults[i].reg)
159+
sort_defaults = true;
160+
161+
reg_prev = config->reg_defaults[i].reg;
162+
}
163+
156164
for (i = 0; i < ARRAY_SIZE(cache_types); i++)
157165
if (cache_types[i]->type == map->cache_type)
158166
break;
@@ -186,6 +194,13 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
186194
sizeof(*map->reg_defaults), GFP_KERNEL);
187195
if (!tmp_buf)
188196
return -ENOMEM;
197+
198+
/* regcache_lookup_reg() bsearch()es this array */
199+
if (sort_defaults) {
200+
dev_warn(map->dev,
201+
"Driver needs fixing: Unsorted reg_defaults, sorting the copy\n");
202+
regcache_sort_defaults(tmp_buf, map->num_reg_defaults);
203+
}
189204
map->reg_defaults = tmp_buf;
190205
} else if (map->num_reg_defaults_raw) {
191206
count = regcache_count_cacheable_registers(map);

0 commit comments

Comments
 (0)