From 1a113794d24be608da7b834cd31d750fffbd8b64 Mon Sep 17 00:00:00 2001 From: Abhijeet Dharmapurikar Date: Thu, 21 Jan 2016 10:58:22 -0800 Subject: [PATCH] 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 Signed-off-by: David Collins --- drivers/base/regmap/Kconfig | 9 ++++ drivers/base/regmap/internal.h | 5 ++ drivers/base/regmap/regmap-debugfs.c | 73 ++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+) diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index 0fd6f97ee523..c3f3820af0ca 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -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. diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 3d80c4b43f72..995fd28d95a5 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -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; diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 7d1df3d9a1d9..590ff440442c 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -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, ®map_map_fops); debugfs_create_file("access", 0400, map->debugfs, map, ®map_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, ®map_data_fops); +#endif } if (map->cache_type) {