diff options
Diffstat (limited to 'drivers/base/regmap/regcache.c')
-rw-r--r-- | drivers/base/regmap/regcache.c | 83 |
1 files changed, 79 insertions, 4 deletions
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 507ee2da0f6e..e69102696533 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -250,6 +250,38 @@ int regcache_write(struct regmap *map, return 0; } +static int regcache_default_sync(struct regmap *map, unsigned int min, + unsigned int max) +{ + unsigned int reg; + + for (reg = min; reg <= max; reg++) { + unsigned int val; + int ret; + + if (regmap_volatile(map, reg)) + continue; + + ret = regcache_read(map, reg, &val); + if (ret) + return ret; + + /* Is this the hardware default? If so skip. */ + ret = regcache_lookup_reg(map, reg); + if (ret >= 0 && val == map->reg_defaults[ret].def) + continue; + + map->cache_bypass = 1; + ret = _regmap_write(map, reg, val); + map->cache_bypass = 0; + if (ret) + return ret; + dev_dbg(map->dev, "Synced register %#x, value %#x\n", reg, val); + } + + return 0; +} + /** * regcache_sync: Sync the register cache with the hardware. * @@ -268,7 +300,7 @@ int regcache_sync(struct regmap *map) const char *name; unsigned int bypass; - BUG_ON(!map->cache_ops || !map->cache_ops->sync); + BUG_ON(!map->cache_ops); map->lock(map->lock_arg); /* Remember the initial bypass state */ @@ -297,7 +329,10 @@ int regcache_sync(struct regmap *map) } map->cache_bypass = 0; - ret = map->cache_ops->sync(map, 0, map->max_register); + if (map->cache_ops->sync) + ret = map->cache_ops->sync(map, 0, map->max_register); + else + ret = regcache_default_sync(map, 0, map->max_register); if (ret == 0) map->cache_dirty = false; @@ -331,7 +366,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min, const char *name; unsigned int bypass; - BUG_ON(!map->cache_ops || !map->cache_ops->sync); + BUG_ON(!map->cache_ops); map->lock(map->lock_arg); @@ -346,7 +381,10 @@ int regcache_sync_region(struct regmap *map, unsigned int min, if (!map->cache_dirty) goto out; - ret = map->cache_ops->sync(map, min, max); + if (map->cache_ops->sync) + ret = map->cache_ops->sync(map, min, max); + else + ret = regcache_default_sync(map, min, max); out: trace_regcache_sync(map->dev, name, "stop region"); @@ -359,6 +397,43 @@ out: EXPORT_SYMBOL_GPL(regcache_sync_region); /** + * regcache_drop_region: Discard part of the register cache + * + * @map: map to operate on + * @min: first register to discard + * @max: last register to discard + * + * Discard part of the register cache. + * + * Return a negative value on failure, 0 on success. + */ +int regcache_drop_region(struct regmap *map, unsigned int min, + unsigned int max) +{ + unsigned int reg; + int ret = 0; + + if (!map->cache_present && !(map->cache_ops && map->cache_ops->drop)) + return -EINVAL; + + map->lock(map->lock_arg); + + trace_regcache_drop_region(map->dev, min, max); + + if (map->cache_present) + for (reg = min; reg < max + 1; reg++) + clear_bit(reg, map->cache_present); + + if (map->cache_ops && map->cache_ops->drop) + ret = map->cache_ops->drop(map, min, max); + + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regcache_drop_region); + +/** * regcache_cache_only: Put a register map into cache only mode * * @map: map to configure |