diff options
author | Takashi Iwai <tiwai@suse.de> | 2012-03-18 18:22:37 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-03-18 18:22:37 +0100 |
commit | cb3f2adc03ab055b19c677a6283523861fafebdd (patch) | |
tree | 59cfb6800f0635a4aec16c8e0da619f27e51ee79 /drivers/base/regmap | |
parent | 44c76a960a62fcc46cbcaa0a22a34e666a729329 (diff) | |
parent | 828006de1bddf83b6ecf03ec459c15f7c7c22db7 (diff) |
Merge branch 'topic/asoc' into for-linus
Diffstat (limited to 'drivers/base/regmap')
-rw-r--r-- | drivers/base/regmap/internal.h | 3 | ||||
-rw-r--r-- | drivers/base/regmap/regcache.c | 15 | ||||
-rw-r--r-- | drivers/base/regmap/regmap.c | 76 |
3 files changed, 92 insertions, 2 deletions
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 1a02b7537c8b..d141b80479b5 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -75,6 +75,9 @@ struct regmap { const void *reg_defaults_raw; void *cache; bool cache_dirty; + + struct reg_default *patch; + int patch_regs; }; struct regcache_ops { diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 1ead66186b7c..5cd2a37e7688 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -53,7 +53,7 @@ static int regcache_hw_init(struct regmap *map) for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) { val = regcache_get_val(map->reg_defaults_raw, i, map->cache_word_size); - if (!val) + if (regmap_volatile(map, i)) continue; count++; } @@ -70,7 +70,7 @@ static int regcache_hw_init(struct regmap *map) for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) { val = regcache_get_val(map->reg_defaults_raw, i, map->cache_word_size); - if (!val) + if (regmap_volatile(map, i)) continue; map->reg_defaults[j].reg = i; map->reg_defaults[j].def = val; @@ -268,6 +268,17 @@ int regcache_sync(struct regmap *map) map->cache_ops->name); name = map->cache_ops->name; trace_regcache_sync(map->dev, name, "start"); + + /* Apply any patch first */ + for (i = 0; i < map->patch_regs; i++) { + 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", + map->patch[i].reg, map->patch[i].def, ret); + goto out; + } + } + if (!map->cache_dirty) goto out; if (map->cache_ops->sync) { diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index be10a4ff6609..7ac234f0b1c5 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -284,6 +284,9 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) map->precious_reg = config->precious_reg; map->cache_type = config->cache_type; + map->cache_bypass = false; + map->cache_only = false; + ret = regcache_init(map, config); mutex_unlock(&map->lock); @@ -669,6 +672,79 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_update_bits_check); +/** + * regmap_register_patch: Register and apply register updates to be applied + * on device initialistion + * + * @map: Register map to apply updates to. + * @regs: Values to update. + * @num_regs: Number of entries in regs. + * + * Register a set of register updates to be applied to the device + * whenever the device registers are synchronised with the cache and + * apply them immediately. Typically this is used to apply + * corrections to be applied to the device defaults on startup, such + * as the updates some vendors provide to undocumented registers. + */ +int regmap_register_patch(struct regmap *map, const struct reg_default *regs, + int num_regs) +{ + int i, ret; + bool bypass; + + /* If needed the implementation can be extended to support this */ + if (map->patch) + return -EBUSY; + + mutex_lock(&map->lock); + + bypass = map->cache_bypass; + + map->cache_bypass = true; + + /* Write out first; it's useful to apply even if we fail later. */ + for (i = 0; i < num_regs; i++) { + ret = _regmap_write(map, regs[i].reg, regs[i].def); + if (ret != 0) { + dev_err(map->dev, "Failed to write %x = %x: %d\n", + regs[i].reg, regs[i].def, ret); + goto out; + } + } + + map->patch = kcalloc(sizeof(struct reg_default), num_regs, GFP_KERNEL); + if (map->patch != NULL) { + memcpy(map->patch, regs, + num_regs * sizeof(struct reg_default)); + map->patch_regs = num_regs; + } else { + ret = -ENOMEM; + } + +out: + map->cache_bypass = bypass; + + mutex_unlock(&map->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_register_patch); + +/* + * regmap_get_val_bytes(): Report the size of a register value + * + * Report the size of a register value, mainly intended to for use by + * generic infrastructure built on top of regmap. + */ +int regmap_get_val_bytes(struct regmap *map) +{ + if (map->format.format_write) + return -EINVAL; + + return map->format.val_bytes; +} +EXPORT_SYMBOL_GPL(regmap_get_val_bytes); + static int __init regmap_initcall(void) { regmap_debugfs_initcall(); |