From c3ec23288a92e20e0aff84a4cb6fbc7cc9bcf567 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 23 Feb 2012 20:48:40 +0000 Subject: regmap: Remove default cache sync implementation It's not used as all cache types have sync operations so it's just dead code which never gets tested. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index ee36bed..4d43ed3 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -254,12 +254,11 @@ EXPORT_SYMBOL_GPL(regcache_write); int regcache_sync(struct regmap *map) { int ret = 0; - unsigned int val; unsigned int i; const char *name; unsigned int bypass; - BUG_ON(!map->cache_ops); + BUG_ON(!map->cache_ops || !map->cache_ops->sync); mutex_lock(&map->lock); /* Remember the initial bypass state */ @@ -284,24 +283,8 @@ int regcache_sync(struct regmap *map) } map->cache_bypass = 0; - if (map->cache_ops->sync) { - ret = map->cache_ops->sync(map); - } else { - for (i = 0; i < map->num_reg_defaults; i++) { - ret = regcache_read(map, i, &val); - if (ret < 0) - goto out; - map->cache_bypass = 1; - ret = _regmap_write(map, i, val); - map->cache_bypass = 0; - if (ret < 0) - goto out; - dev_dbg(map->dev, "Synced register %#x, value %#x\n", - map->reg_defaults[i].reg, - map->reg_defaults[i].def); - } + ret = map->cache_ops->sync(map); - } out: trace_regcache_sync(map->dev, name, "stop"); /* Restore the bypass state */ -- cgit v0.10.2 From 6ff7373809a9b4eb644d83e2e299da297e1cbffa Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 23 Feb 2012 22:05:59 +0000 Subject: regmap: Mark the cache as clean after a successful sync Previously the cache would never be marked clean, meaning syncs would never be suppressed which isn't the end of the world but could be inefficient. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 4d43ed3..46c42d3 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -285,6 +285,9 @@ int regcache_sync(struct regmap *map) ret = map->cache_ops->sync(map); + if (ret == 0) + map->cache_dirty = false; + out: trace_regcache_sync(map->dev, name, "stop"); /* Restore the bypass state */ -- cgit v0.10.2 From ac8d91c801905a061ca883dca427a5e19602a1e7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 23 Feb 2012 19:31:04 +0000 Subject: regmap: Supply ranges to the sync operations In order to allow us to support partial sync operations add minimum and maximum register arguments to the sync operation and update the rbtree and lzo caches to use this new information. The LZO implementation is obviously not good, we could exit the iteration earlier, but there may be room for more wide reaching optimisation there. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index d141b80..9c1d62e 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -87,7 +87,7 @@ struct regcache_ops { int (*exit)(struct regmap *map); int (*read)(struct regmap *map, unsigned int reg, unsigned int *value); int (*write)(struct regmap *map, unsigned int reg, unsigned int value); - int (*sync)(struct regmap *map); + int (*sync)(struct regmap *map, unsigned int min, unsigned int max); }; bool regmap_writeable(struct regmap *map, unsigned int reg); diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index b7d1614..5e964e9 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -331,7 +331,8 @@ out: return ret; } -static int regcache_lzo_sync(struct regmap *map) +static int regcache_lzo_sync(struct regmap *map, unsigned int min, + unsigned int max) { struct regcache_lzo_ctx **lzo_blocks; unsigned int val; @@ -339,7 +340,12 @@ static int regcache_lzo_sync(struct regmap *map) int ret; lzo_blocks = map->cache; - for_each_set_bit(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) { + i = min; + for_each_set_bit_from(i, lzo_blocks[0]->sync_bmp, + lzo_blocks[0]->sync_bmp_nbits) { + if (i > max) + continue; + ret = regcache_read(map, i, &val); if (ret) return ret; diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 32620c4..bae183c 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -357,7 +357,8 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, return 0; } -static int regcache_rbtree_sync(struct regmap *map) +static int regcache_rbtree_sync(struct regmap *map, unsigned int min, + unsigned int max) { struct regcache_rbtree_ctx *rbtree_ctx; struct rb_node *node; @@ -365,12 +366,30 @@ static int regcache_rbtree_sync(struct regmap *map) unsigned int regtmp; unsigned int val; int ret; - int i; + int i, 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); - for (i = 0; i < rbnode->blklen; i++) { + + if (rbnode->base_reg < min) + continue; + if (rbnode->base_reg > max) + break; + if (rbnode->base_reg + rbnode->blklen < min) + continue; + + if (min < rbnode->base_reg + rbnode->blklen) + base = min - rbnode->base_reg; + else + base = 0; + + if (max < rbnode->base_reg + rbnode->blklen) + end = rbnode->base_reg + rbnode->blklen - max; + else + end = rbnode->blklen; + + for (i = base; i < end; i++) { regtmp = rbnode->base_reg + i; val = regcache_rbtree_get_register(rbnode, i, map->cache_word_size); diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 46c42d3..aec5a74 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -283,7 +283,7 @@ int regcache_sync(struct regmap *map) } map->cache_bypass = 0; - ret = map->cache_ops->sync(map); + ret = map->cache_ops->sync(map, 0, map->max_register); if (ret == 0) map->cache_dirty = false; -- cgit v0.10.2 From 4d4cfd1656b5f6c88eae51c40741a695b108b006 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 23 Feb 2012 20:53:37 +0000 Subject: regmap: Allow drivers to sync only part of the register cache Provide a regcache_sync_region() operation which allows drivers to write only part of the cache back to the hardware. This is intended for use in cases like power domains or DSP memories where part of the device register map may be reset without fully resetting the device. Fully supporting these devices is likely to require additional work to make specific regions of the register map cache only while they are in reset, but this is enough for most devices. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index aec5a74..b35f875 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -299,6 +299,51 @@ out: EXPORT_SYMBOL_GPL(regcache_sync); /** + * regcache_sync_region: Sync part of the register cache with the hardware. + * + * @map: map to sync. + * @min: first register to sync + * @max: last register to sync + * + * Write all non-default register values in the specified region to + * the hardware. + * + * Return a negative value on failure, 0 on success. + */ +int regcache_sync_region(struct regmap *map, unsigned int min, + unsigned int max) +{ + int ret = 0; + const char *name; + unsigned int bypass; + + BUG_ON(!map->cache_ops || !map->cache_ops->sync); + + mutex_lock(&map->lock); + + /* Remember the initial bypass state */ + bypass = map->cache_bypass; + + name = map->cache_ops->name; + dev_dbg(map->dev, "Syncing %s cache from %d-%d\n", name, min, max); + + trace_regcache_sync(map->dev, name, "start region"); + + if (!map->cache_dirty) + goto out; + + ret = map->cache_ops->sync(map, min, max); + +out: + trace_regcache_sync(map->dev, name, "stop region"); + /* Restore the bypass state */ + map->cache_bypass = bypass; + mutex_unlock(&map->lock); + + return ret; +} + +/** * regcache_cache_only: Put a register map into cache only mode * * @map: map to configure diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 860739a..5ff7d44 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -145,6 +145,8 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, bool *change); int regcache_sync(struct regmap *map); +int regcache_sync_region(struct regmap *map, unsigned int min, + unsigned int max); void regcache_cache_only(struct regmap *map, bool enable); void regcache_cache_bypass(struct regmap *map, bool enable); void regcache_mark_dirty(struct regmap *map); -- cgit v0.10.2 From a0941e562e8008804f9fe4400315ceb164752fac Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 27 Feb 2012 14:35:33 +0000 Subject: regmap: Fix x86_64 breakage Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index 5e964e9..f0f0406 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -341,7 +341,7 @@ static int regcache_lzo_sync(struct regmap *map, unsigned int min, lzo_blocks = map->cache; i = min; - for_each_set_bit_from(i, lzo_blocks[0]->sync_bmp, + for_each_set_bit_cont(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) { if (i > max) continue; -- cgit v0.10.2 From f9353e70bcebd00cd182d946083afd7d8eddd259 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 5 Mar 2012 23:28:49 +0000 Subject: regmap: Fix rbtree block base in sync Otherwise we'll end up running with bogus register numbers. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index bae183c..313c20f 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -379,7 +379,7 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min, if (rbnode->base_reg + rbnode->blklen < min) continue; - if (min < rbnode->base_reg + rbnode->blklen) + if (min > rbnode->base_reg) base = min - rbnode->base_reg; else base = 0; -- cgit v0.10.2