]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
regcache: Sort the local copy of an unsorted reg_defaults array
authorPeter Ujfalusi <peter.ujfalusi@linux.intel.com>
Wed, 5 Aug 2026 13:22:50 +0000 (16:22 +0300)
committerMark Brown <broonie@kernel.org>
Thu, 6 Aug 2026 12:01:39 +0000 (13:01 +0100)
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>
drivers/base/regmap/regcache.c

index 480bc76f9a02bad3f65946bbb5a8a37a802bc25f..623db7a1f3bd670c76860070fa5ea02ea4a75514 100644 (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);