diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-23 17:25:32 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-23 17:25:32 -0700 |
commit | e12bdf0d9265a90664a4767f4ffc20af06807278 (patch) | |
tree | 8ac3ac304d774aab05314fff8723c20628b93f15 | |
parent | 1a13e36a79f25f4cf506258637e96a07ac1451f2 (diff) | |
parent | 8ca006adeffca5c760e86e660018ec9253d53dd3 (diff) | |
download | linux-e12bdf0d9265a90664a4767f4ffc20af06807278.tar.bz2 |
Merge tag 'regmap-v4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap
Pull regmap updates from Mark Brown:
"As well as a few fixes and updates for API changes there's two new
features for the API:
- Better support for handling a reset of the underlying hardware,
marking the register map as needing a resync to the device when we
need to do that automatically
- Support for querying the size and stride of the register map,
allowing higher level frameworks to configure themselves more
readily"
* tag 'regmap-v4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
regmap: Fix possible shift overflow in regmap_field_init()
regmap: Fix regmap_bulk_read in BE mode
regmap: kill off set_irq_flags usage
regmap: irq: Fixed a typo error
regmap: drop unneeded goto
regmap: Introduce regmap_get_reg_stride
regmap: Introduce regmap_get_max_register
regmap: Use regcache_mark_dirty() to indicate power loss or reset
regmap: Add a helper function for regcache sync test
regmap: Constify irq_domain_ops
-rw-r--r-- | drivers/base/regmap/internal.h | 3 | ||||
-rw-r--r-- | drivers/base/regmap/regcache.c | 45 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-irq.c | 11 | ||||
-rw-r--r-- | drivers/base/regmap/regmap.c | 32 | ||||
-rw-r--r-- | include/linux/regmap.h | 14 |
5 files changed, 75 insertions, 30 deletions
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index a13587b5c2be..b2b2849fc6d3 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -131,7 +131,10 @@ struct regmap { struct reg_default *reg_defaults; const void *reg_defaults_raw; void *cache; + /* if set, the cache contains newer data than the HW */ u32 cache_dirty; + /* if set, the HW registers are known to match map->reg_defaults */ + bool no_sync_defaults; struct reg_default *patch; int patch_regs; diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 7eb7b3b98794..b9862d741a56 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -249,6 +249,22 @@ int regcache_write(struct regmap *map, return 0; } +static bool regcache_reg_needs_sync(struct regmap *map, unsigned int reg, + unsigned int val) +{ + int ret; + + /* If we don't know the chip just got reset, then sync everything. */ + if (!map->no_sync_defaults) + return true; + + /* Is this the hardware default? If so skip. */ + ret = regcache_lookup_reg(map, reg); + if (ret >= 0 && val == map->reg_defaults[ret].def) + return false; + return true; +} + static int regcache_default_sync(struct regmap *map, unsigned int min, unsigned int max) { @@ -266,9 +282,7 @@ static int regcache_default_sync(struct regmap *map, unsigned int min, if (ret) return ret; - /* Is this the hardware default? If so skip. */ - ret = regcache_lookup_reg(map, reg); - if (ret >= 0 && val == map->reg_defaults[ret].def) + if (!regcache_reg_needs_sync(map, reg, val)) continue; map->cache_bypass = 1; @@ -342,6 +356,7 @@ out: /* Restore the bypass state */ map->async = false; map->cache_bypass = bypass; + map->no_sync_defaults = false; map->unlock(map->lock_arg); regmap_async_complete(map); @@ -397,6 +412,7 @@ out: /* Restore the bypass state */ map->cache_bypass = bypass; map->async = false; + map->no_sync_defaults = false; map->unlock(map->lock_arg); regmap_async_complete(map); @@ -461,18 +477,23 @@ void regcache_cache_only(struct regmap *map, bool enable) EXPORT_SYMBOL_GPL(regcache_cache_only); /** - * regcache_mark_dirty: Mark the register cache as dirty + * regcache_mark_dirty: Indicate that HW registers were reset to default values * * @map: map to mark * - * Mark the register cache as dirty, for example due to the device - * having been powered down for suspend. If the cache is not marked - * as dirty then the cache sync will be suppressed. + * Inform regcache that the device has been powered down or reset, so that + * on resume, regcache_sync() knows to write out all non-default values + * stored in the cache. + * + * If this function is not called, regcache_sync() will assume that + * the hardware state still matches the cache state, modulo any writes that + * happened when cache_only was true. */ void regcache_mark_dirty(struct regmap *map) { map->lock(map->lock_arg); map->cache_dirty = true; + map->no_sync_defaults = true; map->unlock(map->lock_arg); } EXPORT_SYMBOL_GPL(regcache_mark_dirty); @@ -613,10 +634,7 @@ static int regcache_sync_block_single(struct regmap *map, void *block, continue; val = regcache_get_val(map, block, i); - - /* Is this the hardware default? If so skip. */ - ret = regcache_lookup_reg(map, regtmp); - if (ret >= 0 && val == map->reg_defaults[ret].def) + if (!regcache_reg_needs_sync(map, regtmp, val)) continue; map->cache_bypass = 1; @@ -688,10 +706,7 @@ static int regcache_sync_block_raw(struct regmap *map, void *block, } val = regcache_get_val(map, block, i); - - /* Is this the hardware default? If so skip. */ - ret = regcache_lookup_reg(map, regtmp); - if (ret >= 0 && val == map->reg_defaults[ret].def) { + if (!regcache_reg_needs_sync(map, regtmp, val)) { ret = regcache_sync_block_raw_flush(map, &data, base, regtmp); if (ret != 0) diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index a6c3f75b4b01..2597600a5d26 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -109,7 +109,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) if (!d->chip->init_ack_masked) continue; /* - * Ack all the masked interrupts uncondictionly, + * Ack all the masked interrupts unconditionally, * OR if there is masked interrupt which hasn't been Acked, * it'll be ignored in irq handler, then may introduce irq storm */ @@ -306,19 +306,12 @@ static int regmap_irq_map(struct irq_domain *h, unsigned int virq, irq_set_chip_data(virq, data); irq_set_chip(virq, &data->irq_chip); irq_set_nested_thread(virq, 1); - - /* ARM needs us to explicitly flag the IRQ as valid - * and will set them noprobe when we do so. */ -#ifdef CONFIG_ARM - set_irq_flags(virq, IRQF_VALID); -#else irq_set_noprobe(virq); -#endif return 0; } -static struct irq_domain_ops regmap_domain_ops = { +static const struct irq_domain_ops regmap_domain_ops = { .map = regmap_irq_map, .xlate = irq_domain_xlate_twocell, }; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 6273ff072f3e..7111d04f2621 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -945,11 +945,10 @@ EXPORT_SYMBOL_GPL(devm_regmap_init); static void regmap_field_init(struct regmap_field *rm_field, struct regmap *regmap, struct reg_field reg_field) { - int field_bits = reg_field.msb - reg_field.lsb + 1; rm_field->regmap = regmap; rm_field->reg = reg_field.reg; rm_field->shift = reg_field.lsb; - rm_field->mask = ((BIT(field_bits) - 1) << reg_field.lsb); + rm_field->mask = GENMASK(reg_field.msb, reg_field.lsb); rm_field->id_size = reg_field.id_size; rm_field->id_offset = reg_field.id_offset; } @@ -2318,7 +2317,7 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, &ival); if (ret != 0) return ret; - memcpy(val + (i * val_bytes), &ival, val_bytes); + map->format.format_val(val + (i * val_bytes), ival, 0); } } @@ -2583,10 +2582,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, map->async = true; ret = _regmap_multi_reg_write(map, regs, num_regs); - if (ret != 0) - goto out; -out: map->async = false; map->cache_bypass = bypass; @@ -2613,6 +2609,30 @@ int regmap_get_val_bytes(struct regmap *map) } EXPORT_SYMBOL_GPL(regmap_get_val_bytes); +/** + * regmap_get_max_register(): Report the max register value + * + * Report the max register value, mainly intended to for use by + * generic infrastructure built on top of regmap. + */ +int regmap_get_max_register(struct regmap *map) +{ + return map->max_register ? map->max_register : -EINVAL; +} +EXPORT_SYMBOL_GPL(regmap_get_max_register); + +/** + * regmap_get_reg_stride(): Report the register address stride + * + * Report the register address stride, mainly intended to for use by + * generic infrastructure built on top of regmap. + */ +int regmap_get_reg_stride(struct regmap *map) +{ + return map->reg_stride; +} +EXPORT_SYMBOL_GPL(regmap_get_reg_stride); + int regmap_parse_val(struct regmap *map, const void *buf, unsigned int *val) { diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 116655d92269..59c55ea0f0b5 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -433,6 +433,8 @@ int regmap_update_bits_check_async(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); int regmap_get_val_bytes(struct regmap *map); +int regmap_get_max_register(struct regmap *map); +int regmap_get_reg_stride(struct regmap *map); int regmap_async_complete(struct regmap *map); bool regmap_can_raw_write(struct regmap *map); @@ -676,6 +678,18 @@ static inline int regmap_get_val_bytes(struct regmap *map) return -EINVAL; } +static inline int regmap_get_max_register(struct regmap *map) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + +static inline int regmap_get_reg_stride(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"); |