diff options
Diffstat (limited to 'drivers/base/regmap/regcache.c')
| -rw-r--r-- | drivers/base/regmap/regcache.c | 173 |
1 files changed, 110 insertions, 63 deletions
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 507ee2da0f6..29b4128da0b 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -121,8 +121,6 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) map->reg_defaults_raw = config->reg_defaults_raw; map->cache_word_size = DIV_ROUND_UP(config->val_bits, 8); map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw; - map->cache_present = NULL; - map->cache_present_nbits = 0; map->cache = NULL; map->cache_ops = cache_types[i]; @@ -181,7 +179,6 @@ void regcache_exit(struct regmap *map) BUG_ON(!map->cache_ops); - kfree(map->cache_present); kfree(map->reg_defaults); if (map->cache_free) kfree(map->reg_defaults_raw); @@ -241,15 +238,45 @@ int regcache_write(struct regmap *map, BUG_ON(!map->cache_ops); - if (!regmap_writeable(map, reg)) - return -EIO; - if (!regmap_volatile(map, reg)) return map->cache_ops->write(map, reg, value); 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 += map->reg_stride) { + unsigned int val; + int ret; + + if (regmap_volatile(map, reg) || + !regmap_writeable(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 +295,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 */ @@ -281,13 +308,11 @@ int regcache_sync(struct regmap *map) if (!map->cache_dirty) goto out; + map->async = true; + /* Apply any patch first */ map->cache_bypass = 1; for (i = 0; i < map->patch_regs; i++) { - if (map->patch[i].reg % map->reg_stride) { - ret = -EINVAL; - goto out; - } ret = _regmap_write(map, map->patch[i].reg, map->patch[i].def); if (ret != 0) { dev_err(map->dev, "Failed to write %x = %x: %d\n", @@ -297,17 +322,24 @@ 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; out: - trace_regcache_sync(map->dev, name, "stop"); /* Restore the bypass state */ + map->async = false; map->cache_bypass = bypass; map->unlock(map->lock_arg); + regmap_async_complete(map); + + trace_regcache_sync(map->dev, name, "stop"); + return ret; } EXPORT_SYMBOL_GPL(regcache_sync); @@ -331,7 +363,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,19 +378,59 @@ int regcache_sync_region(struct regmap *map, unsigned int min, if (!map->cache_dirty) goto out; - ret = map->cache_ops->sync(map, min, max); + map->async = true; + + 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"); /* Restore the bypass state */ map->cache_bypass = bypass; + map->async = false; map->unlock(map->lock_arg); + regmap_async_complete(map); + + trace_regcache_sync(map->dev, name, "stop region"); + return ret; } 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) +{ + int ret = 0; + + if (!map->cache_ops || !map->cache_ops->drop) + return -EINVAL; + + map->lock(map->lock_arg); + + trace_regcache_drop_region(map->dev, min, max); + + 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 @@ -418,42 +490,6 @@ void regcache_cache_bypass(struct regmap *map, bool enable) } EXPORT_SYMBOL_GPL(regcache_cache_bypass); -int regcache_set_reg_present(struct regmap *map, unsigned int reg) -{ - unsigned long *cache_present; - unsigned int cache_present_size; - unsigned int nregs; - int i; - - nregs = reg + 1; - cache_present_size = BITS_TO_LONGS(nregs); - cache_present_size *= sizeof(long); - - if (!map->cache_present) { - cache_present = kmalloc(cache_present_size, GFP_KERNEL); - if (!cache_present) - return -ENOMEM; - bitmap_zero(cache_present, nregs); - map->cache_present = cache_present; - map->cache_present_nbits = nregs; - } - - if (nregs > map->cache_present_nbits) { - cache_present = krealloc(map->cache_present, - cache_present_size, GFP_KERNEL); - if (!cache_present) - return -ENOMEM; - for (i = 0; i < nregs; i++) - if (i >= map->cache_present_nbits) - clear_bit(i, cache_present); - map->cache_present = cache_present; - map->cache_present_nbits = nregs; - } - - set_bit(reg, map->cache_present); - return 0; -} - bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, unsigned int val) { @@ -545,7 +581,16 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg) return -ENOENT; } +static bool regcache_reg_present(unsigned long *cache_present, unsigned int idx) +{ + if (!cache_present) + return true; + + return test_bit(idx, cache_present); +} + static int regcache_sync_block_single(struct regmap *map, void *block, + unsigned long *cache_present, unsigned int block_base, unsigned int start, unsigned int end) { @@ -555,7 +600,7 @@ static int regcache_sync_block_single(struct regmap *map, void *block, for (i = start; i < end; i++) { regtmp = block_base + (i * map->reg_stride); - if (!regcache_reg_present(map, regtmp)) + if (!regcache_reg_present(cache_present, i)) continue; val = regcache_get_val(map, block, i); @@ -588,15 +633,14 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data, if (*data == NULL) return 0; - count = cur - base; + count = (cur - base) / map->reg_stride; dev_dbg(map->dev, "Writing %zu bytes for %d registers from 0x%x-0x%x\n", - count * val_bytes, count, base, cur - 1); + count * val_bytes, count, base, cur - map->reg_stride); map->cache_bypass = 1; - ret = _regmap_raw_write(map, base, *data, count * val_bytes, - false); + ret = _regmap_raw_write(map, base, *data, count * val_bytes); map->cache_bypass = 0; @@ -606,6 +650,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data, } static int regcache_sync_block_raw(struct regmap *map, void *block, + unsigned long *cache_present, unsigned int block_base, unsigned int start, unsigned int end) { @@ -618,7 +663,7 @@ static int regcache_sync_block_raw(struct regmap *map, void *block, for (i = start; i < end; i++) { regtmp = block_base + (i * map->reg_stride); - if (!regcache_reg_present(map, regtmp)) { + if (!regcache_reg_present(cache_present, i)) { ret = regcache_sync_block_raw_flush(map, &data, base, regtmp); if (ret != 0) @@ -644,17 +689,19 @@ static int regcache_sync_block_raw(struct regmap *map, void *block, } } - return regcache_sync_block_raw_flush(map, &data, base, regtmp); + return regcache_sync_block_raw_flush(map, &data, base, regtmp + + map->reg_stride); } int regcache_sync_block(struct regmap *map, void *block, + unsigned long *cache_present, unsigned int block_base, unsigned int start, unsigned int end) { if (regmap_can_raw_write(map)) - return regcache_sync_block_raw(map, block, block_base, - start, end); + return regcache_sync_block_raw(map, block, cache_present, + block_base, start, end); else - return regcache_sync_block_single(map, block, block_base, - start, end); + return regcache_sync_block_single(map, block, cache_present, + block_base, start, end); } |
