From 82159ba8e6ef8c38e3e0452d90b4ff8da9e4b2c1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 18 Jan 2012 10:52:25 +0000 Subject: regmap: Add support for padding between register and address Some devices, especially those with high speed control interfaces, require padding between the register and the data. Support this in the regmap API by providing a pad_bits configuration parameter. Only devices with integer byte counts are supported. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap.c | 32 +++++++++++++++++++++----------- include/linux/regmap.h | 2 ++ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 1a02b7537c8b..e33f1be2b299 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -22,6 +22,7 @@ struct regcache_ops; struct regmap_format { size_t buf_size; size_t reg_bytes; + size_t pad_bytes; size_t val_bytes; void (*format_write)(struct regmap *map, unsigned int reg, unsigned int val); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index be10a4ff6609..e9c2ac0174c6 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -160,7 +160,9 @@ struct regmap *regmap_init(struct device *dev, mutex_init(&map->lock); map->format.buf_size = (config->reg_bits + config->val_bits) / 8; map->format.reg_bytes = config->reg_bits / 8; + map->format.pad_bytes = config->pad_bits / 8; map->format.val_bytes = config->val_bits / 8; + map->format.buf_size += map->format.pad_bytes; map->dev = dev; map->bus = bus; map->max_register = config->max_register; @@ -235,7 +237,7 @@ struct regmap *regmap_init(struct device *dev, !(map->format.format_reg && map->format.format_val)) goto err_map; - map->work_buf = kmalloc(map->format.buf_size, GFP_KERNEL); + map->work_buf = kzalloc(map->format.buf_size, GFP_KERNEL); if (map->work_buf == NULL) { ret = -ENOMEM; goto err_map; @@ -329,23 +331,28 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, * send the work_buf directly, otherwise try to do a gather * write. */ - if (val == map->work_buf + map->format.reg_bytes) + if (val == (map->work_buf + map->format.pad_bytes + + map->format.reg_bytes)) ret = map->bus->write(map->dev, map->work_buf, - map->format.reg_bytes + val_len); + map->format.reg_bytes + + map->format.pad_bytes + + val_len); else if (map->bus->gather_write) ret = map->bus->gather_write(map->dev, map->work_buf, - map->format.reg_bytes, + map->format.reg_bytes + + map->format.pad_bytes, val, val_len); /* If that didn't work fall back on linearising by hand. */ if (ret == -ENOTSUPP) { - len = map->format.reg_bytes + val_len; - buf = kmalloc(len, GFP_KERNEL); + len = map->format.reg_bytes + map->format.pad_bytes + val_len; + buf = kzalloc(len, GFP_KERNEL); if (!buf) return -ENOMEM; memcpy(buf, map->work_buf, map->format.reg_bytes); - memcpy(buf + map->format.reg_bytes, val, val_len); + memcpy(buf + map->format.reg_bytes + map->format.pad_bytes, + val, val_len); ret = map->bus->write(map->dev, buf, len); kfree(buf); @@ -387,10 +394,12 @@ int _regmap_write(struct regmap *map, unsigned int reg, return ret; } else { - map->format.format_val(map->work_buf + map->format.reg_bytes, - val); + map->format.format_val(map->work_buf + map->format.reg_bytes + + map->format.pad_bytes, val); return _regmap_raw_write(map, reg, - map->work_buf + map->format.reg_bytes, + map->work_buf + + map->format.reg_bytes + + map->format.pad_bytes, map->format.val_bytes); } } @@ -473,7 +482,8 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, trace_regmap_hw_read_start(map->dev, reg, val_len / map->format.val_bytes); - ret = map->bus->read(map->dev, map->work_buf, map->format.reg_bytes, + ret = map->bus->read(map->dev, map->work_buf, + map->format.reg_bytes + map->format.pad_bytes, val, val_len); trace_regmap_hw_read_done(map->dev, reg, diff --git a/include/linux/regmap.h b/include/linux/regmap.h index eb93921cdd30..a6ed6e6e27ac 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -44,6 +44,7 @@ struct reg_default { * Configuration for the register map of a device. * * @reg_bits: Number of bits in a register address, mandatory. + * @pad_bits: Number of bits of padding between register and value. * @val_bits: Number of bits in a register value, mandatory. * * @writeable_reg: Optional callback returning true if the register @@ -74,6 +75,7 @@ struct reg_default { */ struct regmap_config { int reg_bits; + int pad_bits; int val_bits; bool (*writeable_reg)(struct device *dev, unsigned int reg); -- cgit v1.2.3-58-ga151 From 7e53b195e412a813e915843adc7e4d91868e8e94 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 25 Jan 2012 20:46:53 +0000 Subject: regmap: Unexport regcache_write() and regcache_read() They have no current users which is fortunate as they don't take the lock and therefore aren't safe to use externally. We'll need to add new operations if direct cache access is needed. Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 1ead66186b7c..99beccfad4d7 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -211,7 +211,6 @@ int regcache_read(struct regmap *map, return -EINVAL; } -EXPORT_SYMBOL_GPL(regcache_read); /** * regcache_write: Set the value of a given register in the cache. @@ -238,7 +237,6 @@ int regcache_write(struct regmap *map, return 0; } -EXPORT_SYMBOL_GPL(regcache_write); /** * regcache_sync: Sync the register cache with the hardware. -- cgit v1.2.3-58-ga151 From d9db762708e27c2892db9d8a54e735a8e506e16e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 25 Jan 2012 21:06:33 +0000 Subject: regmap: Skip patch application when the cache is not dirty on sync On the basis that if we don't actually need to resync the cache then the patches are probably also already applied. Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index ce2034c10ffb..9c6a5c13f1da 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -269,6 +269,9 @@ int regcache_sync(struct regmap *map) name = map->cache_ops->name; trace_regcache_sync(map->dev, name, "start"); + if (!map->cache_dirty) + goto out; + /* Apply any patch first */ for (i = 0; i < map->patch_regs; i++) { ret = _regmap_write(map, map->patch[i].reg, map->patch[i].def); @@ -279,8 +282,6 @@ int regcache_sync(struct regmap *map) } } - if (!map->cache_dirty) - goto out; if (map->cache_ops->sync) { ret = map->cache_ops->sync(map); } else { -- cgit v1.2.3-58-ga151 From 8a892d6996b60c822f19ad1844eb15b96ce393c7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 25 Jan 2012 21:05:48 +0000 Subject: regmap: Bypass the cache when applying patches Otherwise any patch that affects a register which is writable may trash cached values. Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 9c6a5c13f1da..ee36bed9479c 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -273,6 +273,7 @@ int regcache_sync(struct regmap *map) goto out; /* Apply any patch first */ + map->cache_bypass = 1; for (i = 0; i < map->patch_regs; i++) { ret = _regmap_write(map, map->patch[i].reg, map->patch[i].def); if (ret != 0) { @@ -281,6 +282,7 @@ int regcache_sync(struct regmap *map) goto out; } } + map->cache_bypass = 0; if (map->cache_ops->sync) { ret = map->cache_ops->sync(map); -- cgit v1.2.3-58-ga151 From a24f64a648376766497fddd8bc24b1ca5b906431 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 26 Jan 2012 18:30:16 +0000 Subject: regmap: Reset device debugfs when reinitialising the cache Most of the data exposed via debugfs is for or from the cache so reset all the debugfs configuration to make sure everything is up to date with the latest configuration, especially if we're changing cache type. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index e9c2ac0174c6..2d71aeae3109 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -278,6 +278,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) mutex_lock(&map->lock); regcache_exit(map); + regmap_debugfs_exit(map); map->max_register = config->max_register; map->writeable_reg = config->writeable_reg; @@ -286,6 +287,8 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) map->precious_reg = config->precious_reg; map->cache_type = config->cache_type; + regmap_debugfs_init(map); + ret = regcache_init(map, config); mutex_unlock(&map->lock); -- cgit v1.2.3-58-ga151 From 9aa507505cdcd10b7390398790f013374ee74a26 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 27 Jan 2012 16:10:22 +0100 Subject: regmap: Add support for 2/6 register formating Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 2d71aeae3109..b4475d87f4a0 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -76,6 +76,14 @@ static bool regmap_volatile_range(struct regmap *map, unsigned int reg, return true; } +static void regmap_format_2_6_write(struct regmap *map, + unsigned int reg, unsigned int val) +{ + u8 *out = map->work_buf; + + *out = (reg << 6) | val; +} + static void regmap_format_4_12_write(struct regmap *map, unsigned int reg, unsigned int val) { @@ -180,6 +188,16 @@ struct regmap *regmap_init(struct device *dev, } switch (config->reg_bits) { + case 2: + switch (config->val_bits) { + case 6: + map->format.format_write = regmap_format_2_6_write; + break; + default: + goto err_map; + } + break; + case 4: switch (config->val_bits) { case 12: -- cgit v1.2.3-58-ga151 From c212acccc368a087a53559aac2b7d3be941b1252 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 28 Jan 2012 02:16:41 +0100 Subject: regmap: Properly round reg_bytes and val_bytes For the upcoming 2/6-format, we don't see debugfs output otherwise, since the current division results in 0. I'd think 10/14 is broken currently, too. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index b4475d87f4a0..3c34526091da 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -167,9 +167,9 @@ struct regmap *regmap_init(struct device *dev, mutex_init(&map->lock); map->format.buf_size = (config->reg_bits + config->val_bits) / 8; - map->format.reg_bytes = config->reg_bits / 8; + map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); map->format.pad_bytes = config->pad_bits / 8; - map->format.val_bytes = config->val_bits / 8; + map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8); map->format.buf_size += map->format.pad_bytes; map->dev = dev; map->bus = bus; -- cgit v1.2.3-58-ga151 From 4191f19792bf91267835eb090d970e9cd6277a65 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 30 Jan 2012 15:08:16 +0100 Subject: regmap: if format_write is used, declare all registers as "unreadable" Using .format_write means, we have a custom function to write to the chip, but not to read back. Also, mark registers as "not precious" and "not volatile" which is implicit because we cannot read them. Make those functions use 'regmap_readable' to reuse the checks done there. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 3c34526091da..b7198f57b69c 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -36,6 +36,9 @@ bool regmap_readable(struct regmap *map, unsigned int reg) if (map->max_register && reg > map->max_register) return false; + if (map->format.format_write) + return false; + if (map->readable_reg) return map->readable_reg(map->dev, reg); @@ -44,7 +47,7 @@ bool regmap_readable(struct regmap *map, unsigned int reg) bool regmap_volatile(struct regmap *map, unsigned int reg) { - if (map->max_register && reg > map->max_register) + if (!regmap_readable(map, reg)) return false; if (map->volatile_reg) @@ -55,7 +58,7 @@ bool regmap_volatile(struct regmap *map, unsigned int reg) bool regmap_precious(struct regmap *map, unsigned int reg) { - if (map->max_register && reg > map->max_register) + if (!regmap_readable(map, reg)) return false; if (map->precious_reg) -- cgit v1.2.3-58-ga151 From c0eb46766d395da8d62148bda2e59bad5e6ee2f2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 30 Jan 2012 19:56:52 +0000 Subject: regmap: Implement managed regmap_init() Save error handling and unwinding code in drivers by providing managed versions of the regmap init functions, simplifying usage. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-i2c.c | 17 +++++++++++++++++ drivers/base/regmap/regmap-spi.c | 17 +++++++++++++++++ drivers/base/regmap/regmap.c | 39 +++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 8 ++++++++ 4 files changed, 81 insertions(+) diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 38621ec87c05..9a3a8c564389 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -111,4 +111,21 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c, } EXPORT_SYMBOL_GPL(regmap_init_i2c); +/** + * devm_regmap_init_i2c(): Initialise managed register map + * + * @i2c: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, + const struct regmap_config *config) +{ + return devm_regmap_init(&i2c->dev, ®map_i2c, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_i2c); + MODULE_LICENSE("GPL"); diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 2560658de344..7c0c35a39c33 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -70,4 +70,21 @@ struct regmap *regmap_init_spi(struct spi_device *spi, } EXPORT_SYMBOL_GPL(regmap_init_spi); +/** + * devm_regmap_init_spi(): Initialise register map + * + * @spi: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The map will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_spi(struct spi_device *spi, + const struct regmap_config *config) +{ + return devm_regmap_init(&spi->dev, ®map_spi, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_spi); + MODULE_LICENSE("GPL"); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index be10a4ff6609..839cc82bd176 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -258,6 +258,45 @@ err: } EXPORT_SYMBOL_GPL(regmap_init); +static void devm_regmap_release(struct device *dev, void *res) +{ + regmap_exit(*(struct regmap **)res); +} + +/** + * devm_regmap_init(): Initialise managed register map + * + * @dev: Device that will be interacted with + * @bus: Bus-specific callbacks to use with device + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. This function should generally not be called + * directly, it should be called by bus-specific init functions. The + * map will be automatically freed by the device management code. + */ +struct regmap *devm_regmap_init(struct device *dev, + const struct regmap_bus *bus, + const struct regmap_config *config) +{ + struct regmap **ptr, *regmap; + + ptr = devres_alloc(devm_regmap_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + regmap = regmap_init(dev, bus, config); + if (!IS_ERR(regmap)) { + *ptr = regmap; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return regmap; +} +EXPORT_SYMBOL_GPL(devm_regmap_init); + /** * regmap_reinit_cache(): Reinitialise the current register cache * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index eb93921cdd30..195db51ec79b 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -127,6 +127,14 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c, struct regmap *regmap_init_spi(struct spi_device *dev, const struct regmap_config *config); +struct regmap *devm_regmap_init(struct device *dev, + const struct regmap_bus *bus, + const struct regmap_config *config); +struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, + const struct regmap_config *config); +struct regmap *devm_regmap_init_spi(struct spi_device *dev, + const struct regmap_config *config); + void regmap_exit(struct regmap *map); int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config); -- cgit v1.2.3-58-ga151 From aa795d129246cb4c973076e3242b8a2eb374f1ef Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 31 Jan 2012 11:48:18 +0800 Subject: regmap: Remove incorrect unreachable comment in regcache_set_val() regcache_set_val() returns false if cache[idx] != val. Thus it actually is not unreachable. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 99beccfad4d7..7e18d1e70f12 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -375,7 +375,6 @@ bool regcache_set_val(void *base, unsigned int idx, default: BUG(); } - /* unreachable */ return false; } -- cgit v1.2.3-58-ga151 From 847fb6fdf58c0ef4c207d2853a043a4da3db9c76 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 6 Feb 2012 18:01:35 +0000 Subject: regmap: Don't use bitfields for booleans This was a cut'n'paste from some older code. Since we're about to add debugfs support don't do the obvious thing and use bool, use u32 instead (which debugfs has been using since time immemorial). Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index e33f1be2b299..e93d7b7d1cf9 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -66,16 +66,16 @@ struct regmap { unsigned int num_reg_defaults_raw; /* if set, only the cache is modified not the HW */ - unsigned int cache_only:1; + u32 cache_only; /* if set, only the HW is modified not the cache */ - unsigned int cache_bypass:1; + u32 cache_bypass; /* if set, remember to free reg_defaults_raw */ - unsigned int cache_free:1; + bool cache_free; struct reg_default *reg_defaults; const void *reg_defaults_raw; void *cache; - bool cache_dirty; + u32 cache_dirty; }; struct regcache_ops { -- cgit v1.2.3-58-ga151 From 028a01e601487b5991b70dba506dfe87d83543f6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 6 Feb 2012 18:02:06 +0000 Subject: regmap: Add debugfs information for the cache status Show all the cache status flags in debugfs if we have a cache. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 6f397476e27c..b3b4b8f7f409 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -192,6 +192,15 @@ void regmap_debugfs_init(struct regmap *map) debugfs_create_file("access", 0400, map->debugfs, map, ®map_access_fops); } + + if (map->cache_type) { + debugfs_create_bool("cache_only", 0400, map->debugfs, + &map->cache_only); + debugfs_create_bool("cache_dirty", 0400, map->debugfs, + &map->cache_dirty); + debugfs_create_bool("cache_bypass", 0400, map->debugfs, + &map->cache_bypass); + } } void regmap_debugfs_exit(struct regmap *map) -- cgit v1.2.3-58-ga151 From 2a14d7d9b7439fe62082a60a7f8983ccb463d134 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 10 Feb 2012 19:29:55 +0800 Subject: regmap: Fix kcalloc parameters swapped The first parameter should be "number of elements" and the second parameter should be "element size". Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 28e89fd7c28d..46b2bd742ee6 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -709,7 +709,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, } } - map->patch = kcalloc(sizeof(struct reg_default), num_regs, GFP_KERNEL); + map->patch = kcalloc(num_regs, sizeof(struct reg_default), GFP_KERNEL); if (map->patch != NULL) { memcpy(map->patch, regs, num_regs * sizeof(struct reg_default)); -- cgit v1.2.3-58-ga151 From c9157198417076c0c2664ba997e7b0217f61fcce Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 10 Feb 2012 21:30:27 +0530 Subject: regmap: Support for caching in reg_raw_write() Adding support for caching of data into the non-volatile register from the call of reg_raw_write(). This will allow the larger block of data write into multiple register without worrying whether register is cached or not through reg_raw_write(). Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 65558034318f..74a6d4f8e3b8 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -321,6 +321,26 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, if (!map->writeable_reg(map->dev, reg + i)) return -EINVAL; + if (!map->cache_bypass && map->format.parse_val) { + unsigned int ival; + int val_bytes = map->format.val_bytes; + for (i = 0; i < val_len / val_bytes; i++) { + memcpy(map->work_buf, val + (i * val_bytes), val_bytes); + ival = map->format.parse_val(map->work_buf); + ret = regcache_write(map, reg + i, ival); + if (ret) { + dev_err(map->dev, + "Error in caching of register: %u ret: %d\n", + reg + i, ret); + return ret; + } + } + if (map->cache_only) { + map->cache_dirty = true; + return 0; + } + } + map->format.format_reg(map->work_buf, reg); u8[0] |= map->write_flag_mask; @@ -366,7 +386,7 @@ 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) { + if (!map->cache_bypass && map->format.format_write) { ret = regcache_write(map, reg, val); if (ret != 0) return ret; @@ -441,12 +461,8 @@ EXPORT_SYMBOL_GPL(regmap_write); int regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len) { - size_t val_count = val_len / map->format.val_bytes; int ret; - WARN_ON(!regmap_volatile_range(map, reg, val_count) && - map->cache_type != REGCACHE_NONE); - mutex_lock(&map->lock); ret = _regmap_raw_write(map, reg, val, val_len); -- cgit v1.2.3-58-ga151 From 8eaeb21925563075ae036c2e5ba8d041b70e18fa Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 12 Feb 2012 19:49:43 +0530 Subject: regmap: add regmap_bulk_write() for register write The bulk_write() supports the data transfer to multi register which takes the data into cpu_endianness format and does formatting of data to device format before sending to device. The transfer can be completed in single transfer or multiple transfer based on data formatting. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 50 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 2 ++ 2 files changed, 52 insertions(+) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 74a6d4f8e3b8..2366b6299f07 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -473,6 +473,56 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_raw_write); +/* + * regmap_bulk_write(): Write multiple registers to the device + * + * @map: Register map to write to + * @reg: First register to be write from + * @val: Block of data to be written, in native register size for device + * @val_count: Number of registers to write + * + * This function is intended to be used for writing a large block of + * data to be device either in single transfer or multiple transfer. + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, + size_t val_count) +{ + int ret = 0, i; + size_t val_bytes = map->format.val_bytes; + void *wval; + + if (!map->format.parse_val) + return -EINVAL; + + mutex_lock(&map->lock); + + /* No formatting is require if val_byte is 1 */ + if (val_bytes == 1) { + wval = (void *)val; + } else { + wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL); + if (!wval) { + ret = -ENOMEM; + dev_err(map->dev, "Error in memory allocation\n"); + goto out; + } + for (i = 0; i < val_count * val_bytes; i += val_bytes) + map->format.parse_val(wval + i); + } + ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count); + + if (val_bytes != 1) + kfree(wval); + +out: + mutex_unlock(&map->lock); + return ret; +} +EXPORT_SYMBOL_GPL(regmap_bulk_write); + static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int val_len) { diff --git a/include/linux/regmap.h b/include/linux/regmap.h index eb93921cdd30..1859793bc4c0 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -133,6 +133,8 @@ int regmap_reinit_cache(struct regmap *map, int regmap_write(struct regmap *map, unsigned int reg, unsigned int val); int regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len); +int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, + size_t val_count); int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val); int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, size_t val_len); -- cgit v1.2.3-58-ga151 From df00c79f78d8b0ad788daf689ea461ace9d0811f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 17 Feb 2012 18:57:26 +0530 Subject: regmap: Bypassing cache when initializing cache During regcache_init, if client has not passed the default data of cached register then it is directly read from the hw to initialize cache. This hw register read happens before cache ops are initialized and hence avoiding register read to check for the data available on cache or not by enabling flag of cache_bypass. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 1ead66186b7c..4b903a8e92a2 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -35,12 +35,17 @@ static int regcache_hw_init(struct regmap *map) return -EINVAL; if (!map->reg_defaults_raw) { + u32 cache_bypass = map->cache_bypass; dev_warn(map->dev, "No cache defaults, reading back from HW\n"); + + /* Bypass the cache access till data read from HW*/ + map->cache_bypass = 1; tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL); if (!tmp_buf) return -EINVAL; ret = regmap_bulk_read(map, 0, tmp_buf, map->num_reg_defaults_raw); + map->cache_bypass = cache_bypass; if (ret < 0) { kfree(tmp_buf); return ret; -- cgit v1.2.3-58-ga151 From 9cde5fcd435fd929db7b0b486efb435cdb1ddca4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 17 Feb 2012 14:49:51 -0800 Subject: regmap: Add stub regmap calls as a build crutch for infrastructure users Make life easier for subsystems which build infrastructure on top of the regmap API by providing stub definitions for most of the API so that users can at least build even if the regmap API is not enabled without having to add ifdefs if they don't want to. These stubs are not expected to be useful and should never actually get called, they just exist so that we can link so there are WARN_ONCE()s in the stubs. Signed-off-by: Mark Brown --- include/linux/regmap.h | 106 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a6ed6e6e27ac..42c69e75b131 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -16,6 +16,8 @@ #include #include +#ifdef CONFIG_REGMAP + struct module; struct i2c_client; struct spi_device; @@ -199,4 +201,108 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *data); int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data); +#else + +/* + * These stubs should only ever be called by generic code which has + * regmap based facilities, if they ever get called at runtime + * something is going wrong and something probably needs to select + * REGMAP. + */ + +static inline int regmap_write(struct regmap *map, unsigned int reg, + unsigned int val) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_raw_write(struct regmap *map, unsigned int reg, + const void *val, size_t val_len) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_bulk_write(struct regmap *map, unsigned int reg, + const void *val, size_t val_count) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_read(struct regmap *map, unsigned int reg, + unsigned int *val) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_raw_read(struct regmap *map, unsigned int reg, + void *val, size_t val_len) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_bulk_read(struct regmap *map, unsigned int reg, + void *val, size_t val_count) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_update_bits(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_update_bits_check(struct regmap *map, + unsigned int reg, + unsigned int mask, unsigned int val, + bool *change) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_get_val_bytes(struct regmap *map) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regcache_sync(struct regmap *map) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline void regcache_cache_only(struct regmap *map, bool enable) +{ + WARN_ONCE(1, "regmap API is disabled"); +} + +static inline void regcache_cache_bypass(struct regmap *map, bool enable) +{ + WARN_ONCE(1, "regmap API is disabled"); +} + +static inline void regcache_mark_dirty(struct regmap *map) +{ + WARN_ONCE(1, "regmap API is disabled"); +} + +static inline int regmap_register_patch(struct regmap *map, + const struct reg_default *regs, + int num_regs) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +#endif + #endif -- cgit v1.2.3-58-ga151 From 7d5e525b9ceda0e3b85da0acdaa2de19fea51edc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 17 Feb 2012 15:58:25 -0800 Subject: regmap: Implement support for 32 bit registers and values Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 11 +++++++++++ drivers/base/regmap/regmap.c | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 7e18d1e70f12..69d4c9c5a4a8 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -372,6 +372,13 @@ bool regcache_set_val(void *base, unsigned int idx, cache[idx] = val; break; } + case 4: { + u32 *cache = base; + if (cache[idx] == val) + return true; + cache[idx] = val; + break; + } default: BUG(); } @@ -393,6 +400,10 @@ unsigned int regcache_get_val(const void *base, unsigned int idx, const u16 *cache = base; return cache[idx]; } + case 4: { + const u32 *cache = base; + return cache[idx]; + } default: BUG(); } diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index b7198f57b69c..73a8111aea93 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -125,6 +125,13 @@ static void regmap_format_16(void *buf, unsigned int val) b[0] = cpu_to_be16(val); } +static void regmap_format_32(void *buf, unsigned int val) +{ + __be32 *b = buf; + + b[0] = cpu_to_be32(val); +} + static unsigned int regmap_parse_8(void *buf) { u8 *b = buf; @@ -141,6 +148,15 @@ static unsigned int regmap_parse_16(void *buf) return b[0]; } +static unsigned int regmap_parse_32(void *buf) +{ + __be32 *b = buf; + + b[0] = be32_to_cpu(b[0]); + + return b[0]; +} + /** * regmap_init(): Initialise register map * @@ -239,6 +255,10 @@ struct regmap *regmap_init(struct device *dev, map->format.format_reg = regmap_format_16; break; + case 32: + map->format.format_reg = regmap_format_32; + break; + default: goto err_map; } @@ -252,6 +272,10 @@ struct regmap *regmap_init(struct device *dev, map->format.format_val = regmap_format_16; map->format.parse_val = regmap_parse_16; break; + case 32: + map->format.format_val = regmap_format_32; + map->format.parse_val = regmap_parse_32; + break; } if (!map->format.format_write && -- cgit v1.2.3-58-ga151 From b8fb5ab156055b745254609f4635fcfd6b7dabc8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 21 Feb 2012 19:12:47 +0000 Subject: regmap: Support raw reads from cached registers Fall back to a register by register read to do so; most likely we'll be cache only so the overhead will be low. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 73a8111aea93..ef7022d45744 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -604,16 +604,32 @@ EXPORT_SYMBOL_GPL(regmap_read); int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, size_t val_len) { - size_t val_count = val_len / map->format.val_bytes; - int ret; - - WARN_ON(!regmap_volatile_range(map, reg, val_count) && - map->cache_type != REGCACHE_NONE); + size_t val_bytes = map->format.val_bytes; + size_t val_count = val_len / val_bytes; + unsigned int v; + int ret, i; mutex_lock(&map->lock); - ret = _regmap_raw_read(map, reg, val, val_len); + if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass || + map->cache_type == REGCACHE_NONE) { + /* Physical block read if there's no cache involved */ + ret = _regmap_raw_read(map, reg, val, val_len); + + } else { + /* Otherwise go word by word for the cache; should be low + * cost as we expect to hit the cache. + */ + for (i = 0; i < val_count; i++) { + ret = _regmap_read(map, reg + i, &v); + if (ret != 0) + goto out; + + map->format.format_val(val + (i * val_bytes), v); + } + } + out: mutex_unlock(&map->lock); return ret; -- cgit v1.2.3-58-ga151 From 09c6ecd394105c4864a0e409e181c9b1578c2a63 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Wed, 22 Feb 2012 12:43:50 +0000 Subject: regmap: Add support for writing to regmap registers via debugfs To enable writing to the regmap debugfs registers file users will need to modify the source directly and #define REGMAP_ALLOW_WRITE_DEBUGFS. The reason for this is that it is dangerous to expose this functionality in general where clients could potentially be PMICs. [A couple of minor style updates -- broonie] Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index b3b4b8f7f409..7accb81dd94e 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -103,9 +103,51 @@ out: return ret; } +#undef REGMAP_ALLOW_WRITE_DEBUGFS +#ifdef REGMAP_ALLOW_WRITE_DEBUGFS +/* + * This can be dangerous especially when we have clients such as + * PMICs, therefore don't provide any real compile time configuration option + * for this feature, people who want to use this will need to modify + * the source code directly. + */ +static ssize_t regmap_map_write_file(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[32]; + size_t buf_size; + char *start = buf; + unsigned long reg, value; + struct regmap *map = file->private_data; + + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + while (*start == ' ') + start++; + reg = simple_strtoul(start, &start, 16); + while (*start == ' ') + start++; + if (strict_strtoul(start, 16, &value)) + return -EINVAL; + + /* Userspace has been fiddling around behind the kernel's back */ + add_taint(TAINT_USER); + + regmap_write(map, reg, value); + return buf_size; +} +#else +#define regmap_map_write_file NULL +#endif + static const struct file_operations regmap_map_fops = { .open = regmap_open_file, .read = regmap_map_read_file, + .write = regmap_map_write_file, .llseek = default_llseek, }; -- cgit v1.2.3-58-ga151 From 027d676650e690c6bcb479d96fb601e91213ee56 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 17 Feb 2012 15:57:01 -0800 Subject: mfd: wm8994: Add __devinit and __devexit annotations for probe and remove Fixes warnings and needed for correctness. Signed-off-by: Mark Brown --- drivers/mfd/wm8994-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index f117e7fb9321..980f8419580b 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -599,8 +599,8 @@ static const struct of_device_id wm8994_of_match[] = { }; MODULE_DEVICE_TABLE(of, wm8994_of_match); -static int wm8994_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static __devinit int wm8994_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { struct wm8994 *wm8994; int ret; @@ -625,7 +625,7 @@ static int wm8994_i2c_probe(struct i2c_client *i2c, return wm8994_device_init(wm8994, i2c->irq); } -static int wm8994_i2c_remove(struct i2c_client *i2c) +static __devexit int wm8994_i2c_remove(struct i2c_client *i2c) { struct wm8994 *wm8994 = i2c_get_clientdata(i2c); @@ -654,7 +654,7 @@ static struct i2c_driver wm8994_i2c_driver = { .of_match_table = wm8994_of_match, }, .probe = wm8994_i2c_probe, - .remove = wm8994_i2c_remove, + .remove = __devexit_p(wm8994_i2c_remove), .id_table = wm8994_i2c_id, }; -- cgit v1.2.3-58-ga151 From abc0cceaeeab6d3101f4d9492063a4e4ae813b85 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 21 Jan 2012 12:13:30 +0000 Subject: mfd: Add __devinit and __devexit annotations in wm8994 The wm8994_device_init() and wm8994_device_exit() functions were not annotated as device init and exit functions, meaning they shouldn't reference __devinitdata. Signed-off-by: Mark Brown Acked-by: Samuel Oritz --- drivers/mfd/wm8994-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 980f8419580b..398cb37e8621 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -348,7 +348,7 @@ static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo) /* * Instantiate the generic non-control parts of the device. */ -static int wm8994_device_init(struct wm8994 *wm8994, int irq) +static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) { struct wm8994_pdata *pdata = wm8994->dev->platform_data; struct regmap_config *regmap_config; @@ -580,7 +580,7 @@ err_regmap: return ret; } -static void wm8994_device_exit(struct wm8994 *wm8994) +static __devexit void wm8994_device_exit(struct wm8994 *wm8994) { pm_runtime_disable(wm8994->dev); mfd_remove_devices(wm8994->dev); -- cgit v1.2.3-58-ga151 From 462835e4a7f898662cc30064a33177af4823ef9d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 21 Jan 2012 12:11:53 +0000 Subject: mfd/ASoC: Convert WM8994 driver to use regmap patches Early revisions of several of the WM8994 variants have register updates to improve performance. Move these over to using the regmap patch system instead of open coding them in the audio driver. Since the regmap init is done by the MFD the code is moved there. Signed-off-by: Mark Brown Acked-by: Samuel Ortiz --- drivers/mfd/wm8994-core.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++- sound/soc/codecs/wm8994.c | 25 +------------------ 2 files changed, 61 insertions(+), 25 deletions(-) diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 398cb37e8621..ac0b8f91f815 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -345,6 +345,27 @@ static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo) } #endif +static const __devinitdata struct reg_default wm8994_revc_patch[] = { + { 0x102, 0x3 }, + { 0x56, 0x3 }, + { 0x817, 0x0 }, + { 0x102, 0x0 }, +}; + +static const __devinitdata struct reg_default wm8958_reva_patch[] = { + { 0x102, 0x3 }, + { 0xcb, 0x81 }, + { 0x817, 0x0 }, + { 0x102, 0x0 }, +}; + +static const __devinitdata struct reg_default wm1811_reva_patch[] = { + { 0x102, 0x3 }, + { 0x5d, 0x7e }, + { 0x5e, 0x0 }, + { 0x102, 0x0 }, +}; + /* * Instantiate the generic non-control parts of the device. */ @@ -352,8 +373,9 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) { struct wm8994_pdata *pdata = wm8994->dev->platform_data; struct regmap_config *regmap_config; + const struct reg_default *regmap_patch = NULL; const char *devname; - int ret, i; + int ret, i, patch_regs; int pulls = 0; dev_set_drvdata(wm8994->dev, wm8994); @@ -474,15 +496,42 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) "revision %c not fully supported\n", 'A' + wm8994->revision); break; + case 2: + case 3: + regmap_patch = wm8994_revc_patch; + patch_regs = ARRAY_SIZE(wm8994_revc_patch); + break; + default: + break; + } + break; + + case WM8958: + switch (wm8994->revision) { + case 0: + regmap_patch = wm8958_reva_patch; + patch_regs = ARRAY_SIZE(wm8958_reva_patch); + break; default: break; } break; + case WM1811: /* Revision C did not change the relevant layer */ if (wm8994->revision > 1) wm8994->revision++; + switch (wm8994->revision) { + case 0: + case 1: + regmap_patch = wm1811_reva_patch; + patch_regs = ARRAY_SIZE(wm1811_reva_patch); + break; + default: + break; + } break; + default: break; } @@ -512,6 +561,16 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) return ret; } + if (regmap_patch) { + ret = regmap_register_patch(wm8994->regmap, regmap_patch, + patch_regs); + if (ret != 0) { + dev_err(wm8994->dev, "Failed to register patch: %d\n", + ret); + goto err_regmap; + } + } + if (pdata) { wm8994->irq_base = pdata->irq_base; wm8994->gpio_base = pdata->gpio_base; diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index ec69a6c152fe..7c686abf8bd9 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2099,26 +2099,9 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { switch (control->type) { - case WM8994: - if (wm8994->revision < 4) { - /* Tweak DC servo and DSP - * configuration for improved - * performance. */ - snd_soc_write(codec, 0x102, 0x3); - snd_soc_write(codec, 0x56, 0x3); - snd_soc_write(codec, 0x817, 0); - snd_soc_write(codec, 0x102, 0); - } - break; - case WM8958: if (wm8994->revision == 0) { /* Optimise performance for rev A */ - snd_soc_write(codec, 0x102, 0x3); - snd_soc_write(codec, 0xcb, 0x81); - snd_soc_write(codec, 0x817, 0); - snd_soc_write(codec, 0x102, 0); - snd_soc_update_bits(codec, WM8958_CHARGE_PUMP_2, WM8958_CP_DISCH, @@ -2126,13 +2109,7 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, } break; - case WM1811: - if (wm8994->revision < 2) { - snd_soc_write(codec, 0x102, 0x3); - snd_soc_write(codec, 0x5d, 0x7e); - snd_soc_write(codec, 0x5e, 0x0); - snd_soc_write(codec, 0x102, 0x0); - } + default: break; } -- cgit v1.2.3-58-ga151 From 9db4249fa0fb808ea0c27dfe0fcedc1884ebfb5a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 30 Jan 2012 20:06:05 +0000 Subject: mfd: wm8994: Convert to devm_regmap_init() Signed-off-by: Mark Brown Acked-by: Samuel Ortiz --- drivers/mfd/wm8994-core.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index ac0b8f91f815..4b36f021413f 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -387,7 +387,7 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) NULL, 0); if (ret != 0) { dev_err(wm8994->dev, "Failed to add children: %d\n", ret); - goto err_regmap; + goto err; } switch (wm8994->type) { @@ -402,7 +402,7 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) break; default: BUG(); - goto err_regmap; + goto err; } wm8994->supplies = devm_kzalloc(wm8994->dev, @@ -410,7 +410,7 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) wm8994->num_supplies, GFP_KERNEL); if (!wm8994->supplies) { ret = -ENOMEM; - goto err_regmap; + goto err; } switch (wm8994->type) { @@ -428,14 +428,14 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) break; default: BUG(); - goto err_regmap; + goto err; } ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies, wm8994->supplies); if (ret != 0) { dev_err(wm8994->dev, "Failed to get supplies: %d\n", ret); - goto err_regmap; + goto err; } ret = regulator_bulk_enable(wm8994->num_supplies, @@ -567,7 +567,7 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) if (ret != 0) { dev_err(wm8994->dev, "Failed to register patch: %d\n", ret); - goto err_regmap; + goto err; } } @@ -633,8 +633,7 @@ err_enable: wm8994->supplies); err_get: regulator_bulk_free(wm8994->num_supplies, wm8994->supplies); -err_regmap: - regmap_exit(wm8994->regmap); +err: mfd_remove_devices(wm8994->dev); return ret; } @@ -647,7 +646,6 @@ static __devexit void wm8994_device_exit(struct wm8994 *wm8994) regulator_bulk_disable(wm8994->num_supplies, wm8994->supplies); regulator_bulk_free(wm8994->num_supplies, wm8994->supplies); - regmap_exit(wm8994->regmap); } static const struct of_device_id wm8994_of_match[] = { @@ -673,7 +671,7 @@ static __devinit int wm8994_i2c_probe(struct i2c_client *i2c, wm8994->irq = i2c->irq; wm8994->type = id->driver_data; - wm8994->regmap = regmap_init_i2c(i2c, &wm8994_base_regmap_config); + wm8994->regmap = devm_regmap_init_i2c(i2c, &wm8994_base_regmap_config); if (IS_ERR(wm8994->regmap)) { ret = PTR_ERR(wm8994->regmap); dev_err(wm8994->dev, "Failed to allocate register map: %d\n", -- cgit v1.2.3-58-ga151 From 130a70323c323046b15a76ef1147b1fe700afd10 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 30 Jan 2012 20:08:06 +0000 Subject: mfd: wm831x: Convert to devm_regmap_init() Signed-off-by: Mark Brown Acked-by: Samuel Ortiz --- drivers/mfd/wm831x-core.c | 20 +++++++++----------- drivers/mfd/wm831x-i2c.c | 2 +- drivers/mfd/wm831x-spi.c | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index f5e54fae8ada..838056c3493a 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -1631,7 +1631,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID); if (ret < 0) { dev_err(wm831x->dev, "Failed to read parent ID: %d\n", ret); - goto err_regmap; + goto err; } switch (ret) { case 0x6204: @@ -1640,20 +1640,20 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) default: dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret); ret = -EINVAL; - goto err_regmap; + goto err; } ret = wm831x_reg_read(wm831x, WM831X_REVISION); if (ret < 0) { dev_err(wm831x->dev, "Failed to read revision: %d\n", ret); - goto err_regmap; + goto err; } rev = (ret & WM831X_PARENT_REV_MASK) >> WM831X_PARENT_REV_SHIFT; ret = wm831x_reg_read(wm831x, WM831X_RESET_ID); if (ret < 0) { dev_err(wm831x->dev, "Failed to read device ID: %d\n", ret); - goto err_regmap; + goto err; } /* Some engineering samples do not have the ID set, rely on @@ -1728,7 +1728,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) default: dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret); ret = -EINVAL; - goto err_regmap; + goto err; } /* This will need revisiting in future but is OK for all @@ -1742,7 +1742,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY); if (ret < 0) { dev_err(wm831x->dev, "Failed to read security key: %d\n", ret); - goto err_regmap; + goto err; } if (ret != 0) { dev_warn(wm831x->dev, "Security key had non-zero value %x\n", @@ -1755,7 +1755,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) ret = pdata->pre_init(wm831x); if (ret != 0) { dev_err(wm831x->dev, "pre_init() failed: %d\n", ret); - goto err_regmap; + goto err; } } @@ -1778,7 +1778,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) ret = wm831x_irq_init(wm831x, irq); if (ret != 0) - goto err_regmap; + goto err; wm831x_auxadc_init(wm831x); @@ -1874,9 +1874,8 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) err_irq: wm831x_irq_exit(wm831x); -err_regmap: +err: mfd_remove_devices(wm831x->dev); - regmap_exit(wm831x->regmap); return ret; } @@ -1887,7 +1886,6 @@ void wm831x_device_exit(struct wm831x *wm831x) if (wm831x->irq_base) free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x); wm831x_irq_exit(wm831x); - regmap_exit(wm831x->regmap); } int wm831x_device_suspend(struct wm831x *wm831x) diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c index cb15609b0a48..2b29caebc9cf 100644 --- a/drivers/mfd/wm831x-i2c.c +++ b/drivers/mfd/wm831x-i2c.c @@ -37,7 +37,7 @@ static int wm831x_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, wm831x); wm831x->dev = &i2c->dev; - wm831x->regmap = regmap_init_i2c(i2c, &wm831x_regmap_config); + wm831x->regmap = devm_regmap_init_i2c(i2c, &wm831x_regmap_config); if (IS_ERR(wm831x->regmap)) { ret = PTR_ERR(wm831x->regmap); dev_err(wm831x->dev, "Failed to allocate register map: %d\n", diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index 62ef3254105f..745c87945664 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -40,7 +40,7 @@ static int __devinit wm831x_spi_probe(struct spi_device *spi) dev_set_drvdata(&spi->dev, wm831x); wm831x->dev = &spi->dev; - wm831x->regmap = regmap_init_spi(spi, &wm831x_regmap_config); + wm831x->regmap = devm_regmap_init_spi(spi, &wm831x_regmap_config); if (IS_ERR(wm831x->regmap)) { ret = PTR_ERR(wm831x->regmap); dev_err(wm831x->dev, "Failed to allocate register map: %d\n", -- cgit v1.2.3-58-ga151 From 2b40e9d97d196f98ef356003d400d5675174b4fc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 30 Jan 2012 21:18:01 +0000 Subject: mfd: wm8400: Convert to devm_regmap_init_i2c() Signed-off-by: Mark Brown Acked-by: Samuel Ortiz --- drivers/mfd/wm8400-core.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index 2204893444a6..237764ae5f9b 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -350,7 +350,7 @@ static int wm8400_i2c_probe(struct i2c_client *i2c, goto err; } - wm8400->regmap = regmap_init_i2c(i2c, &wm8400_regmap_config); + wm8400->regmap = devm_regmap_init_i2c(i2c, &wm8400_regmap_config); if (IS_ERR(wm8400->regmap)) { ret = PTR_ERR(wm8400->regmap); goto err; @@ -361,12 +361,10 @@ static int wm8400_i2c_probe(struct i2c_client *i2c, ret = wm8400_init(wm8400, i2c->dev.platform_data); if (ret != 0) - goto map_err; + goto err; return 0; -map_err: - regmap_exit(wm8400->regmap); err: return ret; } @@ -376,7 +374,6 @@ static int wm8400_i2c_remove(struct i2c_client *i2c) struct wm8400 *wm8400 = i2c_get_clientdata(i2c); wm8400_release(wm8400); - regmap_exit(wm8400->regmap); return 0; } -- cgit v1.2.3-58-ga151 From f0c2319f9f196726ebe4d7508fd8fbd804988db3 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Wed, 22 Feb 2012 14:20:09 +0000 Subject: regmap: Expose the driver name in debugfs Add a file called 'name' containing the name of the driver. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 7accb81dd94e..c32b60a93ceb 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -33,6 +33,35 @@ static int regmap_open_file(struct inode *inode, struct file *file) return 0; } +static ssize_t regmap_name_read_file(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct regmap *map = file->private_data; + int ret; + char *buf; + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = snprintf(buf, PAGE_SIZE, "%s\n", map->dev->driver->name); + if (ret < 0) { + kfree(buf); + return ret; + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + kfree(buf); + return ret; +} + +static const struct file_operations regmap_name_fops = { + .open = regmap_open_file, + .read = regmap_name_read_file, + .llseek = default_llseek, +}; + static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -228,6 +257,9 @@ void regmap_debugfs_init(struct regmap *map) return; } + debugfs_create_file("name", 0400, map->debugfs, + map, ®map_name_fops); + if (map->max_register) { debugfs_create_file("registers", 0400, map->debugfs, map, ®map_map_fops); -- cgit v1.2.3-58-ga151 From a3c3774176838bbfa4f6e48133644903818e56dc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 23 Feb 2012 20:11:58 +0000 Subject: regmap: Skip hardware defaults for LZO caches Saves some I/O when resyncing; we assume that syncs start from the device reset state. Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-lzo.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index b7d16143edeb..3025cf920f25 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -343,6 +343,12 @@ static int regcache_lzo_sync(struct regmap *map) ret = regcache_read(map, i, &val); if (ret) return ret; + + /* Is this the hardware default? If so skip. */ + ret = regcache_lookup_reg(map, i); + if (ret > 0 && val == map->reg_defaults[ret].def) + continue; + map->cache_bypass = 1; ret = _regmap_write(map, i, val); map->cache_bypass = 0; -- cgit v1.2.3-58-ga151 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 --- drivers/base/regmap/regcache.c | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index ee36bed9479c..4d43ed363481 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 v1.2.3-58-ga151 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 --- drivers/base/regmap/regcache.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 4d43ed363481..46c42d3a4655 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 v1.2.3-58-ga151 From 5d5b7d4f80ed6e861c1c220fd57e3dad0912526e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 23 Feb 2012 22:02:57 +0000 Subject: regmap: Add tracepoints for cache only and cache bypass Useful for figuring out where the hardware interaction went or came from. Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 2 ++ include/trace/events/regmap.h | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 69d4c9c5a4a8..2d89ce08f137 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -313,6 +313,7 @@ void regcache_cache_only(struct regmap *map, bool enable) mutex_lock(&map->lock); WARN_ON(map->cache_bypass && enable); map->cache_only = enable; + trace_regmap_cache_only(map->dev, enable); mutex_unlock(&map->lock); } EXPORT_SYMBOL_GPL(regcache_cache_only); @@ -350,6 +351,7 @@ void regcache_cache_bypass(struct regmap *map, bool enable) mutex_lock(&map->lock); WARN_ON(map->cache_only && enable); map->cache_bypass = enable; + trace_regmap_cache_bypass(map->dev, enable); mutex_unlock(&map->lock); } EXPORT_SYMBOL_GPL(regcache_cache_bypass); diff --git a/include/trace/events/regmap.h b/include/trace/events/regmap.h index 12fbf43524e9..d69738280ffa 100644 --- a/include/trace/events/regmap.h +++ b/include/trace/events/regmap.h @@ -139,6 +139,42 @@ TRACE_EVENT(regcache_sync, __get_str(type), __get_str(status)) ); +DECLARE_EVENT_CLASS(regmap_bool, + + TP_PROTO(struct device *dev, bool flag), + + TP_ARGS(dev, flag), + + TP_STRUCT__entry( + __string( name, dev_name(dev) ) + __field( int, flag ) + ), + + TP_fast_assign( + __assign_str(name, dev_name(dev)); + __entry->flag = flag; + ), + + TP_printk("%s flag=%d", __get_str(name), + (int)__entry->flag) +); + +DEFINE_EVENT(regmap_bool, regmap_cache_only, + + TP_PROTO(struct device *dev, bool flag), + + TP_ARGS(dev, flag) + +); + +DEFINE_EVENT(regmap_bool, regmap_cache_bypass, + + TP_PROTO(struct device *dev, bool flag), + + TP_ARGS(dev, flag) + +); + #endif /* _TRACE_REGMAP_H */ /* This part must be outside protection */ -- cgit v1.2.3-58-ga151 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 --- drivers/base/regmap/internal.h | 2 +- drivers/base/regmap/regcache-lzo.c | 10 ++++++++-- drivers/base/regmap/regcache-rbtree.c | 25 ++++++++++++++++++++++--- drivers/base/regmap/regcache.c | 2 +- 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index d141b80479b5..9c1d62e3e15c 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 b7d16143edeb..5e964e9b2bab 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 32620c4f1683..bae183c6bcb1 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 46c42d3a4655..aec5a7486a29 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 v1.2.3-58-ga151 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 --- drivers/base/regmap/regcache.c | 45 ++++++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 2 ++ 2 files changed, 47 insertions(+) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index aec5a7486a29..b35f8751471d 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -298,6 +298,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 * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 860739a8a6dd..5ff7d44730e5 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 v1.2.3-58-ga151 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 --- drivers/base/regmap/regcache-lzo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index 5e964e9b2bab..f0f04060a4ec 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 v1.2.3-58-ga151 From a0cc0209abb9fe2b9ab71aa41be70eddd0cbdd61 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 28 Feb 2012 10:48:49 +0000 Subject: mfd: Improve performance of later WM1811 revisions Apply tunings from earlier silicon revisions to revisions up to D and also tweak an additional setting for improved DC servo performance. Signed-off-by: Mark Brown --- drivers/mfd/wm8994-core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 4b36f021413f..4b8b78c39767 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -361,6 +361,7 @@ static const __devinitdata struct reg_default wm8958_reva_patch[] = { static const __devinitdata struct reg_default wm1811_reva_patch[] = { { 0x102, 0x3 }, + { 0x56, 0x7 }, { 0x5d, 0x7e }, { 0x5e, 0x0 }, { 0x102, 0x0 }, @@ -524,6 +525,8 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) switch (wm8994->revision) { case 0: case 1: + case 2: + case 3: regmap_patch = wm1811_reva_patch; patch_regs = ARRAY_SIZE(wm1811_reva_patch); break; -- cgit v1.2.3-58-ga151 From a313f9f565d84f2fcc81c818a6b0baaae752a821 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 23 Feb 2012 19:49:43 +0000 Subject: regmap: Add stub for regcache_sync_region() Added on another branch. Signed-off-by: Mark Brown --- include/linux/regmap.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 42c69e75b131..33d5f1d9f882 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -280,6 +280,13 @@ static inline int regcache_sync(struct regmap *map) return -EINVAL; } +static inline int regcache_sync_region(struct regmap *map, unsigned int min, + unsigned int max) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline void regcache_cache_only(struct regmap *map, bool enable) { WARN_ONCE(1, "regmap API is disabled"); -- cgit v1.2.3-58-ga151 From 19694b5ea1d3a723dafe9544b5ee9a935414dc28 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Tue, 28 Feb 2012 19:28:02 -0500 Subject: regmap: delete unused module.h from drivers/base/regmap files Remove unused module.h and/or replace with export.h as required. Signed-off-by: Paul Gortmaker Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 1 - drivers/base/regmap/regmap.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index c32b60a93ceb..372f81a21201 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -11,7 +11,6 @@ */ #include -#include #include #include #include diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ef7022d45744..b47f19146eed 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -11,7 +11,7 @@ */ #include -#include +#include #include #include -- cgit v1.2.3-58-ga151 From 994f5db65ef4b83db0321842bd43c6bc0a51f000 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 5 Mar 2012 23:31:39 +0000 Subject: regcache: Make sure we sync register 0 in an rbtree cache Most of the current users have register 0 as a volatile register or don't have a register 0 so it's not been apparent that it's not getting synced. Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 32620c4f1683..861ad2c81dff 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -377,7 +377,7 @@ static int regcache_rbtree_sync(struct regmap *map) /* Is this the hardware default? If so skip. */ ret = regcache_lookup_reg(map, i); - if (ret > 0 && val == map->reg_defaults[ret].def) + if (ret >= 0 && val == map->reg_defaults[ret].def) continue; map->cache_bypass = 1; -- cgit v1.2.3-58-ga151 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 --- drivers/base/regmap/regcache-rbtree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index bae183c6bcb1..313c20f8cc28 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 v1.2.3-58-ga151 From b83d2ff01376cf3799394693c0dd089f657bdf84 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 11 Mar 2012 11:49:17 +0000 Subject: regmap: Rejig struct declarations for stubbed API Ensure we have a forward declaration of struct regmap that isn't just the return value of regmap_init() and make the definition of the register defaults available. Reported-by: Randy Dunlap Signed-off-by: Mark Brown --- include/linux/regmap.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 33d5f1d9f882..14b8252d8ed0 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -16,11 +16,10 @@ #include #include -#ifdef CONFIG_REGMAP - struct module; struct i2c_client; struct spi_device; +struct regmap; /* An enum of all the supported cache types */ enum regcache_type { @@ -42,6 +41,8 @@ struct reg_default { unsigned int def; }; +#ifdef CONFIG_REGMAP + /** * Configuration for the register map of a device. * -- cgit v1.2.3-58-ga151 From f5d6eba74b8aac7d4bf646c5445807aa6a247e6c Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 9 Mar 2012 13:17:28 -0700 Subject: regmap: Fix future missing prototype of devres_alloc() and friends [Fix for breakage which will be introduced during the merge window via header reworks in another tree, the regmap tree does include device.h but Paul's tree breaks that. Reworded subject to reflect -- broonie] regmap.s uses devres_alloc() and others that are prototyped in device.h. Include that to solve the following: drivers/base/regmap/regmap.c: In function 'devm_regmap_init': drivers/base/regmap/regmap.c:331:2: error: implicit declaration of function 'devres_alloc' [-Werror=implicit-function-declaration] drivers/base/regmap/regmap.c:338:3: error: implicit declaration of function 'devres_add' [-Werror=implicit-function-declaration] drivers/base/regmap/regmap.c:340:3: error: implicit declaration of function 'devres_free' [-Werror=implicit-function-declaration] drivers/base/regmap/regmap.c: In function '_regmap_raw_write': drivers/base/regmap/regmap.c:421:5: error: implicit declaration of function 'dev_err' [-Werror=implicit-function-declaration] Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index b47f19146eed..253882d8392a 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -10,6 +10,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include -- cgit v1.2.3-58-ga151