summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/base/regmap/internal.h3
-rw-r--r--drivers/base/regmap/regcache.c14
-rw-r--r--drivers/base/regmap/regmap.c58
-rw-r--r--include/linux/regmap.h3
4 files changed, 78 insertions, 0 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 d1daa5e9fadf..e9032c329067 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -268,8 +268,22 @@ int regcache_sync(struct regmap *map)
map->cache_ops->name);
name = map->cache_ops->name;
trace_regcache_sync(map->dev, name, "start");
+
if (!map->cache_dirty)
goto out;
+
+ /* Apply any patch first */
+ map->cache_bypass = 1;
+ 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;
+ }
+ }
+ map->cache_bypass = 0;
+
if (map->cache_ops->sync) {
ret = map->cache_ops->sync(map);
} else {
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 677aeab24f75..1752f13ddebc 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -711,6 +711,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(num_regs, sizeof(struct reg_default), 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();
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 195db51ec79b..4a957fdb46f1 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -157,6 +157,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.
*