Skip to content

Commit 0ec7731

Browse files
committed
regmap: Ensure range selector registers are updated after cache sync
When we sync the register cache we do so with the cache bypassed in order to avoid overhead from writing the synced values back into the cache. If the regmap has ranges and the selector register for those ranges is in a register which is cached this has the unfortunate side effect of meaning that the physical and cached copies of the selector register can be out of sync after a cache sync. The cache will have whatever the selector was when the sync started and the hardware will have the selector for the register that was synced last. Fix this by rewriting all cached selector registers after every sync, ensuring that the hardware and cache have the same content. This will result in extra writes that wouldn't otherwise be needed but is simple so hopefully robust. We don't read from the hardware since not all devices have physical read support. Given that nobody noticed this until now it is likely that we are rarely if ever hitting this case. Reported-by: Hector Martin <[email protected]> Cc: [email protected] Signed-off-by: Mark Brown <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent c6df843 commit 0ec7731

File tree

1 file changed

+30
-0
lines changed

1 file changed

+30
-0
lines changed

drivers/base/regmap/regcache.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,11 @@ static int regcache_default_sync(struct regmap *map, unsigned int min,
334334
return 0;
335335
}
336336

337+
static int rbtree_all(const void *key, const struct rb_node *node)
338+
{
339+
return 0;
340+
}
341+
337342
/**
338343
* regcache_sync - Sync the register cache with the hardware.
339344
*
@@ -351,6 +356,7 @@ int regcache_sync(struct regmap *map)
351356
unsigned int i;
352357
const char *name;
353358
bool bypass;
359+
struct rb_node *node;
354360

355361
if (WARN_ON(map->cache_type == REGCACHE_NONE))
356362
return -EINVAL;
@@ -392,6 +398,30 @@ int regcache_sync(struct regmap *map)
392398
/* Restore the bypass state */
393399
map->cache_bypass = bypass;
394400
map->no_sync_defaults = false;
401+
402+
/*
403+
* If we did any paging with cache bypassed and a cached
404+
* paging register then the register and cache state might
405+
* have gone out of sync, force writes of all the paging
406+
* registers.
407+
*/
408+
rb_for_each(node, 0, &map->range_tree, rbtree_all) {
409+
struct regmap_range_node *this =
410+
rb_entry(node, struct regmap_range_node, node);
411+
412+
/* If there's nothing in the cache there's nothing to sync */
413+
ret = regcache_read(map, this->selector_reg, &i);
414+
if (ret != 0)
415+
continue;
416+
417+
ret = _regmap_write(map, this->selector_reg, i);
418+
if (ret != 0) {
419+
dev_err(map->dev, "Failed to write %x = %x: %d\n",
420+
this->selector_reg, i, ret);
421+
break;
422+
}
423+
}
424+
395425
map->unlock(map->lock_arg);
396426

397427
regmap_async_complete(map);

0 commit comments

Comments
 (0)