]> Pileus Git - ~andy/linux/blobdiff - drivers/base/regmap/regcache-rbtree.c
regmap: cache: Factor out reg_present support from rbtree cache
[~andy/linux] / drivers / base / regmap / regcache-rbtree.c
index e6732cf7c06eea7aedf62e383a390ef3e61c3fe2..00c3506f542ffee82944491d17e1928a27435581 100644 (file)
@@ -47,22 +47,27 @@ static inline void regcache_rbtree_get_base_top_reg(
        *top = rbnode->base_reg + ((rbnode->blklen - 1) * map->reg_stride);
 }
 
-static unsigned int regcache_rbtree_get_register(
-       struct regcache_rbtree_node *rbnode, unsigned int idx,
-       unsigned int word_size)
+static unsigned int regcache_rbtree_get_register(struct regmap *map,
+       struct regcache_rbtree_node *rbnode, unsigned int idx)
 {
-       return regcache_get_val(rbnode->block, idx, word_size);
+       return regcache_get_val(map, rbnode->block, idx);
 }
 
-static void regcache_rbtree_set_register(struct regcache_rbtree_node *rbnode,
-                                        unsigned int idx, unsigned int val,
-                                        unsigned int word_size)
+static const void *regcache_rbtree_get_reg_addr(struct regmap *map,
+       struct regcache_rbtree_node *rbnode, unsigned int idx)
 {
-       regcache_set_val(rbnode->block, idx, val, word_size);
+       return regcache_get_val_addr(map, rbnode->block, idx);
+}
+
+static void regcache_rbtree_set_register(struct regmap *map,
+                                        struct regcache_rbtree_node *rbnode,
+                                        unsigned int idx, unsigned int val)
+{
+       regcache_set_val(map, rbnode->block, idx, val);
 }
 
 static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
