blob: 2be8bf86825b3013e4699297d8f7652cedf95e82 [file] [log] [blame]
Mark Brown31244e32011-07-20 22:56:53 +01001/*
2 * Register map access API - debugfs
3 *
4 * Copyright 2011 Wolfson Microelectronics plc
5 *
6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/slab.h>
14#include <linux/module.h>
15#include <linux/mutex.h>
16#include <linux/debugfs.h>
17#include <linux/uaccess.h>
18
19#include "internal.h"
20
21static struct dentry *regmap_debugfs_root;
22
23static int regmap_map_open_file(struct inode *inode, struct file *file)
24{
25 file->private_data = inode->i_private;
26 return 0;
27}
28
29static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
30 size_t count, loff_t *ppos)
31{
32 size_t reg_len, val_len, tot_len;
33 size_t buf_pos = 0;
34 loff_t p = 0;
35 ssize_t ret;
36 int i;
37 struct regmap *map = file->private_data;
38 char *buf;
39 unsigned int val;
40
41 if (*ppos < 0 || !count)
42 return -EINVAL;
43
44 buf = kmalloc(count, GFP_KERNEL);
45 if (!buf)
46 return -ENOMEM;
47
48 /* Calculate the length of a fixed format */
49 snprintf(buf, count, "%x", map->max_register);
50 reg_len = strlen(buf);
51 val_len = 2 * map->format.val_bytes;
52 tot_len = reg_len + val_len + 3; /* : \n */
53
54 for (i = 0; i < map->max_register; i++) {
55 if (map->readable_reg &&
56 !map->readable_reg(map->dev, i))
57 continue;
58
59 /* If we're in the region the user is trying to read */
60 if (p >= *ppos) {
61 /* ...but not beyond it */
62 if (buf_pos >= count - 1 - tot_len)
63 break;
64
65 /* Format the register */
66 snprintf(buf + buf_pos, count - buf_pos, "%.*x: ",
67 reg_len, i);
68 buf_pos += reg_len + 2;
69
70 /* Format the value, write all X if we can't read */
71 ret = regmap_read(map, i, &val);
72 if (ret == 0)
73 snprintf(buf + buf_pos, count - buf_pos,
74 "%.*x", val_len, val);
75 else
76 memset(buf + buf_pos, 'X', val_len);
77 buf_pos += 2 * map->format.val_bytes;
78
79 buf[buf_pos++] = '\n';
80 }
81 p += tot_len;
82 }
83
84 ret = buf_pos;
85
86 if (copy_to_user(user_buf, buf, buf_pos)) {
87 ret = -EFAULT;
88 goto out;
89 }
90
91 *ppos += buf_pos;
92
93out:
94 kfree(buf);
95 return ret;
96}
97
98static const struct file_operations regmap_map_fops = {
99 .open = regmap_map_open_file,
100 .read = regmap_map_read_file,
101 .llseek = default_llseek,
102};
103
104
105void regmap_debugfs_init(struct regmap *map)
106{
107 map->debugfs = debugfs_create_dir(dev_name(map->dev),
108 regmap_debugfs_root);
109 if (!map->debugfs) {
110 dev_warn(map->dev, "Failed to create debugfs directory\n");
111 return;
112 }
113
114 if (map->max_register)
115 debugfs_create_file("registers", 0400, map->debugfs,
116 map, &regmap_map_fops);
117}
118
119void regmap_debugfs_exit(struct regmap *map)
120{
121 debugfs_remove_recursive(map->debugfs);
122}
123
124void regmap_debugfs_initcall(void)
125{
126 regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
127 if (!regmap_debugfs_root) {
128 pr_warn("regmap: Failed to create debugfs root\n");
129 return;
130 }
131}