regmap: Support some block operations on cached devices
[deliverable/linux.git] / drivers / base / regmap / regmap.c
index fa2bd896eb208fa8a92732eb6a2accf3ee3a4732..bf441db1ee90af507ac3597e6a32dd1a3cbfec10 100644 (file)
@@ -146,6 +146,20 @@ struct regmap *regmap_init(struct device *dev,
        map->readable_reg = config->readable_reg;
        map->volatile_reg = config->volatile_reg;
        map->precious_reg = config->precious_reg;
+       map->cache_type = config->cache_type;
+       map->reg_defaults = config->reg_defaults;
+       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_size_raw = (config->val_bits / 8) * config->num_reg_defaults_raw;
+       map->cache_word_size = config->val_bits / 8;
+
+       if (config->read_flag_mask || config->write_flag_mask) {
+               map->read_flag_mask = config->read_flag_mask;
+               map->write_flag_mask = config->write_flag_mask;
+       } else {
+               map->read_flag_mask = bus->read_flag_mask;
+       }
 
        switch (config->reg_bits) {
        case 4:
@@ -198,15 +212,17 @@ struct regmap *regmap_init(struct device *dev,
        map->work_buf = kmalloc(map->format.buf_size, GFP_KERNEL);
        if (map->work_buf == NULL) {
                ret = -ENOMEM;
-               goto err_bus;
+               goto err_map;
        }
 
+       ret = regcache_init(map);
+       if (ret < 0)
+               goto err_map;
+
        regmap_debugfs_init(map);
 
        return map;
 
-err_bus:
-       module_put(map->bus->owner);
 err_map:
        kfree(map);
 err:
@@ -219,9 +235,9 @@ EXPORT_SYMBOL_GPL(regmap_init);
  */
 void regmap_exit(struct regmap *map)
 {
+       regcache_exit(map);
        regmap_debugfs_exit(map);
        kfree(map->work_buf);
-       module_put(map->bus->owner);
        kfree(map);
 }
 EXPORT_SYMBOL_GPL(regmap_exit);
@@ -229,6 +245,7 @@ EXPORT_SYMBOL_GPL(regmap_exit);
 static int _regmap_raw_write(struct regmap *map, unsigned int reg,
                             const void *val, size_t val_len)
 {
+       u8 *u8 = map->work_buf;
        void *buf;
        int ret = -ENOTSUPP;
        size_t len;
@@ -242,6 +259,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
 
        map->format.format_reg(map->work_buf, reg);
 
+       u8[0] |= map->write_flag_mask;
+
        trace_regmap_hw_write_start(map->dev, reg,
                                    val_len / map->format.val_bytes);
 
@@ -277,12 +296,20 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
        return ret;
 }
 
-static int _regmap_write(struct regmap *map, unsigned int reg,
-                        unsigned int val)
+int _regmap_write(struct regmap *map, unsigned int reg,
+                 unsigned int val)
 {
        int ret;
        BUG_ON(!map->format.format_write && !map->format.format_val);
 
+       if (!map->cache_bypass) {
+               ret = regcache_write(map, reg, val);
+               if (ret != 0)
+                       return ret;
+               if (map->cache_only)
+                       return 0;
+       }
+
        trace_regmap_reg_write(map->dev, reg, val);
 
        if (map->format.format_write) {
@@ -350,6 +377,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
 {
        int ret;
 
+       WARN_ON(map->cache_type != REGCACHE_NONE);
+
        mutex_lock(&map->lock);
 
        ret = _regmap_raw_write(map, reg, val, val_len);
@@ -369,13 +398,12 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
        map->format.format_reg(map->work_buf, reg);
 
        /*
-        * Some buses flag reads by setting the high bits in the
+        * Some buses or devices flag reads by setting the high bits in the
         * register addresss; since it's always the high bits for all
         * current formats we can do this here rather than in
         * formatting.  This may break if we get interesting formats.
         */
-       if (map->bus->read_flag_mask)
-               u8[0] |= map->bus->read_flag_mask;
+       u8[0] |= map->read_flag_mask;
 
        trace_regmap_hw_read_start(map->dev, reg,
                                   val_len / map->format.val_bytes);
@@ -397,6 +425,15 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
        if (!map->format.parse_val)
                return -EINVAL;
 
+       if (!map->cache_bypass) {
+               ret = regcache_read(map, reg, val);
+               if (ret == 0)
+                       return 0;
+       }
+
+       if (map->cache_only)
+               return -EBUSY;
+
        ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
        if (ret == 0) {
                *val = map->format.parse_val(map->work_buf);
@@ -445,6 +482,14 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
                    size_t val_len)
 {
        int ret;
+       int i;
+       bool vol = true;
+
+       for (i = 0; i < val_len / map->format.val_bytes; i++)
+               if (!regmap_volatile(map, reg + i))
+                       vol = false;
+
+       WARN_ON(!vol && map->cache_type != REGCACHE_NONE);
 
        mutex_lock(&map->lock);
 
@@ -472,16 +517,30 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
 {
        int ret, i;
        size_t val_bytes = map->format.val_bytes;
+       bool vol = true;
 
        if (!map->format.parse_val)
                return -EINVAL;
 
-       ret = regmap_raw_read(map, reg, val, val_bytes * val_count);
-       if (ret != 0)
-               return ret;
+       /* Is this a block of volatile registers? */
+       for (i = 0; i < val_count; i++)
+               if (!regmap_volatile(map, reg + i))
+                       vol = false;
+
+       if (vol || map->cache_type == REGCACHE_NONE) {
+               ret = regmap_raw_read(map, reg, val, val_bytes * val_count);
+               if (ret != 0)
+                       return ret;
 
-       for (i = 0; i < val_count * val_bytes; i += val_bytes)
-               map->format.parse_val(val + i);
+               for (i = 0; i < val_count * val_bytes; i += val_bytes)
+                       map->format.parse_val(val + i);
+       } else {
+               for (i = 0; i < val_count; i++) {
+                       ret = regmap_read(map, reg + i, val + (i * val_bytes));
+                       if (ret != 0)
+                               return ret;
+               }
+       }
 
        return 0;
 }
This page took 0.029227 seconds and 5 git commands to generate.