-       unsigned int reg)
+                                                          unsigned int reg)
 {
        struct regcache_rbtree_ctx *rbtree_ctx = map->cache;
        struct rb_node *node;
@@ -139,15 +144,21 @@ static int rbtree_show(struct seq_file *s, void *ignored)
        struct regcache_rbtree_node *n;
        struct rb_node *node;
        unsigned int base, top;
+       size_t mem_size;
        int nodes = 0;
        int registers = 0;
        int this_registers, average;
 
        map->lock(map);
 
+       mem_size = sizeof(*rbtree_ctx);
+       mem_size += BITS_TO_LONGS(map->cache_present_nbits) * sizeof(long);
+
        for (node = rb_first(&rbtree_ctx->root); node != NULL;
             node = rb_next(node)) {
                n = container_of(node, struct regcache_rbtree_node, node);
+               mem_size += sizeof(*n);
+               mem_size += (n->blklen * map->cache_word_size);
 
                regcache_rbtree_get_base_top_reg(map, n, &base, &top);
                this_registers = ((top - base) / map->reg_stride) + 1;
@@ -162,8 +173,8 @@ static int rbtree_show(struct seq_file *s, void *ignored)
        else
                average = 0;
 
-       seq_printf(s, "%d nodes, %d registers, average %d registers\n",
-                  nodes, registers, average);
+       seq_printf(s, "%d nodes, %d registers, average %d registers, used %zu bytes\n",
+                  nodes, registers, average, mem_size);
 
        map->unlock(map);
 
@@ -260,8 +271,9 @@ static int regcache_rbtree_read(struct regmap *map,
        rbnode = regcache_rbtree_lookup(map, reg);
        if (rbnode) {
                reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
-               *value = regcache_rbtree_get_register(rbnode, reg_tmp,
-                                                     map->cache_word_size);
+               if (!regcache_reg_present(map, reg))
+                       return -ENOENT;
+               *value = regcache_rbtree_get_register(map, rbnode, reg_tmp);
        } else {
                return -ENOENT;
        }
@@ -270,21 +282,23 @@ static int regcache_rbtree_read(struct regmap *map,
 }
 
 
-static int regcache_rbtree_insert_to_block(struct regcache_rbtree_node *rbnode,
+static int regcache_rbtree_insert_to_block(struct regmap *map,
+                                          struct regcache_rbtree_node *rbnode,
                                           unsigned int pos, unsigned int reg,
-                                          unsigned int value, unsigned int word_size)
+                                          unsigned int value)
 {
        u8 *blk;
 
        blk = krealloc(rbnode->block,
-                      (rbnode->blklen + 1) * word_size, GFP_KERNEL);
+                      (rbnode->blklen + 1) * map->cache_word_size,
+                      GFP_KERNEL);
        if (!blk)
                return -ENOMEM;
 
        /* insert the register value in the correct place in the rbnode block */
-       memmove(blk + (pos + 1) * word_size,
-               blk + pos * word_size,
-               (rbnode->blklen - pos) * word_size);
+       memmove(blk + (pos + 1) * map->cache_word_size,
+               blk + pos * map->cache_word_size,
+               (rbnode->blklen - pos) * map->cache_word_size);
 
        /* update the rbnode block, its size and the base register */
        rbnode->block = blk;
@@ -292,7 +306,7 @@ static int regcache_rbtree_insert_to_block(struct regcache_rbtree_node *rbnode,
        if (!pos)
                rbnode->base_reg = reg;
 
-       regcache_rbtree_set_register(rbnode, pos, value, word_size);
+       regcache_rbtree_set_register(map, rbnode, pos, value);
        return 0;
 }
 
@@ -302,25 +316,24 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
        struct regcache_rbtree_ctx *rbtree_ctx;
        struct regcache_rbtree_node *rbnode, *rbnode_tmp;
        struct rb_node *node;
-       unsigned int val;
        unsigned int reg_tmp;
        unsigned int pos;
        int i;
        int ret;
 
        rbtree_ctx = map->cache;
+       /* update the reg_present bitmap, make space if necessary */
+       ret = regcache_set_reg_present(map, reg);
+       if (ret < 0)
+               return ret;
+
        /* if we can't locate it in the cached rbnode we'll have
         * to traverse the rbtree looking for it.
         */
        rbnode = regcache_rbtree_lookup(map, reg);
        if (rbnode) {
                reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
-               val = regcache_rbtree_get_register(rbnode, reg_tmp,
-                                                  map->cache_word_size);
-               if (val == value)
-                       return 0;
-               regcache_rbtree_set_register(rbnode, reg_tmp, value,
-                                            map->cache_word_size);
+               regcache_rbtree_set_register(map, rbnode, reg_tmp, value);
        } else {
                /* look for an adjacent register to the one we are about to add */
                for (node = rb_first(&rbtree_ctx->root); node;
@@ -337,9 +350,10 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
                                        pos = i + 1;
                                else
                                        pos = i;
-                               ret = regcache_rbtree_insert_to_block(rbnode_tmp, pos,
-                                                                     reg, value,
-                                                                     map->cache_word_size);
+                               ret = regcache_rbtree_insert_to_block(map,
+                                                                     rbnode_tmp,
+                                                                     pos, reg,
+                                                                     value);
                                if (ret)
                                        return ret;
                                rbtree_ctx->cached_rbnode = rbnode_tmp;
@@ -354,7 +368,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
                rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL);
                if (!rbnode)
                        return -ENOMEM;
-               rbnode->blklen = 1;
+               rbnode->blklen = sizeof(*rbnode);
                rbnode->base_reg = reg;
                rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size,
                                        GFP_KERNEL);
@@ -362,7 +376,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
                        kfree(rbnode);
                        return -ENOMEM;
                }
-               regcache_rbtree_set_register(rbnode, 0, value, map->cache_word_size);
+               regcache_rbtree_set_register(map, rbnode, 0, value);
                regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode);
                rbtree_ctx->cached_rbnode = rbnode;
        }
@@ -378,6 +392,7 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
        struct regcache_rbtree_node *rbnode;
        unsigned int regtmp;
        unsigned int val;
+       const void *addr;
        int ret;
        int i, base, end;
 
@@ -404,8 +419,11 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
 
                for (i = base; i < end; i++) {
                        regtmp = rbnode->base_reg + (i * map->reg_stride);
-                       val = regcache_rbtree_get_register(rbnode, i,
-                                                          map->cache_word_size);
+
+                       if (!regcache_reg_present(map, regtmp))
+                               continue;
+
+                       val = regcache_rbtree_get_register(map, rbnode, i);
 
                        /* Is this the hardware default?  If so skip. */
                        ret = regcache_lookup_reg(map, regtmp);
@@ -413,7 +431,17 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
                                continue;
 
                        map->cache_bypass = 1;
-                       ret = _regmap_write(map, regtmp, val);
+
+                       if (regmap_can_raw_write(map)) {
+                               addr = regcache_rbtree_get_reg_addr(map,
+                                                                   rbnode, i);
+                               ret = _regmap_raw_write(map, regtmp, addr,
+                                                       map->format.val_bytes,
+                                                       false);
+                       } else {
+                               ret = _regmap_write(map, regtmp, val);
+                       }
+
                        map->cache_bypass = 0;
                        if (ret)
                                return ret;