diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/regmap/Makefile | 3 | ||||
-rw-r--r-- | drivers/base/regmap/internal.h | 6 | ||||
-rw-r--r-- | drivers/base/regmap/regcache-indexed.c | 64 | ||||
-rw-r--r-- | drivers/base/regmap/regcache-lzo.c | 21 | ||||
-rw-r--r-- | drivers/base/regmap/regcache-rbtree.c | 61 | ||||
-rw-r--r-- | drivers/base/regmap/regcache.c | 87 | ||||
-rw-r--r-- | drivers/base/regmap/regmap.c | 179 |
7 files changed, 268 insertions, 153 deletions
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index ce2d18a6465..defd57963c8 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -1,4 +1,5 @@ -obj-$(CONFIG_REGMAP) += regmap.o regcache.o regcache-indexed.o regcache-rbtree.o regcache-lzo.o +obj-$(CONFIG_REGMAP) += regmap.o regcache.o +obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 348ff02eb93..1a02b7537c8 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -74,6 +74,7 @@ struct regmap { struct reg_default *reg_defaults; const void *reg_defaults_raw; void *cache; + bool cache_dirty; }; struct regcache_ops { @@ -105,7 +106,7 @@ static inline void regmap_debugfs_exit(struct regmap *map) { } #endif /* regcache core declarations */ -int regcache_init(struct regmap *map); +int regcache_init(struct regmap *map, const struct regmap_config *config); void regcache_exit(struct regmap *map); int regcache_read(struct regmap *map, unsigned int reg, unsigned int *value); @@ -118,10 +119,7 @@ unsigned int regcache_get_val(const void *base, unsigned int idx, bool regcache_set_val(void *base, unsigned int idx, unsigned int val, unsigned int word_size); int regcache_lookup_reg(struct regmap *map, unsigned int reg); -int regcache_insert_reg(struct regmap *map, unsigned int reg, - unsigned int val); -extern struct regcache_ops regcache_indexed_ops; extern struct regcache_ops regcache_rbtree_ops; extern struct regcache_ops regcache_lzo_ops; diff --git a/drivers/base/regmap/regcache-indexed.c b/drivers/base/regmap/regcache-indexed.c deleted file mode 100644 index 507731ad8ec..00000000000 --- a/drivers/base/regmap/regcache-indexed.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Register cache access API - indexed caching support - * - * Copyright 2011 Wolfson Microelectronics plc - * - * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/slab.h> - -#include "internal.h" - -static int regcache_indexed_read(struct regmap *map, unsigned int reg, - unsigned int *value) -{ - int ret; - - ret = regcache_lookup_reg(map, reg); - if (ret >= 0) - *value = map->reg_defaults[ret].def; - - return ret; -} - -static int regcache_indexed_write(struct regmap *map, unsigned int reg, - unsigned int value) -{ - int ret; - - ret = regcache_lookup_reg(map, reg); - if (ret < 0) - return regcache_insert_reg(map, reg, value); - map->reg_defaults[ret].def = value; - return 0; -} - -static int regcache_indexed_sync(struct regmap *map) -{ - unsigned int i; - int ret; - - for (i = 0; i < map->num_reg_defaults; i++) { - ret = _regmap_write(map, map->reg_defaults[i].reg, - map->reg_defaults[i].def); - if (ret < 0) - return ret; - dev_dbg(map->dev, "Synced register %#x, value %#x\n", - map->reg_defaults[i].reg, - map->reg_defaults[i].def); - } - return 0; -} - -struct regcache_ops regcache_indexed_ops = { - .type = REGCACHE_INDEXED, - .name = "indexed", - .read = regcache_indexed_read, - .write = regcache_indexed_write, - .sync = regcache_indexed_sync -}; diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index 066aeece362..b7d16143ede 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -15,6 +15,8 @@ #include "internal.h" +static int regcache_lzo_exit(struct regmap *map); + struct regcache_lzo_ctx { void *wmem; void *dst; @@ -27,7 +29,7 @@ struct regcache_lzo_ctx { }; #define LZO_BLOCK_NUM 8 -static int regcache_lzo_block_count(void) +static int regcache_lzo_block_count(struct regmap *map) { return LZO_BLOCK_NUM; } @@ -106,19 +108,22 @@ static inline int regcache_lzo_get_blkindex(struct regmap *map, unsigned int reg) { return (reg * map->cache_word_size) / - DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count()); + DIV_ROUND_UP(map->cache_size_raw, + regcache_lzo_block_count(map)); } static inline int regcache_lzo_get_blkpos(struct regmap *map, unsigned int reg) { - return reg % (DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count()) / + return reg % (DIV_ROUND_UP(map->cache_size_raw, + regcache_lzo_block_count(map)) / map->cache_word_size); } static inline int regcache_lzo_get_blksize(struct regmap *map) { - return DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count()); + return DIV_ROUND_UP(map->cache_size_raw, + regcache_lzo_block_count(map)); } static int regcache_lzo_init(struct regmap *map) @@ -131,7 +136,7 @@ static int regcache_lzo_init(struct regmap *map) ret = 0; - blkcount = regcache_lzo_block_count(); + blkcount = regcache_lzo_block_count(map); map->cache = kzalloc(blkcount * sizeof *lzo_blocks, GFP_KERNEL); if (!map->cache) @@ -190,7 +195,7 @@ static int regcache_lzo_init(struct regmap *map) return 0; err: - regcache_exit(map); + regcache_lzo_exit(map); return ret; } @@ -203,7 +208,7 @@ static int regcache_lzo_exit(struct regmap *map) if (!lzo_blocks) return 0; - blkcount = regcache_lzo_block_count(); + blkcount = regcache_lzo_block_count(map); /* * the pointer to the bitmap used for syncing the cache * is shared amongst all lzo_blocks. Ensure it is freed @@ -351,7 +356,7 @@ static int regcache_lzo_sync(struct regmap *map) } struct regcache_ops regcache_lzo_ops = { - .type = REGCACHE_LZO, + .type = REGCACHE_COMPRESSED, .name = "lzo", .init = regcache_lzo_init, .exit = regcache_lzo_exit, diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index e31498499b0..32620c4f168 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -11,12 +11,15 @@ */ #include <linux/slab.h> +#include <linux/debugfs.h> #include <linux/rbtree.h> +#include <linux/seq_file.h> #include "internal.h" static int regcache_rbtree_write(struct regmap *map, unsigned int reg, unsigned int value); +static int regcache_rbtree_exit(struct regmap *map); struct regcache_rbtree_node { /* the actual rbtree node holding this block */ @@ -124,6 +127,60 @@ static int regcache_rbtree_insert(struct rb_root *root, return 1; } +#ifdef CONFIG_DEBUG_FS +static int rbtree_show(struct seq_file *s, void *ignored) +{ + struct regmap *map = s->private; + struct regcache_rbtree_ctx *rbtree_ctx = map->cache; + struct regcache_rbtree_node *n; + struct rb_node *node; + unsigned int base, top; + int nodes = 0; + int registers = 0; + + mutex_lock(&map->lock); + + for (node = rb_first(&rbtree_ctx->root); node != NULL; + node = rb_next(node)) { + n = container_of(node, struct regcache_rbtree_node, node); + + regcache_rbtree_get_base_top_reg(n, &base, &top); + seq_printf(s, "%x-%x (%d)\n", base, top, top - base + 1); + + nodes++; + registers += top - base + 1; + } + + seq_printf(s, "%d nodes, %d registers, average %d registers\n", + nodes, registers, registers / nodes); + + mutex_unlock(&map->lock); + + return 0; +} + +static int rbtree_open(struct inode *inode, struct file *file) +{ + return single_open(file, rbtree_show, inode->i_private); +} + +static const struct file_operations rbtree_fops = { + .open = rbtree_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void rbtree_debugfs_init(struct regmap *map) +{ + debugfs_create_file("rbtree", 0400, map->debugfs, map, &rbtree_fops); +} +#else +static void rbtree_debugfs_init(struct regmap *map) +{ +} +#endif + static int regcache_rbtree_init(struct regmap *map) { struct regcache_rbtree_ctx *rbtree_ctx; @@ -146,10 +203,12 @@ static int regcache_rbtree_init(struct regmap *map) goto err; } + rbtree_debugfs_init(map); + return 0; err: - regcache_exit(map); + regcache_rbtree_exit(map); return ret; } diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 666f6f5011d..1ead66186b7 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -19,7 +19,6 @@ #include "internal.h" static const struct regcache_ops *cache_types[] = { - ®cache_indexed_ops, ®cache_rbtree_ops, ®cache_lzo_ops, }; @@ -61,8 +60,10 @@ static int regcache_hw_init(struct regmap *map) map->reg_defaults = kmalloc(count * sizeof(struct reg_default), GFP_KERNEL); - if (!map->reg_defaults) - return -ENOMEM; + if (!map->reg_defaults) { + ret = -ENOMEM; + goto err_free; + } /* fill the reg_defaults */ map->num_reg_defaults = count; @@ -77,9 +78,15 @@ static int regcache_hw_init(struct regmap *map) } return 0; + +err_free: + if (map->cache_free) + kfree(map->reg_defaults_raw); + + return ret; } -int regcache_init(struct regmap *map) +int regcache_init(struct regmap *map, const struct regmap_config *config) { int ret; int i; @@ -100,6 +107,12 @@ int regcache_init(struct regmap *map) return -EINVAL; } + map->num_reg_defaults = config->num_reg_defaults; + map->num_reg_defaults_raw = config->num_reg_defaults_raw; + map->reg_defaults_raw = config->reg_defaults_raw; + map->cache_word_size = DIV_ROUND_UP(config->val_bits, 8); + map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw; + map->cache = NULL; map->cache_ops = cache_types[i]; @@ -112,10 +125,10 @@ int regcache_init(struct regmap *map) * won't vanish from under us. We'll need to make * a copy of it. */ - if (map->reg_defaults) { + if (config->reg_defaults) { if (!map->num_reg_defaults) return -EINVAL; - tmp_buf = kmemdup(map->reg_defaults, map->num_reg_defaults * + tmp_buf = kmemdup(config->reg_defaults, map->num_reg_defaults * sizeof(struct reg_default), GFP_KERNEL); if (!tmp_buf) return -ENOMEM; @@ -136,9 +149,18 @@ int regcache_init(struct regmap *map) if (map->cache_ops->init) { dev_dbg(map->dev, "Initializing %s cache\n", map->cache_ops->name); - return map->cache_ops->init(map); + ret = map->cache_ops->init(map); + if (ret) + goto err_free; } return 0; + +err_free: + kfree(map->reg_defaults); + if (map->cache_free) + kfree(map->reg_defaults_raw); + + return ret; } void regcache_exit(struct regmap *map) @@ -171,16 +193,21 @@ void regcache_exit(struct regmap *map) int regcache_read(struct regmap *map, unsigned int reg, unsigned int *value) { + int ret; + if (map->cache_type == REGCACHE_NONE) return -ENOSYS; BUG_ON(!map->cache_ops); - if (!regmap_readable(map, reg)) - return -EIO; + if (!regmap_volatile(map, reg)) { + ret = map->cache_ops->read(map, reg, value); - if (!regmap_volatile(map, reg)) - return map->cache_ops->read(map, reg, value); + if (ret == 0) + trace_regmap_reg_read_cache(map->dev, reg, *value); + + return ret; + } return -EINVAL; } @@ -241,6 +268,8 @@ int regcache_sync(struct regmap *map) map->cache_ops->name); name = map->cache_ops->name; trace_regcache_sync(map->dev, name, "start"); + if (!map->cache_dirty) + goto out; if (map->cache_ops->sync) { ret = map->cache_ops->sync(map); } else { @@ -291,6 +320,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 + * + * @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. + */ +void regcache_mark_dirty(struct regmap *map) +{ + mutex_lock(&map->lock); + map->cache_dirty = true; + mutex_unlock(&map->lock); +} +EXPORT_SYMBOL_GPL(regcache_mark_dirty); + +/** * regcache_cache_bypass: Put a register map into cache bypass mode * * @map: map to configure @@ -381,22 +427,3 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg) else return -ENOENT; } - -int regcache_insert_reg(struct regmap *map, unsigned int reg, - unsigned int val) -{ - void *tmp; - - tmp = krealloc(map->reg_defaults, - (map->num_reg_defaults + 1) * sizeof(struct reg_default), - GFP_KERNEL); - if (!tmp) - return -ENOMEM; - map->reg_defaults = tmp; - map->num_reg_defaults++; - map->reg_defaults[map->num_reg_defaults - 1].reg = reg; - map->reg_defaults[map->num_reg_defaults - 1].def = val; - sort(map->reg_defaults, map->num_reg_defaults, - sizeof(struct reg_default), regcache_default_cmp, NULL); - return 0; -} diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index bf441db1ee9..be10a4ff660 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -64,6 +64,18 @@ bool regmap_precious(struct regmap *map, unsigned int reg) return false; } +static bool regmap_volatile_range(struct regmap *map, unsigned int reg, + unsigned int num) +{ + unsigned int i; + + for (i = 0; i < num; i++) + if (!regmap_volatile(map, reg + i)) + return false; + + return true; +} + static void regmap_format_4_12_write(struct regmap *map, unsigned int reg, unsigned int val) { @@ -78,6 +90,16 @@ static void regmap_format_7_9_write(struct regmap *map, *out = cpu_to_be16((reg << 9) | val); } +static void regmap_format_10_14_write(struct regmap *map, + unsigned int reg, unsigned int val) +{ + u8 *out = map->work_buf; + + out[2] = val; + out[1] = (val >> 8) | (reg << 6); + out[0] = reg >> 2; +} + static void regmap_format_8(void *buf, unsigned int val) { u8 *b = buf; @@ -127,7 +149,7 @@ struct regmap *regmap_init(struct device *dev, int ret = -EINVAL; if (!bus || !config) - return NULL; + goto err; map = kzalloc(sizeof(*map), GFP_KERNEL); if (map == NULL) { @@ -147,12 +169,6 @@ struct regmap *regmap_init(struct device *dev, map->volatile_reg = config->volatile_reg; map->precious_reg = config->precious_reg; map->cache_type = config->cache_type; - map->reg_defaults = config->reg_defaults; - map->num_reg_defaults = config->num_reg_defaults; - map->num_reg_defaults_raw = config->num_reg_defaults_raw; - map->reg_defaults_raw = config->reg_defaults_raw; - map->cache_size_raw = (config->val_bits / 8) * config->num_reg_defaults_raw; - map->cache_word_size = config->val_bits / 8; if (config->read_flag_mask || config->write_flag_mask) { map->read_flag_mask = config->read_flag_mask; @@ -182,6 +198,16 @@ struct regmap *regmap_init(struct device *dev, } break; + case 10: + switch (config->val_bits) { + case 14: + map->format.format_write = regmap_format_10_14_write; + break; + default: + goto err_map; + } + break; + case 8: map->format.format_reg = regmap_format_8; break; @@ -215,14 +241,16 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } - ret = regcache_init(map); - if (ret < 0) - goto err_map; - regmap_debugfs_init(map); + ret = regcache_init(map, config); + if (ret < 0) + goto err_free_workbuf; + return map; +err_free_workbuf: + kfree(map->work_buf); err_map: kfree(map); err: @@ -231,6 +259,39 @@ err: EXPORT_SYMBOL_GPL(regmap_init); /** + * regmap_reinit_cache(): Reinitialise the current register cache + * + * @map: Register map to operate on. + * @config: New configuration. Only the cache data will be used. + * + * Discard any existing register cache for the map and initialize a + * new cache. This can be used to restore the cache to defaults or to + * update the cache configuration to reflect runtime discovery of the + * hardware. + */ +int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) +{ + int ret; + + mutex_lock(&map->lock); + + regcache_exit(map); + + map->max_register = config->max_register; + map->writeable_reg = config->writeable_reg; + map->readable_reg = config->readable_reg; + map->volatile_reg = config->volatile_reg; + map->precious_reg = config->precious_reg; + map->cache_type = config->cache_type; + + ret = regcache_init(map, config); + + mutex_unlock(&map->lock); + + return ret; +} + +/** * regmap_exit(): Free a previously allocated register map */ void regmap_exit(struct regmap *map) @@ -306,8 +367,10 @@ int _regmap_write(struct regmap *map, unsigned int reg, ret = regcache_write(map, reg, val); if (ret != 0) return ret; - if (map->cache_only) + if (map->cache_only) { + map->cache_dirty = true; return 0; + } } trace_regmap_reg_write(map->dev, reg, val); @@ -375,9 +438,11 @@ 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(map->cache_type != REGCACHE_NONE); + WARN_ON(!regmap_volatile_range(map, reg, val_count) && + map->cache_type != REGCACHE_NONE); mutex_lock(&map->lock); @@ -422,15 +487,15 @@ static int _regmap_read(struct regmap *map, unsigned int reg, { int ret; - if (!map->format.parse_val) - return -EINVAL; - if (!map->cache_bypass) { ret = regcache_read(map, reg, val); if (ret == 0) return 0; } + if (!map->format.parse_val) + return -EINVAL; + if (map->cache_only) return -EBUSY; @@ -481,15 +546,11 @@ 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; - int i; - bool vol = true; - for (i = 0; i < val_len / map->format.val_bytes; i++) - if (!regmap_volatile(map, reg + i)) - vol = false; - - WARN_ON(!vol && map->cache_type != REGCACHE_NONE); + WARN_ON(!regmap_volatile_range(map, reg, val_count) && + map->cache_type != REGCACHE_NONE); mutex_lock(&map->lock); @@ -517,16 +578,11 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, { int ret, i; size_t val_bytes = map->format.val_bytes; - bool vol = true; + bool vol = regmap_volatile_range(map, reg, val_count); if (!map->format.parse_val) return -EINVAL; - /* Is this a block of volatile registers? */ - for (i = 0; i < val_count; i++) - if (!regmap_volatile(map, reg + i)) - vol = false; - if (vol || map->cache_type == REGCACHE_NONE) { ret = regmap_raw_read(map, reg, val, val_bytes * val_count); if (ret != 0) @@ -546,40 +602,73 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, } EXPORT_SYMBOL_GPL(regmap_bulk_read); -/** - * remap_update_bits: Perform a read/modify/write cycle on the register map - * - * @map: Register map to update - * @reg: Register to update - * @mask: Bitmask to change - * @val: New value for bitmask - * - * Returns zero for success, a negative number on error. - */ -int regmap_update_bits(struct regmap *map, unsigned int reg, - unsigned int mask, unsigned int val) +static int _regmap_update_bits(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val, + bool *change) { int ret; - unsigned int tmp; + unsigned int tmp, orig; mutex_lock(&map->lock); - ret = _regmap_read(map, reg, &tmp); + ret = _regmap_read(map, reg, &orig); if (ret != 0) goto out; - tmp &= ~mask; + tmp = orig & ~mask; tmp |= val & mask; - ret = _regmap_write(map, reg, tmp); + if (tmp != orig) { + ret = _regmap_write(map, reg, tmp); + *change = true; + } else { + *change = false; + } out: mutex_unlock(&map->lock); return ret; } + +/** + * regmap_update_bits: Perform a read/modify/write cycle on the register map + * + * @map: Register map to update + * @reg: Register to update + * @mask: Bitmask to change + * @val: New value for bitmask + * + * Returns zero for success, a negative number on error. + */ +int regmap_update_bits(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val) +{ + bool change; + return _regmap_update_bits(map, reg, mask, val, &change); +} EXPORT_SYMBOL_GPL(regmap_update_bits); +/** + * regmap_update_bits_check: Perform a read/modify/write cycle on the + * register map and report if updated + * + * @map: Register map to update + * @reg: Register to update + * @mask: Bitmask to change + * @val: New value for bitmask + * @change: Boolean indicating if a write was done + * + * Returns zero for success, a negative number on error. + */ +int regmap_update_bits_check(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val, + bool *change) +{ + return _regmap_update_bits(map, reg, mask, val, change); +} +EXPORT_SYMBOL_GPL(regmap_update_bits_check); + static int __init regmap_initcall(void) { regmap_debugfs_initcall(); |