blob: f4b9dd01c9819ad4e1bc789593876a1f449eb8e3 [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>
Mark Brown31244e32011-07-20 22:56:53 +010014#include <linux/mutex.h>
15#include <linux/debugfs.h>
16#include <linux/uaccess.h>
Paul Gortmaker51990e82012-01-22 11:23:42 -050017#include <linux/device.h>
Mark Brown31244e32011-07-20 22:56:53 +010018
19#include "internal.h"
20
21static struct dentry *regmap_debugfs_root;
22
Mark Brown21f55542011-08-10 17:15:31 +090023/* Calculate the length of a fixed format */
24static 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
Dimitris Papastamosf0c23192012-02-22 14:20:09 +000030static ssize_t regmap_name_read_file(struct file *file,
31 char __user *user_buf, size_t count,
32 loff_t *ppos)
33{
34 struct regmap *map = file->private_data;
35 int ret;
36 char *buf;
37
38 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
39 if (!buf)
40 return -ENOMEM;
41
42 ret = snprintf(buf, PAGE_SIZE, "%s\n", map->dev->driver->name);
43 if (ret < 0) {
44 kfree(buf);
45 return ret;
46 }
47
48 ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
49 kfree(buf);
50 return ret;
51}
52
53static const struct file_operations regmap_name_fops = {
Stephen Boyd234e3402012-04-05 14:25:11 -070054 .open = simple_open,
Dimitris Papastamosf0c23192012-02-22 14:20:09 +000055 .read = regmap_name_read_file,
56 .llseek = default_llseek,
57};
58
Mark Brownbd9cc122012-10-03 12:45:37 +010059static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
60 unsigned int to, char __user *user_buf,
61 size_t count, loff_t *ppos)
Mark Brown31244e32011-07-20 22:56:53 +010062{
Mark Browncb3c2dc2011-08-09 16:47:42 +090063 int reg_len, val_len, tot_len;
Mark Brown31244e32011-07-20 22:56:53 +010064 size_t buf_pos = 0;
65 loff_t p = 0;
66 ssize_t ret;
67 int i;
Mark Brown31244e32011-07-20 22:56:53 +010068 char *buf;
69 unsigned int val;
70
71 if (*ppos < 0 || !count)
72 return -EINVAL;
73
74 buf = kmalloc(count, GFP_KERNEL);
75 if (!buf)
76 return -ENOMEM;
77
78 /* Calculate the length of a fixed format */
Mark Brown21f55542011-08-10 17:15:31 +090079 reg_len = regmap_calc_reg_len(map->max_register, buf, count);
Mark Brown31244e32011-07-20 22:56:53 +010080 val_len = 2 * map->format.val_bytes;
81 tot_len = reg_len + val_len + 3; /* : \n */
82
Mark Brownbd9cc122012-10-03 12:45:37 +010083 for (i = from; i <= to; i += map->reg_stride) {
Mark Brown8de2f082011-08-10 17:14:41 +090084 if (!regmap_readable(map, i))
Mark Brown31244e32011-07-20 22:56:53 +010085 continue;
86
Mark Brown8de2f082011-08-10 17:14:41 +090087 if (regmap_precious(map, i))
Mark Brown2efe1642011-08-08 15:41:46 +090088 continue;
89
Mark Brown31244e32011-07-20 22:56:53 +010090 /* If we're in the region the user is trying to read */
91 if (p >= *ppos) {
92 /* ...but not beyond it */
93 if (buf_pos >= count - 1 - tot_len)
94 break;
95
96 /* Format the register */
97 snprintf(buf + buf_pos, count - buf_pos, "%.*x: ",
Mark Brownbd9cc122012-10-03 12:45:37 +010098 reg_len, i - from);
Mark Brown31244e32011-07-20 22:56:53 +010099 buf_pos += reg_len + 2;
100
101 /* Format the value, write all X if we can't read */
102 ret = regmap_read(map, i, &val);
103 if (ret == 0)
104 snprintf(buf + buf_pos, count - buf_pos,
105 "%.*x", val_len, val);
106 else
107 memset(buf + buf_pos, 'X', val_len);
108 buf_pos += 2 * map->format.val_bytes;
109
110 buf[buf_pos++] = '\n';
111 }
112 p += tot_len;
113 }
114
115 ret = buf_pos;
116
117 if (copy_to_user(user_buf, buf, buf_pos)) {
118 ret = -EFAULT;
119 goto out;
120 }
121
122 *ppos += buf_pos;
123
124out:
125 kfree(buf);
126 return ret;
127}
128
Mark Brownbd9cc122012-10-03 12:45:37 +0100129static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
130 size_t count, loff_t *ppos)
131{
132 struct regmap *map = file->private_data;
133
134 return regmap_read_debugfs(map, 0, map->max_register, user_buf,
135 count, ppos);
136}
137
Dimitris Papastamos09c6ecd2012-02-22 12:43:50 +0000138#undef REGMAP_ALLOW_WRITE_DEBUGFS
139#ifdef REGMAP_ALLOW_WRITE_DEBUGFS
140/*
141 * This can be dangerous especially when we have clients such as
142 * PMICs, therefore don't provide any real compile time configuration option
143 * for this feature, people who want to use this will need to modify
144 * the source code directly.
145 */
146static ssize_t regmap_map_write_file(struct file *file,
147 const char __user *user_buf,
148 size_t count, loff_t *ppos)
149{
150 char buf[32];
151 size_t buf_size;
152 char *start = buf;
153 unsigned long reg, value;
154 struct regmap *map = file->private_data;
155
156 buf_size = min(count, (sizeof(buf)-1));
157 if (copy_from_user(buf, user_buf, buf_size))
158 return -EFAULT;
159 buf[buf_size] = 0;
160
161 while (*start == ' ')
162 start++;
163 reg = simple_strtoul(start, &start, 16);
164 while (*start == ' ')
165 start++;
166 if (strict_strtoul(start, 16, &value))
167 return -EINVAL;
168
169 /* Userspace has been fiddling around behind the kernel's back */
170 add_taint(TAINT_USER);
171
172 regmap_write(map, reg, value);
173 return buf_size;
174}
175#else
176#define regmap_map_write_file NULL
177#endif
178
Mark Brown31244e32011-07-20 22:56:53 +0100179static const struct file_operations regmap_map_fops = {
Stephen Boyd234e3402012-04-05 14:25:11 -0700180 .open = simple_open,
Mark Brown31244e32011-07-20 22:56:53 +0100181 .read = regmap_map_read_file,
Dimitris Papastamos09c6ecd2012-02-22 12:43:50 +0000182 .write = regmap_map_write_file,
Mark Brown31244e32011-07-20 22:56:53 +0100183 .llseek = default_llseek,
184};
185
Mark Brown4b020b32012-10-03 13:13:16 +0100186static ssize_t regmap_range_read_file(struct file *file, char __user *user_buf,
187 size_t count, loff_t *ppos)
188{
189 struct regmap_range_node *range = file->private_data;
190 struct regmap *map = range->map;
191
192 return regmap_read_debugfs(map, range->range_min, range->range_max,
193 user_buf, count, ppos);
194}
195
196static const struct file_operations regmap_range_fops = {
197 .open = simple_open,
198 .read = regmap_range_read_file,
199 .llseek = default_llseek,
200};
201
Mark Brown449e3842011-08-10 17:28:04 +0900202static ssize_t regmap_access_read_file(struct file *file,
203 char __user *user_buf, size_t count,
204 loff_t *ppos)
205{
206 int reg_len, tot_len;
207 size_t buf_pos = 0;
208 loff_t p = 0;
209 ssize_t ret;
210 int i;
211 struct regmap *map = file->private_data;
212 char *buf;
213
214 if (*ppos < 0 || !count)
215 return -EINVAL;
216
217 buf = kmalloc(count, GFP_KERNEL);
218 if (!buf)
219 return -ENOMEM;
220
221 /* Calculate the length of a fixed format */
222 reg_len = regmap_calc_reg_len(map->max_register, buf, count);
223 tot_len = reg_len + 10; /* ': R W V P\n' */
224
Stephen Warrenf01ee602012-04-09 13:40:24 -0600225 for (i = 0; i <= map->max_register; i += map->reg_stride) {
Mark Brown449e3842011-08-10 17:28:04 +0900226 /* Ignore registers which are neither readable nor writable */
227 if (!regmap_readable(map, i) && !regmap_writeable(map, i))
228 continue;
229
230 /* If we're in the region the user is trying to read */
231 if (p >= *ppos) {
232 /* ...but not beyond it */
233 if (buf_pos >= count - 1 - tot_len)
234 break;
235
236 /* Format the register */
237 snprintf(buf + buf_pos, count - buf_pos,
238 "%.*x: %c %c %c %c\n",
239 reg_len, i,
240 regmap_readable(map, i) ? 'y' : 'n',
241 regmap_writeable(map, i) ? 'y' : 'n',
242 regmap_volatile(map, i) ? 'y' : 'n',
243 regmap_precious(map, i) ? 'y' : 'n');
244
245 buf_pos += tot_len;
246 }
247 p += tot_len;
248 }
249
250 ret = buf_pos;
251
252 if (copy_to_user(user_buf, buf, buf_pos)) {
253 ret = -EFAULT;
254 goto out;
255 }
256
257 *ppos += buf_pos;
258
259out:
260 kfree(buf);
261 return ret;
262}
263
264static const struct file_operations regmap_access_fops = {
Stephen Boyd234e3402012-04-05 14:25:11 -0700265 .open = simple_open,
Mark Brown449e3842011-08-10 17:28:04 +0900266 .read = regmap_access_read_file,
267 .llseek = default_llseek,
268};
Mark Brown31244e32011-07-20 22:56:53 +0100269
Stephen Warrend3c242e2012-04-04 15:48:29 -0600270void regmap_debugfs_init(struct regmap *map, const char *name)
Mark Brown31244e32011-07-20 22:56:53 +0100271{
Mark Brown4b020b32012-10-03 13:13:16 +0100272 struct rb_node *next;
273 struct regmap_range_node *range_node;
274
Stephen Warrend3c242e2012-04-04 15:48:29 -0600275 if (name) {
276 map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
277 dev_name(map->dev), name);
278 name = map->debugfs_name;
279 } else {
280 name = dev_name(map->dev);
281 }
282
283 map->debugfs = debugfs_create_dir(name, regmap_debugfs_root);
Mark Brown31244e32011-07-20 22:56:53 +0100284 if (!map->debugfs) {
285 dev_warn(map->dev, "Failed to create debugfs directory\n");
286 return;
287 }
288
Dimitris Papastamosf0c23192012-02-22 14:20:09 +0000289 debugfs_create_file("name", 0400, map->debugfs,
290 map, &regmap_name_fops);
291
Mark Brown449e3842011-08-10 17:28:04 +0900292 if (map->max_register) {
Mark Brown31244e32011-07-20 22:56:53 +0100293 debugfs_create_file("registers", 0400, map->debugfs,
294 map, &regmap_map_fops);
Mark Brown449e3842011-08-10 17:28:04 +0900295 debugfs_create_file("access", 0400, map->debugfs,
296 map, &regmap_access_fops);
297 }
Mark Brown028a01e2012-02-06 18:02:06 +0000298
299 if (map->cache_type) {
300 debugfs_create_bool("cache_only", 0400, map->debugfs,
301 &map->cache_only);
302 debugfs_create_bool("cache_dirty", 0400, map->debugfs,
303 &map->cache_dirty);
304 debugfs_create_bool("cache_bypass", 0400, map->debugfs,
305 &map->cache_bypass);
306 }
Mark Brown4b020b32012-10-03 13:13:16 +0100307
308 next = rb_first(&map->range_tree);
309 while (next) {
310 range_node = rb_entry(next, struct regmap_range_node, node);
311
312 if (range_node->name)
313 debugfs_create_file(range_node->name, 0400,
314 map->debugfs, range_node,
315 &regmap_range_fops);
316
317 next = rb_next(&range_node->node);
318 }
Mark Brown31244e32011-07-20 22:56:53 +0100319}
320
321void regmap_debugfs_exit(struct regmap *map)
322{
323 debugfs_remove_recursive(map->debugfs);
Stephen Warrend3c242e2012-04-04 15:48:29 -0600324 kfree(map->debugfs_name);
Mark Brown31244e32011-07-20 22:56:53 +0100325}
326
327void regmap_debugfs_initcall(void)
328{
329 regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
330 if (!regmap_debugfs_root) {
331 pr_warn("regmap: Failed to create debugfs root\n");
332 return;
333 }
334}