Browse Source

Merge remote-tracking branch 'regmap/fix/rbtree' into regmap-linus

Mark Brown 9 years ago
parent
commit
787ad90332
1 changed files with 28 additions and 10 deletions
  1. 28 10
      drivers/base/regmap/regcache-rbtree.c

+ 28 - 10
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 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,
 				&base_reg, &top_reg);
 				&base_reg, &top_reg);
 
 
 			if (base_reg <= max && top_reg >= min) {
 			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
 				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_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;
 		}
 		}