blob: 8c90a83ae24f8a3961d3c214c57f2b50c6da125c [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>
Paul Gortmaker51990e82012-01-22 11:23:42 -050018#include <linux/device.h>
Mark Brown31244e32011-07-20 22:56:53 +010019
20#include "internal.h"
21
22static struct dentry *regmap_debugfs_root;
23
Mark Brown21f55542011-08-10 17:15:31 +090024/* Calculate the length of a fixed format */
25static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size)
26{
27 snprintf(buf, buf_size, "%x", max_val);
28 return strlen(buf);
29}
30
31static int regmap_open_file(struct inode *inode, struct file *file)
Mark Brown31244e32011-07-20 22:56:53 +010032{
33 file->private_data = inode->i_private;
34 return 0;
35}
36
37static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
38 size_t count, loff_t *ppos)
39{
Mark Browncb3c2dc2011-08-09 16:47:42 +090040 int reg_len, val_len, tot_len;
Mark Brown31244e32011-07-20 22:56:53 +010041 size_t buf_pos = 0;
42 loff_t p = 0;
43 ssize_t ret;
44 int i;
45 struct regmap *map = file->private_data;
46 char *buf;
47 unsigned int val;
48
49 if (*ppos < 0 || !count)
50 return -EINVAL;
51
52 buf = kmalloc(count, GFP_KERNEL);
53 if (!buf)
54 return -ENOMEM;
55
56 /* Calculate the length of a fixed format */
Mark Brown21f55542011-08-10 17:15:31 +090057 reg_len = regmap_calc_reg_len(map->max_register, buf, count);
Mark Brown31244e32011-07-20 22:56:53 +010058 val_len = 2 * map->format.val_bytes;
59 tot_len = reg_len + val_len + 3; /* : \n */
60
Mark Brownd813ae92011-09-05 08:13:07 -070061 for (i = 0; i < map->max_register + 1; i++) {
Mark Brown8de2f082011-08-10 17:14:41 +090062 if (!regmap_readable(map, i))
Mark Brown31244e32011-07-20 22:56:53 +010063 continue;
64
Mark Brown8de2f082011-08-10 17:14:41 +090065 if (regmap_precious(map, i))
Mark Brown2efe1642011-08-08 15:41:46 +090066 continue;
67
Mark Brown31244e32011-07-20 22:56:53 +010068 /* If we're in the region the user is trying to read */
69 if (p >= *ppos) {
70 /* ...but not beyond it */
71 if (buf_pos >= count - 1 - tot_len)
72 break;
73
74 /* Format the register */
75 snprintf(buf + buf_pos, count - buf_pos, "%.*x: ",
76 reg_len, i);
77 buf_pos += reg_len + 2;
78
79 /* Format the value, write all X if we can't read */
80 ret = regmap_read(map, i, &val);
81 if (ret == 0)
82 snprintf(buf + buf_pos, count - buf_pos,
83 "%.*x", val_len, val);
84 else
85 memset(buf + buf_pos, 'X', val_len);
86 buf_pos += 2 * map->format.val_bytes;
87
88 buf[buf_pos++] = '\n';
89 }
90 p += tot_len;
91 }
92
93 ret = buf_pos;
94
95 if (copy_to_user(user_buf, buf, buf_pos)) {
96 ret = -EFAULT;
97 goto out;
98 }
99
100 *ppos += buf_pos;
101
102out:
103 kfree(buf);
104 return ret;
105}
106
107static const struct file_operations regmap_map_fops = {
Mark Brown21f55542011-08-10 17:15:31 +0900108 .open = regmap_open_file,
Mark Brown31244e32011-07-20 22:56:53 +0100109 .read = regmap_map_read_file,
110 .llseek = default_llseek,
111};
112
Mark Brown449e3842011-08-10 17:28:04 +0900113static ssize_t regmap_access_read_file(struct file *file,
114 char __user *user_buf, size_t count,
115 loff_t *ppos)
116{
117 int reg_len, tot_len;
118 size_t buf_pos = 0;
119 loff_t p = 0;
120 ssize_t ret;
121 int i;
122 struct regmap *map = file->private_data;
123 char *buf;
124
125 if (*ppos < 0 || !count)
126 return -EINVAL;
127
128 buf = kmalloc(count, GFP_KERNEL);
129 if (!buf)
130 return -ENOMEM;
131
132 /* Calculate the length of a fixed format */
133 reg_len = regmap_calc_reg_len(map->max_register, buf, count);
134 tot_len = reg_len + 10; /* ': R W V P\n' */
135
Mark Brownd813ae92011-09-05 08:13:07 -0700136 for (i = 0; i < map->max_register + 1; i++) {
Mark Brown449e3842011-08-10 17:28:04 +0900137 /* Ignore registers which are neither readable nor writable */
138 if (!regmap_readable(map, i) && !regmap_writeable(map, i))
139 continue;
140
141 /* If we're in the region the user is trying to read */
142 if (p >= *ppos) {
143 /* ...but not beyond it */
144 if (buf_pos >= count - 1 - tot_len)
145 break;
146
147 /* Format the register */
148 snprintf(buf + buf_pos, count - buf_pos,
149 "%.*x: %c %c %c %c\n",
150 reg_len, i,
151 regmap_readable(map, i) ? 'y' : 'n',
152 regmap_writeable(map, i) ? 'y' : 'n',
153 regmap_volatile(map, i) ? 'y' : 'n',
154 regmap_precious(map, i) ? 'y' : 'n');
155
156 buf_pos += tot_len;
157 }
158 p += tot_len;
159 }
160
161 ret = buf_pos;
162
163 if (copy_to_user(user_buf, buf, buf_pos)) {
164 ret = -EFAULT;
165 goto out;
166 }
167
168 *ppos += buf_pos;
169
170out:
171 kfree(buf);
172 return ret;
173}
174
175static const struct file_operations regmap_access_fops = {
176 .open = regmap_open_file,
177 .read = regmap_access_read_file,
178 .llseek = default_llseek,
179};
Mark Brown31244e32011-07-20 22:56:53 +0100180
181void regmap_debugfs_init(struct regmap *map)
182{
183 map->debugfs = debugfs_create_dir(dev_name(map->dev),
184 regmap_debugfs_root);
185 if (!map->debugfs) {
186 dev_warn(map->dev, "Failed to create debugfs directory\n");
187 return;
188 }
189
Mark Brown449e3842011-08-10 17:28:04 +0900190 if (map->max_register) {
Mark Brown31244e32011-07-20 22:56:53 +0100191 debugfs_create_file("registers", 0400, map->debugfs,
192 map, &regmap_map_fops);
Mark Brown449e3842011-08-10 17:28:04 +0900193 debugfs_create_file("access", 0400, map->debugfs,
194 map, &regmap_access_fops);
195 }
Mark Brown31244e32011-07-20 22:56:53 +0100196}
197
198void regmap_debugfs_exit(struct regmap *map)
199{
200 debugfs_remove_recursive(map->debugfs);
201}
202
203void regmap_debugfs_initcall(void)
204{
205 regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
206 if (!regmap_debugfs_root) {
207 pr_warn("regmap: Failed to create debugfs root\n");
208 return;
209 }
210}