regmap: improve debugfs interface to dump specific addresses

The current method of cat-ing register file dumps the entire
address space. One can use dd command to dump a subrange within
the address space. However one needs to know the string length
of each line which is derived from max address, the character
length of each register entry and the format.

Provide simple means to dump a range by allowing user to specify
the start address and the count of registers. When the data is read
convert the dump address to a starting position in the file. Similarly
if the file offset goes beyond the dump range return 0 to indicate
that the data is already dumped.

Also provide a means to write to a register address.  Use
CONFIG_REGMAP_QTI_DEBUG to enable this new fine-grained
register read/write interface.

Change-Id: I3466ce89007d127151f6760328edad116d679db8
Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
Signed-off-by: David Collins <collinsd@codeaurora.org>
This commit is contained in:
Abhijeet Dharmapurikar 2016-01-21 10:58:22 -08:00 • committed by David Collins
commit 1a113794d2
3 changed files with 87 additions and 0 deletions

View file

@ -53,3 +53,12 @@ config REGMAP_SCCB
config REGMAP_I3C
tristate
depends on I3C
config REGMAP_QTI_DEBUG
bool "Regmap QTI debug feature support"
depends on REGMAP && DEBUG_FS && QGKI
help
Say yes to enable QTI debug features. This provides a runtime debugfs
interface to read and write a subset of regmap registers. This
interface is more performant and easier to use than the traditional
method which dumps all registers defined in a given regmap.

View file

@ -84,6 +84,11 @@ struct regmap {
struct list_head debugfs_off_cache;
struct mutex cache_lock;
#ifdef CONFIG_REGMAP_QTI_DEBUG
unsigned int dump_address;
unsigned int dump_count;
#endif
#endif
unsigned int max_register;

View file

@ -339,6 +339,69 @@ static const struct file_operations regmap_map_fops = {
.llseek = default_llseek,
};
#ifdef CONFIG_REGMAP_QTI_DEBUG
static ssize_t regmap_data_read_file(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct regmap *map = file->private_data;
int new_count;
regmap_calc_tot_len(map, NULL, 0);
new_count = map->dump_count * map->debugfs_tot_len;
if (new_count > count)
new_count = count;
if (*ppos == 0)
*ppos = map->dump_address * map->debugfs_tot_len;
else if (*ppos >= map->dump_address * map->debugfs_tot_len
+ map->dump_count * map->debugfs_tot_len)
return 0;
return regmap_read_debugfs(map, 0, map->max_register, user_buf,
new_count, ppos);
}
#ifdef REGMAP_ALLOW_WRITE_DEBUGFS
static ssize_t regmap_data_write_file(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
char buf[32];
size_t buf_size;
char *start = buf;
unsigned long value;
struct regmap *map = file->private_data;
int ret;
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
while (*start == ' ')
start++;
if (kstrtoul(start, 16, &value))
return -EINVAL;
/* Userspace has been fiddling around behind the kernel's back */
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
ret = regmap_write(map, map->dump_address, value);
if (ret < 0)
return ret;
return buf_size;
}
#else
#define regmap_data_write_file NULL
#endif
static const struct file_operations regmap_data_fops = {
.open = simple_open,
.read = regmap_data_read_file,
.write = regmap_data_write_file,
.llseek = default_llseek,
};
#endif
static ssize_t regmap_range_read_file(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@ -608,6 +671,16 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
map, &regmap_map_fops);
debugfs_create_file("access", 0400, map->debugfs,
map, &regmap_access_fops);
#ifdef CONFIG_REGMAP_QTI_DEBUG
debugfs_create_x32("address", 0600, map->debugfs,
&map->dump_address);
map->dump_count = 1;
debugfs_create_u32("count", 0600, map->debugfs,
&map->dump_count);
debugfs_create_file("data", registers_mode, map->debugfs,
map, &regmap_data_fops);
#endif
}
if (map->cache_type) {