Messages in this thread Patch in this message | | | From | Dimitris Papastamos <> | Subject | [PATCH] regmap: debugfs: Add a registers `range' file | Date | Wed, 30 Jan 2013 13:50:23 +0000 |
| |
This file lists the register ranges in the register map. The condition to split the range is based on the actual register attributes. A range is a contiguous block of registers with the same register attributes.
Signed-off-by: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> --- drivers/base/regmap/regmap-debugfs.c | 179 +++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+)
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index faa93cc..4ed7503 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -295,6 +295,182 @@ static const struct file_operations regmap_range_fops = { .llseek = default_llseek, }; +enum reg_attributes { + READABLE = 0x1, + WRITEABLE = 0x2, + VOLATILE = 0x4, +}; + +static inline unsigned int regmap_attr_bitmap(struct regmap *map, + unsigned int reg) +{ + unsigned int reg_attr = 0; + + if (regmap_readable(map, reg)) + reg_attr |= READABLE; + if (regmap_writeable(map, reg)) + reg_attr |= WRITEABLE; + if (regmap_volatile(map, reg)) + reg_attr |= VOLATILE; + return reg_attr; +} + +enum range_state { + RANGE_START, + RANGE_END, + RANGE_WITHIN, + RANGE_DONE, +}; + +struct regmap_reg_range { + unsigned int start; + unsigned int end; + unsigned int attr; +}; + +static void regmap_range_format_line(struct regmap *map, + struct regmap_reg_range *r, + char *buf, size_t len) +{ + int reg_len; + ssize_t buf_offset; + + reg_len = regmap_calc_reg_len(map->max_register, buf, len); + + buf_offset = snprintf(buf, PAGE_SIZE, "%.*x-%.*x ", + reg_len, r->start, + reg_len, r->end); + buf_offset += snprintf(buf + buf_offset, + PAGE_SIZE - buf_offset, "("); + if (r->attr & READABLE) + buf_offset += snprintf(buf + buf_offset, + PAGE_SIZE - buf_offset, + "read, "); + if (r->attr & WRITEABLE) + buf_offset += snprintf(buf + buf_offset, + PAGE_SIZE - buf_offset, + "write, "); + if (r->attr & VOLATILE) + buf_offset += snprintf(buf + buf_offset, + PAGE_SIZE - buf_offset, + "volatile, "); + /* Rewind the last ", " as well */ + buf_offset += snprintf(buf + buf_offset - 2, + PAGE_SIZE - buf_offset, ")"); +} + +static ssize_t regmap_range_read_file(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct regmap *map = file->private_data; + int ret; + char *buf; + size_t buf_pos = 0; + loff_t p = 0; + unsigned int start_reg; + int i; + unsigned int reg_attr; + unsigned int block_start, block_end, block_attr; + enum range_state state; + struct list_head range_head; + struct regmap_reg_range r; + char *entry; + bool scanning = true; + + if (*ppos < 0 || !count) + return -EINVAL; + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + entry = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!entry) { + kfree(buf); + return -ENOMEM; + } + + INIT_LIST_HEAD(&range_head); + + start_reg = 0; + block_attr = regmap_attr_bitmap(map, start_reg); + block_start = block_end = start_reg; + state = RANGE_START; + + i = start_reg; + while (i <= map->max_register && scanning) { + reg_attr = regmap_attr_bitmap(map, i); + switch (state) { + case RANGE_START: + block_start = i; + block_attr = reg_attr; + state = RANGE_WITHIN; + break; + case RANGE_END: + r.start = block_start; + r.end = block_end; + r.attr = block_attr; + /* Only care about regions with any of the 3 register + attributes */ + if (r.attr) { + regmap_range_format_line(map, &r, entry, PAGE_SIZE); + if (p >= *ppos) { + if (buf_pos + 1 + strlen(entry) >= count) + break; + snprintf(buf + buf_pos, count - buf_pos, + "%s", entry); + buf_pos += strlen(entry); + buf[buf_pos] = '\n'; + buf_pos++; + } + } + p += strlen(entry) + 1; + if (i + 1 > map->max_register) + state = RANGE_DONE; + else + state = RANGE_START; + break; + case RANGE_WITHIN: + if (reg_attr != block_attr) { + block_end = i - 1; + state = RANGE_END; + } else { + if (i + 1 > map->max_register) { + state = RANGE_END; + block_end = map->max_register; + } + else { + i++; + } + } + break; + case RANGE_DONE: + scanning = false; + break; + } + } + + kfree(entry); + ret = buf_pos; + + if (copy_to_user(user_buf, buf, buf_pos)) { + ret = -EFAULT; + goto out; + } + + *ppos += buf_pos; +out: + kfree(buf); + return ret; +} + +static const struct file_operations regmap_range_fops = { + .open = simple_open, + .read = regmap_range_read_file, + .llseek = default_llseek, +}; + static ssize_t regmap_access_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -387,6 +563,9 @@ void regmap_debugfs_init(struct regmap *map, const char *name) debugfs_create_file("name", 0400, map->debugfs, map, ®map_name_fops); + debugfs_create_file("range", 0400, map->debugfs, + map, ®map_range_fops); + if (map->max_register) { debugfs_create_file("registers", 0400, map->debugfs, map, ®map_map_fops); -- 1.8.1.2
| |