regmap: Support register patch sets
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Sat, 21 Jan 2012 12:01:14 +0000 (12:01 +0000)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Mon, 23 Jan 2012 14:01:18 +0000 (14:01 +0000)
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>
drivers/base/regmap/internal.h
drivers/base/regmap/regcache.c
drivers/base/regmap/regmap.c
include/linux/regmap.h

index 1a02b7537c8bbac9d59d21ee51c1361dd03c01c0..d141b80479b5257d9fff00b9be47e47029e0f163 100644 (file)
@@ -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 {
index 1ead66186b7c0a90586f223b0b9bc23d3770a3ed..ce2034c10ffb94d55b31bf0cd83379a44acc82d8 100644 (file)
@@ -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) {
index be10a4ff660915625454375ce9fb30a97d5b7ef9..28e89fd7c28d02399f97b97ad5e42fcd6b95004f 100644 (file)
@@ -669,6 +669,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();
index eb93921cdd30252b1c960cf6872585e308c9e9fc..860739a8a6dde429d637e27202df78509342fb71 100644 (file)
@@ -149,6 +149,9 @@ void regcache_cache_only(struct regmap *map, bool enable);
 void regcache_cache_bypass(struct regmap *map, bool enable);
 void regcache_mark_dirty(struct regmap *map);
 
+int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
+                         int num_regs);
+
 /**
  * Description of an IRQ for the generic regmap irq_chip.
  *