diff options
-rw-r--r-- | drivers/base/regmap/Makefile | 2 | ||||
-rw-r--r-- | drivers/base/regmap/internal.h | 52 | ||||
-rw-r--r-- | drivers/base/regmap/regcache.c | 304 | ||||
-rw-r--r-- | include/linux/regmap.h | 15 |
4 files changed, 371 insertions, 2 deletions
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index 057c13f66a67..2e103ea9a3a0 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -1,4 +1,4 @@ -obj-$(CONFIG_REGMAP) += regmap.o +obj-$(CONFIG_REGMAP) += regmap.o regcache.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 a98493cde5c3..615f5581d5db 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -17,6 +17,7 @@ #include <linux/fs.h> struct regmap; +struct regcache_ops; struct regmap_format { size_t buf_size; @@ -49,6 +50,40 @@ struct regmap { u8 read_flag_mask; u8 write_flag_mask; + + /* regcache specific members */ + const struct regcache_ops *cache_ops; + enum regcache_type cache_type; + + /* number of bytes in reg_defaults_raw */ + unsigned int cache_size_raw; + /* number of bytes per word in reg_defaults_raw */ + unsigned int cache_word_size; + /* number of entries in reg_defaults */ + unsigned int num_reg_defaults; + /* number of entries in reg_defaults_raw */ + unsigned int num_reg_defaults_raw; + + /* if set, only the cache is modified not the HW */ + unsigned int cache_only:1; + /* if set, only the HW is modified not the cache */ + unsigned int cache_bypass:1; + /* if set, remember to free reg_defaults_raw */ + unsigned int cache_free:1; + + struct reg_default *reg_defaults; + const void *reg_defaults_raw; + void *cache; +}; + +struct regcache_ops { + const char *name; + enum regcache_type type; + int (*init)(struct regmap *map); + 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); }; bool regmap_writeable(struct regmap *map, unsigned int reg); @@ -66,4 +101,21 @@ static inline void regmap_debugfs_init(struct regmap *map) { } static inline void regmap_debugfs_exit(struct regmap *map) { } #endif +/* regcache core declarations */ +int regcache_init(struct regmap *map); +void regcache_exit(struct regmap *map); +int regcache_read(struct regmap *map, + unsigned int reg, unsigned int *value); +int regcache_write(struct regmap *map, + unsigned int reg, unsigned int value); +int regcache_sync(struct regmap *map); + +unsigned int regcache_get_val(const void *base, unsigned int idx, + unsigned int word_size); +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); + #endif diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c new file mode 100644 index 000000000000..9575e4c5f34a --- /dev/null +++ b/drivers/base/regmap/regcache.c @@ -0,0 +1,304 @@ +/* + * Register cache access API + * + * 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 <trace/events/regmap.h> + +#include "internal.h" + +static const struct regcache_ops *cache_types[] = { +}; + +static int regcache_hw_init(struct regmap *map) +{ + int i, j; + int ret; + int count; + unsigned int val; + void *tmp_buf; + + if (!map->num_reg_defaults_raw) + return -EINVAL; + + if (!map->reg_defaults_raw) { + dev_warn(map->dev, "No cache defaults, reading back from HW\n"); + 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); + if (ret < 0) { + kfree(tmp_buf); + return ret; + } + map->reg_defaults_raw = tmp_buf; + map->cache_free = 1; + } + + /* calculate the size of reg_defaults */ + for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) { + val = regcache_get_val(map->reg_defaults_raw, + i, map->cache_word_size); + if (!val) + continue; + count++; + } + + map->reg_defaults = kmalloc(count * sizeof(struct reg_default), + GFP_KERNEL); + if (!map->reg_defaults) + return -ENOMEM; + + /* fill the reg_defaults */ + map->num_reg_defaults = count; + for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) { + val = regcache_get_val(map->reg_defaults_raw, + i, map->cache_word_size); + if (!val) + continue; + map->reg_defaults[j].reg = i; + map->reg_defaults[j].def = val; + j++; + } + + return 0; +} + +int regcache_init(struct regmap *map) +{ + int ret; + int i; + void *tmp_buf; + + if (map->cache_type == REGCACHE_NONE) + return 0; + + for (i = 0; i < ARRAY_SIZE(cache_types); i++) + if (cache_types[i]->type == map->cache_type) + break; + + if (i == ARRAY_SIZE(cache_types)) { + dev_err(map->dev, "Could not match compress type: %d\n", + map->cache_type); + return -EINVAL; + } + + map->cache = NULL; + map->cache_ops = cache_types[i]; + + if (!map->cache_ops->read || + !map->cache_ops->write || + !map->cache_ops->name) + return -EINVAL; + + /* We still need to ensure that the reg_defaults + * won't vanish from under us. We'll need to make + * a copy of it. + */ + if (map->reg_defaults) { + if (!map->num_reg_defaults) + return -EINVAL; + tmp_buf = kmemdup(map->reg_defaults, map->num_reg_defaults * + sizeof(struct reg_default), GFP_KERNEL); + if (!tmp_buf) + return -ENOMEM; + map->reg_defaults = tmp_buf; + } else { + /* Some devices such as PMIC's don't have cache defaults, + * we cope with this by reading back the HW registers and + * crafting the cache defaults by hand. + */ + ret = regcache_hw_init(map); + if (ret < 0) + return ret; + } + + if (!map->max_register) + map->max_register = map->num_reg_defaults_raw; + + if (map->cache_ops->init) { + dev_dbg(map->dev, "Initializing %s cache\n", + map->cache_ops->name); + return map->cache_ops->init(map); + } + return 0; +} + +void regcache_exit(struct regmap *map) +{ + if (map->cache_type == REGCACHE_NONE) + return; + + BUG_ON(!map->cache_ops); + + kfree(map->reg_defaults); + if (map->cache_free) + kfree(map->reg_defaults_raw); + + if (map->cache_ops->exit) { + dev_dbg(map->dev, "Destroying %s cache\n", + map->cache_ops->name); + map->cache_ops->exit(map); + } +} + +/** + * regcache_read: Fetch the value of a given register from the cache. + * + * @map: map to configure. + * @reg: The register index. + * @value: The value to be returned. + * + * Return a negative value on failure, 0 on success. + */ +int regcache_read(struct regmap *map, + unsigned int reg, unsigned int *value) +{ + 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)) + return map->cache_ops->read(map, reg, value); + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(regcache_read); + +/** + * regcache_write: Set the value of a given register in the cache. + * + * @map: map to configure. + * @reg: The register index. + * @value: The new register value. + * + * Return a negative value on failure, 0 on success. + */ +int regcache_write(struct regmap *map, + unsigned int reg, unsigned int value) +{ + if (map->cache_type == REGCACHE_NONE) + return 0; + + BUG_ON(!map->cache_ops); + + if (!regmap_writeable(map, reg)) + return -EIO; + + if (!regmap_volatile(map, reg)) + return map->cache_ops->write(map, reg, value); + + return 0; +} +EXPORT_SYMBOL_GPL(regcache_write); + +/** + * regcache_sync: Sync the register cache with the hardware. + * + * @map: map to configure. + * + * Any registers that should not be synced should be marked as + * volatile. In general drivers can choose not to use the provided + * syncing functionality if they so require. + * + * Return a negative value on failure, 0 on success. + */ +int regcache_sync(struct regmap *map) +{ + BUG_ON(!map->cache_ops); + + if (map->cache_ops->sync) { + dev_dbg(map->dev, "Syncing %s cache\n", + map->cache_ops->name); + return map->cache_ops->sync(map); + } + return 0; +} +EXPORT_SYMBOL_GPL(regcache_sync); + +bool regcache_set_val(void *base, unsigned int idx, + unsigned int val, unsigned int word_size) +{ + switch (word_size) { + case 1: { + u8 *cache = base; + if (cache[idx] == val) + return true; + cache[idx] = val; + break; + } + case 2: { + u16 *cache = base; + if (cache[idx] == val) + return true; + cache[idx] = val; + break; + } + default: + BUG(); + } + /* unreachable */ + return false; +} + +unsigned int regcache_get_val(const void *base, unsigned int idx, + unsigned int word_size) +{ + if (!base) + return -EINVAL; + + switch (word_size) { + case 1: { + const u8 *cache = base; + return cache[idx]; + } + case 2: { + const u16 *cache = base; + return cache[idx]; + } + default: + BUG(); + } + /* unreachable */ + return -1; +} + +int regcache_lookup_reg(struct regmap *map, unsigned int reg) +{ + unsigned int i; + + for (i = 0; i < map->num_reg_defaults; i++) + if (map->reg_defaults[i].reg == reg) + return i; + return -1; +} + +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; + return 0; +} diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 18d4afaac166..9d8029449292 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -20,6 +20,11 @@ struct i2c_client; struct spi_device; +/* An enum of all the supported cache types */ +enum regcache_type { + REGCACHE_NONE, +}; + /** * Default value for a register. We use an array of structs rather * than a simple array as many modern devices have very sparse @@ -59,6 +64,11 @@ struct reg_default { * @write_flag_mask: Mask to be set in the top byte of the register when doing * a write. If both read_flag_mask and write_flag_mask are * empty the regmap_bus default masks are used. + * + * @cache_type: The actual cache type. + * @reg_defaults_raw: Power on reset values for registers (for use with + * register cache support). + * @num_reg_defaults_raw: Number of elements in reg_defaults_raw. */ struct regmap_config { int reg_bits; @@ -71,7 +81,10 @@ struct regmap_config { unsigned int max_register; struct reg_default *reg_defaults; - int num_reg_defaults; + unsigned int num_reg_defaults; + enum regcache_type cache_type; + const void *reg_defaults_raw; + unsigned int num_reg_defaults_raw; u8 read_flag_mask; u8 write_flag_mask; |