static int regcache_rbtree_write()

in regmap/regcache-rbtree.c [368:462]


static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
				 unsigned int value)
{
	struct regcache_rbtree_ctx *rbtree_ctx;
	struct regcache_rbtree_node *rbnode, *rbnode_tmp;
	struct rb_node *node;
	unsigned int reg_tmp;
	int ret;

	rbtree_ctx = map->cache;

	/* if we can't locate it in the cached rbnode we'll have
	 * to traverse the rbtree looking for it.
	 */
	rbnode = regcache_rbtree_lookup(map, reg);
	if (rbnode) {
		reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
		regcache_rbtree_set_register(map, rbnode, reg_tmp, value);
	} else {
		unsigned int base_reg, top_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;
		if (reg < max_dist)
			min = 0;
		else
			min = reg - max_dist;
		max = reg + max_dist;

		/* look for an adjacent register to the one we are about to add */
		node = rbtree_ctx->root.rb_node;
		while (node) {
			rbnode_tmp = rb_entry(node, struct regcache_rbtree_node,
					      node);

			regcache_rbtree_get_base_top_reg(map, rbnode_tmp,
				&base_reg, &top_reg);

			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_top_reg = max(reg, top_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;
			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;
			return 0;
		}

		/* We did not manage to find a place to insert it in
		 * an existing block so create a new rbnode.
		 */
		rbnode = regcache_rbtree_node_alloc(map, reg);
		if (!rbnode)
			return -ENOMEM;
		regcache_rbtree_set_register(map, rbnode,
					     reg - rbnode->base_reg, value);
		regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode);
		rbtree_ctx->cached_rbnode = rbnode;
	}

	return 0;
}