summaryrefslogtreecommitdiff
path: root/drivers/base
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2012-01-21 12:01:14 +0000
committerLokesh Pathak <lpathak@nvidia.com>2012-03-05 07:57:23 -0800
commitf18eef2d3decfecb68e012414015e5f9fa8ba904 (patch)
tree877ac32a50ee6686d85ba32fc4d91fb57479e3f3 /drivers/base
parent4f94307fea52790af53c8dcdded22683aa42ed96 (diff)
regmap: Support register patch sets
Device manufacturers frequently provide register sequences, usually not fully documented, to be run at startup in order to provide better defaults for devices (for example, improving performance in the light of silicon evaluation). Support such updates by allowing drivers to register update sets with the core. These updates will be written to the device immediately and will also be rewritten when the cache is synced. The assumption is that the reason for resyncing the cache will always be that the device has been powered off. If this turns out to not be the case then a separate operation can be provided. Currently the implementation only allows a single set of updates to be specified for a device, this could be extended in future. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> (cherry picked from commit 22f0d90a34827812413bb3fbeda6a2a79bb58423) Change-Id: I02f4ead9866a90b3635c4b98f1f9c3be3109c5ea Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> Reviewed-on: http://git-master/r/87577
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/regmap/internal.h3
-rw-r--r--drivers/base/regmap/regcache.c11
-rw-r--r--drivers/base/regmap/regmap.c58
3 files changed, 72 insertions, 0 deletions
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index e33f1be2b299..9ec8d8ff6db2 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -76,6 +76,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 5636e5be57e7..78353c739c29 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -267,6 +267,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 6b3494dc1953..b4e6e97615f4 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -717,6 +717,64 @@ 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);
+
static int __init regmap_initcall(void)
{
regmap_debugfs_initcall();