From fe7d4ccd1d7748bc9919c1bdee1e8286776f75ff Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 21 Feb 2013 19:05:48 +0000 Subject: [PATCH 01/23] regmap: async: Add tracepoints for async I/O Trace when we start and complete async writes, and when we start and finish blocking for their completion. This is useful for performance analysis of the resulting I/O patterns. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 8 ++++++ include/trace/events/regmap.h | 48 +++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 3d2367501fd0..7c6d3be137ba 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -999,6 +999,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, if (!async) return -ENOMEM; + trace_regmap_async_write_start(map->dev, reg, val_len); + async->work_buf = kzalloc(map->format.buf_size, GFP_KERNEL | GFP_DMA); if (!async->work_buf) { @@ -1640,6 +1642,8 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret) struct regmap *map = async->map; bool wake; + trace_regmap_async_io_complete(map->dev); + spin_lock(&map->async_lock); list_del(&async->list); @@ -1686,6 +1690,8 @@ int regmap_async_complete(struct regmap *map) if (!map->bus->async_write) return 0; + trace_regmap_async_complete_start(map->dev); + wait_event(map->async_waitq, regmap_async_is_done(map)); spin_lock_irqsave(&map->async_lock, flags); @@ -1693,6 +1699,8 @@ int regmap_async_complete(struct regmap *map) map->async_ret = 0; spin_unlock_irqrestore(&map->async_lock, flags); + trace_regmap_async_complete_done(map->dev); + return ret; } EXPORT_SYMBOL_GPL(regmap_async_complete); diff --git a/include/trace/events/regmap.h b/include/trace/events/regmap.h index 41a7dbd570e2..a43a2f67bd8e 100644 --- a/include/trace/events/regmap.h +++ b/include/trace/events/regmap.h @@ -175,6 +175,54 @@ DEFINE_EVENT(regmap_bool, regmap_cache_bypass, ); +DECLARE_EVENT_CLASS(regmap_async, + + TP_PROTO(struct device *dev), + + TP_ARGS(dev), + + TP_STRUCT__entry( + __string( name, dev_name(dev) ) + ), + + TP_fast_assign( + __assign_str(name, dev_name(dev)); + ), + + TP_printk("%s", __get_str(name)) +); + +DEFINE_EVENT(regmap_block, regmap_async_write_start, + + TP_PROTO(struct device *dev, unsigned int reg, int count), + + TP_ARGS(dev, reg, count) +); + +DEFINE_EVENT(regmap_async, regmap_async_io_complete, + + TP_PROTO(struct device *dev), + + TP_ARGS(dev) + +); + +DEFINE_EVENT(regmap_async, regmap_async_complete_start, + + TP_PROTO(struct device *dev), + + TP_ARGS(dev) + +); + +DEFINE_EVENT(regmap_async, regmap_async_complete_done, + + TP_PROTO(struct device *dev), + + TP_ARGS(dev) + +); + #endif /* _TRACE_REGMAP_H */ /* This part must be outside protection */ From 66baf407571662f7e2a22dd0764cbe279559446c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 21 Feb 2013 18:01:54 +0000 Subject: [PATCH 02/23] 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(-) diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index e6732cf7c06e..3f21c6ab296f 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 { From 879082c9fe6e8fbddf787170eee605e4be138d0f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 21 Feb 2013 18:03:13 +0000 Subject: [PATCH 03/23] 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(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 5a22bd33ce3d..582d7fdf414b 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 afd6aa91a0df..e210a6d1406a 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 3f21c6ab296f..461cff888bb1 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 e69ff3e4742c..f0a3db6ff9c2 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]; From 325acab447f775bc2258b3a37a780893c203ab6c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 21 Feb 2013 18:07:01 +0000 Subject: [PATCH 04/23] 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(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index f0a3db6ff9c2..6948996d2498 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; } From 8a819ff8abac9ad49f120c84cce01878b3d235c2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 4 Mar 2013 09:04:51 +0800 Subject: [PATCH 05/23] 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(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 582d7fdf414b..2b5851d42dbb 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 3d2367501fd0..aff5a8b73947 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; From eb4cb76ff00e27858e5c80f69dbe8cc15364578c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 21 Feb 2013 18:39:47 +0000 Subject: [PATCH 06/23] 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(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 6948996d2498..0f4fb8bc37e5 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; From 480738de0e076d759a973be623fac195cb901b82 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Wed, 20 Feb 2013 12:15:22 +0000 Subject: [PATCH 07/23] regmap: debugfs: Simplify calculation of `c->max_reg' We don't need to use any of the file position information to calculate the base and max register of each block. Just use the counter directly. Set `i = base' at the top to avoid GCC flow analysis bugs. The value of `i' can never be undefined or 0 in the if (c) { ... }. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 81d6f605c92e..886b2f7682c2 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -88,16 +88,15 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, * If we don't have a cache build one so we don't have to do a * linear scan each time. */ + i = base; if (list_empty(&map->debugfs_off_cache)) { - for (i = base; i <= map->max_register; i += map->reg_stride) { + for (; i <= map->max_register; i += map->reg_stride) { /* Skip unprinted registers, closing off cache entry */ if (!regmap_readable(map, i) || regmap_precious(map, i)) { if (c) { c->max = p - 1; - fpos_offset = c->max - c->min; - reg_offset = fpos_offset / map->debugfs_tot_len; - c->max_reg = c->base_reg + reg_offset; + c->max_reg = i - map->reg_stride; list_add_tail(&c->list, &map->debugfs_off_cache); c = NULL; @@ -124,9 +123,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, /* Close the last entry off if we didn't scan beyond it */ if (c) { c->max = p - 1; - fpos_offset = c->max - c->min; - reg_offset = fpos_offset / map->debugfs_tot_len; - c->max_reg = c->base_reg + reg_offset; + c->max_reg = i - map->reg_stride; list_add_tail(&c->list, &map->debugfs_off_cache); } From 065b4c587557dcd3dc8d3ff1ba2b9ecc6e0c6668 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Wed, 20 Feb 2013 12:15:23 +0000 Subject: [PATCH 08/23] regmap: debugfs: Add a registers `range' file This file lists the register ranges in the register map. The condition to split the range is based on whether the block is readable or not. Ensure that we lock the `debugfs_off_cache' list whenever we access and modify the list. There is a possible race otherwise between the read() operations of the `registers' file and the `range' file. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap-debugfs.c | 83 ++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 5a22bd33ce3d..dc23508745fe 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -76,6 +76,7 @@ struct regmap { unsigned int debugfs_tot_len; struct list_head debugfs_off_cache; + struct mutex cache_lock; #endif unsigned int max_register; diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 886b2f7682c2..23b701f5fd2f 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -88,6 +88,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, * If we don't have a cache build one so we don't have to do a * linear scan each time. */ + mutex_lock(&map->cache_lock); i = base; if (list_empty(&map->debugfs_off_cache)) { for (; i <= map->max_register; i += map->reg_stride) { @@ -110,6 +111,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, c = kzalloc(sizeof(*c), GFP_KERNEL); if (!c) { regmap_debugfs_free_dump_cache(map); + mutex_unlock(&map->cache_lock); return base; } c->min = p; @@ -142,12 +144,14 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, fpos_offset = from - c->min; reg_offset = fpos_offset / map->debugfs_tot_len; *pos = c->min + (reg_offset * map->debugfs_tot_len); + mutex_unlock(&map->cache_lock); return c->base_reg + reg_offset; } *pos = c->max; ret = c->max_reg; } + mutex_unlock(&map->cache_lock); return ret; } @@ -308,6 +312,79 @@ static const struct file_operations regmap_range_fops = { .llseek = default_llseek, }; +static ssize_t regmap_reg_ranges_read_file(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct regmap *map = file->private_data; + struct regmap_debugfs_off_cache *c; + loff_t p = 0; + size_t buf_pos = 0; + char *buf; + char *entry; + int ret; + + if (*ppos < 0 || !count) + return -EINVAL; + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + entry = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!entry) { + kfree(buf); + return -ENOMEM; + } + + /* While we are at it, build the register dump cache + * now so the read() operation on the `registers' file + * can benefit from using the cache. We do not care + * about the file position information that is contained + * in the cache, just about the actual register blocks */ + regmap_calc_tot_len(map, buf, count); + regmap_debugfs_get_dump_start(map, 0, *ppos, &p); + + /* Reset file pointer as the fixed-format of the `registers' + * file is not compatible with the `range' file */ + p = 0; + mutex_lock(&map->cache_lock); + list_for_each_entry(c, &map->debugfs_off_cache, list) { + snprintf(entry, PAGE_SIZE, "%x-%x", + c->base_reg, c->max_reg); + if (p >= *ppos) { + if (buf_pos + 1 + strlen(entry) > count) + break; + snprintf(buf + buf_pos, count - buf_pos, + "%s", entry); + buf_pos += strlen(entry); + buf[buf_pos] = '\n'; + buf_pos++; + } + p += strlen(entry) + 1; + } + mutex_unlock(&map->cache_lock); + + kfree(entry); + ret = buf_pos; + + if (copy_to_user(user_buf, buf, buf_pos)) { + ret = -EFAULT; + goto out_buf; + } + + *ppos += buf_pos; +out_buf: + kfree(buf); + return ret; +} + +static const struct file_operations regmap_reg_ranges_fops = { + .open = simple_open, + .read = regmap_reg_ranges_read_file, + .llseek = default_llseek, +}; + static ssize_t regmap_access_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -382,6 +459,7 @@ void regmap_debugfs_init(struct regmap *map, const char *name) struct regmap_range_node *range_node; INIT_LIST_HEAD(&map->debugfs_off_cache); + mutex_init(&map->cache_lock); if (name) { map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", @@ -400,6 +478,9 @@ void regmap_debugfs_init(struct regmap *map, const char *name) debugfs_create_file("name", 0400, map->debugfs, map, ®map_name_fops); + debugfs_create_file("range", 0400, map->debugfs, + map, ®map_reg_ranges_fops); + if (map->max_register) { debugfs_create_file("registers", 0400, map->debugfs, map, ®map_map_fops); @@ -432,7 +513,9 @@ void regmap_debugfs_init(struct regmap *map, const char *name) void regmap_debugfs_exit(struct regmap *map) { debugfs_remove_recursive(map->debugfs); + mutex_lock(&map->cache_lock); regmap_debugfs_free_dump_cache(map); + mutex_unlock(&map->cache_lock); kfree(map->debugfs_name); } From a42277c739c29b06cb27502347f557e11fed8b0e Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Tue, 12 Mar 2013 17:26:49 +0000 Subject: [PATCH 09/23] 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(-) diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 461cff888bb1..045319615608 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); From eed456f93d01e9476a1e777d22ae8a8382546ab7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 19 Mar 2013 10:45:04 +0000 Subject: [PATCH 10/23] regmap: irq: Clarify error message when we fail to request primary IRQ Display the name for the chip rather than just the primary IRQ so it is clearer what exactly has failed. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 020ea2b9fd2f..1643e889bafc 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -460,7 +460,8 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ret = request_threaded_irq(irq, NULL, regmap_irq_thread, irq_flags, chip->name, d); if (ret != 0) { - dev_err(map->dev, "Failed to request IRQ %d: %d\n", irq, ret); + dev_err(map->dev, "Failed to request IRQ %d for %s: %d\n", + irq, chip->name, ret); goto err_domain; } From f1b5c5c3423b59056d3ca956d2e795b7927d6008 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 13 Mar 2013 19:18:13 +0000 Subject: [PATCH 11/23] 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(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index aff5a8b73947..44a45cf0644b 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); From 584de329ca43cc6d73eb74885e1d5d9fc0549423 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 13 Mar 2013 19:19:34 +0000 Subject: [PATCH 12/23] 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(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 2b5851d42dbb..6d409350f50a 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 44a45cf0644b..9174c9d45a16 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; From 0c7ed8563a0282c032936ae1c667498d59691593 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Fri, 15 Mar 2013 14:54:35 +0000 Subject: [PATCH 13/23] 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(-) diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 045319615608..792a760c8ab1 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. */ From 8817796b75c8847d63d6d4523c79c24b47748a05 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 13 Mar 2013 19:29:36 +0000 Subject: [PATCH 14/23] 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(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 6d409350f50a..95d46a5ea7e7 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 0f4fb8bc37e5..229c804e409e 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: { From 221ad7f2df7c54b3f05471a3599ea7368366aaeb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 26 Mar 2013 21:24:20 +0000 Subject: [PATCH 15/23] 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 ++++++++++++--- include/linux/regmap.h | 1 + 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 9174c9d45a16..9ab1e1fedbc9 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); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index bf77dfdabef9..02d84e24b7c2 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -389,6 +389,7 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, bool *change); int regmap_get_val_bytes(struct regmap *map); int regmap_async_complete(struct regmap *map); +bool regmap_can_raw_write(struct regmap *map); int regcache_sync(struct regmap *map); int regcache_sync_region(struct regmap *map, unsigned int min, From 137b833457864091610ca01d7443a67028a2b3ce Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 26 Mar 2013 21:26:19 +0000 Subject: [PATCH 16/23] 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(-) diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 792a760c8ab1..382a6deb3ca8 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; From 78493f2d7b51d6f6d03982cee559c62dfab4c292 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 29 Mar 2013 19:18:59 +0000 Subject: [PATCH 17/23] 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(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 95d46a5ea7e7..b01fe59fbfe8 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 382a6deb3ca8..00c3506f542f 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 229c804e409e..0fedf4fa0116 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) { From f8bd822cbf953299b2957b45f6a43c08e7931ddc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 29 Mar 2013 19:32:28 +0000 Subject: [PATCH 18/23] 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(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index b01fe59fbfe8..01fbe48e8155 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 00c3506f542f..1fdd8ec6af23 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 0fedf4fa0116..bb317db6818f 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; +} From cfdeb8c37ed32024dcb7d2e3bd70e2a9f9dfc0e6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 29 Mar 2013 20:12:21 +0000 Subject: [PATCH 19/23] 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(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index bb317db6818f..da4d9843520f 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); +} From 75a5f89f635c2b7cd1f94fcaef5f764d480c8481 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 29 Mar 2013 20:50:07 +0000 Subject: [PATCH 20/23] 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(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index da4d9843520f..d81f60576e02 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, From f52687afb87cfb2d26699d7f84088c2b04adde50 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 4 Apr 2013 14:36:18 +0530 Subject: [PATCH 21/23] 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(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index d81f60576e02..3ae8d9bafe68 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) { From 9659293c1784f3d9df2235f6ebf92f6f9059a563 Mon Sep 17 00:00:00 2001 From: Stratos Karafotis Date: Thu, 4 Apr 2013 19:40:45 +0300 Subject: [PATCH 22/23] 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(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 3ae8d9bafe68..75923f2396bd 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; From 5a08d15602987bbdff3407d7645f95b7a70f1a6f Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 20 Mar 2013 17:02:02 -0600 Subject: [PATCH 23/23] 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(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 940fc63ed5f2..c8756c030516 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) {