Merge tag 'device-for-3.4' of git://git.kernel.org/pub/scm/linux/kernel/git/paulg...
[linux-flexiantxendom0-3.2.10.git] / drivers / base / regmap / regmap-debugfs.c
1 /*
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/mutex.h>
15 #include <linux/debugfs.h>
16 #include <linux/uaccess.h>
17 #include <linux/device.h>
18
19 #include "internal.h"
20
21 static struct dentry *regmap_debugfs_root;
22
23 /* Calculate the length of a fixed format  */
24 static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size)
25 {
26         snprintf(buf, buf_size, "%x", max_val);
27         return strlen(buf);
28 }
29
30 static int regmap_open_file(struct inode *inode, struct file *file)
31 {
32         file->private_data = inode->i_private;
33         return 0;
34 }
35
36 static ssize_t regmap_name_read_file(struct file *file,
37                                      char __user *user_buf, size_t count,
38                                      loff_t *ppos)
39 {
40         struct regmap *map = file->private_data;
41         int ret;
42         char *buf;
43
44         buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
45         if (!buf)
46                 return -ENOMEM;
47
48         ret = snprintf(buf, PAGE_SIZE, "%s\n", map->dev->driver->name);
49         if (ret < 0) {
50                 kfree(buf);
51                 return ret;
52         }
53
54         ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
55         kfree(buf);
56         return ret;
57 }
58
59 static const struct file_operations regmap_name_fops = {
60         .open = regmap_open_file,
61         .read = regmap_name_read_file,
62         .llseek = default_llseek,
63 };
64
65 static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
66                                     size_t count, loff_t *ppos)
67 {
68         int reg_len, val_len, tot_len;
69         size_t buf_pos = 0;
70         loff_t p = 0;
71         ssize_t ret;
72         int i;
73         struct regmap *map = file->private_data;
74         char *buf;
75         unsigned int val;
76
77         if (*ppos < 0 || !count)
78                 return -EINVAL;
79
80         buf = kmalloc(count, GFP_KERNEL);
81         if (!buf)
82                 return -ENOMEM;
83
84         /* Calculate the length of a fixed format  */
85         reg_len = regmap_calc_reg_len(map->max_register, buf, count);
86         val_len = 2 * map->format.val_bytes;
87         tot_len = reg_len + val_len + 3;      /* : \n */
88
89         for (i = 0; i < map->max_register + 1; i++) {
90                 if (!regmap_readable(map, i))
91                         continue;
92
93                 if (regmap_precious(map, i))
94                         continue;
95
96                 /* If we're in the region the user is trying to read */
97                 if (p >= *ppos) {
98                         /* ...but not beyond it */
99                         if (buf_pos >= count - 1 - tot_len)
100                                 break;
101
102                         /* Format the register */
103                         snprintf(buf + buf_pos, count - buf_pos, "%.*x: ",
104                                  reg_len, i);
105                         buf_pos += reg_len + 2;
106
107                         /* Format the value, write all X if we can't read */
108                         ret = regmap_read(map, i, &val);
109                         if (ret == 0)
110                                 snprintf(buf + buf_pos, count - buf_pos,
111                                          "%.*x", val_len, val);
112                         else
113                                 memset(buf + buf_pos, 'X', val_len);
114                         buf_pos += 2 * map->format.val_bytes;
115
116                         buf[buf_pos++] = '\n';
117                 }
118                 p += tot_len;
119         }
120
121         ret = buf_pos;
122
123         if (copy_to_user(user_buf, buf, buf_pos)) {
124                 ret = -EFAULT;
125                 goto out;
126         }
127
128         *ppos += buf_pos;
129
130 out:
131         kfree(buf);
132         return ret;
133 }
134
135 #undef REGMAP_ALLOW_WRITE_DEBUGFS
136 #ifdef REGMAP_ALLOW_WRITE_DEBUGFS
137 /*
138  * This can be dangerous especially when we have clients such as
139  * PMICs, therefore don't provide any real compile time configuration option
140  * for this feature, people who want to use this will need to modify
141  * the source code directly.
142  */
143 static ssize_t regmap_map_write_file(struct file *file,
144                                      const char __user *user_buf,
145                                      size_t count, loff_t *ppos)
146 {
147         char buf[32];
148         size_t buf_size;
149         char *start = buf;
150         unsigned long reg, value;
151         struct regmap *map = file->private_data;
152
153         buf_size = min(count, (sizeof(buf)-1));
154         if (copy_from_user(buf, user_buf, buf_size))
155                 return -EFAULT;
156         buf[buf_size] = 0;
157
158         while (*start == ' ')
159                 start++;
160         reg = simple_strtoul(start, &start, 16);
161         while (*start == ' ')
162                 start++;
163         if (strict_strtoul(start, 16, &value))
164                 return -EINVAL;
165
166         /* Userspace has been fiddling around behind the kernel's back */
167         add_taint(TAINT_USER);
168
169         regmap_write(map, reg, value);
170         return buf_size;
171 }
172 #else
173 #define regmap_map_write_file NULL
174 #endif
175
176 static const struct file_operations regmap_map_fops = {
177         .open = regmap_open_file,
178         .read = regmap_map_read_file,
179         .write = regmap_map_write_file,
180         .llseek = default_llseek,
181 };
182
183 static ssize_t regmap_access_read_file(struct file *file,
184                                        char __user *user_buf, size_t count,
185                                        loff_t *ppos)
186 {
187         int reg_len, tot_len;
188         size_t buf_pos = 0;
189         loff_t p = 0;
190         ssize_t ret;
191         int i;
192         struct regmap *map = file->private_data;
193         char *buf;
194
195         if (*ppos < 0 || !count)
196                 return -EINVAL;
197
198         buf = kmalloc(count, GFP_KERNEL);
199         if (!buf)
200                 return -ENOMEM;
201
202         /* Calculate the length of a fixed format  */
203         reg_len = regmap_calc_reg_len(map->max_register, buf, count);
204         tot_len = reg_len + 10; /* ': R W V P\n' */
205
206         for (i = 0; i < map->max_register + 1; i++) {
207                 /* Ignore registers which are neither readable nor writable */
208                 if (!regmap_readable(map, i) && !regmap_writeable(map, i))
209                         continue;
210
211                 /* If we're in the region the user is trying to read */
212                 if (p >= *ppos) {
213                         /* ...but not beyond it */
214                         if (buf_pos >= count - 1 - tot_len)
215                                 break;
216
217                         /* Format the register */
218                         snprintf(buf + buf_pos, count - buf_pos,
219                                  "%.*x: %c %c %c %c\n",
220                                  reg_len, i,
221                                  regmap_readable(map, i) ? 'y' : 'n',
222                                  regmap_writeable(map, i) ? 'y' : 'n',
223                                  regmap_volatile(map, i) ? 'y' : 'n',
224                                  regmap_precious(map, i) ? 'y' : 'n');
225
226                         buf_pos += tot_len;
227                 }
228                 p += tot_len;
229         }
230
231         ret = buf_pos;
232
233         if (copy_to_user(user_buf, buf, buf_pos)) {
234                 ret = -EFAULT;
235                 goto out;
236         }
237
238         *ppos += buf_pos;
239
240 out:
241         kfree(buf);
242         return ret;
243 }
244
245 static const struct file_operations regmap_access_fops = {
246         .open = regmap_open_file,
247         .read = regmap_access_read_file,
248         .llseek = default_llseek,
249 };
250
251 void regmap_debugfs_init(struct regmap *map)
252 {
253         map->debugfs = debugfs_create_dir(dev_name(map->dev),
254                                           regmap_debugfs_root);
255         if (!map->debugfs) {
256                 dev_warn(map->dev, "Failed to create debugfs directory\n");
257                 return;
258         }
259
260         debugfs_create_file("name", 0400, map->debugfs,
261                             map, &regmap_name_fops);
262
263         if (map->max_register) {
264                 debugfs_create_file("registers", 0400, map->debugfs,
265                                     map, &regmap_map_fops);
266                 debugfs_create_file("access", 0400, map->debugfs,
267                                     map, &regmap_access_fops);
268         }
269
270         if (map->cache_type) {
271                 debugfs_create_bool("cache_only", 0400, map->debugfs,
272                                     &map->cache_only);
273                 debugfs_create_bool("cache_dirty", 0400, map->debugfs,
274                                     &map->cache_dirty);
275                 debugfs_create_bool("cache_bypass", 0400, map->debugfs,
276                                     &map->cache_bypass);
277         }
278 }
279
280 void regmap_debugfs_exit(struct regmap *map)
281 {
282         debugfs_remove_recursive(map->debugfs);
283 }
284
285 void regmap_debugfs_initcall(void)
286 {
287         regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
288         if (!regmap_debugfs_root) {
289                 pr_warn("regmap: Failed to create debugfs root\n");
290                 return;
291         }
292 }