blob: 184b618e318e82979386de3a4bcd3a6b4537083d [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>
18
19#include "internal.h"
20
21static struct dentry *regmap_debugfs_root;
22
23static int regmap_map_open_file(struct inode *inode, struct file *file)
24{
25 file->private_data = inode->i_private;
26 return 0;
27}
28
29static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
30 size_t count, loff_t *ppos)
31{
32 size_t reg_len, val_len, tot_len;
33 size_t buf_pos = 0;
34 loff_t p = 0;
35 ssize_t ret;
36 int i;
37 struct regmap *map = file->private_data;
38 char *buf;
39 unsigned int val;
40
41 if (*ppos < 0 || !count)
42 return -EINVAL;
43
44 buf = kmalloc(count, GFP_KERNEL);
45 if (!buf)
46 return -ENOMEM;
47
48 /* Calculate the length of a fixed format */
49 snprintf(buf, count, "%x", map->max_register);
50 reg_len = strlen(buf);
51 val_len = 2 * map->format.val_bytes;
52 tot_len = reg_len + val_len + 3; /* : \n */
53
54 for (i = 0; i < map->max_register; i++) {
55 if (map->readable_reg &&
56 !map->readable_reg(map->dev, i))
57 continue;
58
Mark Brown2efe1642011-08-08 15:41:46 +090059 if (map->precious_reg &&
60 map->precious_reg(map->dev, i))
61 continue;
62
Mark Brown31244e32011-07-20 22:56:53 +010063 /* If we're in the region the user is trying to read */
64 if (p >= *ppos) {
65 /* ...but not beyond it */
66 if (buf_pos >= count - 1 - tot_len)
67 break;
68
69 /* Format the register */
70 snprintf(buf + buf_pos, count - buf_pos, "%.*x: ",
71 reg_len, i);
72 buf_pos += reg_len + 2;
73
74 /* Format the value, write all X if we can't read */
75 ret = regmap_read(map, i, &val);
76 if (ret == 0)
77 snprintf(buf + buf_pos, count - buf_pos,
78 "%.*x", val_len, val);
79 else
80 memset(buf + buf_pos, 'X', val_len);
81 buf_pos += 2 * map->format.val_bytes;
82
83 buf[buf_pos++] = '\n';
84 }
85 p += tot_len;
86 }
87
88 ret = buf_pos;
89
90 if (copy_to_user(user_buf, buf, buf_pos)) {
91 ret = -EFAULT;
92 goto out;
93 }
94
95 *ppos += buf_pos;
96
97out:
98 kfree(buf);
99 return ret;
100}
101
102static const struct file_operations regmap_map_fops = {
103 .open = regmap_map_open_file,
104 .read = regmap_map_read_file,
105 .llseek = default_llseek,
106};
107
108
109void regmap_debugfs_init(struct regmap *map)
110{
111 map->debugfs = debugfs_create_dir(dev_name(map->dev),
112 regmap_debugfs_root);
113 if (!map->debugfs) {
114 dev_warn(map->dev, "Failed to create debugfs directory\n");
115 return;
116 }
117
118 if (map->max_register)
119 debugfs_create_file("registers", 0400, map->debugfs,
120 map, &regmap_map_fops);
121}
122
123void regmap_debugfs_exit(struct regmap *map)
124{
125 debugfs_remove_recursive(map->debugfs);
126}
127
128void regmap_debugfs_initcall(void)
129{
130 regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
131 if (!regmap_debugfs_root) {
132 pr_warn("regmap: Failed to create debugfs root\n");
133 return;
134 }
135}