diff options
Diffstat (limited to 'drivers/base/regmap/regcache.c')
| -rw-r--r-- | drivers/base/regmap/regcache.c | 135 |
1 files changed, 89 insertions, 46 deletions
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index b1f8508c3966..27616b05111c 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -16,51 +16,61 @@ #include "internal.h" static const struct regcache_ops *cache_types[] = { + ®cache_flat_sparse_ops, ®cache_rbtree_ops, ®cache_maple_ops, ®cache_flat_ops, }; -static int regcache_hw_init(struct regmap *map) +static int regcache_defaults_cmp(const void *a, const void *b) { - int i, j; - int ret; - int count; - unsigned int reg, val; - void *tmp_buf; + const struct reg_default *x = a; + const struct reg_default *y = b; - if (!map->num_reg_defaults_raw) - return -EINVAL; + if (x->reg > y->reg) + return 1; + else if (x->reg < y->reg) + return -1; + else + return 0; +} + +void regcache_sort_defaults(struct reg_default *defaults, unsigned int ndefaults) +{ + sort(defaults, ndefaults, sizeof(*defaults), + regcache_defaults_cmp, NULL); +} +EXPORT_SYMBOL_GPL(regcache_sort_defaults); + +static int regcache_count_cacheable_registers(struct regmap *map) +{ + unsigned int count; /* calculate the size of reg_defaults */ - for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) + count = 0; + for (unsigned int i = 0; i < map->num_reg_defaults_raw; i++) if (regmap_readable(map, i * map->reg_stride) && !regmap_volatile(map, i * map->reg_stride)) count++; - /* all registers are unreadable or volatile, so just bypass */ - if (!count) { - map->cache_bypass = true; - return 0; - } + return count; +} - map->num_reg_defaults = count; - map->reg_defaults = kmalloc_array(count, sizeof(struct reg_default), - GFP_KERNEL); - if (!map->reg_defaults) - return -ENOMEM; +static int regcache_hw_init(struct regmap *map) +{ + int ret; + unsigned int reg, val; + void *tmp_buf; if (!map->reg_defaults_raw) { bool cache_bypass = map->cache_bypass; - dev_warn(map->dev, "No cache defaults, reading back from HW\n"); + dev_dbg(map->dev, "No cache defaults, reading back from HW\n"); /* Bypass the cache access till data read from HW */ map->cache_bypass = true; tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL); - if (!tmp_buf) { - ret = -ENOMEM; - goto err_free; - } + if (!tmp_buf) + return -ENOMEM; ret = regmap_raw_read(map, 0, tmp_buf, map->cache_size_raw); map->cache_bypass = cache_bypass; @@ -73,7 +83,7 @@ static int regcache_hw_init(struct regmap *map) } /* fill the reg_defaults */ - for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) { + for (unsigned int i = 0, j = 0; i < map->num_reg_defaults_raw; i++) { reg = i * map->reg_stride; if (!regmap_readable(map, reg)) @@ -91,9 +101,9 @@ static int regcache_hw_init(struct regmap *map) ret = regmap_read(map, reg, &val); map->cache_bypass = cache_bypass; if (ret != 0) { - dev_err(map->dev, "Failed to read %d: %d\n", + dev_err(map->dev, "Failed to read %x: %d\n", reg, ret); - goto err_free; + return ret; } } @@ -103,15 +113,17 @@ static int regcache_hw_init(struct regmap *map) } return 0; +} -err_free: - kfree(map->reg_defaults); - - return ret; +static void regcache_hw_exit(struct regmap *map) +{ + if (map->cache_free) + kfree(map->reg_defaults_raw); } int regcache_init(struct regmap *map, const struct regmap_config *config) { + int count = 0; int ret; int i; void *tmp_buf; @@ -176,15 +188,18 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) return -ENOMEM; map->reg_defaults = tmp_buf; } else if (map->num_reg_defaults_raw) { - /* Some devices such as PMICs 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; + count = regcache_count_cacheable_registers(map); + if (!count) + map->cache_bypass = true; + + /* All registers are unreadable or volatile, so just bypass */ if (map->cache_bypass) return 0; + + map->num_reg_defaults = count; + map->reg_defaults = kmalloc_objs(struct reg_default, count); + if (!map->reg_defaults) + return -ENOMEM; } if (!map->max_register_is_set && map->num_reg_defaults_raw) { @@ -199,14 +214,42 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) ret = map->cache_ops->init(map); map->unlock(map->lock_arg); if (ret) + goto err_free_reg_defaults; + } + + /* + * Some devices such as PMICs don't have cache defaults, + * we cope with this by reading back the HW registers and + * crafting the cache defaults by hand. + */ + if (count) { + ret = regcache_hw_init(map); + if (ret) + goto err_exit; + } + + if (map->cache_ops->populate && + (map->num_reg_defaults || map->reg_default_cb)) { + dev_dbg(map->dev, "Populating %s cache\n", map->cache_ops->name); + map->lock(map->lock_arg); + ret = map->cache_ops->populate(map); + map->unlock(map->lock_arg); + if (ret) goto err_free; } return 0; err_free: + regcache_hw_exit(map); +err_exit: + if (map->cache_ops->exit) { + dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name); + map->lock(map->lock_arg); + ret = map->cache_ops->exit(map); + map->unlock(map->lock_arg); + } +err_free_reg_defaults: kfree(map->reg_defaults); - if (map->cache_free) - kfree(map->reg_defaults_raw); return ret; } @@ -218,9 +261,7 @@ void regcache_exit(struct regmap *map) BUG_ON(!map->cache_ops); - kfree(map->reg_defaults); - if (map->cache_free) - kfree(map->reg_defaults_raw); + regcache_hw_exit(map); if (map->cache_ops->exit) { dev_dbg(map->dev, "Destroying %s cache\n", @@ -229,6 +270,8 @@ void regcache_exit(struct regmap *map) map->cache_ops->exit(map); map->unlock(map->lock_arg); } + + kfree(map->reg_defaults); } /** @@ -467,7 +510,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min, bypass = map->cache_bypass; name = map->cache_ops->name; - dev_dbg(map->dev, "Syncing %s cache from %d-%d\n", name, min, max); + dev_dbg(map->dev, "Syncing %s cache from %#x-%#x\n", name, min, max); trace_regcache_sync(map, name, "start region"); @@ -798,13 +841,13 @@ static int regcache_sync_block_raw(struct regmap *map, void *block, unsigned int block_base, unsigned int start, unsigned int end) { - unsigned int i, val; unsigned int regtmp = 0; unsigned int base = 0; const void *data = NULL; + unsigned int val; int ret; - for (i = start; i < end; i++) { + for (unsigned int i = start; i < end; i++) { regtmp = block_base + (i * map->reg_stride); if (!regcache_reg_present(cache_present, i) || |
