diff options
Diffstat (limited to 'drivers/base/regmap/regcache-rbtree.c')
| -rw-r--r-- | drivers/base/regmap/regcache-rbtree.c | 38 | 
1 files changed, 28 insertions, 10 deletions
diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index aa56af87d941..b11af3f2c1db 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -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 min, max;  		unsigned int max_dist; +		unsigned int dist, best_dist = UINT_MAX;  		max_dist = map->reg_stride * sizeof(*rbnode_tmp) /  			map->cache_word_size; @@ -423,24 +424,41 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,  				&base_reg, &top_reg);  			if (base_reg <= max && top_reg >= min) { -				new_base_reg = min(reg, base_reg); -				new_top_reg = max(reg, top_reg); -			} else { -				if (max < base_reg) -					node = node->rb_left; +				if (reg < base_reg) +					dist = base_reg - reg; +				else if (reg > top_reg) +					dist = reg - top_reg;  				else -					node = node->rb_right; - -				continue; +					dist = 0; +				if (dist < best_dist) { +					rbnode = rbnode_tmp; +					best_dist = dist; +					new_base_reg = min(reg, base_reg); +					new_top_reg = max(reg, top_reg); +				}  			} -			ret = regcache_rbtree_insert_to_block(map, rbnode_tmp, +			/* +			 * 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; +			else if (reg > top_reg) +				node = node->rb_right; +			else +				break; +		} + +		if (rbnode) { +			ret = regcache_rbtree_insert_to_block(map, rbnode,  							      new_base_reg,  							      new_top_reg, reg,  							      value);  			if (ret)  				return ret; -			rbtree_ctx->cached_rbnode = rbnode_tmp; +			rbtree_ctx->cached_rbnode = rbnode;  			return 0;  		}  | 
