Messages in this thread Patch in this message | | | Date | Mon, 1 Jun 2015 10:20:12 +0100 | From | Nariman Poushin <> | Subject | [RFC][PATCH] regmap: Add reg_sequence for use with multi_reg_write / register_patch |
| |
Support write sequences / patches with specified delays (in microseconds) after some (or all) writes. Logically separate reg_default from the new reg_sequence structure (which has an additional delay_us member) as the reg_default tables can run in to the thousands (for modern devices) and the additional memory usage due to an added unsigned int could be large (especially on 64-bit systems).
We treat a delay in a sequence the same as we treat a page change as they are logically similar (you can coalesce all writes within the same page or if none of them require a delay after being written).
Signed-off-by: Nariman Poushin <nariman@opensource.wolfsonmicro.com> ---
I am not sure how best to update all the users of this patch (should it be accepted), should I: - Squash all the updates in to this patch (I suppose the benefit there is that we don't break the kernel build from one patch to the other) - Provide separate patch(es) for the users
Thanks Nariman
drivers/base/regmap/internal.h | 2 +- drivers/base/regmap/regmap.c | 56 ++++++++++++++++++++++++++++++------------ include/linux/regmap.h | 21 +++++++++++++--- 3 files changed, 59 insertions(+), 20 deletions(-)
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index b2b2849..873ddf9 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -136,7 +136,7 @@ struct regmap { /* if set, the HW registers are known to match map->reg_defaults */ bool no_sync_defaults; - struct reg_default *patch; + struct reg_sequence *patch; int patch_regs; /* if set, converts bulk rw to single rw */ diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 6273ff0..75773fd 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -18,6 +18,7 @@ #include <linux/of.h> #include <linux/rbtree.h> #include <linux/sched.h> +#include <linux/delay.h> #define CREATE_TRACE_POINTS #include "trace.h" @@ -47,6 +48,17 @@ static int _regmap_bus_reg_write(void *context, unsigned int reg, static int _regmap_bus_raw_write(void *context, unsigned int reg, unsigned int val); +void regmap_sequence_delay(unsigned int delay_us) +{ + /* For small delays it isn't worth setting up the hrtimers + * so fall back on udelay + */ + if (delay_us < 10) + udelay(delay_us); + else + usleep_range(delay_us, delay_us * 2); +} + bool regmap_reg_in_ranges(unsigned int reg, const struct regmap_range *ranges, unsigned int nranges) @@ -1744,7 +1756,7 @@ EXPORT_SYMBOL_GPL(regmap_bulk_write); * 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, + const struct reg_sequence *regs, size_t num_regs) { int ret; @@ -1801,17 +1813,18 @@ static unsigned int _regmap_register_page(struct regmap *map, } static int _regmap_range_multi_paged_reg_write(struct regmap *map, - struct reg_default *regs, + struct reg_sequence *regs, size_t num_regs) { int ret; int i, n; - struct reg_default *base; + struct reg_sequence *base; unsigned int this_page = 0; /* * 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 + * chops the set each time the page changes. This also applies + * if there is a delay required at any point in the sequence. */ base = regs; for (i = 0, n = 0; i < num_regs; i++, n++) { @@ -1819,17 +1832,21 @@ static int _regmap_range_multi_paged_reg_write(struct regmap *map, struct regmap_range_node *range; range = _regmap_range_lookup(map, reg); - if (range) { + if (range || regs[i].delay_us) { unsigned int win_page = _regmap_register_page(map, reg, range); if (i == 0) this_page = win_page; - if (win_page != this_page) { + if (win_page != this_page || regs[i].delay_us) { this_page = win_page; ret = _regmap_raw_multi_reg_write(map, base, n); if (ret != 0) return ret; + + if (regs[i].delay_us) + regmap_sequence_delay(regs[i].delay_us); + base += n; n = 0; } @@ -1844,7 +1861,7 @@ static int _regmap_range_multi_paged_reg_write(struct regmap *map, } static int _regmap_multi_reg_write(struct regmap *map, - const struct reg_default *regs, + const struct reg_sequence *regs, size_t num_regs) { int i; @@ -1855,6 +1872,9 @@ static int _regmap_multi_reg_write(struct regmap *map, ret = _regmap_write(map, regs[i].reg, regs[i].def); if (ret != 0) return ret; + + if (regs[i].delay_us) + regmap_sequence_delay(regs[i].delay_us); } return 0; } @@ -1894,11 +1914,15 @@ static int _regmap_multi_reg_write(struct regmap *map, for (i = 0; i < num_regs; i++) { unsigned int reg = regs[i].reg; struct regmap_range_node *range; + + /* Coalesce all the writes between a page break or a delay + * in a sequence + */ 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 (range || regs[i].delay_us) { + size_t len = sizeof(struct reg_sequence)*num_regs; + struct reg_sequence *base = kmemdup(regs, len, + GFP_KERNEL); if (!base) return -ENOMEM; ret = _regmap_range_multi_paged_reg_write(map, base, @@ -1930,7 +1954,7 @@ static int _regmap_multi_reg_write(struct regmap *map, * 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 regmap_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs) { int ret; @@ -1963,7 +1987,7 @@ EXPORT_SYMBOL_GPL(regmap_multi_reg_write); * be returned in error cases. */ int regmap_multi_reg_write_bypassed(struct regmap *map, - const struct reg_default *regs, + const struct reg_sequence *regs, int num_regs) { int ret; @@ -2553,10 +2577,10 @@ EXPORT_SYMBOL_GPL(regmap_async_complete); * The caller must ensure that this function cannot be called * concurrently with either itself or regcache_sync(). */ -int regmap_register_patch(struct regmap *map, const struct reg_default *regs, +int regmap_register_patch(struct regmap *map, const struct reg_sequence *regs, int num_regs) { - struct reg_default *p; + struct reg_sequence *p; int ret; bool bypass; @@ -2565,7 +2589,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, return 0; p = krealloc(map->patch, - sizeof(struct reg_default) * (map->patch_regs + num_regs), + sizeof(struct reg_sequence) * (map->patch_regs + num_regs), GFP_KERNEL); if (p) { memcpy(p + map->patch_regs, regs, num_regs * sizeof(*regs)); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 116655d..c3bf366 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -50,6 +50,21 @@ struct reg_default { unsigned int def; }; +/** + * Register / Value pairs for sequences of writes, incorporating an optional + * delay in microseconds. + * + * @reg: Register address. + * @def: Register default value. + * @delay_us: Delay in microseconds + */ + +struct reg_sequence { + unsigned reg; + unsigned def; + unsigned delay_us; +}; + #ifdef CONFIG_REGMAP enum regmap_endian { @@ -410,10 +425,10 @@ 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, const struct reg_default *regs, +int regmap_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs); int regmap_multi_reg_write_bypassed(struct regmap *map, - const struct reg_default *regs, + const struct reg_sequence *regs, int num_regs); int regmap_raw_write_async(struct regmap *map, unsigned int reg, const void *val, size_t val_len); @@ -448,7 +463,7 @@ void regcache_mark_dirty(struct regmap *map); bool regmap_check_range_table(struct regmap *map, unsigned int reg, const struct regmap_access_table *table); -int regmap_register_patch(struct regmap *map, const struct reg_default *regs, +int regmap_register_patch(struct regmap *map, const struct reg_sequence *regs, int num_regs); int regmap_parse_val(struct regmap *map, const void *buf, unsigned int *val); -- 2.1.4
| |