blob: 58517a5dac1360b96b0779c0724d12e926d11c77 [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
30static int regmap_open_file(struct inode *inode, struct file *file)
Mark Brown31244e32011-07-20 22:56:53 +010031{
32 file->private_data = inode->i_private;
33 return 0;
34}
35
Dimitris Papastamosf0c23192012-02-22 14:20:09 +000036static 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
59static const struct file_operations regmap_name_fops = {
60 .open = regmap_open_file,
61 .read = regmap_name_read_file,
62 .llseek = default_llseek,
63};
64
Mark Brown31244e32011-07-20 22:56:53 +010065static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
66 size_t count, loff_t *ppos)
67{
Mark Browncb3c2dc2011-08-09 16:47:42 +090068 int reg_len, val_len, tot_len;
Mark Brown31244e32011-07-20 22:56:53 +010069 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 */
Mark Brown21f55542011-08-10 17:15:31 +090085 reg_len = regmap_calc_reg_len(map->max_register, buf, count);
Mark Brown31244e32011-07-20 22:56:53 +010086 val_len = 2 * map->format.val_bytes;
87 tot_len = reg_len + val_len + 3; /* : \n */
88
Mark Brownd813ae92011-09-05 08:13:07 -070089 for (i = 0; i < map->max_register + 1; i++) {
Mark Brown8de2f082011-08-10 17:14:41 +090090 if (!regmap_readable(map, i))
Mark Brown31244e32011-07-20 22:56:53 +010091 continue;
92
Mark Brown8de2f082011-08-10 17:14:41 +090093 if (regmap_precious(map, i))
Mark Brown2efe1642011-08-08 15:41:46 +090094 continue;
95
Mark Brown31244e32011-07-20 22:56:53 +010096 /* 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
130out:
131 kfree(buf);
132 return ret;
133}
134
Dimitris Papastamos09c6ecd2012-02-22 12:43:50 +0000135#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 */
143static 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
Mark Brown31244e32011-07-20 22:56:53 +0100176static const struct file_operations regmap_map_fops = {
Mark Brown21f55542011-08-10 17:15:31 +0900177 .open = regmap_open_file,
Mark Brown31244e32011-07-20 22:56:53 +0100178 .read = regmap_map_read_file,
Dimitris Papastamos09c6ecd2012-02-22 12:43:50 +0000179 .write = regmap_map_write_file,
Mark Brown31244e32011-07-20 22:56:53 +0100180 .llseek = default_llseek,
181};
182
Mark Brown449e3842011-08-10 17:28:04 +0900183static 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
Mark Brownd813ae92011-09-05 08:13:07 -0700206 for (i = 0; i < map->max_register + 1; i++) {
Mark Brown449e3842011-08-10 17:28:04 +0900207 /* 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
240out:
241 kfree(buf);
242 return ret;
243}
244
245static const struct file_operations regmap_access_fops = {
246 .open = regmap_open_file,
247 .read = regmap_access_read_file,
248 .llseek = default_llseek,
249};
Mark Brown31244e32011-07-20 22:56:53 +0100250
251void 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
Dimitris Papastamosf0c23192012-02-22 14:20:09 +0000260 debugfs_create_file("name", 0400, map->debugfs,
261 map, &regmap_name_fops);
262
Mark Brown449e3842011-08-10 17:28:04 +0900263 if (map->max_register) {
Mark Brown31244e32011-07-20 22:56:53 +0100264 debugfs_create_file("registers", 0400, map->debugfs,
265 map, &regmap_map_fops);
Mark Brown449e3842011-08-10 17:28:04 +0900266 debugfs_create_file("access", 0400, map->debugfs,
267 map, &regmap_access_fops);
268 }
Mark Brown028a01e2012-02-06 18:02:06 +0000269
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 }
Mark Brown31244e32011-07-20 22:56:53 +0100278}
279
280void regmap_debugfs_exit(struct regmap *map)
281{
282 debugfs_remove_recursive(map->debugfs);
283}
284
285void 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}