From 66baf407571662f7e2a22dd0764cbe279559446c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 21 Feb 2013 18:01:54 +0000 Subject: regmap: rbtree: Don't bother checking for noop updates If we're updating a value in place it's more work to read the value and compare the value with what we're about to set than it is to just write the value into the cache; there are no further operations after writing in the code even though there's an early return here. Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index e6732cf7c06..3f21c6ab296 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -302,7 +302,6 @@ 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; @@ -315,10 +314,6 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, 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); } else { -- cgit v1.2.3 From 879082c9fe6e8fbddf787170eee605e4be138d0f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 21 Feb 2013 18:03:13 +0000 Subject: regmap: cache: Pass the map rather than the word size when updating values It's more idiomatic to pass the map structure around and this means we can use other bits of information from the map. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 8 +++--- drivers/base/regmap/regcache-lzo.c | 6 ++--- drivers/base/regmap/regcache-rbtree.c | 51 +++++++++++++++++------------------ drivers/base/regmap/regcache.c | 18 ++++++------- 4 files changed, 39 insertions(+), 44 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 5a22bd33ce3..582d7fdf414 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -188,10 +188,10 @@ int regcache_write(struct regmap *map, unsigned int reg, unsigned int value); int regcache_sync(struct regmap *map); -unsigned int regcache_get_val(const void *base, unsigned int idx, - unsigned int word_size); -bool regcache_set_val(void *base, unsigned int idx, - unsigned int val, unsigned int word_size); +unsigned int regcache_get_val(struct regmap *map, const void *base, + unsigned int idx); +bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, + unsigned int val); int regcache_lookup_reg(struct regmap *map, unsigned int reg); void regmap_async_complete_cb(struct regmap_async *async, int ret); diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index afd6aa91a0d..e210a6d1406 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -260,8 +260,7 @@ static int regcache_lzo_read(struct regmap *map, ret = regcache_lzo_decompress_cache_block(map, lzo_block); if (ret >= 0) /* fetch the value from the cache */ - *value = regcache_get_val(lzo_block->dst, blkpos, - map->cache_word_size); + *value = regcache_get_val(map, lzo_block->dst, blkpos); kfree(lzo_block->dst); /* restore the pointer and length of the compressed block */ @@ -304,8 +303,7 @@ static int regcache_lzo_write(struct regmap *map, } /* write the new value to the cache */ - if (regcache_set_val(lzo_block->dst, blkpos, value, - map->cache_word_size)) { + if (regcache_set_val(map, lzo_block->dst, blkpos, value)) { kfree(lzo_block->dst); goto out; } diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 3f21c6ab296..461cff888bb 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -47,22 +47,21 @@ 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 void regcache_rbtree_set_register(struct regmap *map, + struct regcache_rbtree_node *rbnode, + unsigned int idx, unsigned int val) { - regcache_set_val(rbnode->block, idx, val, word_size); + 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; @@ -260,8 +259,7 @@ 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); + *value = regcache_rbtree_get_register(map, rbnode, reg_tmp); } else { return -ENOENT; } @@ -270,21 +268,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 +292,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; } @@ -314,8 +314,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, rbnode = regcache_rbtree_lookup(map, reg); if (rbnode) { reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; - 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; @@ -332,9 +331,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; @@ -357,7 +357,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; } @@ -399,8 +399,7 @@ 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); + val = regcache_rbtree_get_register(map, rbnode, i); /* Is this the hardware default? If so skip. */ ret = regcache_lookup_reg(map, regtmp); diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index e69ff3e4742..f0a3db6ff9c 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -58,8 +58,7 @@ static int regcache_hw_init(struct regmap *map) /* calculate the size of reg_defaults */ 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); + val = regcache_get_val(map, map->reg_defaults_raw, i); if (regmap_volatile(map, i * map->reg_stride)) continue; count++; @@ -75,8 +74,7 @@ static int regcache_hw_init(struct regmap *map) /* 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); + val = regcache_get_val(map, map->reg_defaults_raw, i); if (regmap_volatile(map, i * map->reg_stride)) continue; map->reg_defaults[j].reg = i * map->reg_stride; @@ -417,10 +415,10 @@ void regcache_cache_bypass(struct regmap *map, bool enable) } EXPORT_SYMBOL_GPL(regcache_cache_bypass); -bool regcache_set_val(void *base, unsigned int idx, - unsigned int val, unsigned int word_size) +bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, + unsigned int val) { - switch (word_size) { + switch (map->cache_word_size) { case 1: { u8 *cache = base; if (cache[idx] == val) @@ -448,13 +446,13 @@ bool regcache_set_val(void *base, unsigned int idx, return false; } -unsigned int regcache_get_val(const void *base, unsigned int idx, - unsigned int word_size) +unsigned int regcache_get_val(struct regmap *map, const void *base, + unsigned int idx) { if (!base) return -EINVAL; - switch (word_size) { + switch (map->cache_word_size) { case 1: { const u8 *cache = base; return cache[idx]; -- cgit v1.2.3 From 325acab447f775bc2258b3a37a780893c203ab6c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 21 Feb 2013 18:07:01 +0000 Subject: regmap: cache: Use regcache_get_value() to check if we updated Factor things out a little. Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index f0a3db6ff9c..6948996d249 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -418,25 +418,22 @@ EXPORT_SYMBOL_GPL(regcache_cache_bypass); bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, unsigned int val) { + if (regcache_get_val(map, base, idx) == val) + return true; + switch (map->cache_word_size) { case 1: { u8 *cache = base; - if (cache[idx] == val) - return true; cache[idx] = val; break; } case 2: { u16 *cache = base; - if (cache[idx] == val) - return true; cache[idx] = val; break; } case 4: { u32 *cache = base; - if (cache[idx] == val) - return true; cache[idx] = val; break; } -- cgit v1.2.3 From 8a819ff8abac9ad49f120c84cce01878b3d235c2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 4 Mar 2013 09:04:51 +0800 Subject: regmap: core: Split out in place value parsing Currently the value parsing operations both return the parsed value and modify the passed buffer. This precludes their use in places like the cache code so split out the in place modification into a new parse_inplace() operation. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 3 ++- drivers/base/regmap/regmap.c | 52 +++++++++++++++++++++++++++++------------- 2 files changed, 38 insertions(+), 17 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 582d7fdf414..2b5851d42db 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -38,7 +38,8 @@ struct regmap_format { unsigned int reg, unsigned int val); void (*format_reg)(void *buf, unsigned int reg, unsigned int shift); void (*format_val)(void *buf, unsigned int val, unsigned int shift); - unsigned int (*parse_val)(void *buf); + unsigned int (*parse_val)(const void *buf); + void (*parse_inplace)(void *buf); }; struct regmap_async { diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 3d2367501fd..aff5a8b7394 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -228,30 +228,39 @@ static void regmap_format_32_native(void *buf, unsigned int val, *(u32 *)buf = val << shift; } -static unsigned int regmap_parse_8(void *buf) +static void regmap_parse_inplace_noop(void *buf) { - u8 *b = buf; +} + +static unsigned int regmap_parse_8(const void *buf) +{ + const u8 *b = buf; return b[0]; } -static unsigned int regmap_parse_16_be(void *buf) +static unsigned int regmap_parse_16_be(const void *buf) +{ + const __be16 *b = buf; + + return be16_to_cpu(b[0]); +} + +static void regmap_parse_16_be_inplace(void *buf) { __be16 *b = buf; b[0] = be16_to_cpu(b[0]); - - return b[0]; } -static unsigned int regmap_parse_16_native(void *buf) +static unsigned int regmap_parse_16_native(const void *buf) { return *(u16 *)buf; } -static unsigned int regmap_parse_24(void *buf) +static unsigned int regmap_parse_24(const void *buf) { - u8 *b = buf; + const u8 *b = buf; unsigned int ret = b[2]; ret |= ((unsigned int)b[1]) << 8; ret |= ((unsigned int)b[0]) << 16; @@ -259,16 +268,21 @@ static unsigned int regmap_parse_24(void *buf) return ret; } -static unsigned int regmap_parse_32_be(void *buf) +static unsigned int regmap_parse_32_be(const void *buf) +{ + const __be32 *b = buf; + + return be32_to_cpu(b[0]); +} + +static void regmap_parse_32_be_inplace(void *buf) { __be32 *b = buf; b[0] = be32_to_cpu(b[0]); - - return b[0]; } -static unsigned int regmap_parse_32_native(void *buf) +static unsigned int regmap_parse_32_native(const void *buf) { return *(u32 *)buf; } @@ -555,16 +569,21 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } + if (val_endian == REGMAP_ENDIAN_NATIVE) + map->format.parse_inplace = regmap_parse_inplace_noop; + switch (config->val_bits) { case 8: map->format.format_val = regmap_format_8; map->format.parse_val = regmap_parse_8; + map->format.parse_inplace = regmap_parse_inplace_noop; break; case 16: switch (val_endian) { case REGMAP_ENDIAN_BIG: map->format.format_val = regmap_format_16_be; map->format.parse_val = regmap_parse_16_be; + map->format.parse_inplace = regmap_parse_16_be_inplace; break; case REGMAP_ENDIAN_NATIVE: map->format.format_val = regmap_format_16_native; @@ -585,6 +604,7 @@ struct regmap *regmap_init(struct device *dev, case REGMAP_ENDIAN_BIG: map->format.format_val = regmap_format_32_be; map->format.parse_val = regmap_parse_32_be; + map->format.parse_inplace = regmap_parse_32_be_inplace; break; case REGMAP_ENDIAN_NATIVE: map->format.format_val = regmap_format_32_native; @@ -1240,7 +1260,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (!map->bus) return -EINVAL; - if (!map->format.parse_val) + if (!map->format.parse_inplace) return -EINVAL; if (reg % map->reg_stride) return -EINVAL; @@ -1258,7 +1278,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, goto out; } for (i = 0; i < val_count * val_bytes; i += val_bytes) - map->format.parse_val(wval + i); + map->format.parse_inplace(wval + i); } /* * Some devices does not support bulk write, for @@ -1519,7 +1539,7 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, if (!map->bus) return -EINVAL; - if (!map->format.parse_val) + if (!map->format.parse_inplace) return -EINVAL; if (reg % map->reg_stride) return -EINVAL; @@ -1546,7 +1566,7 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, } for (i = 0; i < val_count * val_bytes; i += val_bytes) - map->format.parse_val(val + i); + map->format.parse_inplace(val + i); } else { for (i = 0; i < val_count; i++) { unsigned int ival; -- cgit v1.2.3 From eb4cb76ff00e27858e5c80f69dbe8cc15364578c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 21 Feb 2013 18:39:47 +0000 Subject: regmap: cache: Store caches in native register format where possible This allows the cached data to be sent directly to the device when we sync it. Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 6948996d249..0f4fb8bc37e 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -45,8 +45,8 @@ static int regcache_hw_init(struct regmap *map) tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL); if (!tmp_buf) return -EINVAL; - ret = regmap_bulk_read(map, 0, tmp_buf, - map->num_reg_defaults_raw); + ret = regmap_raw_read(map, 0, tmp_buf, + map->num_reg_defaults_raw); map->cache_bypass = cache_bypass; if (ret < 0) { kfree(tmp_buf); @@ -421,6 +421,13 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, if (regcache_get_val(map, base, idx) == val) return true; + /* Use device native format if possible */ + if (map->format.format_val) { + map->format.format_val(base + (map->cache_word_size * idx), + val, 0); + return false; + } + switch (map->cache_word_size) { case 1: { u8 *cache = base; @@ -449,6 +456,11 @@ unsigned int regcache_get_val(struct regmap *map, const void *base, if (!base) return -EINVAL; + /* Use device native format if possible */ + if (map->format.parse_val) + return map->format.parse_val(base + + (map->cache_word_size * idx)); + switch (map->cache_word_size) { case 1: { const u8 *cache = base; -- cgit v1.2.3 From a42277c739c29b06cb27502347f557e11fed8b0e Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Tue, 12 Mar 2013 17:26:49 +0000 Subject: regmap: rbtree Expose total memory consumption in the rbtree debugfs entry Provide a feel of how much overhead the rbtree cache adds to the game. [Slightly reworded output in debugfs -- broonie] Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 461cff888bb..04531961560 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -138,15 +138,20 @@ 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); + 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; @@ -161,8 +166,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); -- cgit v1.2.3 From f1b5c5c3423b59056d3ca956d2e795b7927d6008 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 13 Mar 2013 19:18:13 +0000 Subject: regmap: core: Warn on invalid operation combinations Don't grind to a screaming halt, just generate a warning. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index aff5a8b7394..44a45cf0644 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -950,7 +950,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, size_t len; int i; - BUG_ON(!map->bus); + WARN_ON(!map->bus); /* Check for unwritable registers before we start */ if (map->writeable_reg) @@ -1104,7 +1104,7 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg, struct regmap_range_node *range; struct regmap *map = context; - BUG_ON(!map->bus || !map->format.format_write); + WARN_ON(!map->bus || !map->format.format_write); range = _regmap_range_lookup(map, reg); if (range) { @@ -1130,7 +1130,7 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg, { struct regmap *map = context; - BUG_ON(!map->bus || !map->format.format_val); + WARN_ON(!map->bus || !map->format.format_val); map->format.format_val(map->work_buf + map->format.reg_bytes + map->format.pad_bytes, val, 0); @@ -1356,7 +1356,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, u8 *u8 = map->work_buf; int ret; - BUG_ON(!map->bus); + WARN_ON(!map->bus); range = _regmap_range_lookup(map, reg); if (range) { @@ -1411,7 +1411,7 @@ static int _regmap_read(struct regmap *map, unsigned int reg, int ret; void *context = _regmap_map_get_context(map); - BUG_ON(!map->reg_read); + WARN_ON(!map->reg_read); if (!map->cache_bypass) { ret = regcache_read(map, reg, val); -- cgit v1.2.3 From 584de329ca43cc6d73eb74885e1d5d9fc0549423 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 13 Mar 2013 19:19:34 +0000 Subject: regmap: core: Make raw write available to regcache This allows the cache to sync values directly to the device when stored in native format and also allows asynchronous I/O. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 3 +++ drivers/base/regmap/regmap.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 2b5851d42db..6d409350f50 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -195,6 +195,9 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, unsigned int val); int regcache_lookup_reg(struct regmap *map, unsigned int reg); +int _regmap_raw_write(struct regmap *map, unsigned int reg, + const void *val, size_t val_len, bool async); + void regmap_async_complete_cb(struct regmap_async *async, int ret); extern struct regcache_ops regcache_rbtree_ops; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 44a45cf0644..9174c9d45a1 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -937,8 +937,8 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg, return 0; } -static int _regmap_raw_write(struct regmap *map, unsigned int reg, - const void *val, size_t val_len, bool async) +int _regmap_raw_write(struct regmap *map, unsigned int reg, + const void *val, size_t val_len, bool async) { struct regmap_range_node *range; unsigned long flags; -- cgit v1.2.3 From 0c7ed8563a0282c032936ae1c667498d59691593 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Fri, 15 Mar 2013 14:54:35 +0000 Subject: regmap: Cut down on the average # of nodes in the rbtree cache This patch aims to bring down the average number of nodes in the rbtree cache and increase the average number of registers per node. This should improve general lookup and traversal times. This is achieved by setting the minimum size of a block within the rbnode to the size of the rbnode itself. This will essentially cache possibly non-existent registers so to combat this scenario, we keep a separate bitmap in memory which keeps track of which register exists. The memory overhead of this change is likely in the order of ~5-10%, possibly less depending on the register file layout. On my test system with a bitmap of ~4300 bits and a relatively sparse register layout, the memory requirements for the entire cache did not increase (the cutting down of nodes which was about 50% of the original number compensated the situation). A second patch that can be built on top of this can look at the ratio `sizeof(*rbnode) / map->cache_word_size' in order to suitably adjust the block length of each block. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 70 ++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 04531961560..792a760c8ab 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -36,6 +36,8 @@ struct regcache_rbtree_node { struct regcache_rbtree_ctx { struct rb_root root; struct regcache_rbtree_node *cached_rbnode; + unsigned long *reg_present; + unsigned int reg_present_nbits; }; static inline void regcache_rbtree_get_base_top_reg( @@ -146,6 +148,7 @@ static int rbtree_show(struct seq_file *s, void *ignored) map->lock(map); mem_size = sizeof(*rbtree_ctx); + mem_size += BITS_TO_LONGS(rbtree_ctx->reg_present_nbits) * sizeof(long); for (node = rb_first(&rbtree_ctx->root); node != NULL; node = rb_next(node)) { @@ -196,6 +199,44 @@ static void rbtree_debugfs_init(struct regmap *map) } #endif +static int enlarge_reg_present_bitmap(struct regmap *map, unsigned int reg) +{ + struct regcache_rbtree_ctx *rbtree_ctx; + unsigned long *reg_present; + unsigned int reg_present_size; + unsigned int nregs; + int i; + + rbtree_ctx = map->cache; + nregs = reg + 1; + reg_present_size = BITS_TO_LONGS(nregs); + reg_present_size *= sizeof(long); + + if (!rbtree_ctx->reg_present) { + reg_present = kmalloc(reg_present_size, GFP_KERNEL); + if (!reg_present) + return -ENOMEM; + bitmap_zero(reg_present, nregs); + rbtree_ctx->reg_present = reg_present; + rbtree_ctx->reg_present_nbits = nregs; + return 0; + } + + if (nregs > rbtree_ctx->reg_present_nbits) { + reg_present = krealloc(rbtree_ctx->reg_present, + reg_present_size, GFP_KERNEL); + if (!reg_present) + return -ENOMEM; + for (i = 0; i < nregs; i++) + if (i >= rbtree_ctx->reg_present_nbits) + clear_bit(i, reg_present); + rbtree_ctx->reg_present = reg_present; + rbtree_ctx->reg_present_nbits = nregs; + } + + return 0; +} + static int regcache_rbtree_init(struct regmap *map) { struct regcache_rbtree_ctx *rbtree_ctx; @@ -209,6 +250,8 @@ static int regcache_rbtree_init(struct regmap *map) rbtree_ctx = map->cache; rbtree_ctx->root = RB_ROOT; rbtree_ctx->cached_rbnode = NULL; + rbtree_ctx->reg_present = NULL; + rbtree_ctx->reg_present_nbits = 0; for (i = 0; i < map->num_reg_defaults; i++) { ret = regcache_rbtree_write(map, @@ -238,6 +281,8 @@ static int regcache_rbtree_exit(struct regmap *map) if (!rbtree_ctx) return 0; + kfree(rbtree_ctx->reg_present); + /* free up the rbtree */ next = rb_first(&rbtree_ctx->root); while (next) { @@ -255,6 +300,17 @@ static int regcache_rbtree_exit(struct regmap *map) return 0; } +static int regcache_reg_present(struct regmap *map, unsigned int reg) +{ + struct regcache_rbtree_ctx *rbtree_ctx; + + rbtree_ctx = map->cache; + if (!(rbtree_ctx->reg_present[BIT_WORD(reg)] & BIT_MASK(reg))) + return 0; + return 1; + +} + static int regcache_rbtree_read(struct regmap *map, unsigned int reg, unsigned int *value) { @@ -264,6 +320,8 @@ 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; + if (!regcache_reg_present(map, reg)) + return -ENOENT; *value = regcache_rbtree_get_register(map, rbnode, reg_tmp); } else { return -ENOENT; @@ -313,6 +371,12 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, int ret; rbtree_ctx = map->cache; + /* update the reg_present bitmap, make space if necessary */ + ret = enlarge_reg_present_bitmap(map, reg); + if (ret < 0) + return ret; + set_bit(reg, rbtree_ctx->reg_present); + /* if we can't locate it in the cached rbnode we'll have * to traverse the rbtree looking for it. */ @@ -354,7 +418,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); @@ -404,6 +468,10 @@ 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); + + if (!regcache_reg_present(map, regtmp)) + continue; + val = regcache_rbtree_get_register(map, rbnode, i); /* Is this the hardware default? If so skip. */ -- cgit v1.2.3 From 8817796b75c8847d63d6d4523c79c24b47748a05 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 13 Mar 2013 19:29:36 +0000 Subject: regmap: cache: Provide a get address of value operation Provide a helper to do the size based index into a block of registers and use it when reading a value. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 7 +++++++ drivers/base/regmap/regcache.c | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 6d409350f50..95d46a5ea7e 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -189,6 +189,13 @@ int regcache_write(struct regmap *map, unsigned int reg, unsigned int value); int regcache_sync(struct regmap *map); +static inline const void *regcache_get_val_addr(struct regmap *map, + const void *base, + unsigned int idx) +{ + return base + (map->cache_word_size * idx); +} + unsigned int regcache_get_val(struct regmap *map, const void *base, unsigned int idx); bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 0f4fb8bc37e..229c804e409 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -458,8 +458,8 @@ unsigned int regcache_get_val(struct regmap *map, const void *base, /* Use device native format if possible */ if (map->format.parse_val) - return map->format.parse_val(base + - (map->cache_word_size * idx)); + return map->format.parse_val(regcache_get_val_addr(map, base, + idx)); switch (map->cache_word_size) { case 1: { -- cgit v1.2.3 From 221ad7f2df7c54b3f05471a3599ea7368366aaeb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 26 Mar 2013 21:24:20 +0000 Subject: regmap: core: Provide regmap_can_raw_write() operation Mainly useful internally but exported since this is a public API that's being checked for. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 9174c9d45a1..9ab1e1fedbc 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1097,6 +1097,17 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, return ret; } +/** + * regmap_can_raw_write - Test if regmap_raw_write() is supported + * + * @map: Map to check. + */ +bool regmap_can_raw_write(struct regmap *map) +{ + return map->bus && map->format.format_val && map->format.format_reg; +} +EXPORT_SYMBOL_GPL(regmap_can_raw_write); + static int _regmap_bus_formatted_write(void *context, unsigned int reg, unsigned int val) { @@ -1220,12 +1231,10 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, { int ret; - if (!map->bus) + if (!regmap_can_raw_write(map)) return -EINVAL; if (val_len % map->format.val_bytes) return -EINVAL; - if (reg % map->reg_stride) - return -EINVAL; map->lock(map->lock_arg); -- cgit v1.2.3 From 137b833457864091610ca01d7443a67028a2b3ce Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 26 Mar 2013 21:26:19 +0000 Subject: regmap: cache: Use raw I/O to sync rbtrees if we can This will bring no meaningful benefit by itself, it is done as a separate commit to aid bisection if there are problems with the following commits adding support for coalescing adjacent writes. Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 792a760c8ab..382a6deb3ca 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -55,6 +55,12 @@ static unsigned int regcache_rbtree_get_register(struct regmap *map, return regcache_get_val(map, rbnode->block, idx); } +static const void *regcache_rbtree_get_reg_addr(struct regmap *map, + struct regcache_rbtree_node *rbnode, unsigned int idx) +{ + 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) @@ -442,6 +448,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; @@ -480,7 +487,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; -- cgit v1.2.3 From 78493f2d7b51d6f6d03982cee559c62dfab4c292 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 29 Mar 2013 19:18:59 +0000 Subject: regmap: cache: Factor out reg_present support from rbtree cache The idea of maintaining a bitmap of present registers is something that can usefully be used by other cache types that maintain blocks of cached registers so move the code out of the rbtree cache and into the generic regcache code. Refactor the interface slightly as we go to wrap the set bit and enlarge bitmap operations (since we never do one without the other) and make it more robust for reads of uncached registers by bounds checking before we look at the bitmap. Signed-off-by: Mark Brown Reviewed-by: Dimitris Papastamos --- drivers/base/regmap/internal.h | 13 ++++++++ drivers/base/regmap/regcache-rbtree.c | 60 ++--------------------------------- drivers/base/regmap/regcache.c | 39 +++++++++++++++++++++++ 3 files changed, 54 insertions(+), 58 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 95d46a5ea7e..b01fe59fbfe 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -126,6 +126,9 @@ struct regmap { void *cache; u32 cache_dirty; + unsigned long *cache_present; + unsigned int cache_present_nbits; + struct reg_default *patch; int patch_regs; @@ -201,6 +204,16 @@ unsigned int regcache_get_val(struct regmap *map, const void *base, bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, unsigned int val); int regcache_lookup_reg(struct regmap *map, unsigned int reg); +int regcache_set_reg_present(struct regmap *map, unsigned int reg); + +static inline bool regcache_reg_present(struct regmap *map, unsigned int reg) +{ + if (!map->cache_present) + return true; + if (reg > map->cache_present_nbits) + return false; + return map->cache_present[BIT_WORD(reg)] & BIT_MASK(reg); +} int _regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len, bool async); diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 382a6deb3ca..00c3506f542 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -36,8 +36,6 @@ struct regcache_rbtree_node { struct regcache_rbtree_ctx { struct rb_root root; struct regcache_rbtree_node *cached_rbnode; - unsigned long *reg_present; - unsigned int reg_present_nbits; }; static inline void regcache_rbtree_get_base_top_reg( @@ -154,7 +152,7 @@ static int rbtree_show(struct seq_file *s, void *ignored) map->lock(map); mem_size = sizeof(*rbtree_ctx); - mem_size += BITS_TO_LONGS(rbtree_ctx->reg_present_nbits) * sizeof(long); + mem_size += BITS_TO_LONGS(map->cache_present_nbits) * sizeof(long); for (node = rb_first(&rbtree_ctx->root); node != NULL; node = rb_next(node)) { @@ -205,44 +203,6 @@ static void rbtree_debugfs_init(struct regmap *map) } #endif -static int enlarge_reg_present_bitmap(struct regmap *map, unsigned int reg) -{ - struct regcache_rbtree_ctx *rbtree_ctx; - unsigned long *reg_present; - unsigned int reg_present_size; - unsigned int nregs; - int i; - - rbtree_ctx = map->cache; - nregs = reg + 1; - reg_present_size = BITS_TO_LONGS(nregs); - reg_present_size *= sizeof(long); - - if (!rbtree_ctx->reg_present) { - reg_present = kmalloc(reg_present_size, GFP_KERNEL); - if (!reg_present) - return -ENOMEM; - bitmap_zero(reg_present, nregs); - rbtree_ctx->reg_present = reg_present; - rbtree_ctx->reg_present_nbits = nregs; - return 0; - } - - if (nregs > rbtree_ctx->reg_present_nbits) { - reg_present = krealloc(rbtree_ctx->reg_present, - reg_present_size, GFP_KERNEL); - if (!reg_present) - return -ENOMEM; - for (i = 0; i < nregs; i++) - if (i >= rbtree_ctx->reg_present_nbits) - clear_bit(i, reg_present); - rbtree_ctx->reg_present = reg_present; - rbtree_ctx->reg_present_nbits = nregs; - } - - return 0; -} - static int regcache_rbtree_init(struct regmap *map) { struct regcache_rbtree_ctx *rbtree_ctx; @@ -256,8 +216,6 @@ static int regcache_rbtree_init(struct regmap *map) rbtree_ctx = map->cache; rbtree_ctx->root = RB_ROOT; rbtree_ctx->cached_rbnode = NULL; - rbtree_ctx->reg_present = NULL; - rbtree_ctx->reg_present_nbits = 0; for (i = 0; i < map->num_reg_defaults; i++) { ret = regcache_rbtree_write(map, @@ -287,8 +245,6 @@ static int regcache_rbtree_exit(struct regmap *map) if (!rbtree_ctx) return 0; - kfree(rbtree_ctx->reg_present); - /* free up the rbtree */ next = rb_first(&rbtree_ctx->root); while (next) { @@ -306,17 +262,6 @@ static int regcache_rbtree_exit(struct regmap *map) return 0; } -static int regcache_reg_present(struct regmap *map, unsigned int reg) -{ - struct regcache_rbtree_ctx *rbtree_ctx; - - rbtree_ctx = map->cache; - if (!(rbtree_ctx->reg_present[BIT_WORD(reg)] & BIT_MASK(reg))) - return 0; - return 1; - -} - static int regcache_rbtree_read(struct regmap *map, unsigned int reg, unsigned int *value) { @@ -378,10 +323,9 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, rbtree_ctx = map->cache; /* update the reg_present bitmap, make space if necessary */ - ret = enlarge_reg_present_bitmap(map, reg); + ret = regcache_set_reg_present(map, reg); if (ret < 0) return ret; - set_bit(reg, rbtree_ctx->reg_present); /* if we can't locate it in the cached rbnode we'll have * to traverse the rbtree looking for it. diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 229c804e409..0fedf4fa011 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -121,6 +121,8 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) 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_present = NULL; + map->cache_present_nbits = 0; map->cache = NULL; map->cache_ops = cache_types[i]; @@ -179,6 +181,7 @@ void regcache_exit(struct regmap *map) BUG_ON(!map->cache_ops); + kfree(map->cache_present); kfree(map->reg_defaults); if (map->cache_free) kfree(map->reg_defaults_raw); @@ -415,6 +418,42 @@ void regcache_cache_bypass(struct regmap *map, bool enable) } EXPORT_SYMBOL_GPL(regcache_cache_bypass); +int regcache_set_reg_present(struct regmap *map, unsigned int reg) +{ + unsigned long *cache_present; + unsigned int cache_present_size; + unsigned int nregs; + int i; + + nregs = reg + 1; + cache_present_size = BITS_TO_LONGS(nregs); + cache_present_size *= sizeof(long); + + if (!map->cache_present) { + cache_present = kmalloc(cache_present_size, GFP_KERNEL); + if (!cache_present) + return -ENOMEM; + bitmap_zero(cache_present, nregs); + map->cache_present = cache_present; + map->cache_present_nbits = nregs; + } + + if (nregs > map->cache_present_nbits) { + cache_present = krealloc(map->cache_present, + cache_present_size, GFP_KERNEL); + if (!cache_present) + return -ENOMEM; + for (i = 0; i < nregs; i++) + if (i >= map->cache_present_nbits) + clear_bit(i, cache_present); + map->cache_present = cache_present; + map->cache_present_nbits = nregs; + } + + set_bit(reg, map->cache_present); + return 0; +} + bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, unsigned int val) { -- cgit v1.2.3 From f8bd822cbf953299b2957b45f6a43c08e7931ddc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 29 Mar 2013 19:32:28 +0000 Subject: regmap: cache: Factor out block sync The idea of holding blocks of registers in device format is shared between at least rbtree and lzo cache formats so split out the loop that does the sync from the rbtree code so optimisations on it can be reused. Signed-off-by: Mark Brown Reviewed-by: Dimitris Papastamos --- drivers/base/regmap/internal.h | 3 +++ drivers/base/regmap/regcache-rbtree.c | 48 +++++------------------------------ drivers/base/regmap/regcache.c | 42 ++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 42 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index b01fe59fbfe..01fbe48e815 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -191,6 +191,9 @@ int regcache_read(struct regmap *map, int regcache_write(struct regmap *map, unsigned int reg, unsigned int value); int regcache_sync(struct regmap *map); +int regcache_sync_block(struct regmap *map, void *block, + unsigned int block_base, unsigned int start, + unsigned int end); static inline const void *regcache_get_val_addr(struct regmap *map, const void *base, diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 00c3506f542..1fdd8ec6af2 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -53,12 +53,6 @@ static unsigned int regcache_rbtree_get_register(struct regmap *map, return regcache_get_val(map, rbnode->block, idx); } -static const void *regcache_rbtree_get_reg_addr(struct regmap *map, - struct regcache_rbtree_node *rbnode, unsigned int idx) -{ - 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) @@ -390,11 +384,8 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min, struct regcache_rbtree_ctx *rbtree_ctx; struct rb_node *node; struct regcache_rbtree_node *rbnode; - unsigned int regtmp; - unsigned int val; - const void *addr; int ret; - int i, base, end; + int base, end; rbtree_ctx = map->cache; for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { @@ -417,40 +408,13 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min, else end = rbnode->blklen; - for (i = base; i < end; i++) { - regtmp = rbnode->base_reg + (i * map->reg_stride); - - 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); - if (ret >= 0 && val == map->reg_defaults[ret].def) - continue; - - map->cache_bypass = 1; - - 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; - dev_dbg(map->dev, "Synced register %#x, value %#x\n", - regtmp, val); - } + ret = regcache_sync_block(map, rbnode->block, rbnode->base_reg, + base, end); + if (ret != 0) + return ret; } - return 0; + return regmap_async_complete(map); } struct regcache_ops regcache_rbtree_ops = { diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 0fedf4fa011..bb317db6818 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -544,3 +544,45 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg) else return -ENOENT; } + +int regcache_sync_block(struct regmap *map, void *block, + unsigned int block_base, unsigned int start, + unsigned int end) +{ + unsigned int i, regtmp, val; + const void *addr; + int ret; + + for (i = start; i < end; i++) { + regtmp = block_base + (i * map->reg_stride); + + if (!regcache_reg_present(map, regtmp)) + continue; + + val = regcache_get_val(map, block, i); + + /* Is this the hardware default? If so skip. */ + ret = regcache_lookup_reg(map, regtmp); + if (ret >= 0 && val == map->reg_defaults[ret].def) + continue; + + map->cache_bypass = 1; + + if (regmap_can_raw_write(map)) { + addr = regcache_get_val_addr(map, block, 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 != 0) + return ret; + dev_dbg(map->dev, "Synced register %#x, value %#x\n", + regtmp, val); + } + + return 0; +} -- cgit v1.2.3 From cfdeb8c37ed32024dcb7d2e3bd70e2a9f9dfc0e6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 29 Mar 2013 20:12:21 +0000 Subject: regmap: cache: Split raw and non-raw syncs For code clarity after implementing block writes split out the raw and non-raw I/O sync implementations. Signed-off-by: Mark Brown Reviewed-by: Dimitris Papastamos --- drivers/base/regmap/regcache.c | 64 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 11 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index bb317db6818..da4d9843520 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -545,9 +545,43 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg) return -ENOENT; } -int regcache_sync_block(struct regmap *map, void *block, - unsigned int block_base, unsigned int start, - unsigned int end) +static int regcache_sync_block_single(struct regmap *map, void *block, + unsigned int block_base, + unsigned int start, unsigned int end) +{ + unsigned int i, regtmp, val; + int ret; + + for (i = start; i < end; i++) { + regtmp = block_base + (i * map->reg_stride); + + if (!regcache_reg_present(map, regtmp)) + continue; + + val = regcache_get_val(map, block, i); + + /* Is this the hardware default? If so skip. */ + ret = regcache_lookup_reg(map, regtmp); + if (ret >= 0 && val == map->reg_defaults[ret].def) + continue; + + map->cache_bypass = 1; + + ret = _regmap_write(map, regtmp, val); + + map->cache_bypass = 0; + if (ret != 0) + return ret; + dev_dbg(map->dev, "Synced register %#x, value %#x\n", + regtmp, val); + } + + return 0; +} + +int regcache_sync_block_raw(struct regmap *map, void *block, + unsigned int block_base, unsigned int start, + unsigned int end) { unsigned int i, regtmp, val; const void *addr; @@ -568,14 +602,10 @@ int regcache_sync_block(struct regmap *map, void *block, map->cache_bypass = 1; - if (regmap_can_raw_write(map)) { - addr = regcache_get_val_addr(map, block, i); - ret = _regmap_raw_write(map, regtmp, addr, - map->format.val_bytes, - false); - } else { - ret = _regmap_write(map, regtmp, val); - } + addr = regcache_get_val_addr(map, block, i); + ret = _regmap_raw_write(map, regtmp, addr, + map->format.val_bytes, + false); map->cache_bypass = 0; if (ret != 0) @@ -586,3 +616,15 @@ int regcache_sync_block(struct regmap *map, void *block, return 0; } + +int regcache_sync_block(struct regmap *map, void *block, + unsigned int block_base, unsigned int start, + unsigned int end) +{ + if (regmap_can_raw_write(map)) + return regcache_sync_block_raw(map, block, block_base, + start, end); + else + return regcache_sync_block_single(map, block, block_base, + start, end); +} -- cgit v1.2.3 From 75a5f89f635c2b7cd1f94fcaef5f764d480c8481 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 29 Mar 2013 20:50:07 +0000 Subject: regmap: cache: Write consecutive registers in a single block write When syncing blocks of data using raw writes combine the writes into a single block write, saving us bus overhead for setup, addressing and teardown. Currently the block write is done unconditionally as it is expected that hardware which has a register format which can support raw writes will support auto incrementing writes, this decision may need to be revised in future. Signed-off-by: Mark Brown Reviewed-by: Dimitris Papastamos --- drivers/base/regmap/regcache.c | 64 +++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 17 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index da4d9843520..d81f60576e0 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -579,42 +579,72 @@ static int regcache_sync_block_single(struct regmap *map, void *block, return 0; } +static int regcache_sync_block_raw_flush(struct regmap *map, const void **data, + unsigned int base, unsigned int cur) +{ + size_t val_bytes = map->format.val_bytes; + int ret, count; + + if (*data == NULL) + return 0; + + count = cur - base; + + dev_dbg(map->dev, "Writing %d bytes for %d registers from 0x%x-0x%x\n", + count * val_bytes, count, base, cur - 1); + + map->cache_bypass = 1; + + ret = _regmap_raw_write(map, base, *data, count * val_bytes, + false); + + map->cache_bypass = 0; + + *data = NULL; + + return ret; +} + int regcache_sync_block_raw(struct regmap *map, void *block, unsigned int block_base, unsigned int start, unsigned int end) { - unsigned int i, regtmp, val; - const void *addr; + unsigned int i, val; + unsigned int regtmp = 0; + unsigned int base = 0; + const void *data = NULL; int ret; for (i = start; i < end; i++) { regtmp = block_base + (i * map->reg_stride); - if (!regcache_reg_present(map, regtmp)) + if (!regcache_reg_present(map, regtmp)) { + ret = regcache_sync_block_raw_flush(map, &data, + base, regtmp); + if (ret != 0) + return ret; continue; + } val = regcache_get_val(map, block, i); /* Is this the hardware default? If so skip. */ ret = regcache_lookup_reg(map, regtmp); - if (ret >= 0 && val == map->reg_defaults[ret].def) + if (ret >= 0 && val == map->reg_defaults[ret].def) { + ret = regcache_sync_block_raw_flush(map, &data, + base, regtmp); + if (ret != 0) + return ret; continue; + } - map->cache_bypass = 1; - - addr = regcache_get_val_addr(map, block, i); - ret = _regmap_raw_write(map, regtmp, addr, - map->format.val_bytes, - false); - - map->cache_bypass = 0; - if (ret != 0) - return ret; - dev_dbg(map->dev, "Synced register %#x, value %#x\n", - regtmp, val); + if (!data) { + data = regcache_get_val_addr(map, block, i); + base = regtmp; + } } - return 0; + return regcache_sync_block_raw_flush(map, &data, base, regtmp); } int regcache_sync_block(struct regmap *map, void *block, -- cgit v1.2.3 From f52687afb87cfb2d26699d7f84088c2b04adde50 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 4 Apr 2013 14:36:18 +0530 Subject: regmap: cache: Make regcache_sync_block_raw static regcache_sync_block_raw is used only in this file. Hence make it static. Silences the following warning: drivers/base/regmap/regcache.c:608:5: warning: symbol 'regcache_sync_block_raw' was not declared. Should it be static? Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index d81f60576e0..3ae8d9bafe6 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -605,7 +605,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data, return ret; } -int regcache_sync_block_raw(struct regmap *map, void *block, +static int regcache_sync_block_raw(struct regmap *map, void *block, unsigned int block_base, unsigned int start, unsigned int end) { -- cgit v1.2.3 From 9659293c1784f3d9df2235f6ebf92f6f9059a563 Mon Sep 17 00:00:00 2001 From: Stratos Karafotis Date: Thu, 4 Apr 2013 19:40:45 +0300 Subject: regmap: cache: Fix format specifier in dev_dbg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix format specifier in dev_dbg and suppress the following warning drivers/base/regmap/regcache.c: In function ‘regcache_sync_block_raw_flush’: drivers/base/regmap/regcache.c:593:2: warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘size_t’ [-Wformat] Signed-off-by: Stratos Karafotis Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 3ae8d9bafe6..75923f2396b 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -590,7 +590,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data, count = cur - base; - dev_dbg(map->dev, "Writing %d bytes for %d registers from 0x%x-0x%x\n", + dev_dbg(map->dev, "Writing %zu bytes for %d registers from 0x%x-0x%x\n", count * val_bytes, count, base, cur - 1); map->cache_bypass = 1; -- cgit v1.2.3 From 5a08d15602987bbdff3407d7645f95b7a70f1a6f Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 20 Mar 2013 17:02:02 -0600 Subject: regmap: don't corrupt work buffer in _regmap_raw_write() _regmap_raw_write() contains code to call regcache_write() to write values to the cache. That code calls memcpy() to copy the value data to the start of the work_buf. However, at least when _regmap_raw_write() is called from _regmap_bus_raw_write(), the value data is in the work_buf, and this memcpy() operation may over-write part of that value data, depending on the value of reg_bytes + pad_bytes. At least when using reg_bytes==1 and pad_bytes==0, corruption of the value data does occur. To solve this, remove the memcpy() operation, and modify the subsequent .parse_val() call to parse the original value buffer directly. At least in the case of 8-bit register address and 16-bit values, and writes of single registers at a time, this memcpy-then-parse combination used to cancel each-other out; for a work-buffer containing xx 89 03, the memcpy changed it to 89 03 03, and the parse_val changed it back to 89 89 03, thus leaving the value uncorrupted. This appears completely accidental though. Since commit 8a819ff "regmap: core: Split out in place value parsing", .parse_val only returns the parsed value, and does not modify the buffer, and hence does not (accidentally) undo the corruption caused by memcpy(). This caused bogus values to get written to HW, thus preventing e.g. audio playback on systems with a WM8903 CODEC. This patch fixes that. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 940fc63ed5f..c8756c03051 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -963,8 +963,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, unsigned int ival; int val_bytes = map->format.val_bytes; for (i = 0; i < val_len / val_bytes; i++) { - memcpy(map->work_buf, val + (i * val_bytes), val_bytes); - ival = map->format.parse_val(map->work_buf); + ival = map->format.parse_val(val + (i * val_bytes)); ret = regcache_write(map, reg + (i * map->reg_stride), ival); if (ret) { -- cgit v1.2.3