]> Pileus Git - ~andy/linux/blobdiff - drivers/base/regmap/regcache.c
Merge remote-tracking branch 'regmap/topic/drivers' into regmap-next
[~andy/linux] / drivers / base / regmap / regcache.c
index 666f6f5011dc85339287f9b982f991e22081626b..8db713ffef66eed4eb4739f90af93eed4b616d7d 100644 (file)
@@ -19,7 +19,6 @@
 #include "internal.h"
 
 static const struct regcache_ops *cache_types[] = {
-       &regcache_indexed_ops,
        &regcache_rbtree_ops,
        &regcache_lzo_ops,
 };
@@ -54,22 +53,24 @@ 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++;
        }
 
        map->reg_defaults = kmalloc(count * sizeof(struct reg_default),
                                      GFP_KERNEL);
-       if (!map->reg_defaults)
-               return -ENOMEM;
+       if (!map->reg_defaults) {
+               ret = -ENOMEM;
+               goto err_free;
+       }
 
        /* fill the reg_defaults */
        map->num_reg_defaults = count;
        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;
@@ -77,9 +78,15 @@ static int regcache_hw_init(struct regmap *map)
        }
 
        return 0;
+
+err_free:
+       if (map->cache_free)
+               kfree(map->reg_defaults_raw);
+
+       return ret;
 }
 
-int regcache_init(struct regmap *map)
+int regcache_init(struct regmap *map, const struct regmap_config *config)
 {
        int ret;
        int i;
@@ -100,6 +107,12 @@ int regcache_init(struct regmap *map)
                return -EINVAL;
        }
 
+       map->num_reg_defaults = config->num_reg_defaults;
+       map->num_reg_defaults_raw = config->num_reg_defaults_raw;
+       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 = NULL;
        map->cache_ops = cache_types[i];
 
@@ -112,10 +125,10 @@ int regcache_init(struct regmap *map)
         * won't vanish from under us.  We'll need to make
         * a copy of it.
         */
-       if (map->reg_defaults) {
+       if (config->reg_defaults) {
                if (!map->num_reg_defaults)
                        return -EINVAL;
-               tmp_buf = kmemdup(map->reg_defaults, map->num_reg_defaults *
+               tmp_buf = kmemdup(config->reg_defaults, map->num_reg_defaults *
                                  sizeof(struct reg_default), GFP_KERNEL);
                if (!tmp_buf)
                        return -ENOMEM;
@@ -136,9 +149,18 @@ int regcache_init(struct regmap *map)
        if (map->cache_ops->init) {
                dev_dbg(map->dev, "Initializing %s cache\n",
                        map->cache_ops->name);
-               return map->cache_ops->init(map);
+               ret = map->cache_ops->init(map);
+               if (ret)
+                       goto err_free;
        }
        return 0;
+
+err_free:
+       kfree(map->reg_defaults);
+       if (map->cache_free)
+               kfree(map->reg_defaults_raw);
+
+       return ret;
 }
 
 void regcache_exit(struct regmap *map)
@@ -171,20 +193,24 @@ void regcache_exit(struct regmap *map)
 int regcache_read(struct regmap *map,
                  unsigned int reg, unsigned int *value)
 {
+       int ret;
+
        if (map->cache_type == REGCACHE_NONE)
                return -ENOSYS;
 
        BUG_ON(!map->cache_ops);
 
-       if (!regmap_readable(map, reg))
-               return -EIO;
+       if (!regmap_volatile(map, reg)) {
+               ret = map->cache_ops->read(map, reg, value);
 
-       if (!regmap_volatile(map, reg))
-               return map->cache_ops->read(map, reg, value);
+               if (ret == 0)
+                       trace_regmap_reg_read_cache(map->dev, reg, *value);
+
+               return ret;
+       }
 
        return -EINVAL;
 }
-EXPORT_SYMBOL_GPL(regcache_read);
 
 /**
  * regcache_write: Set the value of a given register in the cache.
@@ -211,7 +237,6 @@ int regcache_write(struct regmap *map,
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(regcache_write);
 
 /**
  * regcache_sync: Sync the register cache with the hardware.
@@ -241,6 +266,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 {
@@ -286,10 +327,28 @@ void regcache_cache_only(struct regmap *map, bool enable)
        mutex_lock(&map->lock);
        WARN_ON(map->cache_bypass && enable);
        map->cache_only = enable;
+       trace_regmap_cache_only(map->dev, enable);
        mutex_unlock(&map->lock);
 }
 EXPORT_SYMBOL_GPL(regcache_cache_only);
 
+/**
+ * regcache_mark_dirty: Mark the register cache as dirty
+ *
+ * @map: map to mark
+ *
+ * Mark the register cache as dirty, for example due to the device
+ * having been powered down for suspend.  If the cache is not marked
+ * as dirty then the cache sync will be suppressed.
+ */
+void regcache_mark_dirty(struct regmap *map)
+{
+       mutex_lock(&map->lock);
+       map->cache_dirty = true;
+       mutex_unlock(&map->lock);
+}
+EXPORT_SYMBOL_GPL(regcache_mark_dirty);
+
 /**
  * regcache_cache_bypass: Put a register map into cache bypass mode
  *
@@ -306,6 +365,7 @@ void regcache_cache_bypass(struct regmap *map, bool enable)
        mutex_lock(&map->lock);
        WARN_ON(map->cache_only && enable);
        map->cache_bypass = enable;
+       trace_regmap_cache_bypass(map->dev, enable);
        mutex_unlock(&map->lock);
 }
 EXPORT_SYMBOL_GPL(regcache_cache_bypass);
@@ -328,10 +388,16 @@ bool regcache_set_val(void *base, unsigned int idx,
                cache[idx] = val;
                break;
        }
+       case 4: {
+               u32 *cache = base;
+               if (cache[idx] == val)
+                       return true;
+               cache[idx] = val;
+               break;
+       }
        default:
                BUG();
        }
-       /* unreachable */
        return false;
 }
 
@@ -350,6 +416,10 @@ unsigned int regcache_get_val(const void *base, unsigned int idx,
                const u16 *cache = base;
                return cache[idx];
        }
+       case 4: {
+               const u32 *cache = base;
+               return cache[idx];
+       }
        default:
                BUG();
        }
@@ -381,22 +451,3 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg)
        else
                return -ENOENT;
 }
-
-int regcache_insert_reg(struct regmap *map, unsigned int reg,
-                       unsigned int val)
-{
-       void *tmp;
-
-       tmp = krealloc(map->reg_defaults,
-                      (map->num_reg_defaults + 1) * sizeof(struct reg_default),
-                      GFP_KERNEL);
-       if (!tmp)
-               return -ENOMEM;
-       map->reg_defaults = tmp;
-       map->num_reg_defaults++;
-       map->reg_defaults[map->num_reg_defaults - 1].reg = reg;
-       map->reg_defaults[map->num_reg_defaults - 1].def = val;
-       sort(map->reg_defaults, map->num_reg_defaults,
-            sizeof(struct reg_default), regcache_default_cmp, NULL);
-       return 0;
-}