* [PATCH v2] regmap: debugfs: Add a registers `range' file
@ 2013-01-30 16:30 Dimitris Papastamos
0 siblings, 0 replies; 2+ messages in thread
From: Dimitris Papastamos @ 2013-01-30 16:30 UTC (permalink / raw)
To: Mark Brown; +Cc: linux-kernel, patches
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 | 177 +++++++++++++++++++++++++++++++++++
1 file changed, 177 insertions(+)
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
index 0866c42..00db799 100644
--- a/drivers/base/regmap/regmap-debugfs.c
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -240,6 +240,180 @@ static const struct file_operations regmap_map_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;
+
+ buf_offset = snprintf(buf, PAGE_SIZE, "%x-%x ",
+ r->start, 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 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;
+ }
+
+ 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 where any of the 3 register
+ attributes are set */
+ if (r.attr) {
+ regmap_range_format_line(map, &r, entry, PAGE_SIZE);
+ if (p >= *ppos) {
+ /* We can't fit any more data into this buf
+ * so no use to loop around anymore, just
+ * give the data back to user */
+ if (buf_pos + 1 + strlen(entry) >= count)
+ goto out_entry;
+ 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;
+ }
+ }
+
+out_entry:
+ kfree(entry);
+ ret = buf_pos;
+
+ if (copy_to_user(user_buf, buf, buf_pos)) {
+ ret = -EFAULT;
+ goto out_buf;
+ }
+
+ *ppos += buf_pos;
+out_buf:
+ 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)
@@ -322,6 +496,9 @@ void regmap_debugfs_init(struct regmap *map)
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
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [PATCH v2] regmap: debugfs: Add a registers `range' file
@ 2013-02-11 14:10 Dimitris Papastamos
0 siblings, 0 replies; 2+ messages in thread
From: Dimitris Papastamos @ 2013-02-11 14:10 UTC (permalink / raw)
To: Mark Brown; +Cc: linux-kernel, patches
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/internal.h | 1 +
drivers/base/regmap/regmap-debugfs.c | 116 +++++++++++++++++++++++++++++++++++
2 files changed, 117 insertions(+)
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index 5a22bd3..b4e55a0 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -27,6 +27,7 @@ struct regmap_debugfs_off_cache {
off_t max;
unsigned int base_reg;
unsigned int max_reg;
+ unsigned int reg_attr;
};
struct regmap_format {
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
index 78d5f20..e19359f 100644
--- a/drivers/base/regmap/regmap-debugfs.c
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -56,6 +56,26 @@ static const struct file_operations regmap_name_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;
+}
+
static void regmap_debugfs_free_dump_cache(struct regmap *map)
{
struct regmap_debugfs_off_cache *c;
@@ -98,6 +118,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
fpos_offset = c->max - c->min;
reg_offset = fpos_offset / map->debugfs_tot_len;
c->max_reg = c->base_reg + reg_offset;
+ c->reg_attr = regmap_attr_bitmap(map, c->max_reg);
list_add_tail(&c->list,
&map->debugfs_off_cache);
c = NULL;
@@ -127,6 +148,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
fpos_offset = c->max - c->min;
reg_offset = fpos_offset / map->debugfs_tot_len;
c->max_reg = c->base_reg + reg_offset;
+ c->reg_attr = regmap_attr_bitmap(map, c->max_reg);
list_add_tail(&c->list,
&map->debugfs_off_cache);
}
@@ -311,6 +333,97 @@ static const struct file_operations regmap_range_fops = {
.llseek = default_llseek,
};
+static void regmap_range_format_line(struct regmap *map,
+ struct regmap_debugfs_off_cache *c,
+ char *buf, size_t len)
+{
+ ssize_t buf_offset;
+
+ buf_offset = snprintf(buf, PAGE_SIZE, "%x-%x ",
+ c->base_reg, c->max_reg);
+ buf_offset += snprintf(buf + buf_offset,
+ PAGE_SIZE - buf_offset, "(");
+ if (c->reg_attr & READABLE)
+ buf_offset += snprintf(buf + buf_offset,
+ PAGE_SIZE - buf_offset,
+ "read, ");
+ if (c->reg_attr & WRITEABLE)
+ buf_offset += snprintf(buf + buf_offset,
+ PAGE_SIZE - buf_offset,
+ "write, ");
+ if (c->reg_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_reg_ranges_read_file(struct file *file,
+ char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ struct regmap *map = file->private_data;
+ struct regmap_debugfs_off_cache *c;
+ loff_t p = 0;
+ size_t buf_pos = 0;
+ char *buf;
+ char *entry;
+ int ret;
+
+ 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;
+ }
+
+ regmap_calc_tot_len(map, buf, count);
+
+ /* Build up the cache if necessary */
+ regmap_debugfs_get_dump_start(map, 0, *ppos, &p);
+ p = 0;
+ list_for_each_entry(c, &map->debugfs_off_cache, list) {
+ regmap_range_format_line(map, c, 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;
+ }
+
+ kfree(entry);
+ ret = buf_pos;
+
+ if (copy_to_user(user_buf, buf, buf_pos)) {
+ ret = -EFAULT;
+ goto out_buf;
+ }
+
+ *ppos += buf_pos;
+out_buf:
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations regmap_reg_ranges_fops = {
+ .open = simple_open,
+ .read = regmap_reg_ranges_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)
@@ -403,6 +516,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_reg_ranges_fops);
+
if (map->max_register) {
debugfs_create_file("registers", 0400, map->debugfs,
map, ®map_map_fops);
--
1.8.1.3
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2013-02-11 14:11 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-01-30 16:30 [PATCH v2] regmap: debugfs: Add a registers `range' file Dimitris Papastamos
2013-02-11 14:10 Dimitris Papastamos
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).