blob: 25b6843d6a41b4ad9054a875090b1cb881d602d2 [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 Brown449e3842011-08-10 17:28:04 +0900186static ssize_t regmap_access_read_file(struct file *file,
187 char __user *user_buf, size_t count,
188 loff_t *ppos)
189{
190 int reg_len, tot_len;
191 size_t buf_pos = 0;
192 loff_t p = 0;
193 ssize_t ret;
194 int i;
195 struct regmap *map = file->private_data;
196 char *buf;
197
198 if (*ppos < 0 || !count)
199 return -EINVAL;
200
201 buf = kmalloc(count, GFP_KERNEL);
202 if (!buf)
203 return -ENOMEM;
204
205 /* Calculate the length of a fixed format */
206 reg_len = regmap_calc_reg_len(map->max_register, buf, count);
207 tot_len = reg_len + 10; /* ': R W V P\n' */
208
Stephen Warrenf01ee602012-04-09 13:40:24 -0600209 for (i = 0; i <= map->max_register; i += map->reg_stride) {
Mark Brown449e3842011-08-10 17:28:04 +0900210 /* Ignore registers which are neither readable nor writable */
211 if (!regmap_readable(map, i) && !regmap_writeable(map, i))
212 continue;
213
214 /* If we're in the region the user is trying to read */
215 if (p >= *ppos) {
216 /* ...but not beyond it */
217 if (buf_pos >= count - 1 - tot_len)
218 break;
219
220 /* Format the register */
221 snprintf(buf + buf_pos, count - buf_pos,
222 "%.*x: %c %c %c %c\n",
223 reg_len, i,
224 regmap_readable(map, i) ? 'y' : 'n',
225 regmap_writeable(map, i) ? 'y' : 'n',
226 regmap_volatile(map, i) ? 'y' : 'n',
227 regmap_precious(map, i) ? 'y' : 'n');
228
229 buf_pos += tot_len;
230 }
231 p += tot_len;
232 }
233
234 ret = buf_pos;
235
236 if (copy_to_user(user_buf, buf, buf_pos)) {
237 ret = -EFAULT;
238 goto out;
239 }
240
241 *ppos += buf_pos;
242
243out:
244 kfree(buf);
245 return ret;
246}
247
248static const struct file_operations regmap_access_fops = {
Stephen Boyd234e3402012-04-05 14:25:11 -0700249 .open = simple_open,
Mark Brown449e3842011-08-10 17:28:04 +0900250 .read = regmap_access_read_file,
251 .llseek = default_llseek,
252};
Mark Brown31244e32011-07-20 22:56:53 +0100253
Stephen Warrend3c242e2012-04-04 15:48:29 -0600254void regmap_debugfs_init(struct regmap *map, const char *name)
Mark Brown31244e32011-07-20 22:56:53 +0100255{
Stephen Warrend3c242e2012-04-04 15:48:29 -0600256 if (name) {
257 map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
258 dev_name(map->dev), name);
259 name = map->debugfs_name;
260 } else {
261 name = dev_name(map->dev);
262 }
263
264 map->debugfs = debugfs_create_dir(name, regmap_debugfs_root);
Mark Brown31244e32011-07-20 22:56:53 +0100265 if (!map->debugfs) {
266 dev_warn(map->dev, "Failed to create debugfs directory\n");
267 return;
268 }
269
Dimitris Papastamosf0c23192012-02-22 14:20:09 +0000270 debugfs_create_file("name", 0400, map->debugfs,
271 map, &regmap_name_fops);
272
Mark Brown449e3842011-08-10 17:28:04 +0900273 if (map->max_register) {
Mark Brown31244e32011-07-20 22:56:53 +0100274 debugfs_create_file("registers", 0400, map->debugfs,
275 map, &regmap_map_fops);
Mark Brown449e3842011-08-10 17:28:04 +0900276 debugfs_create_file("access", 0400, map->debugfs,
277 map, &regmap_access_fops);
278 }
Mark Brown028a01e2012-02-06 18:02:06 +0000279
280 if (map->cache_type) {
281 debugfs_create_bool("cache_only", 0400, map->debugfs,
282 &map->cache_only);
283 debugfs_create_bool("cache_dirty", 0400, map->debugfs,
284 &map->cache_dirty);
285 debugfs_create_bool("cache_bypass", 0400, map->debugfs,
286 &map->cache_bypass);
287 }
Mark Brown31244e32011-07-20 22:56:53 +0100288}
289
290void regmap_debugfs_exit(struct regmap *map)
291{
292 debugfs_remove_recursive(map->debugfs);
Stephen Warrend3c242e2012-04-04 15:48:29 -0600293 kfree(map->debugfs_name);
Mark Brown31244e32011-07-20 22:56:53 +0100294}
295
296void regmap_debugfs_initcall(void)
297{
298 regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
299 if (!regmap_debugfs_root) {
300 pr_warn("regmap: Failed to create debugfs root\n");
301 return;
302 }
303}