Introduce caching support for regmap
Subsystems (3)
v4 cover Introduce caching support for regmap
This patch series introduces register caching support for regmap. I've mostly implemented all the suggestions, except the use of a dedicated init function for each of the compress types.
Some things have been left out, such as support for bulk read operations, various optimizations, shared caches etc.
Most of this stuff will be implemented incrementally.
This version has only been build tested, so any testing would be highly appreciated. The changes though since v3 are trivial so it should work.
Thanks to Lars-Peter Clausen and Mark Brown for all the constructive comments.
Dimitris Papastamos (6):
regmap: Introduce caching support
regmap: Add the indexed cache support
regmap: Add the rbtree cache support
regmap: Add the LZO cache support
regmap: Add the regcache_sync trace event
regmap: Incorporate the regcache core into regmap
drivers/base/regmap/Kconfig | 2 + drivers/base/regmap/Makefile | 2 +- drivers/base/regmap/internal.h | 56 +++++ drivers/base/regmap/regcache-indexed.c | 65 +++++ drivers/base/regmap/regcache-lzo.c | 361 +++++++++++++++++++++++++++++ drivers/base/regmap/regcache-rbtree.c | 399 ++++++++++++++++++++++++++++++++ drivers/base/regmap/regcache.c | 311 +++++++++++++++++++++++++ drivers/base/regmap/regmap.c | 28 +++ include/linux/regmap.h | 27 ++- include/trace/events/regmap.h | 24 ++ 10 files changed, 1269 insertions(+), 6 deletions(-) create mode 100644 drivers/base/regmap/regcache-indexed.c create mode 100644 drivers/base/regmap/regcache-lzo.c create mode 100644 drivers/base/regmap/regcache-rbtree.c create mode 100644 drivers/base/regmap/regcache.c
Signature · 1 line
1.7.6.1
PATCH v4 1/6 regmap: Introduce caching support
This patch introduces caching support for regmap. The regcache API has evolved essentially out of ASoC soc-cache so most of the actual caching types (except LZO) have been tested in the past.
The purpose of regcache is to optimize in time and space the handling of register caches. Time optimization is achieved by not having to go over a slow bus like I2C to read the value of a register, instead it is cached locally in memory and can be retrieved faster. Regarding space optimization, some of the cache types are better at packing the caches, for e.g. the rbtree and the LZO caches. By doing this the sacrifice in time still wins over doing I2C transactions.
--- drivers/base/regmap/Makefile | 2 +- drivers/base/regmap/internal.h | 52 +++++++ drivers/base/regmap/regcache.c | 302 ++++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 24 +++- 4 files changed, 374 insertions(+), 6 deletions(-) create mode 100644 drivers/base/regmap/regcache.c
drivers/base/regmap/Makefile +1 -1
@@ -1,4 +1,4 @@ -obj-$(CONFIG_REGMAP) += regmap.o +obj-$(CONFIG_REGMAP) += regmap.o regcache.o obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
drivers/base/regmap/internal.h +52 -0
68 lines not on this page · raw message
drivers/base/regmap/regcache.c new file +302 -0
302 lines not on this page · raw message
include/linux/regmap.h +19 -5
@@ -20,6 +20,11 @@struct i2c_client ;struct spi_device ; +/* An enum of all the supported cache types */ +enum regcache_type { +REGCACHE_NONE , +}; +/** * Default value for a register. We use an array of structs rather * than a simple array as many modern devices have very sparse
@@ -50,15 +55,20 @@ struct reg_default {* (eg, a clear on read interrupt status register). * * @max_register: Optional, specifies the maximum valid register index. -* @reg_defaults: Power on reset values for registers (for use with -* register cache support). -* @num_reg_defaults: Number of elements in reg_defaults. * * @read_flag_mask: Mask to be set in the top byte of the register when doing * a read. * @write_flag_mask: Mask to be set in the top byte of the register when doing * a write. If both read_flag_mask and write_flag_mask are * empty the regmap_bus default masks are used. +* +* @cache_type: The actual cache type. +* @cache_defaults: Power on reset values for registers (for use with +* register cache support). +* @num_cache_defaults: Number of elements in cache_defaults. +* @cache_defaults_raw: Power on reset values for registers (for use with +* register cache support). +* @num_cache_defaults_raw: Number of elements in cache_defaults_raw. */ struct regmap_config {int reg_bits;
@@ -70,11 +80,15 @@ struct regmap_config {bool (*precious_reg)(struct device *dev,unsigned int reg);unsigned int max_register; -struct reg_default *reg_defaults; -int num_reg_defaults;u8 read_flag_mask;u8 write_flag_mask; + +enum regcache_type cache_type; +struct reg_default *cache_defaults; +unsigned int num_cache_defaults; +const void *cache_defaults_raw; +unsigned int num_cache_defaults_raw; }; typedef int (*regmap_hw_write)(struct device *dev, const void *data,
Signature · 1 line
1.7.6.1
PATCH v4 2/6 regmap: Add the indexed cache support
In reply to Dimitris PapastamosThis is the simplest form of a cache available in regcache. Any registers whose default value is 0 are ignored. If any of those registers are modified in the future, they will be placed in the cache on demand. The cache layout is essentially using the provided register defaults by the regcache core directly and does not re-map it to another representation.
--- drivers/base/regmap/Makefile | 2 +- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regcache-indexed.c | 65 ++++++++++++++++++++++++++++++++ drivers/base/regmap/regcache.c | 1 + include/linux/regmap.h | 1 + 5 files changed, 69 insertions(+), 1 deletions(-) create mode 100644 drivers/base/regmap/regcache-indexed.c
drivers/base/regmap/Makefile +1 -1
@@ -1,4 +1,4 @@ -obj-$(CONFIG_REGMAP) += regmap.o regcache.o +obj-$(CONFIG_REGMAP) += regmap.o regcache.o regcache-indexed.o obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
drivers/base/regmap/internal.h +1 -0
@@ -118,4 +118,5 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg);int regcache_insert_reg (struct regmap *map,unsigned int reg,unsigned int val); +extern struct regcache_ops regcache_indexed_ops;#endif
drivers/base/regmap/regcache-indexed.c new file +65 -0
65 lines not on this page · raw message
drivers/base/regmap/regcache.c +1 -0
@@ -16,6 +16,7 @@#include "internal.h" static const struct regcache_ops *cache_types[] = { + ®cache_indexed_ops, };static int regcache_hw_init (struct regmap *map)
include/linux/regmap.h +1 -0
@@ -23,6 +23,7 @@ struct spi_device;/* An enum of all the supported cache types */ enum regcache_type {REGCACHE_NONE , +REGCACHE_INDEXED , };/**
Signature · 1 line
1.7.6.1
PATCH v4 3/6 regmap: Add the rbtree cache support
In reply to Dimitris PapastamosThis patch adds support for the rbtree cache compression type.
Each rbnode manages a variable length block of registers. There can be no two nodes with overlapping blocks. Each block has a base register and a currently top register, all the other registers, if any, lie in between these two and in ascending order.
The reasoning behind the construction of this rbtree is simple. In the snd_soc_rbtree_cache_init() function, we iterate over the register defaults provided by the regcache core. For each register value that is non-zero we insert it in the rbtree. In order to determine in which rbnode we need to add the register, we first look if there is another register already added that is adjacent to the one we are about to add. If that is the case we append it in that rbnode block, otherwise we create a new rbnode with a single register in its block and add it to the tree.
There are various optimizations across the implementation to speed up lookups by caching the most recently used rbnode.
--- drivers/base/regmap/Makefile | 2 +- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regcache-rbtree.c | 399 +++++++++++++++++++++++++++++++++ drivers/base/regmap/regcache.c | 1 + include/linux/regmap.h | 1 + 5 files changed, 403 insertions(+), 1 deletions(-) create mode 100644 drivers/base/regmap/regcache-rbtree.c
drivers/base/regmap/Makefile +1 -1
@@ -1,4 +1,4 @@ -obj-$(CONFIG_REGMAP) += regmap.o regcache.o regcache-indexed.o +obj-$(CONFIG_REGMAP) += regmap.o regcache.o regcache-indexed.o regcache-rbtree.o obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
drivers/base/regmap/internal.h +1 -0
@@ -119,4 +119,5 @@ int regcache_insert_reg(struct regmap *map, unsigned int reg, unsigned int val);extern struct regcache_ops regcache_indexed_ops; +extern struct regcache_ops regcache_rbtree_ops;#endif
drivers/base/regmap/regcache-rbtree.c new file +399 -0
399 lines not on this page · raw message
drivers/base/regmap/regcache.c +1 -0
@@ -17,6 +17,7 @@static const struct regcache_ops *cache_types[] = { ®cache_indexed_ops, + ®cache_rbtree_ops, };static int regcache_hw_init (struct regmap *map)
include/linux/regmap.h +1 -0
@@ -24,6 +24,7 @@ struct spi_device;enum regcache_type {REGCACHE_NONE ,REGCACHE_INDEXED , +REGCACHE_RBTREE , };/**
Signature · 1 line
1.7.6.1
PATCH v4 4/6 regmap: Add the LZO cache support
In reply to Dimitris PapastamosThis patch adds support for LZO compression when storing the register cache.
For a typical device whose register map would normally occupy 25kB or 50kB by using the LZO compression technique, one can get down to ~5-7kB. There might be a performance penalty associated with each individual read/write due to decompressing/compressing the underlying cache, however that should not be noticeable. These memory benefits depend on whether the target architecture can get rid of the memory occupied by the original register defaults cache which is marked as __devinitconst. Nevertheless there will be some memory gain even if the target architecture can't get rid of the original register map, this should be around ~30-32kB instead of 50kB.
--- drivers/base/regmap/Kconfig | 2 + drivers/base/regmap/Makefile | 2 +- drivers/base/regmap/internal.h | 2 + drivers/base/regmap/regcache-lzo.c | 361 ++++++++++++++++++++++++++++++++++++ drivers/base/regmap/regcache.c | 1 + include/linux/regmap.h | 1 + 6 files changed, 368 insertions(+), 1 deletions(-) create mode 100644 drivers/base/regmap/regcache-lzo.c
drivers/base/regmap/Kconfig +2 -0
@@ -4,6 +4,8 @@ config REGMAP default y if (REGMAP_I2C || REGMAP_SPI) + select LZO_COMPRESS + select LZO_DECOMPRESS bool config REGMAP_I2C
drivers/base/regmap/Makefile +1 -1
@@ -1,4 +1,4 @@ -obj-$(CONFIG_REGMAP) += regmap.o regcache.o regcache-indexed.o regcache-rbtree.o +obj-$(CONFIG_REGMAP) += regmap.o regcache.o regcache-indexed.o regcache-rbtree.o regcache-lzo.o obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
drivers/base/regmap/internal.h +2 -0
@@ -120,4 +120,6 @@ int regcache_insert_reg(struct regmap *map, unsigned int reg,extern struct regcache_ops regcache_indexed_ops;extern struct regcache_ops regcache_rbtree_ops; +extern struct regcache_ops regcache_lzo_ops; +#endif
drivers/base/regmap/regcache-lzo.c new file +361 -0
361 lines not on this page · raw message
drivers/base/regmap/regcache.c +1 -0
@@ -18,6 +18,7 @@static const struct regcache_ops *cache_types[] = { ®cache_indexed_ops, ®cache_rbtree_ops, + ®cache_lzo_ops, };static int regcache_hw_init (struct regmap *map)
include/linux/regmap.h +1 -0
@@ -25,6 +25,7 @@ enum regcache_type {REGCACHE_NONE ,REGCACHE_INDEXED ,REGCACHE_RBTREE , + REGCACHE_LZO };/**
Signature · 1 line
1.7.6.1
PATCH v4 5/6 regmap: Add the regcache_sync trace event
In reply to Dimitris Papastamos--- drivers/base/regmap/regcache.c | 8 +++++++- include/trace/events/regmap.h | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletions(-)
drivers/base/regmap/regcache.c +7 -1
@@ -219,12 +219,18 @@ EXPORT_SYMBOL_GPL(regcache_write);*/ int regcache_sync (struct regmap *map) { +int ret; +const char *name; +BUG_ON (!map->cache_ops);if (map->cache_ops->sync) {dev_dbg (map->dev,"Syncing %s cache\n" , map->cache_ops->name); -return map->cache_ops->sync (map); + name = map->cache_ops->name; +trace_regcache_sync (map->dev, name,"start" ); + ret = map->cache_ops->sync (map); +trace_regcache_sync (map->dev, name,"stop" ); }return 0 ; }
include/trace/events/regmap.h +24 -0
@@ -106,6 +106,30 @@ DEFINE_EVENT(regmap_block, regmap_hw_write_done,TP_ARGS (dev, reg, count) ); +TRACE_EVENT (regcache_sync, + + TP_PROTO(struct device *dev, const char *type, + const char *status), + +TP_ARGS (dev, type, status), + +TP_STRUCT__entry ( + __string( name, dev_name(dev) ) + __string( status, status ) + __string( type, type ) +__field ( int, type ) + ), + +TP_fast_assign ( + __assign_str(name, dev_name(dev)); + __assign_str(status, status); + __assign_str(type, type); + ), + +TP_printk ("%s type=%s status=%s" ,__get_str (name), +__get_str (type),__get_str (status)) +); +#endif /* _TRACE_REGMAP_H */ /* This part must be outside protection */
Signature · 1 line
1.7.6.1
PATCH v4 6/6 regmap: Incorporate the regcache core into regmap
In reply to Dimitris PapastamosThis patch incorporates the regcache core code into regmap. All previous patches have been no-ops essentially up to this point.
The bulk read operation is not supported by regcache at the moment. This will be implemented incrementally.
--- drivers/base/regmap/regmap.c | 28 ++++++++++++++++++++++++++++ 1 files changed, 28 insertions(+), 0 deletions(-)
drivers/base/regmap/regmap.c +28 -0
@@ -146,6 +146,13 @@ struct regmap *regmap_init(struct device *dev, map->readable_reg = config->readable_reg; map->volatile_reg = config->volatile_reg; map->precious_reg = config->precious_reg; + map->cache_type = config->cache_type; + map->cache_defaults = config->cache_defaults; + map->num_cache_defaults = config->num_cache_defaults; + map->num_cache_defaults_raw = config->num_cache_defaults_raw; + map->cache_defaults_raw = config->cache_defaults_raw; + map->cache_size_raw = (config->val_bits /8 ) * config->num_cache_defaults_raw; + map->cache_word_size = config->val_bits /8 ;if (config->read_flag_mask || config->write_flag_mask) { map->read_flag_mask = config->read_flag_mask;
@@ -208,6 +215,10 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } + ret =regcache_init (map); +if (ret <0 ) + goto err_map; +regmap_debugfs_init (map);return map;
@@ -224,6 +235,7 @@ EXPORT_SYMBOL_GPL(regmap_init);*/ void regmap_exit (struct regmap *map) { +regcache_exit (map);regmap_debugfs_exit (map);kfree (map->work_buf);kfree (map);
@@ -290,6 +302,12 @@ static int _regmap_write(struct regmap *map, unsigned int reg,int ret;BUG_ON (!map->format.format_write && !map->format.format_val); +if (!map->cache_bypass) { + ret =regcache_write (map, reg, val); +if (!ret || map->cache_only) +return 0 ; + } +trace_regmap_reg_write (map->dev, reg, val);if (map->format.format_write) {
@@ -428,6 +446,14 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)mutex_lock (&map->lock); +if (!map->cache_bypass) { + ret =regcache_read (map, reg, val); +if (!ret) { +mutex_unlock (&map->lock); +return 0 ; + } + } + ret =_regmap_read (map, reg, val);mutex_unlock (&map->lock);
@@ -479,6 +505,8 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,int ret, i;size_t val_bytes = map->format.val_bytes; +WARN_ON (map->cache_type !=REGCACHE_NONE ); +if (!map->format.parse_val)return -EINVAL ;
Signature · 1 line
1.7.6.1