Introduce caching support for regmap
Subsystems (3)
cover Introduce caching support for regmap
This patch series introduces register caching support for regmap. 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.
Dimitris Papastamos (8):
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
regmap: It is impossible to be given a NULL defaults cache
regmap: Support NULL cache_defaults_raw
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 | 400 ++++++++++++++++++++++++++++++++ drivers/base/regmap/regcache.c | 287 +++++++++++++++++++++++ drivers/base/regmap/regmap.c | 55 +++++ include/linux/regmap.h | 22 ++- include/trace/events/regmap.h | 24 ++ 9 files changed, 1266 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 1/8 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 | 50 ++++++++ drivers/base/regmap/regcache.c | 251 ++++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 19 +++- 4 files changed, 316 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 +50 -0
66 lines not on this page · raw message
drivers/base/regmap/regcache.c new file +251 -0
251 lines not on this page · raw message
include/linux/regmap.h +14 -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,9 +55,11 @@ 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. +* +* @cache_type: The actual cache type. +* @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;
@@ -64,8 +71,10 @@ 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; + +enum regcache_type cache_type; +const void *cache_defaults_raw; +unsigned num_cache_defaults_raw; }; typedef int (*regmap_hw_write)(struct device *dev, const void *data,
Signature · 1 line
1.7.6.1
Re: [PATCH 1/8] regmap: Introduce caching support
Quoted 49 lines · Dimitris Papastamos
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.
Signed-off-by Dimitris Papastamos <[email protected]>--- drivers/base/regmap/Makefile | 2 +- drivers/base/regmap/internal.h | 50 ++++++++ drivers/base/regmap/regcache.c | 251 ++++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 19 +++- 4 files changed, 316 insertions(+), 6 deletions(-) create mode 100644 drivers/base/regmap/regcache.cdrivers/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.odrivers/base/regmap/internal.h +0
-0drivers/base/regmap/regcache.c new file +9
-0@@ -0,0 +1,251 @@ +/* +* Register cache access API +* +* Copyright 2011 Wolfson Microelectronics plc +* +* Author: Dimitris Papastamos <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as
drivers/base/regmap/regcache.c line 19
Quoted 10 lines
drivers/base/regmap/regcache.c new file +10
-0+* published by the Free Software Foundation. +*/ + +#include <linux/slab.h> +#include <trace/events/regmap.h> + +#include "internal.h" + +static const struct regcache_ops *cache_types[] = { +};
I wonder if it makes sense to keep a list of regcache_ops this way, or whether it is not better to just pass the ops we want to use to regcache_init directly.
Quoted 19 lines
drivers/base/regmap/regcache.c new file +19
-0+ +int regcache_init (struct regmap *map) +{ +int i, j; +int count; +unsigned int val; + +if (map->cache_type ==REGCACHE_NONE ) +return 0 ; + +for (i =0 ; i <ARRAY_SIZE (cache_types); ++i) +if (cache_types[i]->type == map->cache_type) +break ; + +if (i ==ARRAY_SIZE (cache_types)) { +dev_err (map->dev,"Could not match compress type: %d\n" , + map->cache_type); +return -EINVAL ; + }
drivers/base/regmap/regcache.c line 48
Quoted 10 lines
drivers/base/regmap/regcache.c new file +10
-0+ + map->cache =NULL ; + map->cache_ops = cache_types[i]; +if (!map->cache_ops->name) +return -EINVAL ; + +if (!map->cache_defaults_raw || !map->num_cache_defaults_raw) { +dev_err (map->dev,"Client has not provided a defaults cache\n" ); +return -EINVAL ; + }
It should be OK to provide no default register values, in this case regmap should assume that the default for all registers is 0.
Quoted 58 lines
drivers/base/regmap/regcache.c new file +58
-0+ +/* calculate the size of cache_defaults */ +for (count =0 , i =0 ; i < map->num_cache_defaults_raw; i++) { + val =regcache_get_val (map->cache_defaults_raw, + i, map->cache_word_size); +if (!val) +continue ; + count++; + } + + map->cache_defaults =kmalloc (count *sizeof (struct reg_default ), +GFP_KERNEL ); +if (!map->cache_defaults) +return -ENOMEM ; + +/* fill the cache_defaults */ + map->num_cache_defaults = count; +for (i =0 , j =0 ; i < map->num_cache_defaults_raw; i++) { + val =regcache_get_val (map->cache_defaults_raw, + i, map->cache_word_size); +if (!val) +continue ; + map->cache_defaults[j].reg = i; + map->cache_defaults[j].def = val; + j++; + } + +if (map->cache_ops->init) { +dev_dbg (map->dev,"Initializing %s cache\n" , + map->cache_ops->name); +return map->cache_ops->init (map); + } +return 0 ; +} + +void regcache_exit (struct regmap *map) +{ +if (map->cache_type ==REGCACHE_NONE ) +return ; + +BUG_ON (!map->cache_ops); + +kfree (map->cache_defaults); +if (map->cache_ops->exit) { +dev_dbg (map->dev,"Destroying %s cache\n" , + map->cache_ops->name); + map->cache_ops->exit (map); + } +} + +/** +* regcache_read: Fetch the value of a given register from the cache. +* +* @map: map to configure. +* @reg: The register index. +* @value: The value to be returned. +* +* Return a negative value on failure, 0 on success.
drivers/base/regmap/regcache.c line 116
Quoted 10 lines
drivers/base/regmap/regcache.c new file +10
-0+*/ +int regcache_read (struct regmap *map, +unsigned int reg,unsigned int *value) +{ +if (map->cache_type ==REGCACHE_NONE ) +return 0 ; + +BUG_ON (!map->cache_ops); + +if (reg < map->num_cache_defaults_raw) {
maybe use regmap_readable(map, reg)), but at least check against max_register and not num_cache_defaults_raw
drivers/base/regmap/regcache.c line 118
Quoted 2 lines
drivers/base/regmap/regcache.c new file +2
-0+if (!map->volatile_reg || + (map->volatile_reg && !map->volatile_reg (map->dev, reg))) {
if (!regmap_volatile(map, reg))
drivers/base/regmap/regcache.c line 119
Quoted 1 line
drivers/base/regmap/regcache.c new file +1
-0+if (value && map->cache_ops->read)
Will value or cache_ops->read ever be NULL? A register cache that either only provides read or write would be pretty useless in my opinion, and we should probably check for this at initialization time and not upon each access.
Quoted 24 lines
drivers/base/regmap/regcache.c new file +24
-0+return map->cache_ops->read (map, reg, value); + } + } +return -EINVAL ; +} +EXPORT_SYMBOL_GPL (regcache_read); + +/** +* regcache_write: Set the value of a given register in the cache. +* +* @map: map to configure. +* @reg: The register index. +* @value: The new register value. +* +* Return a negative value on failure, 0 on success. +*/ +int regcache_write (struct regmap *map, +unsigned int reg,unsigned int value) +{ +if (map->cache_type ==REGCACHE_NONE ) +return 0 ; + +BUG_ON (!map->cache_ops); +
drivers/base/regmap/regcache.c line 153
Quoted 10 lines
drivers/base/regmap/regcache.c new file +10
-0+if (reg < map->num_cache_defaults_raw) { +if (!map->volatile_reg || + (map->volatile_reg && !map->volatile_reg (map->dev, reg))) { +if (map->cache_ops->write) +return map->cache_ops->write (map, reg, value); + } + } +return 0 ; +} +EXPORT_SYMBOL_GPL (regcache_write);
Same comments as for regcache_read
Quoted 65 lines
drivers/base/regmap/regcache.c new file +65
-0+ +/** +* regcache_sync: Sync the register cache with the hardware. +* +* @map: map to configure. +* +* Any registers that should not be synced should be marked as +* volatile. In general drivers can choose not to use the provided +* syncing functionality if they so require. +* +* Return a negative value on failure, 0 on success. +*/ +int regcache_sync (struct regmap *map) +{ +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); + } +return 0 ; +} +EXPORT_SYMBOL_GPL (regcache_sync); + +bool regcache_set_val (void *base,unsigned int idx, +unsigned int val,unsigned int word_size) +{ +switch (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 ; + } +default : +BUG (); + } +/* unreachable */ +return false; +} + +unsigned int regcache_get_val (const void *base,unsigned int idx, +unsigned int word_size) +{ +if (!base) +return -EINVAL ; + +switch (word_size) { +case 1 : { +const u8 *cache = base; +return cache[idx]; + } +case 2 : { +const u16 *cache = base; +return cache[idx]; + }
drivers/base/regmap/regcache.c line 228
Quoted 10 lines
drivers/base/regmap/regcache.c new file +10
-0+default : +BUG (); + } +/* unreachable */ +return -1 ; +} + +int regcache_lookup_reg (struct regmap *map,unsigned int reg) +{ +int i;
unsigned int
Quoted 55 lines
drivers/base/regmap/regcache.c new file +23
-0+ +for (i =0 ; i < map->num_cache_defaults; ++i) +if (map->cache_defaults[i].reg == reg) +return i; +return -1 ; +} + +int regcache_insert_reg (struct regmap *map,unsigned int reg, +unsigned int val) +{ +void *tmp; + + tmp =krealloc (map->cache_defaults, + (map->num_cache_defaults +1 ) *sizeof (struct reg_default ), +GFP_KERNEL ); +if (!tmp) +return -ENOMEM ; + map->cache_defaults = tmp; + ++map->num_cache_defaults; + map->cache_defaults[map->num_cache_defaults -1 ].reg = reg; + map->cache_defaults[map->num_cache_defaults -1 ].def = val; +return 0 ; +}include/linux/regmap.h +10
-3@@ -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,9 +55,11 @@ 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. +* +* @cache_type: The actual cache type. +* @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;
include/linux/regmap.h struct regmap_config { · line 68
Quoted 5 lines
include/linux/regmap.h +0
-2@@ -64,8 +71,10 @@ 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;
I guess these were removed by accident due to some merge conflict as they were added just recently. It would be good if you could re-add them and if they are set initialize cache_defaults using a memdup instead of reading cache_defaults_raw.
Quoted 7 lines
include/linux/regmap.h +4
-0+ +enum regcache_type cache_type; +const void *cache_defaults_raw; +unsigned num_cache_defaults_raw; }; typedef int (*regmap_hw_write)(struct device *dev, const void *data,
Re: [PATCH 1/8] regmap: Introduce caching support
Quoted 7 lines · Lars-Peter Clausen
drivers/base/regmap/regcache.c line 19
Quoted 2 lines · Dimitris Papastamos
drivers/base/regmap/regcache.c new file +2
-0+static const struct regcache_ops *cache_types[] = { +};I wonder if it makes sense to keep a list of
regcache_opsthis way, or whether it is not better to just pass the ops we want to use toregcache_initdirectly.
Or have a function per cache type. I'm keen to hide the ops from users because I don't want to have to worry about them peering inside the internals.
Quoted 7 lines
drivers/base/regmap/regcache.c line 48
Quoted 4 lines
drivers/base/regmap/regcache.c new file +4
-0+if (!map->cache_defaults_raw || !map->num_cache_defaults_raw) { +dev_err (map->dev,"Client has not provided a defaults cache\n" ); +return -EINVAL ; + }It should be OK to provide no default register values, in this case regmap should assume that the default for all registers is 0.
Yes - Dimitris, as we discussed offline it's pretty much essential for things like PMICs where the defaults aren't meaningful and may even be unknowable.
Quoted 5 lines
drivers/base/regmap/regcache.c line 119
Quoted 1 line
drivers/base/regmap/regcache.c new file +1
-0+if (value && map->cache_ops->read)Will value or
cache_ops->readever be NULL? A register cache that either only provides read or write would be pretty useless in my opinion, and we should probably check for this at initialization time and not upon each access.
I agree that it's safe to assume read() if you've got cache_ops. I think in the case where we don't cache we should be able to come up with a suitable noop cache type which we can assign as the ops so like you say we can just assume a read() op.
Re: [PATCH 1/8] regmap: Introduce caching support
Quoted 21 lines · Mark Brown
Quoted 7 lines · Lars-Peter Clausen
drivers/base/regmap/regcache.c line 19
Quoted 2 lines · Dimitris Papastamos
drivers/base/regmap/regcache.c new file +2
-0+static const struct regcache_ops *cache_types[] = { +};I wonder if it makes sense to keep a list of
regcache_opsthis way, or whether it is not better to just pass the ops we want to use toregcache_initdirectly.Or have a function per cache type. I'm keen to hide the ops from users because I don't want to have to worry about them peering inside the internals.
Quoted 7 lines
drivers/base/regmap/regcache.c line 48
Quoted 4 lines
drivers/base/regmap/regcache.c new file +4
-0+if (!map->cache_defaults_raw || !map->num_cache_defaults_raw) { +dev_err (map->dev,"Client has not provided a defaults cache\n" ); +return -EINVAL ; + }It should be OK to provide no default register values, in this case regmap should assume that the default for all registers is 0.
Quoted 3 lines
Yes - Dimitris, as we discussed offline it's pretty much essential for things like PMICs where the defaults aren't meaningful and may even be unknowable.
That's implemented in patch 8/8. I can of course squash that patch into the other patch.
Thanks,
Dimitris
Re: [PATCH 1/8] regmap: Introduce caching support
Quoted 11 lines · Dimitris Papastamos
Quoted 7 lines · Mark Brown
Quoted 2 lines · Lars-Peter Clausen
It should be OK to provide no default register values, in this case regmap should assume that the default for all registers is 0.
Yes - Dimitris, as we discussed offline it's pretty much essential for things like PMICs where the defaults aren't meaningful and may even be
That's implemented in patch 8/8. I can of course squash that patch into the other patch.
Ah, good - I was a bit surprised as I did remember discussing this with you. Please do move that code into the intial patch, in general it's usually OK to miss features out of early patches or simplify them but making them actively buggy isn't great.
Re: [PATCH 1/8] regmap: Introduce caching support
In reply to Mark BrownQuoted 9 lines · Mark Brown
Quoted 7 lines · Lars-Peter Clausen
drivers/base/regmap/regcache.c line 19
Quoted 2 lines · Dimitris Papastamos
drivers/base/regmap/regcache.c new file +2
-0+static const struct regcache_ops *cache_types[] = { +};I wonder if it makes sense to keep a list of
regcache_opsthis way, or whether it is not better to just pass the ops we want to use toregcache_initdirectly.
Quoted 3 lines
Or have a function per cache type. I'm keen to hide the ops from users because I don't want to have to worry about them peering inside the internals.
Yes I guess I'll do that, sounds cleaner.
Thanks,
Dimitris
Re: [PATCH 1/8] regmap: Introduce caching support
In reply to Lars-Peter ClausenQuoted 55 lines · Lars-Peter Clausen
Quoted 54 lines · Dimitris Papastamos
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.
Signed-off-by Dimitris Papastamos <[email protected]>--- drivers/base/regmap/Makefile | 2 +- drivers/base/regmap/internal.h | 50 ++++++++ drivers/base/regmap/regcache.c | 251 ++++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 19 +++- 4 files changed, 316 insertions(+), 6 deletions(-) create mode 100644 drivers/base/regmap/regcache.cdrivers/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.odrivers/base/regmap/internal.h +0
-0drivers/base/regmap/regcache.c new file +14
-0@@ -0,0 +1,251 @@ +/* +* Register cache access API +* +* Copyright 2011 Wolfson Microelectronics plc +* +* Author: Dimitris Papastamos <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#include <linux/slab.h> +#include <trace/events/regmap.h>
Quoted 8 lines
drivers/base/regmap/regcache.c line 19
Quoted 5 lines
drivers/base/regmap/regcache.c new file +5
-0+ +#include "internal.h" + +static const struct regcache_ops *cache_types[] = { +};I wonder if it makes sense to keep a list of
regcache_opsthis way, or whether it is not better to just pass the ops we want to use toregcache_initdirectly.
Something like the user actually setting the ops pointer to one of the exported cache operation types, and then having regcache to use just that directly?
Quoted 24 lines
Quoted 24 lines
drivers/base/regmap/regcache.c new file +24
-0+ +int regcache_init (struct regmap *map) +{ +int i, j; +int count; +unsigned int val; + +if (map->cache_type ==REGCACHE_NONE ) +return 0 ; + +for (i =0 ; i <ARRAY_SIZE (cache_types); ++i) +if (cache_types[i]->type == map->cache_type) +break ; + +if (i ==ARRAY_SIZE (cache_types)) { +dev_err (map->dev,"Could not match compress type: %d\n" , + map->cache_type); +return -EINVAL ; + } + + map->cache =NULL ; + map->cache_ops = cache_types[i]; +if (!map->cache_ops->name) +return -EINVAL ;
Quoted 8 lines
drivers/base/regmap/regcache.c line 48
Quoted 5 lines
drivers/base/regmap/regcache.c new file +5
-0+ +if (!map->cache_defaults_raw || !map->num_cache_defaults_raw) { +dev_err (map->dev,"Client has not provided a defaults cache\n" ); +return -EINVAL ; + }It should be OK to provide no default register values, in this case regmap should assume that the default for all registers is 0.
I guess I'll just squash the patch that implementes readback from the HW right into this patch if that's okay.
Quoted 12 lines
Quoted 12 lines
drivers/base/regmap/regcache.c new file +12
-0+/** +* regcache_read: Fetch the value of a given register from the cache. +* +* @map: map to configure. +* @reg: The register index. +* @value: The value to be returned. +* +* Return a negative value on failure, 0 on success. +*/ +int regcache_read (struct regmap *map, +unsigned int reg,unsigned int *value) +{
Quoted 8 lines
drivers/base/regmap/regcache.c line 116
Quoted 6 lines
drivers/base/regmap/regcache.c new file +6
-0+if (map->cache_type ==REGCACHE_NONE ) +return 0 ; + +BUG_ON (!map->cache_ops); + +if (reg < map->num_cache_defaults_raw) {maybe use
regmap_readable(map, reg)), but at least check againstmax_registerand notnum_cache_defaults_raw
Ok.
Quoted 4 lines
drivers/base/regmap/regcache.c line 68
Quoted 2 lines
drivers/base/regmap/regcache.c new file +2
-0+if (!map->volatile_reg || + (map->volatile_reg && !map->volatile_reg (map->dev, reg))) {if (!regmap_volatile(map, reg))
Yes.
Quoted 5 lines
drivers/base/regmap/regcache.c line 119
Quoted 1 line
drivers/base/regmap/regcache.c new file +1
-0+if (value && map->cache_ops->read)Will value or
cache_ops->readever be NULL? A register cache that either only provides read or write would be pretty useless in my opinion, and we should probably check for this at initialization time and not upon each access.
Yes will check at init time.
Quoted 4 lines
drivers/base/regmap/regcache.c line 228
Quoted 3 lines
drivers/base/regmap/regcache.c new file +3
-0+int regcache_lookup_reg (struct regmap *map,unsigned int reg) +{ +int i;unsigned int
Yes.
Quoted 29 lines
Quoted 19 lines
drivers/base/regmap/regcache.c new file +5
-3/** * Default value for a register. We use an array of structs rather * than a simple array as many modern devices have very sparse @@ -50,9 +55,11 @@ 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. +* +* @cache_type: The actual cache type. +* @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;drivers/base/regmap/regcache.c struct regmap_config { · line 68
Quoted 5 lines
drivers/base/regmap/regcache.c new file +0
-2@@ -64,8 +71,10 @@ 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;I guess these were removed by accident due to some merge conflict as they were added just recently. It would be good if you could re-add them and if they are set initialize
cache_defaultsusing a memdup instead of readingcache_defaults_raw.
Quoted 7 lines
drivers/base/regmap/regcache.c struct regmap_config { · line 80
Quoted 7 lines
drivers/base/regmap/regcache.c new file +4
-0+ +enum regcache_type cache_type; +const void *cache_defaults_raw; +unsigned num_cache_defaults_raw; }; typedef int (*regmap_hw_write)(struct device *dev, const void *data,
I was merely renaming these.
Thanks,
Dimitris
Re: [PATCH 1/8] regmap: Introduce caching support
Quoted 36 lines · Dimitris Papastamos
[...]
Quoted 35 lines
Quoted 19 lines
/**
* Default value for a register. We use an array of structs rather
* than a simple array as many modern devices have very sparseinclude/linux/regmap.h +5
-3@@ -50,9 +55,11 @@ 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. +* +* @cache_type: The actual cache type. +* @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;include/linux/regmap.h struct regmap_config { · line 68
Quoted 5 lines
include/linux/regmap.h +0
-2@@ -64,8 +71,10 @@ 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;I guess these were removed by accident due to some merge conflict as they were added just recently. It would be good if you could re-add them and if they are set initialize
cache_defaultsusing a memdup instead of readingcache_defaults_raw.Quoted 6 lines
include/linux/regmap.h +4
-0+ +enum regcache_type cache_type; +const void *cache_defaults_raw; +unsigned num_cache_defaults_raw; };
Quoted 3 lines
Quoted 1 line
include/linux/regmap.h struct regmap_config { · line 80
Quoted 1 line
include/linux/regmap.h +0
-0typedef int (*regmap_hw_write)(struct device *dev, const void *data,I was merely renaming these.
And you changed the type. We need the reg_default type register defaults for devices which have a sparse registers set or where the base register address is at a larger offset.
- Lars
Re: [PATCH 1/8] regmap: Introduce caching support
Quoted 41 lines · Lars-Peter Clausen
Quoted 36 lines · Dimitris Papastamos
[...]
Quoted 35 lines
Quoted 19 lines
/**
* Default value for a register. We use an array of structs rather
* than a simple array as many modern devices have very sparseinclude/linux/regmap.h +5
-3@@ -50,9 +55,11 @@ 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. +* +* @cache_type: The actual cache type. +* @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;include/linux/regmap.h struct regmap_config { · line 68
Quoted 5 lines
include/linux/regmap.h +0
-2@@ -64,8 +71,10 @@ 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;I guess these were removed by accident due to some merge conflict as they were added just recently. It would be good if you could re-add them and if they are set initialize
cache_defaultsusing a memdup instead of readingcache_defaults_raw.Quoted 6 lines
include/linux/regmap.h +4
-0+ +enum regcache_type cache_type; +const void *cache_defaults_raw; +unsigned num_cache_defaults_raw; };Quoted 3 lines
Quoted 1 line
include/linux/regmap.h struct regmap_config { · line 80
Quoted 1 line
include/linux/regmap.h +0
-0typedef int (*regmap_hw_write)(struct device *dev, const void *data,I was merely renaming these.
Quoted 5 lines
And you changed the type. We need the
reg_defaulttype register defaults for devices which have a sparse registers set or where the base register address is at a larger offset.- Lars
Aw duh, yes. Will add support to regcache to be able to use directly the reg_defaults if they have been provided by the user without having to derive this from cache_defaults_raw.
Thanks,
Dimitris
PATCH 2/8 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 | 3 + include/linux/regmap.h | 1 + 5 files changed, 71 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
@@ -113,4 +113,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 +3 -0
@@ -16,6 +16,9 @@#include "internal.h" static const struct regcache_ops *cache_types[] = { +#ifdef CONFIG_REGCACHE_INDEXED + ®cache_indexed_ops, +#endif };int regcache_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
Re: [PATCH 2/8] regmap: Add the indexed cache support
Quoted 8 lines · Dimitris Papastamos
This 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.
Signed-off-by Dimitris Papastamos <[email protected]>
I wonder if we really still need indexed caches, because ...
Quoted 10 lines
--- drivers/base/regmap/Makefile | 2 +- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regcache-indexed.c | 65 ++++++++++++++++++++++++++++++++ drivers/base/regmap/regcache.c | 3 + include/linux/regmap.h | 1 + 5 files changed, 71 insertions(+), 1 deletions(-) create mode 100644 drivers/base/regmap/regcache-indexed.c[...]
drivers/base/regmap/regcache-indexed.c line 22
Quoted 6 lines
drivers/base/regmap/regcache-indexed.c new file +6
-0+static int regcache_indexed_read (struct regmap *map,unsigned int reg, +unsigned int *value) +{ +int ret; + + ret =regcache_lookup_reg (map, reg);
... this lookup will be slower than an rbtree lookup. And given that we use two integers per register doesn't give it much size advantage either.
I have some patches for the rbtree register cache code, which reduce some of it's complexity and also implements the adjacent node merging and allows smaller holes in the rbnodes register block if the block is smaller then an rbnode. Currently these patches are based on the ASoC register cache code, but they should be easy to rework onto of the regmap register cache code.
I did some small statistics on the number of rbnodes without and with the patches applied:
Without patches:
Nodes:
0: 4000-4000 1 1: 4009-400a 2 2: 400b-400c 2 3: 400d-4010 4 4: 4015-4015 1 5: 4017-4017 1 6: 4019-401b 3 7: 401c-401d 2 8: 401e-4020 3 9: 4021-4022 2 10: 4023-4026 4 11: 4029-402a 2 12: 4031-4031 1 13: 4036-4036 1 14: 40eb-40eb 1 15: 40f2-40f3 2 16: 40f8-40f9 2 17: 40fa-40fa 1
Number of nodes: 18 (504 bytes)
Number of registers: 35 (35 bytes)
Total size: 539 bytes
With patches:
Nodes:
0: 4000-4036 55
1: 40eb-40fa 16
Number of nodes: 2 (56 bytes)
Number of registers: 71 (71 bytes)
Total size: 127 bytes
In comparison a ASoC flat cache for this driver would have used 250 bytes and and regmap indexed cache would use 280 bytes. (Not that it really matters at those sizes anyway)
Quoted 13 lines
drivers/base/regmap/regcache-indexed.c new file +7
-0+if (ret <0 ) + *value =0 ; +else + *value = map->cache_defaults[ret].def; +return 0 ; +} +drivers/base/regmap/regcache.c +0
-0
drivers/base/regmap/regcache.c line 19
Quoted 4 lines
drivers/base/regmap/regcache.c +1
-0@@ -16,6 +16,9 @@#include "internal.h" static const struct regcache_ops *cache_types[] = { +#ifdef CONFIG_REGCACHE_INDEXED
This symbol doesn't seem to be defined anywhere.
Quoted 17 lines
drivers/base/regmap/regcache.c +2
-0+ ®cache_indexed_ops, +#endif };int regcache_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 , };/**
Re: [PATCH 2/8] regmap: Add the indexed cache support
Quoted 22 lines · Lars-Peter Clausen
Quoted 8 lines · Dimitris Papastamos
This 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.
Signed-off-by Dimitris Papastamos <[email protected]>I wonder if we really still need indexed caches, because ...
Quoted 9 lines
--- drivers/base/regmap/Makefile | 2 +- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regcache-indexed.c | 65 ++++++++++++++++++++++++++++++++ drivers/base/regmap/regcache.c | 3 + include/linux/regmap.h | 1 + 5 files changed, 71 insertions(+), 1 deletions(-) create mode 100644 drivers/base/regmap/regcache-indexed.c
Quoted 10 lines
drivers/base/regmap/regcache-indexed.c line 22
Quoted 7 lines
[...]
drivers/base/regmap/regcache-indexed.c new file +6
-0+static int regcache_indexed_read (struct regmap *map,unsigned int reg, +unsigned int *value) +{ +int ret; + + ret =regcache_lookup_reg (map, reg);... this lookup will be slower than an rbtree lookup. And given that we use two integers per register doesn't give it much size advantage either.
The indexed cache grew out of the flat cache somehow. Do you think the flat cache would be more appropriate than the indexed cache? One thing with the flat cache is that we need to make a flat copy of the cache_defaults (either partial or full). With the indexed cache we can use the cache_defaults directly.
I've got a patch that changes the regcache_lookup_reg() to actually use binary search. The array is kept sorted after insertions and since insertions are less common than readbacks it should improve things a bit.
Quoted 39 lines
I have some patches for the rbtree register cache code, which reduce some of it's complexity and also implements the adjacent node merging and allows smaller holes in the rbnodes register block if the block is smaller then an rbnode. Currently these patches are based on the ASoC register cache code, but they should be easy to rework onto of the regmap register cache code.
I did some small statistics on the number of rbnodes without and with the patches applied:
Without patches:
Nodes:0: 4000-4000 1 1: 4009-400a 2 2: 400b-400c 2 3: 400d-4010 4 4: 4015-4015 1 5: 4017-4017 1 6: 4019-401b 3 7: 401c-401d 2 8: 401e-4020 3 9: 4021-4022 2 10: 4023-4026 4 11: 4029-402a 2 12: 4031-4031 1 13: 4036-4036 1 14: 40eb-40eb 1 15: 40f2-40f3 2 16: 40f8-40f9 2 17: 40fa-40fa 1Number of nodes: 18 (504 bytes)
Number of registers: 35 (35 bytes)
Total size: 539 bytesWith patches:
Nodes:
0: 4000-4036 55
1: 40eb-40fa 16
Quoted 7 lines
Number of nodes: 2 (56 bytes)
Number of registers: 71 (71 bytes)
Total size: 127 bytesIn comparison a ASoC flat cache for this driver would have used 250 bytes and and regmap indexed cache would use 280 bytes. (Not that it really matters at those sizes anyway)
Awesome!! You could mail me those patches and I'll re-work them to apply them on top of regcache.
Quoted 15 lines
Quoted 15 lines
drivers/base/regmap/regcache-indexed.c new file +7
-0+if (ret <0 ) + *value =0 ; +else + *value = map->cache_defaults[ret].def; +return 0 ; +} +drivers/base/regmap/regcache.c +0
-0@@ -16,6 +16,9 @@#include "internal.h"
Quoted 4 lines
drivers/base/regmap/regcache.c line 19
Quoted 2 lines
drivers/base/regmap/regcache.c +1
-0static const struct regcache_ops *cache_types[] = { +#ifdef CONFIG_REGCACHE_INDEXEDThis symbol doesn't seem to be defined anywhere.
Yes I need to remove all of these.
Thanks,
Dimitris
Re: [PATCH 2/8] regmap: Add the indexed cache support
Quoted 40 lines · Dimitris Papastamos
Quoted 22 lines · Lars-Peter Clausen
Quoted 8 lines · Dimitris Papastamos
This 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.
Signed-off-by Dimitris Papastamos <[email protected]>I wonder if we really still need indexed caches, because ...
Quoted 9 lines
--- drivers/base/regmap/Makefile | 2 +- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regcache-indexed.c | 65 ++++++++++++++++++++++++++++++++ drivers/base/regmap/regcache.c | 3 + include/linux/regmap.h | 1 + 5 files changed, 71 insertions(+), 1 deletions(-) create mode 100644 drivers/base/regmap/regcache-indexed.cQuoted 10 lines
drivers/base/regmap/regcache-indexed.c line 22
Quoted 7 lines
[...]
drivers/base/regmap/regcache-indexed.c new file +6
-0+static int regcache_indexed_read (struct regmap *map,unsigned int reg, +unsigned int *value) +{ +int ret; + + ret =regcache_lookup_reg (map, reg);... this lookup will be slower than an rbtree lookup. And given that we use two integers per register doesn't give it much size advantage either.
The indexed cache grew out of the flat cache somehow. Do you think the flat cache would be more appropriate than the indexed cache? One thing with the flat cache is that we need to make a flat copy of the
cache_defaults(either partial or full). With the indexed cache we can use thecache_defaultsdirectly.
Quoted 4 lines
I've got a patch that changes the
regcache_lookup_reg()to actually use binary search. The array is kept sorted after insertions and since insertions are less common than readbacks it should improve things a bit.
Even with a binary search the lookup will most likely be more expensive then with an rbtree. Both have an log(n) running time, but the rbtree will most likely have less nodes than registers, while with the indexed cache we have one entry per register.
An option would be to switch back to a flat cache, since here the lookup is basically free. But I don't think it is really worth the maintenance hassle, the gains are rather marginal. Especially with my patches which add support for merging adjacent nodes there is almost no overhead compared to the original ASoC flat cache. You'll end up with one rbnode, so the cached rbnode is always the one your were looking for and no rbtree lookup is necessary.
In my opinion we should work towards having rbtree as the only cache type, but with optional lzo compression on the rbtree node blocks.
- Lars
Re: [PATCH 2/8] regmap: Add the indexed cache support
Quoted 2 lines · Lars-Peter Clausen
In my opinion we should work towards having rbtree as the only cache type, but with optional lzo compression on the rbtree node blocks.
If we are going to do something like that we should probably just go by block size to decide if we should do the compression and do the thing with keeping the most recently used LZO block (or perhaps a few) uncompressed in RAM for speed. Probably also look at the compression we get when we decide to compress and not bother doing the compression if it's a small saving, though we will have burned CPU.
Re: [PATCH 2/8] regmap: Add the indexed cache support
In reply to Lars-Peter ClausenQuoted 5 lines · Lars-Peter Clausen
I have some patches for the rbtree register cache code, which reduce some of it's complexity and also implements the adjacent node merging and allows smaller holes in the rbnodes register block if the block is smaller then an rbnode. Currently these patches are based on the ASoC register cache code, but they should be easy to rework onto of the regmap register cache code.
Dimitris just reminded me about what you said here - do you still have those patches floating around? This would really useful and would make some other optimistations like not cacheing default values much easier.
Re: [PATCH 2/8] regmap: Add the indexed cache support
Quoted 8 lines · Mark Brown
Quoted 5 lines · Lars-Peter Clausen
I have some patches for the rbtree register cache code, which reduce some of it's complexity and also implements the adjacent node merging and allows smaller holes in the rbnodes register block if the block is smaller then an rbnode. Currently these patches are based on the ASoC register cache code, but they should be easy to rework onto of the regmap register cache code.
Quoted 4 lines
Dimitris just reminded me about what you said here - do you still have those patches floating around? This would really useful and would make some other optimistations like not cacheing default values much easier.
Yes, I still have it, but it's on a machine which is currently broken and I wont get the replacement part until next week.
- Lars
PATCH 3/8 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 | 400 +++++++++++++++++++++++++++++++++ drivers/base/regmap/regcache.c | 3 + include/linux/regmap.h | 1 + 5 files changed, 406 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
@@ -114,4 +114,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 +400 -0
400 lines not on this page · raw message
drivers/base/regmap/regcache.c +3 -0
@@ -19,6 +19,9 @@ static const struct regcache_ops *cache_types[] = {#ifdef CONFIG_REGCACHE_INDEXED ®cache_indexed_ops,#endif +#ifdef CONFIG_REGCACHE_RBTREE + ®cache_rbtree_ops, +#endif };int regcache_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
Re: [PATCH 3/8] regmap: Add the rbtree cache support
Quoted 39 lines · Dimitris Papastamos
This 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.
Signed-off-by Dimitris Papastamos <[email protected]>[...]drivers/base/regmap/regcache-rbtree.c new file +18
-0+ +static int regcache_rbtree_init (struct regmap *map) +{ +struct regcache_rbtree_ctx *rbtree_ctx; +int i; +int ret; + + map->cache =kmalloc (sizeof *rbtree_ctx,GFP_KERNEL ); +if (!map->cache) +return -ENOMEM ; + + rbtree_ctx = map->cache; + rbtree_ctx->root =RB_ROOT ; + rbtree_ctx->cached_rbnode =NULL ; + +if (!map->cache_defaults) +return 0 ; +
drivers/base/regmap/regcache-rbtree.c line 169
Quoted 10 lines
drivers/base/regmap/regcache-rbtree.c new file +10
-0+for (i =0 ; i < map->num_cache_defaults_raw; ++i) { + ret =regcache_lookup_reg (map, i); +if (ret <0 ) +continue ; + ret =regcache_rbtree_write (map, + map->cache_defaults[ret].reg, + map->cache_defaults[ret].def); +if (ret) + goto err; + }
You can iterate over the caches_defaults elements directly.
[...]
drivers/base/regmap/regcache.c static const struct regcache_ops *cache_types[] = { · line 22
Quoted 9 lines
drivers/base/regmap/regcache.c +1
-0@@ -19,6 +19,9 @@ static const struct regcache_ops *cache_types[] = {#ifdef CONFIG_REGCACHE_INDEXED ®cache_indexed_ops,#endif +#ifdef CONFIG_REGCACHE_RBTREE
This symbol is also not defined. It looks as if all the Kconfig changes are not included in the patchset.
Quoted 3 lines
drivers/base/regmap/regcache.c +2
-0+ ®cache_rbtree_ops, +#endif };
Re: [PATCH 3/8] regmap: Add the rbtree cache support
Quoted 44 lines · Lars-Peter Clausen
Quoted 43 lines · Dimitris Papastamos
This 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.
Signed-off-by Dimitris Papastamos <[email protected]>[...]drivers/base/regmap/regcache-rbtree.c new file +22
-0+ +static int regcache_rbtree_init (struct regmap *map) +{ +struct regcache_rbtree_ctx *rbtree_ctx; +int i; +int ret; + + map->cache =kmalloc (sizeof *rbtree_ctx,GFP_KERNEL ); +if (!map->cache) +return -ENOMEM ; + + rbtree_ctx = map->cache; + rbtree_ctx->root =RB_ROOT ; + rbtree_ctx->cached_rbnode =NULL ; + +if (!map->cache_defaults) +return 0 ; + +for (i =0 ; i < map->num_cache_defaults_raw; ++i) { + ret =regcache_lookup_reg (map, i); +if (ret <0 ) +continue ;
Quoted 8 lines
drivers/base/regmap/regcache-rbtree.c line 169
Quoted 6 lines
drivers/base/regmap/regcache-rbtree.c new file +6
-0+ ret =regcache_rbtree_write (map, + map->cache_defaults[ret].reg, + map->cache_defaults[ret].def); +if (ret) + goto err; + }You can iterate over the
caches_defaultselements directly.
Yes.
Thanks,
Dimitris
PATCH 4/8 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/Makefile | 2 +- drivers/base/regmap/internal.h | 2 + drivers/base/regmap/regcache-lzo.c | 385 ++++++++++++++++++++++++++++++++++++ drivers/base/regmap/regcache.c | 3 + include/linux/regmap.h | 1 + 5 files changed, 392 insertions(+), 1 deletions(-) create mode 100644 drivers/base/regmap/regcache-lzo.c
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
@@ -115,4 +115,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 +385 -0
385 lines not on this page · raw message
drivers/base/regmap/regcache.c +3 -0
@@ -22,6 +22,9 @@ static const struct regcache_ops *cache_types[] = {#ifdef CONFIG_REGCACHE_RBTREE ®cache_rbtree_ops,#endif +#ifdef CONFIG_REGCACHE_LZO + ®cache_lzo_ops, +#endif };int regcache_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
Re: [PATCH 4/8] regmap: Add the LZO cache support
Quoted 6 lines · Dimitris Papastamos <dp <at> opensource.wolfsonmicro.com> writes:
This 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.
Do you have example of such case ?
Caching 25kB or 50kB looks crazy.
Re: [PATCH 4/8] regmap: Add the LZO cache support
Quoted 1 line · Matthieu CASTET
Dimitris Papastamos <dp <at> opensource.wolfsonmicro.com> writes:
Don't drop CCs from mails if you want people to actually read them.
Quoted 9 lines
Quoted 5 lines
This 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.
Do you have example of such case ?
Caching 25kB or 50kB looks crazy.
Audio CODECs for smartphones can easily have well over a thousand registers (excluding things like DSP memories) connected over rel;atively slow buses like I2C or SPI with lots of read/modify/write cycles (so cutting out the physical read is a real win) and we want to power them off when not in use.
PATCH 5/8 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
@@ -174,12 +174,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 6/8 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 | 55 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 55 insertions(+), 0 deletions(-)
drivers/base/regmap/regmap.c +55 -0
@@ -146,6 +146,12 @@ 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->num_cache_defaults_raw = config->num_cache_defaults_raw; + map->cache_defaults =NULL ; + 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 ;switch (config->reg_bits) {case 4 :
@@ -201,6 +207,12 @@ struct regmap *regmap_init(struct device *dev, goto err_bus; } +#ifdef CONFIG_REGCACHE + ret =regcache_init (map); +if (ret <0 ) + goto err_bus; +#endif +regmap_debugfs_init (map);return map;
@@ -219,6 +231,9 @@ EXPORT_SYMBOL_GPL(regmap_init);*/ void regmap_exit (struct regmap *map) { +#ifdef CONFIG_REGCACHE +regcache_exit (map); +#endif regmap_debugfs_exit (map);kfree (map->work_buf);module_put (map->bus->owner);
@@ -321,6 +336,20 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)mutex_lock (&map->lock); +#ifdef CONFIG_REGCACHE +if (!map->cache_bypass) { + ret =regcache_write (map, reg, val); +if (ret <0 ) { +mutex_unlock (&map->lock); +return ret; + } +if (map->cache_only) { +mutex_unlock (&map->lock); +return 0 ; + } + } +#endif + ret =_regmap_write (map, reg, val);mutex_unlock (&map->lock);
@@ -422,6 +451,16 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)mutex_lock (&map->lock); +#ifdef CONFIG_REGCACHE +if (!map->cache_bypass) { + ret =regcache_read (map, reg, val); +if (!ret) { +mutex_unlock (&map->lock); +return 0 ; + } + } +#endif + ret =_regmap_read (map, reg, val);mutex_unlock (&map->lock);
@@ -473,6 +512,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 ;
@@ -512,6 +553,20 @@ int regmap_update_bits(struct regmap *map, unsigned int reg, tmp &= ~mask; tmp |= val & mask; +#ifdef CONFIG_REGCACHE +if (!map->cache_bypass) { + ret =regcache_write (map, reg, tmp); +if (ret <0 ) { +mutex_unlock (&map->lock); +return ret; + } +if (map->cache_only) { +mutex_unlock (&map->lock); +return 0 ; + } + } +#endif + ret =_regmap_write (map, reg, tmp); out:
Signature · 1 line
1.7.6.1
PATCH 7/8 regmap: It is impossible to be given a NULL defaults cache
In reply to Dimitris PapastamosIn the new regcache API, it is impossible to hand a NULL defaults cache to any of the cache compression types.
--- drivers/base/regmap/regcache-lzo.c | 30 +++--------------------------- 1 files changed, 3 insertions(+), 27 deletions(-)
drivers/base/regmap/regcache-lzo.c +3 -27
@@ -125,32 +125,17 @@ static int regcache_lzo_init(struct regmap *map) {struct regcache_lzo_ctx **lzo_blocks;size_t bmp_size; -int ret, tofree, i, blksize, blkcount; +int ret, i, blksize, blkcount;const char *p, *end;unsigned long *sync_bmp; ret =0 ; -/* -* If we have not been given a default register cache -* then allocate a dummy zero-ed out region, compress it -* and remember to free it afterwards. -*/ - tofree =0 ; -if (!map->cache_defaults_raw) { - map->cache_defaults_raw =kzalloc (map->cache_size_raw,GFP_KERNEL ); -if (!map->cache_defaults_raw) -return -ENOMEM ; - tofree =1 ; - } - blkcount =regcache_lzo_block_count (); map->cache =kzalloc (blkcount *sizeof *lzo_blocks,GFP_KERNEL ); -if (!map->cache) { - ret = -ENOMEM ; - goto err_tofree; - } +if (!map->cache) +return -ENOMEM ; lzo_blocks = map->cache;/*
@@ -203,18 +188,9 @@ static int regcache_lzo_init(struct regmap *map) lzo_blocks[i]->src_len; } -if (tofree) { -kfree (map->cache_defaults_raw); - map->cache_defaults_raw =NULL ; - }return 0 ; err:regcache_exit (map); -err_tofree: -if (tofree) { -kfree (map->cache_defaults_raw); - map->cache_defaults_raw =NULL ; - }return ret; }
Signature · 1 line
1.7.6.1
PATCH 8/8 regmap: Support NULL cache_defaults_raw
In reply to Dimitris PapastamosSome IC's like PMIC's tend not to have cache defaults. Support this by reading back from the hardware using the bulk read operation the values of all registers and constructing the cache defaults manually.
--- drivers/base/regmap/internal.h | 2 ++ drivers/base/regmap/regcache.c | 27 ++++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-)
drivers/base/regmap/internal.h +2 -0
@@ -65,6 +65,8 @@ struct regmap { unsigned int cache_only:1;/* if set, only the HW is modified not the cache */ unsigned int cache_bypass:1; +/* if set, remember to free cache_defaults_raw */ + unsigned int cache_free:1;struct reg_default *cache_defaults;const void *cache_defaults_raw;
drivers/base/regmap/regcache.c +24 -3
@@ -31,7 +31,9 @@ int regcache_init(struct regmap *map) {int i, j;int count; +int ret;unsigned int val; +void *tmp_buf;if (map->cache_type ==REGCACHE_NONE )return 0 ;
@@ -51,9 +53,25 @@ int regcache_init(struct regmap *map)if (!map->cache_ops->name)return -EINVAL ; -if (!map->cache_defaults_raw || !map->num_cache_defaults_raw) { -dev_err (map->dev,"Client has not provided a defaults cache\n" ); -return -EINVAL ; +/* Some devices such as PMIC's don't have cache defaults, +* we cope with this by reading back the HW registers and +* crafting the cache defaults by hand. +*/ +if (!map->cache_defaults_raw) { +if (!map->num_cache_defaults_raw) +return -EINVAL ; +dev_warn (map->dev,"No cache defaults, reading back from HW\n" ); + tmp_buf =kmalloc (map->cache_size_raw,GFP_KERNEL ); +if (!tmp_buf) +return -EINVAL ; + ret =regmap_bulk_read (map,0 , tmp_buf, + map->num_cache_defaults_raw); +if (ret <0 ) { +kfree (tmp_buf); +return ret; + } + map->cache_defaults_raw = tmp_buf; + map->cache_free =1 ; }/* calculate the size of cache_defaults */
@@ -98,6 +116,9 @@ void regcache_exit(struct regmap *map)BUG_ON (!map->cache_ops);kfree (map->cache_defaults); +if (map->cache_free) +kfree (map->cache_defaults_raw); +if (map->cache_ops->exit) {dev_dbg (map->dev,"Destroying %s cache\n" , map->cache_ops->name);
Signature · 1 line
1.7.6.1