diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/regmap/internal.h | 1 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-debugfs.c | 94 |
2 files changed, 88 insertions, 7 deletions
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 01fbe48e8155..c130536e0ab0 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h | |||
@@ -77,6 +77,7 @@ struct regmap { | |||
77 | unsigned int debugfs_tot_len; | 77 | unsigned int debugfs_tot_len; |
78 | 78 | ||
79 | struct list_head debugfs_off_cache; | 79 | struct list_head debugfs_off_cache; |
80 | struct mutex cache_lock; | ||
80 | #endif | 81 | #endif |
81 | 82 | ||
82 | unsigned int max_register; | 83 | unsigned int max_register; |
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 81d6f605c92e..23b701f5fd2f 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c | |||
@@ -88,16 +88,16 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, | |||
88 | * If we don't have a cache build one so we don't have to do a | 88 | * If we don't have a cache build one so we don't have to do a |
89 | * linear scan each time. | 89 | * linear scan each time. |
90 | */ | 90 | */ |
91 | mutex_lock(&map->cache_lock); | ||
92 | i = base; | ||
91 | if (list_empty(&map->debugfs_off_cache)) { | 93 | if (list_empty(&map->debugfs_off_cache)) { |
92 | for (i = base; i <= map->max_register; i += map->reg_stride) { | 94 | for (; i <= map->max_register; i += map->reg_stride) { |
93 | /* Skip unprinted registers, closing off cache entry */ | 95 | /* Skip unprinted registers, closing off cache entry */ |
94 | if (!regmap_readable(map, i) || | 96 | if (!regmap_readable(map, i) || |
95 | regmap_precious(map, i)) { | 97 | regmap_precious(map, i)) { |
96 | if (c) { | 98 | if (c) { |
97 | c->max = p - 1; | 99 | c->max = p - 1; |
98 | fpos_offset = c->max - c->min; | 100 | c->max_reg = i - map->reg_stride; |
99 | reg_offset = fpos_offset / map->debugfs_tot_len; | ||
100 | c->max_reg = c->base_reg + reg_offset; | ||
101 | list_add_tail(&c->list, | 101 | list_add_tail(&c->list, |
102 | &map->debugfs_off_cache); | 102 | &map->debugfs_off_cache); |
103 | c = NULL; | 103 | c = NULL; |
@@ -111,6 +111,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, | |||
111 | c = kzalloc(sizeof(*c), GFP_KERNEL); | 111 | c = kzalloc(sizeof(*c), GFP_KERNEL); |
112 | if (!c) { | 112 | if (!c) { |
113 | regmap_debugfs_free_dump_cache(map); | 113 | regmap_debugfs_free_dump_cache(map); |
114 | mutex_unlock(&map->cache_lock); | ||
114 | return base; | 115 | return base; |
115 | } | 116 | } |
116 | c->min = p; | 117 | c->min = p; |
@@ -124,9 +125,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, | |||
124 | /* Close the last entry off if we didn't scan beyond it */ | 125 | /* Close the last entry off if we didn't scan beyond it */ |
125 | if (c) { | 126 | if (c) { |
126 | c->max = p - 1; | 127 | c->max = p - 1; |
127 | fpos_offset = c->max - c->min; | 128 | c->max_reg = i - map->reg_stride; |
128 | reg_offset = fpos_offset / map->debugfs_tot_len; | ||
129 | c->max_reg = c->base_reg + reg_offset; | ||
130 | list_add_tail(&c->list, | 129 | list_add_tail(&c->list, |
131 | &map->debugfs_off_cache); | 130 | &map->debugfs_off_cache); |
132 | } | 131 | } |
@@ -145,12 +144,14 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, | |||
145 | fpos_offset = from - c->min; | 144 | fpos_offset = from - c->min; |
146 | reg_offset = fpos_offset / map->debugfs_tot_len; | 145 | reg_offset = fpos_offset / map->debugfs_tot_len; |
147 | *pos = c->min + (reg_offset * map->debugfs_tot_len); | 146 | *pos = c->min + (reg_offset * map->debugfs_tot_len); |
147 | mutex_unlock(&map->cache_lock); | ||
148 | return c->base_reg + reg_offset; | 148 | return c->base_reg + reg_offset; |
149 | } | 149 | } |
150 | 150 | ||
151 | *pos = c->max; | 151 | *pos = c->max; |
152 | ret = c->max_reg; | 152 | ret = c->max_reg; |
153 | } | 153 | } |
154 | mutex_unlock(&map->cache_lock); | ||
154 | 155 | ||
155 | return ret; | 156 | return ret; |
156 | } | 157 | } |
@@ -311,6 +312,79 @@ static const struct file_operations regmap_range_fops = { | |||
311 | .llseek = default_llseek, | 312 | .llseek = default_llseek, |
312 | }; | 313 | }; |
313 | 314 | ||
315 | static ssize_t regmap_reg_ranges_read_file(struct file *file, | ||
316 | char __user *user_buf, size_t count, | ||
317 | loff_t *ppos) | ||
318 | { | ||
319 | struct regmap *map = file->private_data; | ||
320 | struct regmap_debugfs_off_cache *c; | ||
321 | loff_t p = 0; | ||
322 | size_t buf_pos = 0; | ||
323 | char *buf; | ||
324 | char *entry; | ||
325 | int ret; | ||
326 | |||
327 | if (*ppos < 0 || !count) | ||
328 | return -EINVAL; | ||
329 | |||
330 | buf = kmalloc(count, GFP_KERNEL); | ||
331 | if (!buf) | ||
332 | return -ENOMEM; | ||
333 | |||
334 | entry = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
335 | if (!entry) { | ||
336 | kfree(buf); | ||
337 | return -ENOMEM; | ||
338 | } | ||
339 | |||
340 | /* While we are at it, build the register dump cache | ||
341 | * now so the read() operation on the `registers' file | ||
342 | * can benefit from using the cache. We do not care | ||
343 | * about the file position information that is contained | ||
344 | * in the cache, just about the actual register blocks */ | ||
345 | regmap_calc_tot_len(map, buf, count); | ||
346 | regmap_debugfs_get_dump_start(map, 0, *ppos, &p); | ||
347 | |||
348 | /* Reset file pointer as the fixed-format of the `registers' | ||
349 | * file is not compatible with the `range' file */ | ||
350 | p = 0; | ||
351 | mutex_lock(&map->cache_lock); | ||
352 | list_for_each_entry(c, &map->debugfs_off_cache, list) { | ||
353 | snprintf(entry, PAGE_SIZE, "%x-%x", | ||
354 | c->base_reg, c->max_reg); | ||
355 | if (p >= *ppos) { | ||
356 | if (buf_pos + 1 + strlen(entry) > count) | ||
357 | break; | ||
358 | snprintf(buf + buf_pos, count - buf_pos, | ||
359 | "%s", entry); | ||
360 | buf_pos += strlen(entry); | ||
361 | buf[buf_pos] = '\n'; | ||
362 | buf_pos++; | ||
363 | } | ||
364 | p += strlen(entry) + 1; | ||
365 | } | ||
366 | mutex_unlock(&map->cache_lock); | ||
367 | |||
368 | kfree(entry); | ||
369 | ret = buf_pos; | ||
370 | |||
371 | if (copy_to_user(user_buf, buf, buf_pos)) { | ||
372 | ret = -EFAULT; | ||
373 | goto out_buf; | ||
374 | } | ||
375 | |||
376 | *ppos += buf_pos; | ||
377 | out_buf: | ||
378 | kfree(buf); | ||
379 | return ret; | ||
380 | } | ||
381 | |||
382 | static const struct file_operations regmap_reg_ranges_fops = { | ||
383 | .open = simple_open, | ||
384 | .read = regmap_reg_ranges_read_file, | ||
385 | .llseek = default_llseek, | ||
386 | }; | ||
387 | |||
314 | static ssize_t regmap_access_read_file(struct file *file, | 388 | static ssize_t regmap_access_read_file(struct file *file, |
315 | char __user *user_buf, size_t count, | 389 | char __user *user_buf, size_t count, |
316 | loff_t *ppos) | 390 | loff_t *ppos) |
@@ -385,6 +459,7 @@ void regmap_debugfs_init(struct regmap *map, const char *name) | |||
385 | struct regmap_range_node *range_node; | 459 | struct regmap_range_node *range_node; |
386 | 460 | ||
387 | INIT_LIST_HEAD(&map->debugfs_off_cache); | 461 | INIT_LIST_HEAD(&map->debugfs_off_cache); |
462 | mutex_init(&map->cache_lock); | ||
388 | 463 | ||
389 | if (name) { | 464 | if (name) { |
390 | map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", | 465 | map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", |
@@ -403,6 +478,9 @@ void regmap_debugfs_init(struct regmap *map, const char *name) | |||
403 | debugfs_create_file("name", 0400, map->debugfs, | 478 | debugfs_create_file("name", 0400, map->debugfs, |
404 | map, ®map_name_fops); | 479 | map, ®map_name_fops); |
405 | 480 | ||
481 | debugfs_create_file("range", 0400, map->debugfs, | ||
482 | map, ®map_reg_ranges_fops); | ||
483 | |||
406 | if (map->max_register) { | 484 | if (map->max_register) { |
407 | debugfs_create_file("registers", 0400, map->debugfs, | 485 | debugfs_create_file("registers", 0400, map->debugfs, |
408 | map, ®map_map_fops); | 486 | map, ®map_map_fops); |
@@ -435,7 +513,9 @@ void regmap_debugfs_init(struct regmap *map, const char *name) | |||
435 | void regmap_debugfs_exit(struct regmap *map) | 513 | void regmap_debugfs_exit(struct regmap *map) |
436 | { | 514 | { |
437 | debugfs_remove_recursive(map->debugfs); | 515 | debugfs_remove_recursive(map->debugfs); |
516 | mutex_lock(&map->cache_lock); | ||
438 | regmap_debugfs_free_dump_cache(map); | 517 | regmap_debugfs_free_dump_cache(map); |
518 | mutex_unlock(&map->cache_lock); | ||
439 | kfree(map->debugfs_name); | 519 | kfree(map->debugfs_name); |
440 | } | 520 | } |
441 | 521 | ||