]> Pileus Git - ~andy/linux/blobdiff - drivers/base/regmap/regmap-debugfs.c
Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[~andy/linux] / drivers / base / regmap / regmap-debugfs.c
index f4b9dd01c9819ad4e1bc789593876a1f449eb8e3..07aad786f817e88300af9c4d6d685391f41b3471 100644 (file)
@@ -56,17 +56,74 @@ static const struct file_operations regmap_name_fops = {
        .llseek = default_llseek,
 };
 
+/*
+ * Work out where the start offset maps into register numbers, bearing
+ * in mind that we suppress hidden registers.
+ */
+static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
+                                                 unsigned int base,
+                                                 loff_t from,
+                                                 loff_t *pos)
+{
+       struct regmap_debugfs_off_cache *c = NULL;
+       loff_t p = 0;
+       unsigned int i, ret;
+
+       /*
+        * If we don't have a cache build one so we don't have to do a
+        * linear scan each time.
+        */
+       if (list_empty(&map->debugfs_off_cache)) {
+               for (i = base; i <= map->max_register; i += map->reg_stride) {
+                       /* Skip unprinted registers, closing off cache entry */
+                       if (!regmap_readable(map, i) ||
+                           regmap_precious(map, i)) {
+                               if (c) {
+                                       c->max = p - 1;
+                                       list_add_tail(&c->list,
+                                                     &map->debugfs_off_cache);
+                                       c = NULL;
+                               }
+
+                               continue;
+                       }
+
+                       /* No cache entry?  Start a new one */
+                       if (!c) {
+                               c = kzalloc(sizeof(*c), GFP_KERNEL);
+                               if (!c)
+                                       break;
+                               c->min = p;
+                               c->base_reg = i;
+                       }
+
+                       p += map->debugfs_tot_len;
+               }
+       }
+
+       /* Find the relevant block */
+       list_for_each_entry(c, &map->debugfs_off_cache, list) {
+               if (*pos >= c->min && *pos <= c->max) {
+                       *pos = c->min;
+                       return c->base_reg;
+               }
+
+               ret = c->max;
+       }
+
+       return ret;
+}
+
 static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
                                   unsigned int to, char __user *user_buf,
                                   size_t count, loff_t *ppos)
 {
-       int reg_len, val_len, tot_len;
        size_t buf_pos = 0;
-       loff_t p = 0;
+       loff_t p = *ppos;
        ssize_t ret;
        int i;
        char *buf;
-       unsigned int val;
+       unsigned int val, start_reg;
 
        if (*ppos < 0 || !count)
                return -EINVAL;
@@ -76,11 +133,18 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
                return -ENOMEM;
 
        /* Calculate the length of a fixed format  */
-       reg_len = regmap_calc_reg_len(map->max_register, buf, count);
-       val_len = 2 * map->format.val_bytes;
-       tot_len = reg_len + val_len + 3;      /* : \n */
+       if (!map->debugfs_tot_len) {
+               map->debugfs_reg_len = regmap_calc_reg_len(map->max_register,
+                                                          buf, count);
+               map->debugfs_val_len = 2 * map->format.val_bytes;
+               map->debugfs_tot_len = map->debugfs_reg_len +
+                       map->debugfs_val_len + 3;      /* : \n */
+       }
+
+       /* Work out which register we're starting at */
+       start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p);
 
-       for (i = from; i <= to; i += map->reg_stride) {
+       for (i = start_reg; i <= to; i += map->reg_stride) {
                if (!regmap_readable(map, i))
                        continue;
 
@@ -90,26 +154,27 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
                /* If we're in the region the user is trying to read */
                if (p >= *ppos) {
                        /* ...but not beyond it */
-                       if (buf_pos >= count - 1 - tot_len)
+                       if (buf_pos + 1 + map->debugfs_tot_len >= count)
                                break;
 
                        /* Format the register */
                        snprintf(buf + buf_pos, count - buf_pos, "%.*x: ",
-                                reg_len, i - from);
-                       buf_pos += reg_len + 2;
+                                map->debugfs_reg_len, i - from);
+                       buf_pos += map->debugfs_reg_len + 2;
 
                        /* Format the value, write all X if we can't read */
                        ret = regmap_read(map, i, &val);
                        if (ret == 0)
                                snprintf(buf + buf_pos, count - buf_pos,
-                                        "%.*x", val_len, val);
+                                        "%.*x", map->debugfs_val_len, val);
                        else
-                               memset(buf + buf_pos, 'X', val_len);
+                               memset(buf + buf_pos, 'X',
+                                      map->debugfs_val_len);
                        buf_pos += 2 * map->format.val_bytes;
 
                        buf[buf_pos++] = '\n';
                }
-               p += tot_len;
+               p += map->debugfs_tot_len;
        }
 
        ret = buf_pos;
@@ -272,6 +337,8 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
        struct rb_node *next;
        struct regmap_range_node *range_node;
 
+       INIT_LIST_HEAD(&map->debugfs_off_cache);
+
        if (name) {
                map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
                                              dev_name(map->dev), name);
@@ -320,7 +387,16 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
 
 void regmap_debugfs_exit(struct regmap *map)
 {
+       struct regmap_debugfs_off_cache *c;
+
        debugfs_remove_recursive(map->debugfs);
+       while (!list_empty(&map->debugfs_off_cache)) {
+               c = list_first_entry(&map->debugfs_off_cache,
+                                    struct regmap_debugfs_off_cache,
+                                    list);
+               list_del(&c->list);
+               kfree(c);
+       }
        kfree(map->debugfs_name);
 }