From b4519c71c45b31f5acd6db984c8830e52120323a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 16 Jul 2013 02:10:11 -0300 Subject: regmap: Provide __acquires/__releases annotations Fix the following sparse warnings: drivers/base/regmap/regmap.c:305:13: warning: context imbalance in 'regmap_lock_spinlock' - wrong count at exit drivers/base/regmap/regmap.c:314:13: warning: context imbalance in 'regmap_unlock_spinlock' - unexpected unlock Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 95920583e31e..bf8a81c930ac 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -303,6 +303,7 @@ static void regmap_unlock_mutex(void *__map) } static void regmap_lock_spinlock(void *__map) +__acquires(&map->spinlock) { struct regmap *map = __map; unsigned long flags; @@ -312,6 +313,7 @@ static void regmap_lock_spinlock(void *__map) } static void regmap_unlock_spinlock(void *__map) +__releases(&map->spinlock) { struct regmap *map = __map; spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags); -- cgit v1.2.3 From 2753e6f8207355ab72574d48287cec187272a151 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 22 Jul 2013 17:15:52 +0200 Subject: regmap: irq: Allow to acknowledge masked interrupts during initialization In case the hardware interrupt mask register does not prevent the chip level irq from being asserted by the corresponding interrupt status bit, already set interrupt bits should to be cleared once after masking them during initialization. Add a flag to let drivers enable this behavior. Signed-off-by: Philipp Zabel Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 25 +++++++++++++++++++++++++ include/linux/regmap.h | 2 ++ 2 files changed, 27 insertions(+) diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 1643e889bafc..d10456ffd811 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -418,6 +418,31 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, reg, ret); goto err_alloc; } + + if (!chip->init_ack_masked) + continue; + + /* Ack masked but set interrupts */ + reg = chip->status_base + + (i * map->reg_stride * d->irq_reg_stride); + ret = regmap_read(map, reg, &d->status_buf[i]); + if (ret != 0) { + dev_err(map->dev, "Failed to read IRQ status: %d\n", + ret); + goto err_alloc; + } + + if (d->status_buf[i] && chip->ack_base) { + reg = chip->ack_base + + (i * map->reg_stride * d->irq_reg_stride); + ret = regmap_write(map, reg, + d->status_buf[i] & d->mask_buf[i]); + if (ret != 0) { + dev_err(map->dev, "Failed to ack 0x%x: %d\n", + reg, ret); + goto err_alloc; + } + } } /* Wake is disabled by default */ diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 75981d0b57dc..34ebe7778033 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -470,6 +470,7 @@ struct regmap_irq { * @ack_base: Base ack address. If zero then the chip is clear on read. * @wake_base: Base address for wake enables. If zero unsupported. * @irq_reg_stride: Stride to use for chips where registers are not contiguous. + * @init_ack_masked: Ack all masked interrupts once during initalization. * @runtime_pm: Hold a runtime PM lock on the device when accessing it. * * @num_regs: Number of registers in each control bank. @@ -485,6 +486,7 @@ struct regmap_irq_chip { unsigned int ack_base; unsigned int wake_base; unsigned int irq_reg_stride; + bool init_ack_masked; unsigned int mask_invert; unsigned int wake_invert; bool runtime_pm; -- cgit v1.2.3 From f484f7a6bc6f0f871627cc1f83f56077a5dc6e47 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 24 Jul 2013 10:26:42 +0200 Subject: regmap: irq: make flags bool and put them in a bitfield This patch makes mask/wake_invert bool and puts all flags into a bitfield for consistency and to save some space. Signed-off-by: Philipp Zabel Signed-off-by: Mark Brown --- include/linux/regmap.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 34ebe7778033..99b0f7cad75c 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -486,10 +486,10 @@ struct regmap_irq_chip { unsigned int ack_base; unsigned int wake_base; unsigned int irq_reg_stride; - bool init_ack_masked; - unsigned int mask_invert; - unsigned int wake_invert; - bool runtime_pm; + bool init_ack_masked:1; + bool mask_invert:1; + bool wake_invert:1; + bool runtime_pm:1; int num_regs; -- cgit v1.2.3 From 68622bdfefb969fd7cf710545d7e758a732ab01d Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 24 Jul 2013 10:26:48 +0200 Subject: regmap: irq: document mask/wake_invert flags Signed-off-by: Philipp Zabel Signed-off-by: Mark Brown --- include/linux/regmap.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 99b0f7cad75c..a3c68aad2381 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -471,6 +471,8 @@ struct regmap_irq { * @wake_base: Base address for wake enables. If zero unsupported. * @irq_reg_stride: Stride to use for chips where registers are not contiguous. * @init_ack_masked: Ack all masked interrupts once during initalization. + * @mask_invert: Inverted mask register: cleared bits are masked out. + * @wake_invert: Inverted wake register: cleared bits are wake enabled. * @runtime_pm: Hold a runtime PM lock on the device when accessing it. * * @num_regs: Number of registers in each control bank. -- cgit v1.2.3 From f161d22081e9b81f0b35411c428af347ca3dd449 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 23 Jul 2013 12:16:02 +0200 Subject: regmap: core: allow a virtual range to cover its own data window I see no reason why a virtual range shouldn't be allowed to cover its own data window if the page selection register is in the same place on every page. For chips which use paged access for all of their registers, but only when connected via I2C, and which can access the whole register space directly when connected via SPI, this allows to avoid acrobatics with the register ranges by simply mapping the I2C ranges over the data window beginning at 0x0, and then using linear access for the SPI variant. Signed-off-by: Philipp Zabel Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index e0d0c7d8a5c5..436fac05b3b6 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -687,6 +687,10 @@ skip_format_initialization: unsigned win_max = win_min + config->ranges[j].window_len - 1; + /* Allow data window inside its own virtual range */ + if (j == i) + continue; + if (range_cfg->range_min <= sel_reg && sel_reg <= range_cfg->range_max) { dev_err(map->dev, -- cgit v1.2.3 From 1767da9e8e9a1dc1416d6bb1029b8cf5659dac2a Mon Sep 17 00:00:00 2001 From: Ionut Nicu Date: Fri, 9 Aug 2013 12:09:15 +0200 Subject: regmap: fix regcache_reg_present() for empty cache In the initial case when no reg_defaults values are provided and no register value was added to the cache yet, the cache_present bitmap is NULL. If this function is invoked for any register it should return false (i.e. the register is not cached) instead of true. Signed-off-by: Ionut Nicu Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 29c83160ca29..5308e3e870ba 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -223,7 +223,7 @@ 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; + return false; if (reg > map->cache_present_nbits) return false; return map->cache_present[BIT_WORD(reg)] & BIT_MASK(reg); -- cgit v1.2.3 From 515f2261703d09c6b647a5687b7d657dd5911065 Mon Sep 17 00:00:00 2001 From: Ionut Nicu Date: Fri, 9 Aug 2013 12:09:20 +0200 Subject: regmap: regcache: allow read-only regs to be cached The regmap_writeable() check should not be done in regcache_write() because this prevents read-only registers to be cached. After a read on a read-only register its value will not be stored in the cache and the next time someone will try to read it the value will be read from the bus instead of the cache. Instead the regmap_writeable() check should be done in _regmap_write() to prevent callers from writing to read-only registers. Signed-off-by: Ionut Nicu Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 3 --- drivers/base/regmap/regmap.c | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index e69102696533..f1aa1bcd36a0 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -241,9 +241,6 @@ int regcache_write(struct regmap *map, BUG_ON(!map->cache_ops); - if (!regmap_writeable(map, reg)) - return -EIO; - if (!regmap_volatile(map, reg)) return map->cache_ops->write(map, reg, value); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index e0d0c7d8a5c5..0e85367e504d 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1261,6 +1261,9 @@ int _regmap_write(struct regmap *map, unsigned int reg, int ret; void *context = _regmap_map_get_context(map); + if (!regmap_writeable(map, reg)) + return -EIO; + if (!map->cache_bypass && !map->defer_caching) { ret = regcache_write(map, reg, val); if (ret != 0) -- cgit v1.2.3 From aab13ebca2c67c3b8f004a013a36f661a3950b00 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 11 Jul 2013 12:41:44 +0100 Subject: regmap: Allow multiple patches to be registered It may be useful to register multiple patches with regmap, for example one that depends on the device revision and one that depends on the system configuration. Add support for doing this, appending any new patches to the existing patches. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 95920583e31e..55d9118d2d6f 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1888,13 +1888,10 @@ EXPORT_SYMBOL_GPL(regmap_async_complete); int regmap_register_patch(struct regmap *map, const struct reg_default *regs, int num_regs) { + struct reg_default *p; int i, ret; bool bypass; - /* If needed the implementation can be extended to support this */ - if (map->patch) - return -EBUSY; - map->lock(map->lock_arg); bypass = map->cache_bypass; @@ -1911,11 +1908,13 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, } } - map->patch = kcalloc(num_regs, sizeof(struct reg_default), GFP_KERNEL); - if (map->patch != NULL) { - memcpy(map->patch, regs, - num_regs * sizeof(struct reg_default)); - map->patch_regs = num_regs; + p = krealloc(map->patch, + sizeof(struct reg_default) * (map->patch_regs + num_regs), + GFP_KERNEL); + if (p) { + memcpy(p + map->patch_regs, regs, num_regs * sizeof(*regs)); + map->patch = p; + map->patch_regs += num_regs; } else { ret = -ENOMEM; } -- cgit v1.2.3 From b6752123ccef4eec3c70c20dbdfc05d1674319c5 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 27 Aug 2013 13:03:03 +0200 Subject: regcache-rbtree: Fix reg_stride != 1 There are a couple of calculations, which convert between register addresses and block indices, in regcache_rbtree_sync() and regcache_rbtree_node_alloc() which assume that reg_stride is 1. This will break the rb cache for configurations which do not use a reg_stride of 1. Also rename 'base' in regcache_rbtree_sync() to 'start' to avoid confusion with 'base_reg'. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 5c1435c4e210..f2384a816cc4 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -325,8 +325,8 @@ regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg) if (i != map->rd_table->n_yes_ranges) { range = &map->rd_table->yes_ranges[i]; - rbnode->blklen = range->range_max - range->range_min - + 1; + rbnode->blklen = (range->range_max - range->range_min) / + map->reg_stride + 1; rbnode->base_reg = range->range_min; } } @@ -418,30 +418,33 @@ 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 base_reg, top_reg; + unsigned int start, end; int ret; - int base, end; rbtree_ctx = map->cache; for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { rbnode = rb_entry(node, struct regcache_rbtree_node, node); - if (rbnode->base_reg > max) + regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg, + &top_reg); + if (base_reg > max) break; - if (rbnode->base_reg + rbnode->blklen < min) + if (top_reg < min) continue; - if (min > rbnode->base_reg) - base = min - rbnode->base_reg; + if (min > base_reg) + start = (min - base_reg) / map->reg_stride; else - base = 0; + start = 0; - if (max < rbnode->base_reg + rbnode->blklen) - end = max - rbnode->base_reg + 1; + if (max < top_reg) + end = (max - base_reg) / map->reg_stride + 1; else end = rbnode->blklen; ret = regcache_sync_block(map, rbnode->block, rbnode->base_reg, - base, end); + start, end); if (ret != 0) return ret; } -- cgit v1.2.3 From 26ee47411ae22caa07d3f3b63ca6d097cba6681b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 28 Aug 2013 17:55:07 +0200 Subject: regmap: debugfs: Fix continued read from registers file The regmap_debugfs_get_dump_start() function maps from a file offset to the register that can be found at that position in the file. This is done using a look-up table. Commit d6814a7d ("regmap: debugfs: Suppress cache for partial register files") added a check to bypass the look-up table for partial register files, since the offsets in that table are only correct for the full register file. The check incorrectly uses the file offset instead of the register base address and returns it. This will cause the file offset to be interpreted as a register address which will result in a incorrect output from the registers file for all reads except at position 0. The issue can easily be reproduced by doing small reads the registers file, e.g. `dd if=registers bs=10 count=5`. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/base/regmap/regmap-debugfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 53495753fbdb..6c2652a8ad50 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -85,8 +85,8 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, unsigned int reg_offset; /* Suppress the cache if we're using a subrange */ - if (from) - return from; + if (base) + return base; /* * If we don't have a cache build one so we don't have to do a -- cgit v1.2.3 From 194c753a214ba7f1497552dd530021884d164146 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 29 Aug 2013 10:26:32 +0200 Subject: regmap: rbtree: Simplify adjacent node look-up A register which is adjacent to a node will either be left to the first register or right to the last register. It will not be within the node's range, so there is no point in checking for each register cached by the node whether the new register is next to it. It is sufficient to check whether the register comes before the first register or after the last register of the node. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 39 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index f2384a816cc4..dbe5ea12c79b 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -352,9 +352,9 @@ 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 base_reg, top_reg; unsigned int reg_tmp; unsigned int pos; - int i; int ret; rbtree_ctx = map->cache; @@ -376,25 +376,24 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, node = rb_next(node)) { rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, node); - for (i = 0; i < rbnode_tmp->blklen; i++) { - reg_tmp = rbnode_tmp->base_reg + - (i * map->reg_stride); - if (abs(reg_tmp - reg) != map->reg_stride) - continue; - /* decide where in the block to place our register */ - if (reg_tmp + map->reg_stride == reg) - pos = i + 1; - else - pos = i; - ret = regcache_rbtree_insert_to_block(map, - rbnode_tmp, - pos, reg, - value); - if (ret) - return ret; - rbtree_ctx->cached_rbnode = rbnode_tmp; - return 0; - } + + regcache_rbtree_get_base_top_reg(map, rbnode_tmp, + &base_reg, &top_reg); + + /* decide where in the block to place our register */ + if (base_reg > 0 && reg == base_reg - map->reg_stride) + pos = 0; + else if (reg > 0 && reg - map->reg_stride == top_reg) + pos = rbnode_tmp->blklen; + else + continue; + + ret = regcache_rbtree_insert_to_block(map, rbnode_tmp, + pos, reg, value); + if (ret) + return ret; + rbtree_ctx->cached_rbnode = rbnode_tmp; + return 0; } /* We did not manage to find a place to insert it in -- cgit v1.2.3 From 472fdec7380cec483e241fa696d9b90bc37ddd4c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 29 Aug 2013 10:26:33 +0200 Subject: regmap: rbtree: Reduce number of nodes, take 2 Support for reducing the number of nodes and memory consumption of the rbtree cache by allowing for small unused holes in the node's register cache block was initially added in commit 0c7ed856 ("regmap: Cut down on the average # of nodes in the rbtree cache"). But the commit had problems and so its effect was reverted again in commit 4e67fb5 ("regmap: rbtree: Fix overlapping rbnodes."). This patch brings the feature back of reducing the average number of nodes, which will speedup node look-up, while at the same time also reducing the memory usage of the rbtree cache. This patch takes a slightly different approach than the original patch though. It modifies the adjacent node look-up to not only consider nodes that are just one to the left or the right of the register but any node that falls in a certain range around the register. The range is calculated based on how much memory it would take to allocate a new node compared to how much memory it takes adding a set of unused registers to an existing node. E.g. if a node takes up 24 bytes and each register in a block uses 1 byte the range will be from the register address - 24 to the register address + 24. If we find a node that falls within this range it is cheaper or as expensive to add the register to the existing node and have a couple of unused registers in the node's cache compared to allocating a new node. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 53 ++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index dbe5ea12c79b..c06478c364b6 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -278,27 +278,34 @@ static int regcache_rbtree_read(struct regmap *map, static int regcache_rbtree_insert_to_block(struct regmap *map, struct regcache_rbtree_node *rbnode, - unsigned int pos, unsigned int reg, + unsigned int base_reg, + unsigned int top_reg, + unsigned int reg, unsigned int value) { + unsigned int blklen; + unsigned int pos, offset; u8 *blk; + blklen = (top_reg - base_reg) / map->reg_stride + 1; + pos = (reg - base_reg) / map->reg_stride; + offset = (rbnode->base_reg - base_reg) / map->reg_stride; + blk = krealloc(rbnode->block, - (rbnode->blklen + 1) * map->cache_word_size, + blklen * 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) * map->cache_word_size, - blk + pos * map->cache_word_size, - (rbnode->blklen - pos) * map->cache_word_size); + if (pos == 0) + memmove(blk + offset * map->cache_word_size, + blk, rbnode->blklen * map->cache_word_size); /* update the rbnode block, its size and the base register */ rbnode->block = blk; - rbnode->blklen++; - if (!pos) - rbnode->base_reg = reg; + rbnode->blklen = blklen; + rbnode->base_reg = base_reg; regcache_rbtree_set_register(map, rbnode, pos, value); return 0; @@ -352,9 +359,7 @@ 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 base_reg, top_reg; unsigned int reg_tmp; - unsigned int pos; int ret; rbtree_ctx = map->cache; @@ -371,6 +376,19 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; regcache_rbtree_set_register(map, rbnode, reg_tmp, value); } else { + unsigned int base_reg, top_reg; + unsigned int new_base_reg, new_top_reg; + unsigned int min, max; + unsigned int max_dist; + + max_dist = map->reg_stride * sizeof(*rbnode_tmp) / + map->cache_word_size; + if (reg < max_dist) + min = 0; + else + min = reg - max_dist; + max = reg + max_dist; + /* look for an adjacent register to the one we are about to add */ for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { @@ -380,16 +398,17 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, regcache_rbtree_get_base_top_reg(map, rbnode_tmp, &base_reg, &top_reg); - /* decide where in the block to place our register */ - if (base_reg > 0 && reg == base_reg - map->reg_stride) - pos = 0; - else if (reg > 0 && reg - map->reg_stride == top_reg) - pos = rbnode_tmp->blklen; - else + if (base_reg <= max && top_reg >= min) { + new_base_reg = min(reg, base_reg); + new_top_reg = max(reg, top_reg); + } else { continue; + } ret = regcache_rbtree_insert_to_block(map, rbnode_tmp, - pos, reg, value); + new_base_reg, + new_top_reg, reg, + value); if (ret) return ret; rbtree_ctx->cached_rbnode = rbnode_tmp; -- cgit v1.2.3 From 3f4ff561bc88b074d5e868dde4012d89cbb06c87 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 29 Aug 2013 10:26:34 +0200 Subject: regmap: rbtree: Make cache_present bitmap per node With devices which have a dense and small register map but placed at a large offset the global cache_present bitmap imposes a huge memory overhead. Making the cache_present per rbtree node avoids the issue and easily reduces the memory footprint by a factor of ten. For devices with a more sparse map or without a large base register offset the memory usage might increase slightly by a few bytes, but not significantly. E.g. for a device which has ~50 registers at offset 0x4000 the memory footprint of the register cache goes down form 2496 bytes to 175 bytes. Moving the bitmap to a per node basis means that the handling of the bitmap is now cache implementation specific and can no longer be managed by the core. The regcache_sync_block() function is extended by a additional parameter so that the cache implementation can tell the core which registers in the block are set and which are not. The parameter is optional and if NULL the core assumes that all registers are set. The rbtree cache also needs to implement its own drop callback instead of relying on the core to handle this. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 14 +----- drivers/base/regmap/regcache-rbtree.c | 86 +++++++++++++++++++++++++++++------ drivers/base/regmap/regcache.c | 72 ++++++++--------------------- 3 files changed, 92 insertions(+), 80 deletions(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 5308e3e870ba..57f777835d97 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -128,9 +128,6 @@ struct regmap { void *cache; u32 cache_dirty; - unsigned long *cache_present; - unsigned int cache_present_nbits; - struct reg_default *patch; int patch_regs; @@ -203,6 +200,7 @@ 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 long *cache_present, unsigned int block_base, unsigned int start, unsigned int end); @@ -218,16 +216,6 @@ 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 false; - 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 c06478c364b6..e9a2261a383b 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -29,6 +29,8 @@ struct regcache_rbtree_node { unsigned int base_reg; /* block of adjacent registers */ void *block; + /* Which registers are present */ + long *cache_present; /* number of registers available in the block */ unsigned int blklen; } __attribute__ ((packed)); @@ -57,6 +59,7 @@ static void regcache_rbtree_set_register(struct regmap *map, struct regcache_rbtree_node *rbnode, unsigned int idx, unsigned int val) { + set_bit(idx, rbnode->cache_present); regcache_set_val(map, rbnode->block, idx, val); } @@ -146,13 +149,13 @@ static int rbtree_show(struct seq_file *s, void *ignored) map->lock(map->lock_arg); mem_size = sizeof(*rbtree_ctx); - mem_size += BITS_TO_LONGS(map->cache_present_nbits) * sizeof(long); for (node = rb_first(&rbtree_ctx->root); node != NULL; node = rb_next(node)) { n = container_of(node, struct regcache_rbtree_node, node); mem_size += sizeof(*n); mem_size += (n->blklen * map->cache_word_size); + mem_size += BITS_TO_LONGS(n->blklen) * sizeof(long); regcache_rbtree_get_base_top_reg(map, n, &base, &top); this_registers = ((top - base) / map->reg_stride) + 1; @@ -245,6 +248,7 @@ static int regcache_rbtree_exit(struct regmap *map) rbtree_node = rb_entry(next, struct regcache_rbtree_node, node); next = rb_next(&rbtree_node->node); rb_erase(&rbtree_node->node, &rbtree_ctx->root); + kfree(rbtree_node->cache_present); kfree(rbtree_node->block); kfree(rbtree_node); } @@ -265,7 +269,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; - if (!regcache_reg_present(map, reg)) + if (!test_bit(reg_tmp, rbnode->cache_present)) return -ENOENT; *value = regcache_rbtree_get_register(map, rbnode, reg_tmp); } else { @@ -285,6 +289,7 @@ static int regcache_rbtree_insert_to_block(struct regmap *map, { unsigned int blklen; unsigned int pos, offset; + unsigned long *present; u8 *blk; blklen = (top_reg - base_reg) / map->reg_stride + 1; @@ -297,15 +302,25 @@ static int regcache_rbtree_insert_to_block(struct regmap *map, if (!blk) return -ENOMEM; + present = krealloc(rbnode->cache_present, + BITS_TO_LONGS(blklen) * sizeof(*present), GFP_KERNEL); + if (!present) { + kfree(blk); + return -ENOMEM; + } + /* insert the register value in the correct place in the rbnode block */ - if (pos == 0) + if (pos == 0) { memmove(blk + offset * map->cache_word_size, blk, rbnode->blklen * map->cache_word_size); + bitmap_shift_right(present, present, offset, blklen); + } /* update the rbnode block, its size and the base register */ rbnode->block = blk; rbnode->blklen = blklen; rbnode->base_reg = base_reg; + rbnode->cache_present = present; regcache_rbtree_set_register(map, rbnode, pos, value); return 0; @@ -345,12 +360,21 @@ regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg) rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, GFP_KERNEL); - if (!rbnode->block) { - kfree(rbnode); - return NULL; - } + if (!rbnode->block) + goto err_free; + + rbnode->cache_present = kzalloc(BITS_TO_LONGS(rbnode->blklen) * + sizeof(*rbnode->cache_present), GFP_KERNEL); + if (!rbnode->cache_present) + goto err_free_block; return rbnode; + +err_free_block: + kfree(rbnode->block); +err_free: + kfree(rbnode); + return NULL; } static int regcache_rbtree_write(struct regmap *map, unsigned int reg, @@ -363,10 +387,6 @@ 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 = regcache_set_reg_present(map, reg); - if (ret < 0) - return ret; /* if we can't locate it in the cached rbnode we'll have * to traverse the rbtree looking for it. @@ -461,8 +481,9 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min, else end = rbnode->blklen; - ret = regcache_sync_block(map, rbnode->block, rbnode->base_reg, - start, end); + ret = regcache_sync_block(map, rbnode->block, + rbnode->cache_present, + rbnode->base_reg, start, end); if (ret != 0) return ret; } @@ -470,6 +491,42 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min, return regmap_async_complete(map); } +static int regcache_rbtree_drop(struct regmap *map, unsigned int min, + unsigned int max) +{ + struct regcache_rbtree_ctx *rbtree_ctx; + struct regcache_rbtree_node *rbnode; + struct rb_node *node; + unsigned int base_reg, top_reg; + unsigned int start, end; + + rbtree_ctx = map->cache; + for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { + rbnode = rb_entry(node, struct regcache_rbtree_node, node); + + regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg, + &top_reg); + if (base_reg > max) + break; + if (top_reg < min) + continue; + + if (min > base_reg) + start = (min - base_reg) / map->reg_stride; + else + start = 0; + + if (max < top_reg) + end = (max - base_reg) / map->reg_stride + 1; + else + end = rbnode->blklen; + + bitmap_clear(rbnode->cache_present, start, end - start); + } + + return 0; +} + struct regcache_ops regcache_rbtree_ops = { .type = REGCACHE_RBTREE, .name = "rbtree", @@ -477,5 +534,6 @@ struct regcache_ops regcache_rbtree_ops = { .exit = regcache_rbtree_exit, .read = regcache_rbtree_read, .write = regcache_rbtree_write, - .sync = regcache_rbtree_sync + .sync = regcache_rbtree_sync, + .drop = regcache_rbtree_drop, }; diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index e2abd0548e7b..d6c2d691b6e8 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -121,8 +121,6 @@ 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]; @@ -181,7 +179,6 @@ 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); @@ -407,22 +404,16 @@ EXPORT_SYMBOL_GPL(regcache_sync_region); int regcache_drop_region(struct regmap *map, unsigned int min, unsigned int max) { - unsigned int reg; int ret = 0; - if (!map->cache_present && !(map->cache_ops && map->cache_ops->drop)) + if (!map->cache_ops || !map->cache_ops->drop) return -EINVAL; map->lock(map->lock_arg); trace_regcache_drop_region(map->dev, min, max); - if (map->cache_present) - for (reg = min; reg < max + 1; reg++) - clear_bit(reg, map->cache_present); - - if (map->cache_ops && map->cache_ops->drop) - ret = map->cache_ops->drop(map, min, max); + ret = map->cache_ops->drop(map, min, max); map->unlock(map->lock_arg); @@ -490,42 +481,6 @@ 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) { @@ -617,7 +572,16 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg) return -ENOENT; } +static bool regcache_reg_present(unsigned long *cache_present, unsigned int idx) +{ + if (!cache_present) + return true; + + return test_bit(idx, cache_present); +} + static int regcache_sync_block_single(struct regmap *map, void *block, + unsigned long *cache_present, unsigned int block_base, unsigned int start, unsigned int end) { @@ -627,7 +591,7 @@ static int regcache_sync_block_single(struct regmap *map, void *block, for (i = start; i < end; i++) { regtmp = block_base + (i * map->reg_stride); - if (!regcache_reg_present(map, regtmp)) + if (!regcache_reg_present(cache_present, i)) continue; val = regcache_get_val(map, block, i); @@ -678,6 +642,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data, } static int regcache_sync_block_raw(struct regmap *map, void *block, + unsigned long *cache_present, unsigned int block_base, unsigned int start, unsigned int end) { @@ -690,7 +655,7 @@ static int regcache_sync_block_raw(struct regmap *map, void *block, for (i = start; i < end; i++) { regtmp = block_base + (i * map->reg_stride); - if (!regcache_reg_present(map, regtmp)) { + if (!regcache_reg_present(cache_present, i)) { ret = regcache_sync_block_raw_flush(map, &data, base, regtmp); if (ret != 0) @@ -721,13 +686,14 @@ static int regcache_sync_block_raw(struct regmap *map, void *block, } int regcache_sync_block(struct regmap *map, void *block, + unsigned long *cache_present, 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); + return regcache_sync_block_raw(map, block, cache_present, + block_base, start, end); else - return regcache_sync_block_single(map, block, block_base, - start, end); + return regcache_sync_block_single(map, block, cache_present, + block_base, start, end); } -- cgit v1.2.3