aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars-Peter Clausen <lars@metafoo.de>2013-08-29 04:26:34 -0400
committerMark Brown <broonie@linaro.org>2013-08-29 08:32:41 -0400
commit3f4ff561bc88b074d5e868dde4012d89cbb06c87 (patch)
treee611b5128911d15253f558ff2a137a26dd7ad695
parent472fdec7380cec483e241fa696d9b90bc37ddd4c (diff)
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 <lars@metafoo.de> Signed-off-by: Mark Brown <broonie@linaro.org>
-rw-r--r--drivers/base/regmap/internal.h14
-rw-r--r--drivers/base/regmap/regcache-rbtree.c86
-rw-r--r--drivers/base/regmap/regcache.c72
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 {
128 void *cache; 128 void *cache;
129 u32 cache_dirty; 129 u32 cache_dirty;
130 130
131 unsigned long *cache_present;
132 unsigned int cache_present_nbits;
133
134 struct reg_default *patch; 131 struct reg_default *patch;
135 int patch_regs; 132 int patch_regs;
136 133
@@ -203,6 +200,7 @@ int regcache_write(struct regmap *map,
203 unsigned int reg, unsigned int value); 200 unsigned int reg, unsigned int value);
204int regcache_sync(struct regmap *map); 201int regcache_sync(struct regmap *map);
205int regcache_sync_block(struct regmap *map, void *block, 202int regcache_sync_block(struct regmap *map, void *block,
203 unsigned long *cache_present,
206 unsigned int block_base, unsigned int start, 204 unsigned int block_base, unsigned int start,
207 unsigned int end); 205 unsigned int end);
208 206
@@ -218,16 +216,6 @@ unsigned int regcache_get_val(struct regmap *map, const void *base,
218bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, 216bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
219 unsigned int val); 217 unsigned int val);
220int regcache_lookup_reg(struct regmap *map, unsigned int reg); 218int regcache_lookup_reg(struct regmap *map, unsigned int reg);
221int regcache_set_reg_present(struct regmap *map, unsigned int reg);
222
223static inline bool regcache_reg_present(struct regmap *map, unsigned int reg)
224{
225 if (!map->cache_present)
226 return false;
227 if (reg > map->cache_present_nbits)
228 return false;
229 return map->cache_present[BIT_WORD(reg)] & BIT_MASK(reg);
230}
231 219
232int _regmap_raw_write(struct regmap *map, unsigned int reg, 220int _regmap_raw_write(struct regmap *map, unsigned int reg,
233 const void *val, size_t val_len, bool async); 221 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 {
29 unsigned int base_reg; 29 unsigned int base_reg;
30 /* block of adjacent registers */ 30 /* block of adjacent registers */
31 void *block; 31 void *block;
32 /* Which registers are present */
33 long *cache_present;
32 /* number of registers available in the block */ 34 /* number of registers available in the block */
33 unsigned int blklen; 35 unsigned int blklen;
34} __attribute__ ((packed)); 36} __attribute__ ((packed));
@@ -57,6 +59,7 @@ static void regcache_rbtree_set_register(struct regmap *map,
57 struct regcache_rbtree_node *rbnode, 59 struct regcache_rbtree_node *rbnode,
58 unsigned int idx, unsigned int val) 60 unsigned int idx, unsigned int val)
59{ 61{
62 set_bit(idx, rbnode->cache_present);
60 regcache_set_val(map, rbnode->block, idx, val); 63 regcache_set_val(map, rbnode->block, idx, val);
61} 64}
62 65
@@ -146,13 +149,13 @@ static int rbtree_show(struct seq_file *s, void *ignored)
146 map->lock(map->lock_arg); 149 map->lock(map->lock_arg);
147 150
148 mem_size = sizeof(*rbtree_ctx); 151 mem_size = sizeof(*rbtree_ctx);
149 mem_size += BITS_TO_LONGS(map->cache_present_nbits) * sizeof(long);
150 152
151 for (node = rb_first(&rbtree_ctx->root); node != NULL; 153 for (node = rb_first(&rbtree_ctx->root); node != NULL;
152 node = rb_next(node)) { 154 node = rb_next(node)) {
153 n = container_of(node, struct regcache_rbtree_node, node); 155 n = container_of(node, struct regcache_rbtree_node, node);
154 mem_size += sizeof(*n); 156 mem_size += sizeof(*n);
155 mem_size += (n->blklen * map->cache_word_size); 157 mem_size += (n->blklen * map->cache_word_size);
158 mem_size += BITS_TO_LONGS(n->blklen) * sizeof(long);
156 159
157 regcache_rbtree_get_base_top_reg(map, n, &base, &top); 160 regcache_rbtree_get_base_top_reg(map, n, &base, &top);
158 this_registers = ((top - base) / map->reg_stride) + 1; 161 this_registers = ((top - base) / map->reg_stride) + 1;
@@ -245,6 +248,7 @@ static int regcache_rbtree_exit(struct regmap *map)
245 rbtree_node = rb_entry(next, struct regcache_rbtree_node, node); 248 rbtree_node = rb_entry(next, struct regcache_rbtree_node, node);
246 next = rb_next(&rbtree_node->node); 249 next = rb_next(&rbtree_node->node);
247 rb_erase(&rbtree_node->node, &rbtree_ctx->root); 250 rb_erase(&rbtree_node->node, &rbtree_ctx->root);
251 kfree(rbtree_node->cache_present);
248 kfree(rbtree_node->block); 252 kfree(rbtree_node->block);
249 kfree(rbtree_node); 253 kfree(rbtree_node);
250 } 254 }
@@ -265,7 +269,7 @@ static int regcache_rbtree_read(struct regmap *map,
265 rbnode = regcache_rbtree_lookup(map, reg); 269 rbnode = regcache_rbtree_lookup(map, reg);
266 if (rbnode) { 270 if (rbnode) {
267 reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; 271 reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
268 if (!regcache_reg_present(map, reg)) 272 if (!test_bit(reg_tmp, rbnode->cache_present))
269 return -ENOENT; 273 return -ENOENT;
270 *value = regcache_rbtree_get_register(map, rbnode, reg_tmp); 274 *value = regcache_rbtree_get_register(map, rbnode, reg_tmp);
271 } else { 275 } else {
@@ -285,6 +289,7 @@ static int regcache_rbtree_insert_to_block(struct regmap *map,
285{ 289{
286 unsigned int blklen; 290 unsigned int blklen;
287 unsigned int pos, offset; 291 unsigned int pos, offset;
292 unsigned long *present;
288 u8 *blk; 293 u8 *blk;
289 294
290 blklen = (top_reg - base_reg) / map->reg_stride + 1; 295 blklen = (top_reg - base_reg) / map->reg_stride + 1;
@@ -297,15 +302,25 @@ static int regcache_rbtree_insert_to_block(struct regmap *map,
297 if (!blk) 302 if (!blk)
298 return -ENOMEM; 303 return -ENOMEM;
299 304
305 present = krealloc(rbnode->cache_present,
306 BITS_TO_LONGS(blklen) * sizeof(*present), GFP_KERNEL);
307 if (!present) {
308 kfree(blk);
309 return -ENOMEM;
310 }
311
300 /* insert the register value in the correct place in the rbnode block */ 312 /* insert the register value in the correct place in the rbnode block */
301 if (pos == 0) 313 if (pos == 0) {
302 memmove(blk + offset * map->cache_word_size, 314 memmove(blk + offset * map->cache_word_size,
303 blk, rbnode->blklen * map->cache_word_size); 315 blk, rbnode->blklen * map->cache_word_size);
316 bitmap_shift_right(present, present, offset, blklen);
317 }
304 318
305 /* update the rbnode block, its size and the base register */ 319 /* update the rbnode block, its size and the base register */
306 rbnode->block = blk; 320 rbnode->block = blk;
307 rbnode->blklen = blklen; 321 rbnode->blklen = blklen;
308 rbnode->base_reg = base_reg; 322 rbnode->base_reg = base_reg;
323 rbnode->cache_present = present;
309 324
310 regcache_rbtree_set_register(map, rbnode, pos, value); 325 regcache_rbtree_set_register(map, rbnode, pos, value);
311 return 0; 326 return 0;
@@ -345,12 +360,21 @@ regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg)
345 360
346 rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, 361 rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size,
347 GFP_KERNEL); 362 GFP_KERNEL);
348 if (!rbnode->block) { 363 if (!rbnode->block)
349 kfree(rbnode); 364 goto err_free;
350 return NULL; 365
351 } 366 rbnode->cache_present = kzalloc(BITS_TO_LONGS(rbnode->blklen) *
367 sizeof(*rbnode->cache_present), GFP_KERNEL);
368 if (!rbnode->cache_present)
369 goto err_free_block;
352 370
353 return rbnode; 371 return rbnode;
372
373err_free_block:
374 kfree(rbnode->block);
375err_free:
376 kfree(rbnode);
377 return NULL;
354} 378}
355 379
356static int regcache_rbtree_write(struct regmap *map, unsigned int reg, 380static 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,
363 int ret; 387 int ret;
364 388
365 rbtree_ctx = map->cache; 389 rbtree_ctx = map->cache;
366 /* update the reg_present bitmap, make space if necessary */
367 ret = regcache_set_reg_present(map, reg);
368 if (ret < 0)
369 return ret;
370 390
371 /* if we can't locate it in the cached rbnode we'll have 391 /* if we can't locate it in the cached rbnode we'll have
372 * to traverse the rbtree looking for it. 392 * to traverse the rbtree looking for it.
@@ -461,8 +481,9 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
461 else 481 else
462 end = rbnode->blklen; 482 end = rbnode->blklen;
463 483
464 ret = regcache_sync_block(map, rbnode->block, rbnode->base_reg, 484 ret = regcache_sync_block(map, rbnode->block,
465 start, end); 485 rbnode->cache_present,
486 rbnode->base_reg, start, end);
466 if (ret != 0) 487 if (ret != 0)
467 return ret; 488 return ret;
468 } 489 }
@@ -470,6 +491,42 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
470 return regmap_async_complete(map); 491 return regmap_async_complete(map);
471} 492}
472 493
494static int regcache_rbtree_drop(struct regmap *map, unsigned int min,
495 unsigned int max)
496{
497 struct regcache_rbtree_ctx *rbtree_ctx;
498 struct regcache_rbtree_node *rbnode;
499 struct rb_node *node;
500 unsigned int base_reg, top_reg;
501 unsigned int start, end;
502
503 rbtree_ctx = map->cache;
504 for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
505 rbnode = rb_entry(node, struct regcache_rbtree_node, node);
506
507 regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
508 &top_reg);
509 if (base_reg > max)
510 break;
511 if (top_reg < min)
512 continue;
513
514 if (min > base_reg)
515 start = (min - base_reg) / map->reg_stride;
516 else
517 start = 0;
518
519 if (max < top_reg)
520 end = (max - base_reg) / map->reg_stride + 1;
521 else
522 end = rbnode->blklen;
523
524 bitmap_clear(rbnode->cache_present, start, end - start);
525 }
526
527 return 0;
528}
529
473struct regcache_ops regcache_rbtree_ops = { 530struct regcache_ops regcache_rbtree_ops = {
474 .type = REGCACHE_RBTREE, 531 .type = REGCACHE_RBTREE,
475 .name = "rbtree", 532 .name = "rbtree",
@@ -477,5 +534,6 @@ struct regcache_ops regcache_rbtree_ops = {
477 .exit = regcache_rbtree_exit, 534 .exit = regcache_rbtree_exit,
478 .read = regcache_rbtree_read, 535 .read = regcache_rbtree_read,
479 .write = regcache_rbtree_write, 536 .write = regcache_rbtree_write,
480 .sync = regcache_rbtree_sync 537 .sync = regcache_rbtree_sync,
538 .drop = regcache_rbtree_drop,
481}; 539};
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)
121 map->reg_defaults_raw = config->reg_defaults_raw; 121 map->reg_defaults_raw = config->reg_defaults_raw;
122 map->cache_word_size = DIV_ROUND_UP(config->val_bits, 8); 122 map->cache_word_size = DIV_ROUND_UP(config->val_bits, 8);
123 map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw; 123 map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw;
124 map->cache_present = NULL;
125 map->cache_present_nbits = 0;
126 124
127 map->cache = NULL; 125 map->cache = NULL;
128 map->cache_ops = cache_types[i]; 126 map->cache_ops = cache_types[i];
@@ -181,7 +179,6 @@ void regcache_exit(struct regmap *map)
181 179
182 BUG_ON(!map->cache_ops); 180 BUG_ON(!map->cache_ops);
183 181
184 kfree(map->cache_present);
185 kfree(map->reg_defaults); 182 kfree(map->reg_defaults);
186 if (map->cache_free) 183 if (map->cache_free)
187 kfree(map->reg_defaults_raw); 184 kfree(map->reg_defaults_raw);
@@ -407,22 +404,16 @@ EXPORT_SYMBOL_GPL(regcache_sync_region);
407int regcache_drop_region(struct regmap *map, unsigned int min, 404int regcache_drop_region(struct regmap *map, unsigned int min,
408 unsigned int max) 405 unsigned int max)
409{ 406{
410 unsigned int reg;
411 int ret = 0; 407 int ret = 0;
412 408
413 if (!map->cache_present && !(map->cache_ops && map->cache_ops->drop)) 409 if (!map->cache_ops || !map->cache_ops->drop)
414 return -EINVAL; 410 return -EINVAL;
415 411
416 map->lock(map->lock_arg); 412 map->lock(map->lock_arg);
417 413
418 trace_regcache_drop_region(map->dev, min, max); 414 trace_regcache_drop_region(map->dev, min, max);
419 415
420 if (map->cache_present) 416 ret = map->cache_ops->drop(map, min, max);
421 for (reg = min; reg < max + 1; reg++)
422 clear_bit(reg, map->cache_present);
423
424 if (map->cache_ops && map->cache_ops->drop)
425 ret = map->cache_ops->drop(map, min, max);
426 417
427 map->unlock(map->lock_arg); 418 map->unlock(map->lock_arg);
428 419
@@ -490,42 +481,6 @@ void regcache_cache_bypass(struct regmap *map, bool enable)
490} 481}
491EXPORT_SYMBOL_GPL(regcache_cache_bypass); 482EXPORT_SYMBOL_GPL(regcache_cache_bypass);
492 483
493int regcache_set_reg_present(struct regmap *map, unsigned int reg)
494{
495 unsigned long *cache_present;
496 unsigned int cache_present_size;
497 unsigned int nregs;
498 int i;
499
500 nregs = reg + 1;
501 cache_present_size = BITS_TO_LONGS(nregs);
502 cache_present_size *= sizeof(long);
503
504 if (!map->cache_present) {
505 cache_present = kmalloc(cache_present_size, GFP_KERNEL);
506 if (!cache_present)
507 return -ENOMEM;
508 bitmap_zero(cache_present, nregs);
509 map->cache_present = cache_present;
510 map->cache_present_nbits = nregs;
511 }
512
513 if (nregs > map->cache_present_nbits) {
514 cache_present = krealloc(map->cache_present,
515 cache_present_size, GFP_KERNEL);
516 if (!cache_present)
517 return -ENOMEM;
518 for (i = 0; i < nregs; i++)
519 if (i >= map->cache_present_nbits)
520 clear_bit(i, cache_present);
521 map->cache_present = cache_present;
522 map->cache_present_nbits = nregs;
523 }
524
525 set_bit(reg, map->cache_present);
526 return 0;
527}
528
529bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, 484bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
530 unsigned int val) 485 unsigned int val)
531{ 486{
@@ -617,7 +572,16 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg)
617 return -ENOENT; 572 return -ENOENT;
618} 573}
619 574
575static bool regcache_reg_present(unsigned long *cache_present, unsigned int idx)
576{
577 if (!cache_present)
578 return true;
579
580 return test_bit(idx, cache_present);
581}
582
620static int regcache_sync_block_single(struct regmap *map, void *block, 583static int regcache_sync_block_single(struct regmap *map, void *block,
584 unsigned long *cache_present,
621 unsigned int block_base, 585 unsigned int block_base,
622 unsigned int start, unsigned int end) 586 unsigned int start, unsigned int end)
623{ 587{
@@ -627,7 +591,7 @@ static int regcache_sync_block_single(struct regmap *map, void *block,
627 for (i = start; i < end; i++) { 591 for (i = start; i < end; i++) {
628 regtmp = block_base + (i * map->reg_stride); 592 regtmp = block_base + (i * map->reg_stride);
629 593
630 if (!regcache_reg_present(map, regtmp)) 594 if (!regcache_reg_present(cache_present, i))
631 continue; 595 continue;
632 596
633 val = regcache_get_val(map, block, i); 597 val = regcache_get_val(map, block, i);
@@ -678,6 +642,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data,
678} 642}
679 643
680static int regcache_sync_block_raw(struct regmap *map, void *block, 644static int regcache_sync_block_raw(struct regmap *map, void *block,
645 unsigned long *cache_present,
681 unsigned int block_base, unsigned int start, 646 unsigned int block_base, unsigned int start,
682 unsigned int end) 647 unsigned int end)
683{ 648{
@@ -690,7 +655,7 @@ static int regcache_sync_block_raw(struct regmap *map, void *block,
690 for (i = start; i < end; i++) { 655 for (i = start; i < end; i++) {
691 regtmp = block_base + (i * map->reg_stride); 656 regtmp = block_base + (i * map->reg_stride);
692 657
693 if (!regcache_reg_present(map, regtmp)) { 658 if (!regcache_reg_present(cache_present, i)) {
694 ret = regcache_sync_block_raw_flush(map, &data, 659 ret = regcache_sync_block_raw_flush(map, &data,
695 base, regtmp); 660 base, regtmp);
696 if (ret != 0) 661 if (ret != 0)
@@ -721,13 +686,14 @@ static int regcache_sync_block_raw(struct regmap *map, void *block,
721} 686}
722 687
723int regcache_sync_block(struct regmap *map, void *block, 688int regcache_sync_block(struct regmap *map, void *block,
689 unsigned long *cache_present,
724 unsigned int block_base, unsigned int start, 690 unsigned int block_base, unsigned int start,
725 unsigned int end) 691 unsigned int end)
726{ 692{
727 if (regmap_can_raw_write(map)) 693 if (regmap_can_raw_write(map))
728 return regcache_sync_block_raw(map, block, block_base, 694 return regcache_sync_block_raw(map, block, cache_present,
729 start, end); 695 block_base, start, end);
730 else 696 else
731 return regcache_sync_block_single(map, block, block_base, 697 return regcache_sync_block_single(map, block, cache_present,
732 start, end); 698 block_base, start, end);
733} 699}