From 6cfec04bcc05a829179c02584bb55f28fee03795 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 10 Feb 2014 16:22:33 +0100 Subject: regmap: Separate regmap dev initialization Create special function regmap_attach_dev which can be called separately out of regmap_init. Signed-off-by: Michal Simek Signed-off-by: Mark Brown --- include/linux/regmap.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 4149f1a9b003..fa4d079fa44c 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -317,6 +317,8 @@ struct regmap *regmap_init(struct device *dev, const struct regmap_bus *bus, void *bus_context, const struct regmap_config *config); +int regmap_attach_dev(struct device *dev, struct regmap *map, + const struct regmap_config *config); struct regmap *regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); struct regmap *regmap_init_spi(struct spi_device *dev, -- cgit v1.2.3 From f7e2cec02b0e5bfe2180f09de9b0bc724774c51a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 25 Feb 2014 13:45:49 +0000 Subject: regmap: Mark reg_defaults in regmap_multi_reg_write as const There should be no need for the writes supplied to this function to be edited by it so mark them as const. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 4 ++-- include/linux/regmap.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 6e19560c7a5b..1e3934aea56a 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1592,8 +1592,8 @@ EXPORT_SYMBOL_GPL(regmap_bulk_write); * A value of zero will be returned on success, a negative errno will * be returned in error cases. */ -int regmap_multi_reg_write(struct regmap *map, struct reg_default *regs, - int num_regs) +int regmap_multi_reg_write(struct regmap *map, const struct reg_default *regs, + int num_regs) { int ret = 0, i; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 4149f1a9b003..e97ac6c8b7be 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -386,7 +386,7 @@ 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_multi_reg_write(struct regmap *map, struct reg_default *regs, +int regmap_multi_reg_write(struct regmap *map, const struct reg_default *regs, int num_regs); int regmap_raw_write_async(struct regmap *map, unsigned int reg, const void *val, size_t val_len); -- cgit v1.2.3 From 1d5b40bccf04994248b39e8ce234a7c1f3235cf5 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 25 Feb 2014 13:45:50 +0000 Subject: regmap: Add bypassed version of regmap_multi_reg_write Devices with more complex boot proceedures may occasionally apply the register patch manual. regmap_multi_reg_write is a logical way to do so, however the patch must be applied with cache bypass on, such that it doesn't override any user settings. This patch adds a regmap_multi_reg_write_bypassed function that applies a set of writes with the bypass enabled. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 75 ++++++++++++++++++++++++++++++++++++-------- include/linux/regmap.h | 3 ++ 2 files changed, 65 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 1e3934aea56a..e6a2c29c0be4 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1576,6 +1576,26 @@ out: } EXPORT_SYMBOL_GPL(regmap_bulk_write); +static int _regmap_multi_reg_write(struct regmap *map, + const struct reg_default *regs, + int num_regs) +{ + int i, ret; + + for (i = 0; i < num_regs; i++) { + if (regs[i].reg % map->reg_stride) + return -EINVAL; + ret = _regmap_write(map, regs[i].reg, regs[i].def); + if (ret != 0) { + dev_err(map->dev, "Failed to write %x = %x: %d\n", + regs[i].reg, regs[i].def, ret); + return ret; + } + } + + return 0; +} + /* * regmap_multi_reg_write(): Write multiple registers to the device * @@ -1595,28 +1615,57 @@ EXPORT_SYMBOL_GPL(regmap_bulk_write); int regmap_multi_reg_write(struct regmap *map, const struct reg_default *regs, int num_regs) { - int ret = 0, i; - - for (i = 0; i < num_regs; i++) { - int reg = regs[i].reg; - if (reg % map->reg_stride) - return -EINVAL; - } + int ret; map->lock(map->lock_arg); - for (i = 0; i < num_regs; i++) { - ret = _regmap_write(map, regs[i].reg, regs[i].def); - if (ret != 0) - goto out; - } -out: + ret = _regmap_multi_reg_write(map, regs, num_regs); + map->unlock(map->lock_arg); return ret; } EXPORT_SYMBOL_GPL(regmap_multi_reg_write); +/* + * regmap_multi_reg_write_bypassed(): Write multiple registers to the + * device but not the cache + * + * where the set of register are supplied in any order + * + * @map: Register map to write to + * @regs: Array of structures containing register,value to be written + * @num_regs: Number of registers to write + * + * This function is intended to be used for writing a large block of data + * atomically to the device in single transfer for those I2C client devices + * that implement this alternative block write mode. + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_multi_reg_write_bypassed(struct regmap *map, + const struct reg_default *regs, + int num_regs) +{ + int ret; + bool bypass; + + map->lock(map->lock_arg); + + bypass = map->cache_bypass; + map->cache_bypass = true; + + ret = _regmap_multi_reg_write(map, regs, num_regs); + + map->cache_bypass = bypass; + + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_multi_reg_write_bypassed); + /** * regmap_raw_write_async(): Write raw values to one or more registers * asynchronously diff --git a/include/linux/regmap.h b/include/linux/regmap.h index e97ac6c8b7be..ca2272fbd014 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -388,6 +388,9 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count); int regmap_multi_reg_write(struct regmap *map, const struct reg_default *regs, int num_regs); +int regmap_multi_reg_write_bypassed(struct regmap *map, + const struct reg_default *regs, + int num_regs); int regmap_raw_write_async(struct regmap *map, unsigned int reg, const void *val, size_t val_len); int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val); -- cgit v1.2.3 From 13ff50c85846338bb9820abd3933227b678dc086 Mon Sep 17 00:00:00 2001 From: Nenghua Cao Date: Wed, 19 Feb 2014 18:44:13 +0800 Subject: regmap: add regmap_parse_val api In some cases, we need regmap's format parse_val function to do be/le translation according to the bus configuration. For example, snd_soc_bytes_put() uses regmap to write/read values, and use cpu_to_be() directly to covert MASK into big endian. This is a defect, and should use regmap's format function to do it according to bus configuration. Signed-off-by: Nenghua Cao Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 12 ++++++++++++ include/linux/regmap.h | 9 +++++++++ 2 files changed, 21 insertions(+) (limited to 'include/linux') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 6a19515f8a45..4b2ed0c9e80d 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2240,6 +2240,18 @@ int regmap_get_val_bytes(struct regmap *map) } EXPORT_SYMBOL_GPL(regmap_get_val_bytes); +int regmap_parse_val(struct regmap *map, const void *buf, + unsigned int *val) +{ + if (!map->format.parse_val) + return -EINVAL; + + *val = map->format.parse_val(buf); + + return 0; +} +EXPORT_SYMBOL_GPL(regmap_parse_val); + static int __init regmap_initcall(void) { regmap_debugfs_initcall(); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 4149f1a9b003..3e1a2e4a92ad 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -423,6 +423,8 @@ bool regmap_check_range_table(struct regmap *map, unsigned int reg, int regmap_register_patch(struct regmap *map, const struct reg_default *regs, int num_regs); +int regmap_parse_val(struct regmap *map, const void *buf, + unsigned int *val); static inline bool regmap_reg_in_range(unsigned int reg, const struct regmap_range *range) @@ -695,6 +697,13 @@ static inline int regmap_register_patch(struct regmap *map, return -EINVAL; } +static inline int regmap_parse_val(struct regmap *map, const void *buf, + unsigned int *val) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline struct regmap *dev_get_regmap(struct device *dev, const char *name) { -- cgit v1.2.3 From e894c3f46c302716d2f156b1f3339e2f96ceb65c Mon Sep 17 00:00:00 2001 From: "Opensource [Anthony Olech]" Date: Tue, 4 Mar 2014 13:54:02 +0000 Subject: regmap: Implementation for regmap_multi_reg_write This is the implementation of regmap_multi_reg_write() There is a new capability 'can_multi_write' that device drivers must set in order to use this multi reg write mode. This replaces the first definition, which just defined the API. Signed-off-by: Anthony Olech Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 2 + drivers/base/regmap/regmap.c | 188 +++++++++++++++++++++++++++++++++++++---- include/linux/regmap.h | 4 + 3 files changed, 178 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 33414b1de201..7d1326985bee 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -134,6 +134,8 @@ struct regmap { /* if set, converts bulk rw to single rw */ bool use_single_rw; + /* if set, the device supports multi write mode */ + bool can_multi_write; struct rb_root range_tree; void *selector_work_buf; /* Scratch buffer used for selector */ diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 35ab7baffcc5..c69bbc06dfbb 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -439,6 +439,7 @@ struct regmap *regmap_init(struct device *dev, else map->reg_stride = 1; map->use_single_rw = config->use_single_rw; + map->can_multi_write = config->can_multi_write; map->dev = dev; map->bus = bus; map->bus_context = bus_context; @@ -1576,41 +1577,196 @@ out: } EXPORT_SYMBOL_GPL(regmap_bulk_write); +/* + * _regmap_raw_multi_reg_write() + * + * the (register,newvalue) pairs in regs have not been formatted, but + * they are all in the same page and have been changed to being page + * relative. The page register has been written if that was neccessary. + */ +static int _regmap_raw_multi_reg_write(struct regmap *map, + const struct reg_default *regs, + size_t num_regs) +{ + int ret; + void *buf; + int i; + u8 *u8; + size_t val_bytes = map->format.val_bytes; + size_t reg_bytes = map->format.reg_bytes; + size_t pad_bytes = map->format.pad_bytes; + size_t pair_size = reg_bytes + pad_bytes + val_bytes; + size_t len = pair_size * num_regs; + + buf = kzalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* We have to linearise by hand. */ + + u8 = buf; + + for (i = 0; i < num_regs; i++) { + int reg = regs[i].reg; + int val = regs[i].def; + trace_regmap_hw_write_start(map->dev, reg, 1); + map->format.format_reg(u8, reg, map->reg_shift); + u8 += reg_bytes + pad_bytes; + map->format.format_val(u8, val, 0); + u8 += val_bytes; + } + u8 = buf; + *u8 |= map->write_flag_mask; + + ret = map->bus->write(map->bus_context, buf, len); + + kfree(buf); + + for (i = 0; i < num_regs; i++) { + int reg = regs[i].reg; + trace_regmap_hw_write_done(map->dev, reg, 1); + } + return ret; +} + +static unsigned int _regmap_register_page(struct regmap *map, + unsigned int reg, + struct regmap_range_node *range) +{ + unsigned int win_page = (reg - range->range_min) / range->window_len; + + return win_page; +} + +static int _regmap_range_multi_paged_reg_write(struct regmap *map, + struct reg_default *regs, + size_t num_regs) +{ + int ret; + int i, n; + struct reg_default *base; + unsigned int this_page; + /* + * the set of registers are not neccessarily in order, but + * since the order of write must be preserved this algorithm + * chops the set each time the page changes + */ + base = regs; + for (i = 0, n = 0; i < num_regs; i++, n++) { + unsigned int reg = regs[i].reg; + struct regmap_range_node *range; + + range = _regmap_range_lookup(map, reg); + if (range) { + unsigned int win_page = _regmap_register_page(map, reg, + range); + + if (i == 0) + this_page = win_page; + if (win_page != this_page) { + this_page = win_page; + ret = _regmap_raw_multi_reg_write(map, base, n); + if (ret != 0) + return ret; + base += n; + n = 0; + } + ret = _regmap_select_page(map, &base[n].reg, range, 1); + if (ret != 0) + return ret; + } + } + if (n > 0) + return _regmap_raw_multi_reg_write(map, base, n); + return 0; +} + static int _regmap_multi_reg_write(struct regmap *map, const struct reg_default *regs, - int num_regs) + size_t num_regs) { - int i, ret; + int i; + int ret; + + if (!map->can_multi_write) { + for (i = 0; i < num_regs; i++) { + ret = _regmap_write(map, regs[i].reg, regs[i].def); + if (ret != 0) + return ret; + } + return 0; + } + + if (!map->format.parse_inplace) + return -EINVAL; + + if (map->writeable_reg) + for (i = 0; i < num_regs; i++) { + int reg = regs[i].reg; + if (!map->writeable_reg(map->dev, reg)) + return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; + } + + if (!map->cache_bypass) { + for (i = 0; i < num_regs; i++) { + unsigned int val = regs[i].def; + unsigned int reg = regs[i].reg; + ret = regcache_write(map, reg, val); + if (ret) { + dev_err(map->dev, + "Error in caching of register: %x ret: %d\n", + reg, ret); + return ret; + } + } + if (map->cache_only) { + map->cache_dirty = true; + return 0; + } + } + + WARN_ON(!map->bus); for (i = 0; i < num_regs; i++) { - if (regs[i].reg % map->reg_stride) - return -EINVAL; - ret = _regmap_write(map, regs[i].reg, regs[i].def); - if (ret != 0) { - dev_err(map->dev, "Failed to write %x = %x: %d\n", - regs[i].reg, regs[i].def, ret); + unsigned int reg = regs[i].reg; + struct regmap_range_node *range; + range = _regmap_range_lookup(map, reg); + if (range) { + size_t len = sizeof(struct reg_default)*num_regs; + struct reg_default *base = kmemdup(regs, len, + GFP_KERNEL); + if (!base) + return -ENOMEM; + ret = _regmap_range_multi_paged_reg_write(map, base, + num_regs); + kfree(base); + return ret; } } - - return 0; + return _regmap_raw_multi_reg_write(map, regs, num_regs); } /* * regmap_multi_reg_write(): Write multiple registers to the device * - * where the set of register are supplied in any order + * where the set of register,value pairs are supplied in any order, + * possibly not all in a single range. * * @map: Register map to write to * @regs: Array of structures containing register,value to be written * @num_regs: Number of registers to write * - * This function is intended to be used for writing a large block of data - * atomically to the device in single transfer for those I2C client devices - * that implement this alternative block write mode. + * The 'normal' block write mode will send ultimately send data on the + * target bus as R,V1,V2,V3,..,Vn where successively higer registers are + * addressed. However, this alternative block multi write mode will send + * the data as R1,V1,R2,V2,..,Rn,Vn on the target bus. The target device + * must of course support the mode. * - * A value of zero will be returned on success, a negative errno will - * be returned in error cases. + * A value of zero will be returned on success, a negative errno will be + * returned in error cases. */ int regmap_multi_reg_write(struct regmap *map, const struct reg_default *regs, int num_regs) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index ca2272fbd014..220aec660064 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -164,6 +164,9 @@ typedef void (*regmap_unlock)(void *); * @use_single_rw: If set, converts the bulk read and write operations into * a series of single read and write operations. This is useful * for device that does not support bulk read and write. + * @can_multi_write: If set, the device supports the multi write mode of bulk + * write operations, if clear multi write requests will be + * split into individual write operations * * @cache_type: The actual cache type. * @reg_defaults_raw: Power on reset values for registers (for use with @@ -215,6 +218,7 @@ struct regmap_config { u8 write_flag_mask; bool use_single_rw; + bool can_multi_write; enum regmap_endian reg_format_endian; enum regmap_endian val_format_endian; -- cgit v1.2.3