Commit ec9a03d4 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'regmap-fix-v4.8-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap

Pull regmap fixes from Mark Brown:
 "Several fixes here, the main one being the change from Lars-Peter
  which I'd been letting soak in -next since the merge window in case it
  uncovered further issues as it's a minimal fix rather than a change
  addressing the root cause of the problems (which would've been too
  invasive for -rc):

   - The biggest change is a fix from Lars-Peter to ensure that we don't
     create overlapping rbtree nodes which in turn avoids returning
     corrupt cache values to users, fixing some issues that were exposed
     by some recent optimisations with certain access patterns but had
     been present for a long time.

   - A fix from Elaine Zhang to stop us updating the cache if we get an
     I/O error when writing to the hardware.

   - A fix fromm Maarten ter Huurne to avoid uninitialized defaults in
     cases where we have non-readable registers but are initializing the
     cache by reading from the device"

* tag 'regmap-fix-v4.8-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: drop cache if the bus transfer error
  regmap: rbtree: Avoid overlapping nodes
  regmap: cache: Fix num_reg_defaults computation from reg_defaults_raw
parents 8ded8f00 787ad903
...@@ -404,6 +404,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, ...@@ -404,6 +404,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
unsigned int new_base_reg, new_top_reg; unsigned int new_base_reg, new_top_reg;
unsigned int min, max; unsigned int min, max;
unsigned int max_dist; unsigned int max_dist;
unsigned int dist, best_dist = UINT_MAX;
max_dist = map->reg_stride * sizeof(*rbnode_tmp) / max_dist = map->reg_stride * sizeof(*rbnode_tmp) /
map->cache_word_size; map->cache_word_size;
...@@ -423,24 +424,41 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, ...@@ -423,24 +424,41 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
&base_reg, &top_reg); &base_reg, &top_reg);
if (base_reg <= max && top_reg >= min) { if (base_reg <= max && top_reg >= min) {
if (reg < base_reg)
dist = base_reg - reg;
else if (reg > top_reg)
dist = reg - top_reg;
else
dist = 0;
if (dist < best_dist) {
rbnode = rbnode_tmp;
best_dist = dist;
new_base_reg = min(reg, base_reg); new_base_reg = min(reg, base_reg);
new_top_reg = max(reg, top_reg); new_top_reg = max(reg, top_reg);
} else { }
if (max < base_reg) }
/*
* Keep looking, we want to choose the closest block,
* otherwise we might end up creating overlapping
* blocks, which breaks the rbtree.
*/
if (reg < base_reg)
node = node->rb_left; node = node->rb_left;
else else if (reg > top_reg)
node = node->rb_right; node = node->rb_right;
else
continue; break;
} }
ret = regcache_rbtree_insert_to_block(map, rbnode_tmp, if (rbnode) {
ret = regcache_rbtree_insert_to_block(map, rbnode,
new_base_reg, new_base_reg,
new_top_reg, reg, new_top_reg, reg,
value); value);
if (ret) if (ret)
return ret; return ret;
rbtree_ctx->cached_rbnode = rbnode_tmp; rbtree_ctx->cached_rbnode = rbnode;
return 0; return 0;
} }
......
...@@ -38,10 +38,11 @@ static int regcache_hw_init(struct regmap *map) ...@@ -38,10 +38,11 @@ static int regcache_hw_init(struct regmap *map)
/* calculate the size of reg_defaults */ /* calculate the size of reg_defaults */
for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++)
if (!regmap_volatile(map, i * map->reg_stride)) if (regmap_readable(map, i * map->reg_stride) &&
!regmap_volatile(map, i * map->reg_stride))
count++; count++;
/* all registers are volatile, so just bypass */ /* all registers are unreadable or volatile, so just bypass */
if (!count) { if (!count) {
map->cache_bypass = true; map->cache_bypass = true;
return 0; return 0;
......
...@@ -1474,6 +1474,8 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, ...@@ -1474,6 +1474,8 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,
ret = map->bus->write(map->bus_context, buf, len); ret = map->bus->write(map->bus_context, buf, len);
kfree(buf); kfree(buf);
} else if (ret != 0 && !map->cache_bypass && map->format.parse_val) {
regcache_drop_region(map, reg, reg + 1);
} }
trace_regmap_hw_write_done(map, reg, val_len / map->format.val_bytes); trace_regmap_hw_write_done(map, reg, val_len / map->format.val_bytes);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment