diff options
Diffstat (limited to 'drivers/base/regmap/regcache-maple.c')
-rw-r--r-- | drivers/base/regmap/regcache-maple.c | 140 |
1 files changed, 128 insertions, 12 deletions
diff --git a/drivers/base/regmap/regcache-maple.c b/drivers/base/regmap/regcache-maple.c index c2e3a0f6c218..283c2e02a298 100644 --- a/drivers/base/regmap/regcache-maple.c +++ b/drivers/base/regmap/regcache-maple.c @@ -186,6 +186,55 @@ out_unlocked: return ret; } +static int regcache_maple_sync_block(struct regmap *map, unsigned long *entry, + struct ma_state *mas, + unsigned int min, unsigned int max) +{ + void *buf; + unsigned long r; + size_t val_bytes = map->format.val_bytes; + int ret = 0; + + mas_pause(mas); + rcu_read_unlock(); + + /* + * Use a raw write if writing more than one register to a + * device that supports raw writes to reduce transaction + * overheads. + */ + if (max - min > 1 && regmap_can_raw_write(map)) { + buf = kmalloc(val_bytes * (max - min), map->alloc_flags); + if (!buf) { + ret = -ENOMEM; + goto out; + } + + /* Render the data for a raw write */ + for (r = min; r < max; r++) { + regcache_set_val(map, buf, r - min, + entry[r - mas->index]); + } + + ret = _regmap_raw_write(map, min, buf, (max - min) * val_bytes, + false); + + kfree(buf); + } else { + for (r = min; r < max; r++) { + ret = _regmap_write(map, r, + entry[r - mas->index]); + if (ret != 0) + goto out; + } + } + +out: + rcu_read_lock(); + + return ret; +} + static int regcache_maple_sync(struct regmap *map, unsigned int min, unsigned int max) { @@ -194,8 +243,9 @@ static int regcache_maple_sync(struct regmap *map, unsigned int min, MA_STATE(mas, mt, min, max); unsigned long lmin = min; unsigned long lmax = max; - unsigned int r; + unsigned int r, v, sync_start; int ret; + bool sync_needed = false; map->cache_bypass = true; @@ -203,18 +253,38 @@ static int regcache_maple_sync(struct regmap *map, unsigned int min, mas_for_each(&mas, entry, max) { for (r = max(mas.index, lmin); r <= min(mas.last, lmax); r++) { - mas_pause(&mas); - rcu_read_unlock(); - ret = regcache_sync_val(map, r, entry[r - mas.index]); + v = entry[r - mas.index]; + + if (regcache_reg_needs_sync(map, r, v)) { + if (!sync_needed) { + sync_start = r; + sync_needed = true; + } + continue; + } + + if (!sync_needed) + continue; + + ret = regcache_maple_sync_block(map, entry, &mas, + sync_start, r); + if (ret != 0) + goto out; + sync_needed = false; + } + + if (sync_needed) { + ret = regcache_maple_sync_block(map, entry, &mas, + sync_start, r); if (ret != 0) goto out; - rcu_read_lock(); + sync_needed = false; } } +out: rcu_read_unlock(); -out: map->cache_bypass = false; return ret; @@ -242,11 +312,41 @@ static int regcache_maple_exit(struct regmap *map) return 0; } +static int regcache_maple_insert_block(struct regmap *map, int first, + int last) +{ + struct maple_tree *mt = map->cache; + MA_STATE(mas, mt, first, last); + unsigned long *entry; + int i, ret; + + entry = kcalloc(last - first + 1, sizeof(unsigned long), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + for (i = 0; i < last - first + 1; i++) + entry[i] = map->reg_defaults[first + i].def; + + mas_lock(&mas); + + mas_set_range(&mas, map->reg_defaults[first].reg, + map->reg_defaults[last].reg); + ret = mas_store_gfp(&mas, entry, GFP_KERNEL); + + mas_unlock(&mas); + + if (ret) + kfree(entry); + + return ret; +} + static int regcache_maple_init(struct regmap *map) { struct maple_tree *mt; int i; int ret; + int range_start; mt = kmalloc(sizeof(*mt), GFP_KERNEL); if (!mt) @@ -255,14 +355,30 @@ static int regcache_maple_init(struct regmap *map) mt_init(mt); - for (i = 0; i < map->num_reg_defaults; i++) { - ret = regcache_maple_write(map, - map->reg_defaults[i].reg, - map->reg_defaults[i].def); - if (ret) - goto err; + if (!map->num_reg_defaults) + return 0; + + range_start = 0; + + /* Scan for ranges of contiguous registers */ + for (i = 1; i < map->num_reg_defaults; i++) { + if (map->reg_defaults[i].reg != + map->reg_defaults[i - 1].reg + 1) { + ret = regcache_maple_insert_block(map, range_start, + i - 1); + if (ret != 0) + goto err; + + range_start = i; + } } + /* Add the last block */ + ret = regcache_maple_insert_block(map, range_start, + map->num_reg_defaults - 1); + if (ret != 0) + goto err; + return 0; err: |