From 28644c809f44498b8cd91d00b4cdb09e63b99843 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Mon, 19 Sep 2011 14:34:02 +0100 Subject: regmap: Add the rbtree cache support This patch adds support for the rbtree cache compression type. Each rbnode manages a variable length block of registers. There can be no two nodes with overlapping blocks. Each block has a base register and a currently top register, all the other registers, if any, lie in between these two and in ascending order. The reasoning behind the construction of this rbtree is simple. In the snd_soc_rbtree_cache_init() function, we iterate over the register defaults provided by the regcache core. For each register value that is non-zero we insert it in the rbtree. In order to determine in which rbnode we need to add the register, we first look if there is another register already added that is adjacent to the one we are about to add. If that is the case we append it in that rbnode block, otherwise we create a new rbnode with a single register in its block and add it to the tree. There are various optimizations across the implementation to speed up lookups by caching the most recently used rbnode. Signed-off-by: Dimitris Papastamos Tested-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 399 ++++++++++++++++++++++++++++++++++ 1 file changed, 399 insertions(+) create mode 100644 drivers/base/regmap/regcache-rbtree.c (limited to 'drivers/base/regmap/regcache-rbtree.c') diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c new file mode 100644 index 000000000000..4d7ba4511755 --- /dev/null +++ b/drivers/base/regmap/regcache-rbtree.c @@ -0,0 +1,399 @@ +/* + * Register cache access API - rbtree caching support + * + * Copyright 2011 Wolfson Microelectronics plc + * + * Author: Dimitris Papastamos + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include "internal.h" + +static int regcache_rbtree_write(struct regmap *map, unsigned int reg, + unsigned int value); + +struct regcache_rbtree_node { + /* the actual rbtree node holding this block */ + struct rb_node node; + /* base register handled by this block */ + unsigned int base_reg; + /* number of bytes needed to represent the register index */ + unsigned int word_size; + /* block of adjacent registers */ + void *block; + /* number of registers available in the block */ + unsigned int blklen; +} __attribute__ ((packed)); + +struct regcache_rbtree_ctx { + struct rb_root root; + struct regcache_rbtree_node *cached_rbnode; +}; + +static inline void regcache_rbtree_get_base_top_reg( + struct regcache_rbtree_node *rbnode, + unsigned int *base, unsigned int *top) +{ + *base = rbnode->base_reg; + *top = rbnode->base_reg + rbnode->blklen - 1; +} + +static unsigned int regcache_rbtree_get_register( + struct regcache_rbtree_node *rbnode, unsigned int idx) +{ + unsigned int val; + + switch (rbnode->word_size) { + case 1: { + u8 *p = rbnode->block; + val = p[idx]; + return val; + } + case 2: { + u16 *p = rbnode->block; + val = p[idx]; + return val; + } + default: + BUG(); + break; + } + return -1; +} + +static void regcache_rbtree_set_register(struct regcache_rbtree_node *rbnode, + unsigned int idx, unsigned int val) +{ + switch (rbnode->word_size) { + case 1: { + u8 *p = rbnode->block; + p[idx] = val; + break; + } + case 2: { + u16 *p = rbnode->block; + p[idx] = val; + break; + } + default: + BUG(); + break; + } +} + +static struct regcache_rbtree_node *regcache_rbtree_lookup( + struct rb_root *root, unsigned int reg) +{ + struct rb_node *node; + struct regcache_rbtree_node *rbnode; + unsigned int base_reg, top_reg; + + node = root->rb_node; + while (node) { + rbnode = container_of(node, struct regcache_rbtree_node, node); + regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + if (reg >= base_reg && reg <= top_reg) + return rbnode; + else if (reg > top_reg) + node = node->rb_right; + else if (reg < base_reg) + node = node->rb_left; + } + + return NULL; +} + +static int regcache_rbtree_insert(struct rb_root *root, + struct regcache_rbtree_node *rbnode) +{ + struct rb_node **new, *parent; + struct regcache_rbtree_node *rbnode_tmp; + unsigned int base_reg_tmp, top_reg_tmp; + unsigned int base_reg; + + parent = NULL; + new = &root->rb_node; + while (*new) { + rbnode_tmp = container_of(*new, struct regcache_rbtree_node, + node); + /* base and top registers of the current rbnode */ + regcache_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp, + &top_reg_tmp); + /* base register of the rbnode to be added */ + base_reg = rbnode->base_reg; + parent = *new; + /* if this register has already been inserted, just return */ + if (base_reg >= base_reg_tmp && + base_reg <= top_reg_tmp) + return 0; + else if (base_reg > top_reg_tmp) + new = &((*new)->rb_right); + else if (base_reg < base_reg_tmp) + new = &((*new)->rb_left); + } + + /* insert the node into the rbtree */ + rb_link_node(&rbnode->node, parent, new); + rb_insert_color(&rbnode->node, root); + + return 1; +} + +static int regcache_rbtree_init(struct regmap *map) +{ + struct regcache_rbtree_ctx *rbtree_ctx; + int i; + int ret; + + map->cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL); + if (!map->cache) + return -ENOMEM; + + rbtree_ctx = map->cache; + rbtree_ctx->root = RB_ROOT; + rbtree_ctx->cached_rbnode = NULL; + + for (i = 0; i < map->num_reg_defaults; i++) { + ret = regcache_rbtree_write(map, + map->reg_defaults[i].reg, + map->reg_defaults[i].def); + if (ret) + goto err; + } + + return 0; + +err: + regcache_exit(map); + return ret; +} + +static int regcache_rbtree_exit(struct regmap *map) +{ + struct rb_node *next; + struct regcache_rbtree_ctx *rbtree_ctx; + struct regcache_rbtree_node *rbtree_node; + + /* if we've already been called then just return */ + rbtree_ctx = map->cache; + if (!rbtree_ctx) + return 0; + + /* free up the rbtree */ + next = rb_first(&rbtree_ctx->root); + while (next) { + rbtree_node = rb_entry(next, struct regcache_rbtree_node, node); + next = rb_next(&rbtree_node->node); + rb_erase(&rbtree_node->node, &rbtree_ctx->root); + kfree(rbtree_node->block); + kfree(rbtree_node); + } + + /* release the resources */ + kfree(map->cache); + map->cache = NULL; + + return 0; +} + +static int regcache_rbtree_read(struct regmap *map, + unsigned int reg, unsigned int *value) +{ + struct regcache_rbtree_ctx *rbtree_ctx; + struct regcache_rbtree_node *rbnode; + unsigned int base_reg, top_reg; + unsigned int reg_tmp; + + rbtree_ctx = map->cache; + /* look up the required register in the cached rbnode */ + rbnode = rbtree_ctx->cached_rbnode; + if (rbnode) { + regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + if (reg >= base_reg && reg <= top_reg) { + reg_tmp = reg - base_reg; + *value = regcache_rbtree_get_register(rbnode, reg_tmp); + return 0; + } + } + /* if we can't locate it in the cached rbnode we'll have + * to traverse the rbtree looking for it. + */ + rbnode = regcache_rbtree_lookup(&rbtree_ctx->root, reg); + if (rbnode) { + reg_tmp = reg - rbnode->base_reg; + *value = regcache_rbtree_get_register(rbnode, reg_tmp); + rbtree_ctx->cached_rbnode = rbnode; + } else { + /* uninitialized registers default to 0 */ + *value = 0; + } + + return 0; +} + + +static int regcache_rbtree_insert_to_block(struct regcache_rbtree_node *rbnode, + unsigned int pos, unsigned int reg, + unsigned int value) +{ + u8 *blk; + + blk = krealloc(rbnode->block, + (rbnode->blklen + 1) * rbnode->word_size, GFP_KERNEL); + if (!blk) + return -ENOMEM; + + /* insert the register value in the correct place in the rbnode block */ + memmove(blk + (pos + 1) * rbnode->word_size, + blk + pos * rbnode->word_size, + (rbnode->blklen - pos) * rbnode->word_size); + + /* update the rbnode block, its size and the base register */ + rbnode->block = blk; + rbnode->blklen++; + if (!pos) + rbnode->base_reg = reg; + + regcache_rbtree_set_register(rbnode, pos, value); + return 0; +} + +static int regcache_rbtree_write(struct regmap *map, unsigned int reg, + unsigned int value) +{ + 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 base_reg, top_reg; + unsigned int pos; + int i; + int ret; + + rbtree_ctx = map->cache; + /* look up the required register in the cached rbnode */ + rbnode = rbtree_ctx->cached_rbnode; + if (rbnode) { + regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + if (reg >= base_reg && reg <= top_reg) { + reg_tmp = reg - base_reg; + val = regcache_rbtree_get_register(rbnode, reg_tmp); + if (val == value) + return 0; + regcache_rbtree_set_register(rbnode, reg_tmp, value); + return 0; + } + } + /* if we can't locate it in the cached rbnode we'll have + * to traverse the rbtree looking for it. + */ + rbnode = regcache_rbtree_lookup(&rbtree_ctx->root, reg); + if (rbnode) { + reg_tmp = reg - rbnode->base_reg; + val = regcache_rbtree_get_register(rbnode, reg_tmp); + if (val == value) + return 0; + regcache_rbtree_set_register(rbnode, reg_tmp, value); + rbtree_ctx->cached_rbnode = rbnode; + } else { + /* bail out early, no need to create the rbnode yet */ + if (!value) + return 0; + /* look for an adjacent register to the one we are about to add */ + for (node = rb_first(&rbtree_ctx->root); node; + node = rb_next(node)) { + rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, node); + for (i = 0; i < rbnode_tmp->blklen; i++) { + reg_tmp = rbnode_tmp->base_reg + i; + if (abs(reg_tmp - reg) != 1) + continue; + /* decide where in the block to place our register */ + if (reg_tmp + 1 == reg) + pos = i + 1; + else + pos = i; + ret = regcache_rbtree_insert_to_block(rbnode_tmp, pos, + reg, value); + if (ret) + return ret; + rbtree_ctx->cached_rbnode = rbnode_tmp; + return 0; + } + } + /* we did not manage to find a place to insert it in an existing + * block so create a new rbnode with a single register in its block. + * This block will get populated further if any other adjacent + * registers get modified in the future. + */ + rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL); + if (!rbnode) + return -ENOMEM; + rbnode->blklen = 1; + rbnode->base_reg = reg; + rbnode->word_size = map->cache_word_size; + rbnode->block = kmalloc(rbnode->blklen * rbnode->word_size, + GFP_KERNEL); + if (!rbnode->block) { + kfree(rbnode); + return -ENOMEM; + } + regcache_rbtree_set_register(rbnode, 0, value); + regcache_rbtree_insert(&rbtree_ctx->root, rbnode); + rbtree_ctx->cached_rbnode = rbnode; + } + + return 0; +} + +static int regcache_rbtree_sync(struct regmap *map) +{ + struct regcache_rbtree_ctx *rbtree_ctx; + struct rb_node *node; + struct regcache_rbtree_node *rbnode; + unsigned int regtmp; + unsigned int val, def; + int ret; + int i; + + rbtree_ctx = map->cache; + for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { + rbnode = rb_entry(node, struct regcache_rbtree_node, node); + for (i = 0; i < rbnode->blklen; i++) { + regtmp = rbnode->base_reg + i; + val = regcache_rbtree_get_register(rbnode, i); + ret = regcache_lookup_reg(map, i); + if (ret < 0) + def = 0; + else + def = map->reg_defaults[ret].def; + if (val == def) + continue; + map->cache_bypass = 1; + ret = regmap_write(map, regtmp, val); + map->cache_bypass = 0; + if (ret) + return ret; + dev_dbg(map->dev, "Synced register %#x, value %#x\n", + regtmp, val); + } + } + + return 0; +} + +struct regcache_ops regcache_rbtree_ops = { + .type = REGCACHE_RBTREE, + .name = "rbtree", + .init = regcache_rbtree_init, + .exit = regcache_rbtree_exit, + .read = regcache_rbtree_read, + .write = regcache_rbtree_write, + .sync = regcache_rbtree_sync +}; -- cgit v1.2.3 From 25ed1156ddf99f6d8feb87d0992b2ecb1fef667a Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Tue, 27 Sep 2011 11:25:07 +0100 Subject: regmap: Remove redundant member `word_size' from regcache_rbtree_node Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 53 ++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 23 deletions(-) (limited to 'drivers/base/regmap/regcache-rbtree.c') diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 4d7ba4511755..dd1b937a0d84 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -23,8 +23,6 @@ struct regcache_rbtree_node { struct rb_node node; /* base register handled by this block */ unsigned int base_reg; - /* number of bytes needed to represent the register index */ - unsigned int word_size; /* block of adjacent registers */ void *block; /* number of registers available in the block */ @@ -45,11 +43,12 @@ static inline void regcache_rbtree_get_base_top_reg( } static unsigned int regcache_rbtree_get_register( - struct regcache_rbtree_node *rbnode, unsigned int idx) + struct regcache_rbtree_node *rbnode, unsigned int idx, + unsigned int word_size) { unsigned int val; - switch (rbnode->word_size) { + switch (word_size) { case 1: { u8 *p = rbnode->block; val = p[idx]; @@ -68,9 +67,10 @@ static unsigned int regcache_rbtree_get_register( } static void regcache_rbtree_set_register(struct regcache_rbtree_node *rbnode, - unsigned int idx, unsigned int val) + unsigned int idx, unsigned int val, + unsigned int word_size) { - switch (rbnode->word_size) { + switch (word_size) { case 1: { u8 *p = rbnode->block; p[idx] = val; @@ -217,7 +217,8 @@ static int regcache_rbtree_read(struct regmap *map, regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); if (reg >= base_reg && reg <= top_reg) { reg_tmp = reg - base_reg; - *value = regcache_rbtree_get_register(rbnode, reg_tmp); + *value = regcache_rbtree_get_register(rbnode, reg_tmp, + map->cache_word_size); return 0; } } @@ -227,7 +228,8 @@ static int regcache_rbtree_read(struct regmap *map, rbnode = regcache_rbtree_lookup(&rbtree_ctx->root, reg); if (rbnode) { reg_tmp = reg - rbnode->base_reg; - *value = regcache_rbtree_get_register(rbnode, reg_tmp); + *value = regcache_rbtree_get_register(rbnode, reg_tmp, + map->cache_word_size); rbtree_ctx->cached_rbnode = rbnode; } else { /* uninitialized registers default to 0 */ @@ -240,19 +242,19 @@ static int regcache_rbtree_read(struct regmap *map, static int regcache_rbtree_insert_to_block(struct regcache_rbtree_node *rbnode, unsigned int pos, unsigned int reg, - unsigned int value) + unsigned int value, unsigned int word_size) { u8 *blk; blk = krealloc(rbnode->block, - (rbnode->blklen + 1) * rbnode->word_size, GFP_KERNEL); + (rbnode->blklen + 1) * word_size, GFP_KERNEL); if (!blk) return -ENOMEM; /* insert the register value in the correct place in the rbnode block */ - memmove(blk + (pos + 1) * rbnode->word_size, - blk + pos * rbnode->word_size, - (rbnode->blklen - pos) * rbnode->word_size); + memmove(blk + (pos + 1) * word_size, + blk + pos * word_size, + (rbnode->blklen - pos) * word_size); /* update the rbnode block, its size and the base register */ rbnode->block = blk; @@ -260,7 +262,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); + regcache_rbtree_set_register(rbnode, pos, value, word_size); return 0; } @@ -284,10 +286,12 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); if (reg >= base_reg && reg <= top_reg) { reg_tmp = reg - base_reg; - val = regcache_rbtree_get_register(rbnode, reg_tmp); + 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); + regcache_rbtree_set_register(rbnode, reg_tmp, value, + map->cache_word_size); return 0; } } @@ -297,10 +301,12 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, rbnode = regcache_rbtree_lookup(&rbtree_ctx->root, reg); if (rbnode) { reg_tmp = reg - rbnode->base_reg; - val = regcache_rbtree_get_register(rbnode, reg_tmp); + 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); + regcache_rbtree_set_register(rbnode, reg_tmp, value, + map->cache_word_size); rbtree_ctx->cached_rbnode = rbnode; } else { /* bail out early, no need to create the rbnode yet */ @@ -320,7 +326,8 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, else pos = i; ret = regcache_rbtree_insert_to_block(rbnode_tmp, pos, - reg, value); + reg, value, + map->cache_word_size); if (ret) return ret; rbtree_ctx->cached_rbnode = rbnode_tmp; @@ -337,14 +344,13 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, return -ENOMEM; rbnode->blklen = 1; rbnode->base_reg = reg; - rbnode->word_size = map->cache_word_size; - rbnode->block = kmalloc(rbnode->blklen * rbnode->word_size, + rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, GFP_KERNEL); if (!rbnode->block) { kfree(rbnode); return -ENOMEM; } - regcache_rbtree_set_register(rbnode, 0, value); + regcache_rbtree_set_register(rbnode, 0, value, map->cache_word_size); regcache_rbtree_insert(&rbtree_ctx->root, rbnode); rbtree_ctx->cached_rbnode = rbnode; } @@ -367,7 +373,8 @@ static int regcache_rbtree_sync(struct regmap *map) rbnode = rb_entry(node, struct regcache_rbtree_node, node); for (i = 0; i < rbnode->blklen; i++) { regtmp = rbnode->base_reg + i; - val = regcache_rbtree_get_register(rbnode, i); + val = regcache_rbtree_get_register(rbnode, i, + map->cache_word_size); ret = regcache_lookup_reg(map, i); if (ret < 0) def = 0; -- cgit v1.2.3 From c5713004b304e89c8c5117d8f226d5a1603571dc Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 27 Sep 2011 20:15:37 +0200 Subject: regmap: regcache_rbtree_{set,get}_register: Use regcache_{set,get}_val Use regcache_{set,get}_val in regcache_rbtree_{set,get}_register instead of re-implementing its functionality. Signed-off-by: Lars-Peter Clausen Acked-by: Dimitris Papastamos Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 35 ++--------------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) (limited to 'drivers/base/regmap/regcache-rbtree.c') diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index dd1b937a0d84..52669dec73b3 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -46,45 +46,14 @@ static unsigned int regcache_rbtree_get_register( struct regcache_rbtree_node *rbnode, unsigned int idx, unsigned int word_size) { - unsigned int val; - - switch (word_size) { - case 1: { - u8 *p = rbnode->block; - val = p[idx]; - return val; - } - case 2: { - u16 *p = rbnode->block; - val = p[idx]; - return val; - } - default: - BUG(); - break; - } - return -1; + return regcache_get_val(rbnode->block, idx, word_size); } static void regcache_rbtree_set_register(struct regcache_rbtree_node *rbnode, unsigned int idx, unsigned int val, unsigned int word_size) { - switch (word_size) { - case 1: { - u8 *p = rbnode->block; - p[idx] = val; - break; - } - case 2: { - u16 *p = rbnode->block; - p[idx] = val; - break; - } - default: - BUG(); - break; - } + regcache_set_val(rbnode->block, idx, val, word_size); } static struct regcache_rbtree_node *regcache_rbtree_lookup( -- cgit v1.2.3 From 3405addd220a0cf2e3a8ffb9051afe766e5f52e8 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 27 Sep 2011 20:15:38 +0200 Subject: regmap: rbtree-cache: Move cached rbnode handling into lookup function Move the handling of the cached rbnode into regcache_rbtree_lookup. This allows us to remove of some duplicated code sections in regcache_rbtree_read and regcache_rbtree_write. Signed-off-by: Lars-Peter Clausen Acked-by: Dimitris Papastamos Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 61 +++++++++++------------------------ 1 file changed, 18 insertions(+), 43 deletions(-) (limited to 'drivers/base/regmap/regcache-rbtree.c') diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 52669dec73b3..de32ced1917a 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -56,23 +56,33 @@ static void regcache_rbtree_set_register(struct regcache_rbtree_node *rbnode, regcache_set_val(rbnode->block, idx, val, word_size); } -static struct regcache_rbtree_node *regcache_rbtree_lookup( - struct rb_root *root, unsigned int reg) +static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map, + unsigned int reg) { + struct regcache_rbtree_ctx *rbtree_ctx = map->cache; struct rb_node *node; struct regcache_rbtree_node *rbnode; unsigned int base_reg, top_reg; - node = root->rb_node; + rbnode = rbtree_ctx->cached_rbnode; + if (rbnode) { + regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + if (reg >= base_reg && reg <= top_reg) + return rbnode; + } + + node = rbtree_ctx->root.rb_node; while (node) { rbnode = container_of(node, struct regcache_rbtree_node, node); regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); - if (reg >= base_reg && reg <= top_reg) + if (reg >= base_reg && reg <= top_reg) { + rbtree_ctx->cached_rbnode = rbnode; return rbnode; - else if (reg > top_reg) + } else if (reg > top_reg) { node = node->rb_right; - else if (reg < base_reg) + } else if (reg < base_reg) { node = node->rb_left; + } } return NULL; @@ -174,32 +184,14 @@ static int regcache_rbtree_exit(struct regmap *map) static int regcache_rbtree_read(struct regmap *map, unsigned int reg, unsigned int *value) { - struct regcache_rbtree_ctx *rbtree_ctx; struct regcache_rbtree_node *rbnode; - unsigned int base_reg, top_reg; unsigned int reg_tmp; - rbtree_ctx = map->cache; - /* look up the required register in the cached rbnode */ - rbnode = rbtree_ctx->cached_rbnode; - if (rbnode) { - regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); - if (reg >= base_reg && reg <= top_reg) { - reg_tmp = reg - base_reg; - *value = regcache_rbtree_get_register(rbnode, reg_tmp, - map->cache_word_size); - return 0; - } - } - /* if we can't locate it in the cached rbnode we'll have - * to traverse the rbtree looking for it. - */ - rbnode = regcache_rbtree_lookup(&rbtree_ctx->root, reg); + rbnode = regcache_rbtree_lookup(map, reg); if (rbnode) { reg_tmp = reg - rbnode->base_reg; *value = regcache_rbtree_get_register(rbnode, reg_tmp, map->cache_word_size); - rbtree_ctx->cached_rbnode = rbnode; } else { /* uninitialized registers default to 0 */ *value = 0; @@ -243,31 +235,15 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, struct rb_node *node; unsigned int val; unsigned int reg_tmp; - unsigned int base_reg, top_reg; unsigned int pos; int i; int ret; rbtree_ctx = map->cache; - /* look up the required register in the cached rbnode */ - rbnode = rbtree_ctx->cached_rbnode; - if (rbnode) { - regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); - if (reg >= base_reg && reg <= top_reg) { - reg_tmp = reg - base_reg; - 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); - return 0; - } - } /* if we can't locate it in the cached rbnode we'll have * to traverse the rbtree looking for it. */ - rbnode = regcache_rbtree_lookup(&rbtree_ctx->root, reg); + rbnode = regcache_rbtree_lookup(map, reg); if (rbnode) { reg_tmp = reg - rbnode->base_reg; val = regcache_rbtree_get_register(rbnode, reg_tmp, @@ -276,7 +252,6 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, return 0; regcache_rbtree_set_register(rbnode, reg_tmp, value, map->cache_word_size); - rbtree_ctx->cached_rbnode = rbnode; } else { /* bail out early, no need to create the rbnode yet */ if (!value) -- cgit v1.2.3 From 13753a9088af23c61e2f5c10a8f3ea136d8ebab5 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Thu, 29 Sep 2011 14:36:25 +0100 Subject: regmap: Lock the sync path, ensure we use the lockless _regmap_write() Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-indexed.c | 4 ++-- drivers/base/regmap/regcache-lzo.c | 2 +- drivers/base/regmap/regcache-rbtree.c | 2 +- drivers/base/regmap/regcache.c | 4 +++- 4 files changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/base/regmap/regcache-rbtree.c') diff --git a/drivers/base/regmap/regcache-indexed.c b/drivers/base/regmap/regcache-indexed.c index 268497aee46d..2e10bb13bfc4 100644 --- a/drivers/base/regmap/regcache-indexed.c +++ b/drivers/base/regmap/regcache-indexed.c @@ -45,8 +45,8 @@ static int regcache_indexed_sync(struct regmap *map) int ret; for (i = 0; i < map->num_reg_defaults; i++) { - ret = regmap_write(map, map->reg_defaults[i].reg, - map->reg_defaults[i].def); + ret = _regmap_write(map, map->reg_defaults[i].reg, + map->reg_defaults[i].def); if (ret < 0) return ret; dev_dbg(map->dev, "Synced register %#x, value %#x\n", diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index 9079cb50b0b9..ad6af925f56c 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -339,7 +339,7 @@ static int regcache_lzo_sync(struct regmap *map) if (ret) return ret; map->cache_bypass = 1; - ret = regmap_write(map, i, val); + ret = _regmap_write(map, i, val); map->cache_bypass = 0; if (ret) return ret; diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index de32ced1917a..40f23dd8478c 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -327,7 +327,7 @@ static int regcache_rbtree_sync(struct regmap *map) if (val == def) continue; map->cache_bypass = 1; - ret = regmap_write(map, regtmp, val); + ret = _regmap_write(map, regtmp, val); map->cache_bypass = 0; if (ret) return ret; diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 2caf6e49c389..59e432c0163d 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -230,6 +230,7 @@ int regcache_sync(struct regmap *map) BUG_ON(!map->cache_ops); + mutex_lock(&map->lock); dev_dbg(map->dev, "Syncing %s cache\n", map->cache_ops->name); name = map->cache_ops->name; @@ -242,7 +243,7 @@ int regcache_sync(struct regmap *map) if (ret < 0) goto out; map->cache_bypass = 1; - ret = regmap_write(map, i, val); + ret = _regmap_write(map, i, val); map->cache_bypass = 0; if (ret < 0) goto out; @@ -254,6 +255,7 @@ int regcache_sync(struct regmap *map) } out: trace_regcache_sync(map->dev, name, "stop"); + mutex_unlock(&map->lock); return ret; } -- cgit v1.2.3 From 6e6ace00a045251bd172b9b9c2379857bbff3dc7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 9 Oct 2011 13:23:31 +0100 Subject: regmap: Return a sensible error code if we fail to read the cache If a register isn't cached then let callers know that so they can fall back or error handle appropriately. Signed-off-by: Mark Brown Acked-by: Dimitris Papastamos --- drivers/base/regmap/regcache-indexed.c | 7 +++---- drivers/base/regmap/regcache-lzo.c | 4 ++-- drivers/base/regmap/regcache-rbtree.c | 3 +-- drivers/base/regmap/regcache.c | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) (limited to 'drivers/base/regmap/regcache-rbtree.c') diff --git a/drivers/base/regmap/regcache-indexed.c b/drivers/base/regmap/regcache-indexed.c index 2e10bb13bfc4..507731ad8ec1 100644 --- a/drivers/base/regmap/regcache-indexed.c +++ b/drivers/base/regmap/regcache-indexed.c @@ -20,11 +20,10 @@ static int regcache_indexed_read(struct regmap *map, unsigned int reg, int ret; ret = regcache_lookup_reg(map, reg); - if (ret < 0) - *value = 0; - else + if (ret >= 0) *value = map->reg_defaults[ret].def; - return 0; + + return ret; } static int regcache_indexed_write(struct regmap *map, unsigned int reg, diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index ad6af925f56c..066aeece3626 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -232,7 +232,6 @@ static int regcache_lzo_read(struct regmap *map, size_t blksize, tmp_dst_len; void *tmp_dst; - *value = 0; /* index of the compressed lzo block */ blkindex = regcache_lzo_get_blkindex(map, reg); /* register index within the decompressed block */ @@ -261,7 +260,8 @@ static int regcache_lzo_read(struct regmap *map, /* restore the pointer and length of the compressed block */ lzo_block->dst = tmp_dst; lzo_block->dst_len = tmp_dst_len; - return 0; + + return ret; } static int regcache_lzo_write(struct regmap *map, diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 40f23dd8478c..887dbce63aff 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -193,8 +193,7 @@ static int regcache_rbtree_read(struct regmap *map, *value = regcache_rbtree_get_register(rbnode, reg_tmp, map->cache_word_size); } else { - /* uninitialized registers default to 0 */ - *value = 0; + return -ENOENT; } return 0; diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index c5379c86de88..409abd282c6c 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -378,7 +378,7 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg) if (r) return r - map->reg_defaults; else - return -1; + return -ENOENT; } int regcache_insert_reg(struct regmap *map, unsigned int reg, -- cgit v1.2.3 From e42c5a9a4230c38ceba0a890b30a2d0dd9314bff Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 9 Oct 2011 14:30:02 +0100 Subject: regmap: Allow rbtree to cache zero default values Ensure that when we start up in cache only mode we can store defaults of zero, otherwise if the hardware is unavailable we won't be able to read. Signed-off-by: Mark Brown Acked-by: Dimitris Papastamos --- drivers/base/regmap/regcache-rbtree.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/base/regmap/regcache-rbtree.c') diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 887dbce63aff..52511f95857a 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -252,9 +252,6 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, regcache_rbtree_set_register(rbnode, reg_tmp, value, map->cache_word_size); } else { - /* bail out early, no need to create the rbnode yet */ - if (!value) - return 0; /* look for an adjacent register to the one we are about to add */ for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { -- cgit v1.2.3 From b03622a80d2206c4179d6a41a0dc5cfbdfc853ee Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 9 Oct 2011 12:54:25 +0100 Subject: regmap: Ensure rbtree syncs registers set to zero properly Simplify the check for registers set at their default value by avoiding picking a default value in the case where we don't have one. Instead we only compare the current value to the current value when we looked one up. This fixes the case where we don't have a default stored but the value was set to zero when that isn't the chip default. Signed-off-by: Mark Brown Acked-by: Dimitris Papastamos --- drivers/base/regmap/regcache-rbtree.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/base/regmap/regcache-rbtree.c') diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 52511f95857a..e31498499b0f 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -304,7 +304,7 @@ static int regcache_rbtree_sync(struct regmap *map) struct rb_node *node; struct regcache_rbtree_node *rbnode; unsigned int regtmp; - unsigned int val, def; + unsigned int val; int ret; int i; @@ -315,13 +315,12 @@ static int regcache_rbtree_sync(struct regmap *map) regtmp = rbnode->base_reg + i; val = regcache_rbtree_get_register(rbnode, i, map->cache_word_size); + + /* Is this the hardware default? If so skip. */ ret = regcache_lookup_reg(map, i); - if (ret < 0) - def = 0; - else - def = map->reg_defaults[ret].def; - if (val == def) + if (ret > 0 && val == map->reg_defaults[ret].def) continue; + map->cache_bypass = 1; ret = _regmap_write(map, regtmp, val); map->cache_bypass = 0; -- cgit v1.2.3