diff options
-rw-r--r-- | drivers/base/regmap/internal.h | 1 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-debugfs.c | 83 |
2 files changed, 84 insertions, 0 deletions
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 5a22bd33ce3d..dc23508745fe 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h | |||
@@ -76,6 +76,7 @@ struct regmap { | |||
76 | unsigned int debugfs_tot_len; | 76 | unsigned int debugfs_tot_len; |
77 | 77 | ||
78 | struct list_head debugfs_off_cache; | 78 | struct list_head debugfs_off_cache; |
79 | struct mutex cache_lock; | ||
79 | #endif | 80 | #endif |
80 | 81 | ||
81 | unsigned int max_register; | 82 | unsigned int max_register; |
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 886b2f7682c2..23b701f5fd2f 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c | |||
@@ -88,6 +88,7 @@ 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); | ||
91 | i = base; | 92 | i = base; |
92 | if (list_empty(&map->debugfs_off_cache)) { | 93 | if (list_empty(&map->debugfs_off_cache)) { |
93 | for (; i <= map->max_register; i += map->reg_stride) { | 94 | for (; i <= map->max_register; i += map->reg_stride) { |
@@ -110,6 +111,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, | |||
110 | c = kzalloc(sizeof(*c), GFP_KERNEL); | 111 | c = kzalloc(sizeof(*c), GFP_KERNEL); |
111 | if (!c) { | 112 | if (!c) { |
112 | regmap_debugfs_free_dump_cache(map); | 113 | regmap_debugfs_free_dump_cache(map); |
114 | mutex_unlock(&map->cache_lock); | ||
113 | return base; | 115 | return base; |
114 | } | 116 | } |
115 | c->min = p; | 117 | c->min = p; |
@@ -142,12 +144,14 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, | |||
142 | fpos_offset = from - c->min; | 144 | fpos_offset = from - c->min; |
143 | reg_offset = fpos_offset / map->debugfs_tot_len; | 145 | reg_offset = fpos_offset / map->debugfs_tot_len; |
144 | *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); | ||
145 | return c->base_reg + reg_offset; | 148 | return c->base_reg + reg_offset; |
146 | } | 149 | } |
147 | 150 | ||
148 | *pos = c->max; | 151 | *pos = c->max; |
149 | ret = c->max_reg; | 152 | ret = c->max_reg; |
150 | } | 153 | } |
154 | mutex_unlock(&map->cache_lock); | ||
151 | 155 | ||
152 | return ret; | 156 | return ret; |
153 | } | 157 | } |
@@ -308,6 +312,79 @@ static const struct file_operations regmap_range_fops = { | |||
308 | .llseek = default_llseek, | 312 | .llseek = default_llseek, |
309 | }; | 313 | }; |
310 | 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 | |||
311 | static ssize_t regmap_access_read_file(struct file *file, | 388 | static ssize_t regmap_access_read_file(struct file *file, |
312 | char __user *user_buf, size_t count, | 389 | char __user *user_buf, size_t count, |
313 | loff_t *ppos) | 390 | loff_t *ppos) |
@@ -382,6 +459,7 @@ void regmap_debugfs_init(struct regmap *map, const char *name) | |||
382 | struct regmap_range_node *range_node; | 459 | struct regmap_range_node *range_node; |
383 | 460 | ||
384 | INIT_LIST_HEAD(&map->debugfs_off_cache); | 461 | INIT_LIST_HEAD(&map->debugfs_off_cache); |
462 | mutex_init(&map->cache_lock); | ||
385 | 463 | ||
386 | if (name) { | 464 | if (name) { |
387 | map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", | 465 | map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", |
@@ -400,6 +478,9 @@ void regmap_debugfs_init(struct regmap *map, const char *name) | |||
400 | debugfs_create_file("name", 0400, map->debugfs, | 478 | debugfs_create_file("name", 0400, map->debugfs, |
401 | map, ®map_name_fops); | 479 | map, ®map_name_fops); |
402 | 480 | ||
481 | debugfs_create_file("range", 0400, map->debugfs, | ||
482 | map, ®map_reg_ranges_fops); | ||
483 | |||
403 | if (map->max_register) { | 484 | if (map->max_register) { |
404 | debugfs_create_file("registers", 0400, map->debugfs, | 485 | debugfs_create_file("registers", 0400, map->debugfs, |
405 | map, ®map_map_fops); | 486 | map, ®map_map_fops); |
@@ -432,7 +513,9 @@ void regmap_debugfs_init(struct regmap *map, const char *name) | |||
432 | void regmap_debugfs_exit(struct regmap *map) | 513 | void regmap_debugfs_exit(struct regmap *map) |
433 | { | 514 | { |
434 | debugfs_remove_recursive(map->debugfs); | 515 | debugfs_remove_recursive(map->debugfs); |
516 | mutex_lock(&map->cache_lock); | ||
435 | regmap_debugfs_free_dump_cache(map); | 517 | regmap_debugfs_free_dump_cache(map); |
518 | mutex_unlock(&map->cache_lock); | ||
436 | kfree(map->debugfs_name); | 519 | kfree(map->debugfs_name); |
437 | } | 520 | } |
438 | 521 | ||