Release 4.7 drivers/base/regmap/regcache-rbtree.c
  
  
/*
 * Register cache access API - rbtree caching support
 *
 * Copyright 2011 Wolfson Microelectronics plc
 *
 * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/rbtree.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include "internal.h"
static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
				 unsigned int value);
static int regcache_rbtree_exit(struct regmap *map);
struct regcache_rbtree_node {
	/* block of adjacent registers */
	
void *block;
	/* Which registers are present */
	
long *cache_present;
	/* base register handled by this block */
	
unsigned int base_reg;
	/* number of registers available in the block */
	
unsigned int blklen;
	/* the actual rbtree node holding this block */
	
struct rb_node node;
} __attribute__ ((packed));
struct regcache_rbtree_ctx {
	
struct rb_root root;
	
struct regcache_rbtree_node *cached_rbnode;
};
static inline void regcache_rbtree_get_base_top_reg(
	struct regmap *map,
	struct regcache_rbtree_node *rbnode,
	unsigned int *base, unsigned int *top)
{
	*base = rbnode->base_reg;
	*top = rbnode->base_reg + ((rbnode->blklen - 1) * map->reg_stride);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dimitris papastamos | dimitris papastamos | 42 | 76.36% | 1 | 50.00% | 
| stephen warren | stephen warren | 13 | 23.64% | 1 | 50.00% | 
 | Total | 55 | 100.00% | 2 | 100.00% | 
static unsigned int regcache_rbtree_get_register(struct regmap *map,
	struct regcache_rbtree_node *rbnode, unsigned int idx)
{
	return regcache_get_val(map, rbnode->block, idx);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dimitris papastamos | dimitris papastamos | 22 | 66.67% | 1 | 33.33% | 
| mark brown | mark brown | 7 | 21.21% | 1 | 33.33% | 
| lars-peter clausen | lars-peter clausen | 4 | 12.12% | 1 | 33.33% | 
 | Total | 33 | 100.00% | 3 | 100.00% | 
static void regcache_rbtree_set_register(struct regmap *map,
					 struct regcache_rbtree_node *rbnode,
					 unsigned int idx, unsigned int val)
{
	set_bit(idx, rbnode->cache_present);
	regcache_set_val(map, rbnode->block, idx, val);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dimitris papastamos | dimitris papastamos | 25 | 54.35% | 1 | 25.00% | 
| lars-peter clausen | lars-peter clausen | 14 | 30.43% | 2 | 50.00% | 
| mark brown | mark brown | 7 | 15.22% | 1 | 25.00% | 
 | Total | 46 | 100.00% | 4 | 100.00% | 
static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
							   unsigned int reg)
{
	struct regcache_rbtree_ctx *rbtree_ctx = map->cache;
	struct rb_node *node;
	struct regcache_rbtree_node *rbnode;
	unsigned int base_reg, top_reg;
	rbnode = rbtree_ctx->cached_rbnode;
	if (rbnode) {
		regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
						 &top_reg);
		if (reg >= base_reg && reg <= top_reg)
			return rbnode;
	}
	node = rbtree_ctx->root.rb_node;
	while (node) {
		rbnode = container_of(node, struct regcache_rbtree_node, node);
		regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
						 &top_reg);
		if (reg >= base_reg && reg <= top_reg) {
			rbtree_ctx->cached_rbnode = rbnode;
			return rbnode;
		} else if (reg > top_reg) {
			node = node->rb_right;
		} else if (reg < base_reg) {
			node = node->rb_left;
		}
	}
	return NULL;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dimitris papastamos | dimitris papastamos | 107 | 61.85% | 1 | 33.33% | 
| lars-peter clausen | lars-peter clausen | 62 | 35.84% | 1 | 33.33% | 
| stephen warren | stephen warren | 4 | 2.31% | 1 | 33.33% | 
 | Total | 173 | 100.00% | 3 | 100.00% | 
static int regcache_rbtree_insert(struct regmap *map, struct rb_root *root,
				  struct regcache_rbtree_node *rbnode)
{
	struct rb_node **new, *parent;
	struct regcache_rbtree_node *rbnode_tmp;
	unsigned int base_reg_tmp, top_reg_tmp;
	unsigned int base_reg;
	parent = NULL;
	new = &root->rb_node;
	while (*new) {
		rbnode_tmp = container_of(*new, struct regcache_rbtree_node,
					  node);
		/* base and top registers of the current rbnode */
		regcache_rbtree_get_base_top_reg(map, rbnode_tmp, &base_reg_tmp,
						 &top_reg_tmp);
		/* base register of the rbnode to be added */
		base_reg = rbnode->base_reg;
		parent = *new;
		/* if this register has already been inserted, just return */
		if (base_reg >= base_reg_tmp &&
		    base_reg <= top_reg_tmp)
			return 0;
		else if (base_reg > top_reg_tmp)
			new = &((*new)->rb_right);
		else if (base_reg < base_reg_tmp)
			new = &((*new)->rb_left);
	}
	/* insert the node into the rbtree */
	rb_link_node(&rbnode->node, parent, new);
	rb_insert_color(&rbnode->node, root);
	return 1;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dimitris papastamos | dimitris papastamos | 173 | 96.11% | 1 | 50.00% | 
| stephen warren | stephen warren | 7 | 3.89% | 1 | 50.00% | 
 | Total | 180 | 100.00% | 2 | 100.00% | 
#ifdef CONFIG_DEBUG_FS
static int rbtree_show(struct seq_file *s, void *ignored)
{
	struct regmap *map = s->private;
	struct regcache_rbtree_ctx *rbtree_ctx = map->cache;
	struct regcache_rbtree_node *n;
	struct rb_node *node;
	unsigned int base, top;
	size_t mem_size;
	int nodes = 0;
	int registers = 0;
	int this_registers, average;
	map->lock(map->lock_arg);
	mem_size = sizeof(*rbtree_ctx);
	for (node = rb_first(&rbtree_ctx->root); node != NULL;
	     node = rb_next(node)) {
		n = container_of(node, struct regcache_rbtree_node, node);
		mem_size += sizeof(*n);
		mem_size += (n->blklen * map->cache_word_size);
		mem_size += BITS_TO_LONGS(n->blklen) * sizeof(long);
		regcache_rbtree_get_base_top_reg(map, n, &base, &top);
		this_registers = ((top - base) / map->reg_stride) + 1;
		seq_printf(s, "%x-%x (%d)\n", base, top, this_registers);
		nodes++;
		registers += this_registers;
	}
	if (nodes)
		average = registers / nodes;
	else
		average = 0;
	seq_printf(s, "%d nodes, %d registers, average %d registers, used %zu bytes\n",
		   nodes, registers, average, mem_size);
	map->unlock(map->lock_arg);
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| mark brown | mark brown | 148 | 60.16% | 1 | 14.29% | 
| stephen warren | stephen warren | 46 | 18.70% | 3 | 42.86% | 
| dimitris papastamos | dimitris papastamos | 34 | 13.82% | 1 | 14.29% | 
| lars-peter clausen | lars-peter clausen | 18 | 7.32% | 2 | 28.57% | 
 | Total | 246 | 100.00% | 7 | 100.00% | 
static int rbtree_open(struct inode *inode, struct file *file)
{
	return single_open(file, rbtree_show, inode->i_private);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| mark brown | mark brown | 28 | 100.00% | 1 | 100.00% | 
 | Total | 28 | 100.00% | 1 | 100.00% | 
static const struct file_operations rbtree_fops = {
	.open		= rbtree_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};
static void rbtree_debugfs_init(struct regmap *map)
{
	debugfs_create_file("rbtree", 0400, map->debugfs, map, &rbtree_fops);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| mark brown | mark brown | 27 | 100.00% | 1 | 100.00% | 
 | Total | 27 | 100.00% | 1 | 100.00% | 
#endif
static int regcache_rbtree_init(struct regmap *map)
{
	struct regcache_rbtree_ctx *rbtree_ctx;
	int i;
	int ret;
	map->cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL);
	if (!map->cache)
		return -ENOMEM;
	rbtree_ctx = map->cache;
	rbtree_ctx->root = RB_ROOT;
	rbtree_ctx->cached_rbnode = NULL;
	for (i = 0; i < map->num_reg_defaults; i++) {
		ret = regcache_rbtree_write(map,
					    map->reg_defaults[i].reg,
					    map->reg_defaults[i].def);
		if (ret)
			goto err;
	}
	return 0;
err:
	regcache_rbtree_exit(map);
	return ret;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dimitris papastamos | dimitris papastamos | 125 | 99.21% | 1 | 50.00% | 
| lars-peter clausen | lars-peter clausen | 1 | 0.79% | 1 | 50.00% | 
 | Total | 126 | 100.00% | 2 | 100.00% | 
static int regcache_rbtree_exit(struct regmap *map)
{
	struct rb_node *next;
	struct regcache_rbtree_ctx *rbtree_ctx;
	struct regcache_rbtree_node *rbtree_node;
	/* if we've already been called then just return */
	rbtree_ctx = map->cache;
	if (!rbtree_ctx)
		return 0;
	/* free up the rbtree */
	next = rb_first(&rbtree_ctx->root);
	while (next) {
		rbtree_node = rb_entry(next, struct regcache_rbtree_node, node);
		next = rb_next(&rbtree_node->node);
		rb_erase(&rbtree_node->node, &rbtree_ctx->root);
		kfree(rbtree_node->cache_present);
		kfree(rbtree_node->block);
		kfree(rbtree_node);
	}
	/* release the resources */
	kfree(map->cache);
	map->cache = NULL;
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dimitris papastamos | dimitris papastamos | 122 | 94.57% | 1 | 50.00% | 
| lars-peter clausen | lars-peter clausen | 7 | 5.43% | 1 | 50.00% | 
 | Total | 129 | 100.00% | 2 | 100.00% | 
static int regcache_rbtree_read(struct regmap *map,
				unsigned int reg, unsigned int *value)
{
	struct regcache_rbtree_node *rbnode;
	unsigned int reg_tmp;
	rbnode = regcache_rbtree_lookup(map, reg);
	if (rbnode) {
		reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
		if (!test_bit(reg_tmp, rbnode->cache_present))
			return -ENOENT;
		*value = regcache_rbtree_get_register(map, rbnode, reg_tmp);
	} else {
		return -ENOENT;
	}
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dimitris papastamos | dimitris papastamos | 78 | 81.25% | 2 | 28.57% | 
| lars-peter clausen | lars-peter clausen | 6 | 6.25% | 2 | 28.57% | 
| mark brown | mark brown | 6 | 6.25% | 2 | 28.57% | 
| stephen warren | stephen warren | 6 | 6.25% | 1 | 14.29% | 
 | Total | 96 | 100.00% | 7 | 100.00% | 
static int regcache_rbtree_insert_to_block(struct regmap *map,
					   struct regcache_rbtree_node *rbnode,
					   unsigned int base_reg,
					   unsigned int top_reg,
					   unsigned int reg,
					   unsigned int value)
{
	unsigned int blklen;
	unsigned int pos, offset;
	unsigned long *present;
	u8 *blk;
	blklen = (top_reg - base_reg) / map->reg_stride + 1;
	pos = (reg - base_reg) / map->reg_stride;
	offset = (rbnode->base_reg - base_reg) / map->reg_stride;
	blk = krealloc(rbnode->block,
		       blklen * map->cache_word_size,
		       GFP_KERNEL);
	if (!blk)
		return -ENOMEM;
	if (BITS_TO_LONGS(blklen) > BITS_TO_LONGS(rbnode->blklen)) {
		present = krealloc(rbnode->cache_present,
				   BITS_TO_LONGS(blklen) * sizeof(*present),
				   GFP_KERNEL);
		if (!present) {
			kfree(blk);
			return -ENOMEM;
		}
		memset(present + BITS_TO_LONGS(rbnode->blklen), 0,
		       (BITS_TO_LONGS(blklen) - BITS_TO_LONGS(rbnode->blklen))
		       * sizeof(*present));
	} else {
		present = rbnode->cache_present;
	}
	/* insert the register value in the correct place in the rbnode block */
	if (pos == 0) {
		memmove(blk + offset * map->cache_word_size,
			blk, rbnode->blklen * map->cache_word_size);
		bitmap_shift_left(present, present, offset, blklen);
	}
	/* update the rbnode block, its size and the base register */
	rbnode->block = blk;
	rbnode->blklen = blklen;
	rbnode->base_reg = base_reg;
	rbnode->cache_present = present;
	regcache_rbtree_set_register(map, rbnode, pos, value);
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| lars-peter clausen | lars-peter clausen | 126 | 42.86% | 3 | 50.00% | 
| dimitris papastamos | dimitris papastamos | 93 | 31.63% | 1 | 16.67% | 
| guenter roeck | guenter roeck | 59 | 20.07% | 1 | 16.67% | 
| mark brown | mark brown | 16 | 5.44% | 1 | 16.67% | 
 | Total | 294 | 100.00% | 6 | 100.00% | 
static struct regcache_rbtree_node *
regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg)
{
	struct regcache_rbtree_node *rbnode;
	const struct regmap_range *range;
	int i;
	rbnode = kzalloc(sizeof(*rbnode), GFP_KERNEL);
	if (!rbnode)
		return NULL;
	/* If there is a read table then use it to guess at an allocation */
	if (map->rd_table) {
		for (i = 0; i < map->rd_table->n_yes_ranges; i++) {
			if (regmap_reg_in_range(reg,
						&map->rd_table->yes_ranges[i]))
				break;
		}
		if (i != map->rd_table->n_yes_ranges) {
			range = &map->rd_table->yes_ranges[i];
			rbnode->blklen = (range->range_max - range->range_min) /
				map->reg_stride	+ 1;
			rbnode->base_reg = range->range_min;
		}
	}
	if (!rbnode->blklen) {
		rbnode->blklen = 1;
		rbnode->base_reg = reg;
	}
	rbnode->block = kmalloc_array(rbnode->blklen, map->cache_word_size,
				      GFP_KERNEL);
	if (!rbnode->block)
		goto err_free;
	rbnode->cache_present = kcalloc(BITS_TO_LONGS(rbnode->blklen),
					sizeof(*rbnode->cache_present),
					GFP_KERNEL);
	if (!rbnode->cache_present)
		goto err_free_block;
	return rbnode;
err_free_block:
	kfree(rbnode->block);
err_free:
	kfree(rbnode);
	return NULL;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| mark brown | mark brown | 194 | 76.38% | 2 | 28.57% | 
| lars-peter clausen | lars-peter clausen | 55 | 21.65% | 2 | 28.57% | 
| xiubo li | xiubo li | 4 | 1.57% | 2 | 28.57% | 
| david jander | david jander | 1 | 0.39% | 1 | 14.29% | 
 | Total | 254 | 100.00% | 7 | 100.00% | 
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;
		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) {
				new_base_reg = min(reg, base_reg);
				new_top_reg = max(reg, top_reg);
			} else {
				if (max < base_reg)
					node = node->rb_left;
				else
					node = node->rb_right;
				continue;
			}
			ret = regcache_rbtree_insert_to_block(map, rbnode_tmp,
							      new_base_reg,
							      new_top_reg, reg,
							      value);
			if (ret)
				return ret;
			rbtree_ctx->cached_rbnode = rbnode_tmp;
			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;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dimitris papastamos | dimitris papastamos | 190 | 56.55% | 3 | 30.00% | 
| lars-peter clausen | lars-peter clausen | 99 | 29.46% | 3 | 30.00% | 
| nikesh oswal | nikesh oswal | 25 | 7.44% | 1 | 10.00% | 
| mark brown | mark brown | 14 | 4.17% | 2 | 20.00% | 
| stephen warren | stephen warren | 8 | 2.38% | 1 | 10.00% | 
 | Total | 336 | 100.00% | 10 | 100.00% | 
static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
				unsigned int max)
{
	struct regcache_rbtree_ctx *rbtree_ctx;
	struct rb_node *node;
	struct regcache_rbtree_node *rbnode;
	unsigned int base_reg, top_reg;
	unsigned int start, end;
	int ret;
	rbtree_ctx = map->cache;
	for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
		rbnode = rb_entry(node, struct regcache_rbtree_node, node);
		regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
			&top_reg);
		if (base_reg > max)
			break;
		if (top_reg < min)
			continue;
		if (min > base_reg)
			start = (min - base_reg) / map->reg_stride;
		else
			start = 0;
		if (max < top_reg)
			end = (max - base_reg) / map->reg_stride + 1;
		else
			end = rbnode->blklen;
		ret = regcache_sync_block(map, rbnode->block,
					  rbnode->cache_present,
					  rbnode->base_reg, start, end);
		if (ret != 0)
			return ret;
	}
	return regmap_async_complete(map);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dimitris papastamos | dimitris papastamos | 91 | 44.39% | 1 | 12.50% | 
| mark brown | mark brown | 68 | 33.17% | 4 | 50.00% | 
| lars-peter clausen | lars-peter clausen | 46 | 22.44% | 3 | 37.50% | 
 | Total | 205 | 100.00% | 8 | 100.00% | 
static int regcache_rbtree_drop(struct regmap *map, unsigned int min,
				unsigned int max)
{
	struct regcache_rbtree_ctx *rbtree_ctx;
	struct regcache_rbtree_node *rbnode;
	struct rb_node *node;
	unsigned int base_reg, top_reg;
	unsigned int start, end;
	rbtree_ctx = map->cache;
	for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
		rbnode = rb_entry(node, struct regcache_rbtree_node, node);
		regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
			&top_reg);
		if (base_reg > max)
			break;
		if (top_reg < min)
			continue;
		if (min > base_reg)
			start = (min - base_reg) / map->reg_stride;
		else
			start = 0;
		if (max < top_reg)
			end = (max - base_reg) / map->reg_stride + 1;
		else
			end = rbnode->blklen;
		bitmap_clear(rbnode->cache_present, start, end - start);
	}
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| lars-peter clausen | lars-peter clausen | 180 | 100.00% | 1 | 100.00% | 
 | Total | 180 | 100.00% | 1 | 100.00% | 
struct regcache_ops regcache_rbtree_ops = {
	.type = REGCACHE_RBTREE,
	.name = "rbtree",
	.init = regcache_rbtree_init,
	.exit = regcache_rbtree_exit,
#ifdef CONFIG_DEBUG_FS
	.debugfs_init = rbtree_debugfs_init,
#endif
	.read = regcache_rbtree_read,
	.write = regcache_rbtree_write,
	.sync = regcache_rbtree_sync,
	.drop = regcache_rbtree_drop,
};
Overall Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dimitris papastamos | dimitris papastamos | 1203 | 46.38% | 4 | 11.11% | 
| lars-peter clausen | lars-peter clausen | 649 | 25.02% | 11 | 30.56% | 
| mark brown | mark brown | 552 | 21.28% | 10 | 27.78% | 
| stephen warren | stephen warren | 84 | 3.24% | 3 | 8.33% | 
| guenter roeck | guenter roeck | 59 | 2.27% | 1 | 2.78% | 
| nikesh oswal | nikesh oswal | 25 | 0.96% | 1 | 2.78% | 
| jean-christophe pince | jean-christophe pince | 10 | 0.39% | 1 | 2.78% | 
| xiubo li | xiubo li | 8 | 0.31% | 3 | 8.33% | 
| paul gortmaker | paul gortmaker | 3 | 0.12% | 1 | 2.78% | 
| david jander | david jander | 1 | 0.04% | 1 | 2.78% | 
 | Total | 2594 | 100.00% | 36 | 100.00% | 
  
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.