blob: 71157267b99a7dcff8d1c68f5630f79efd1b06d3 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
2 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#define pr_fmt(fmt) "%s: " fmt, __func__
15
16#include <linux/kernel.h>
17#include <linux/platform_device.h>
18#include <linux/slab.h>
19#include <linux/err.h>
20#include <linux/mfd/pm8xxx/core.h>
21#include <linux/debugfs.h>
22
23#define PM8XXX_DEBUG_DEV_NAME "pm8xxx-debug"
24
25struct pm8xxx_debug_device {
26 struct mutex debug_mutex;
27 struct device *parent;
28 struct dentry *dir;
29 int addr;
30};
31
32static bool pm8xxx_debug_addr_is_valid(int addr)
33{
34 if (addr < 0 || addr > 0x3FF) {
35 pr_err("PMIC register address is invalid: %d\n", addr);
36 return false;
37 }
38 return true;
39}
40
41static int pm8xxx_debug_data_set(void *data, u64 val)
42{
43 struct pm8xxx_debug_device *debugdev = data;
44 u8 reg = val;
45 int rc;
46
47 mutex_lock(&debugdev->debug_mutex);
48
49 if (pm8xxx_debug_addr_is_valid(debugdev->addr)) {
50 rc = pm8xxx_writeb(debugdev->parent, debugdev->addr, reg);
51
52 if (rc)
53 pr_err("pm8xxx_writeb(0x%03X)=0x%02X failed: rc=%d\n",
54 debugdev->addr, reg, rc);
55 }
56
57 mutex_unlock(&debugdev->debug_mutex);
58 return 0;
59}
60
61static int pm8xxx_debug_data_get(void *data, u64 *val)
62{
63 struct pm8xxx_debug_device *debugdev = data;
64 int rc;
65 u8 reg;
66
67 mutex_lock(&debugdev->debug_mutex);
68
69 if (pm8xxx_debug_addr_is_valid(debugdev->addr)) {
70 rc = pm8xxx_readb(debugdev->parent, debugdev->addr, &reg);
71
72 if (rc)
73 pr_err("pm8xxx_readb(0x%03X) failed: rc=%d\n",
74 debugdev->addr, rc);
75 else
76 *val = reg;
77 }
78
79 mutex_unlock(&debugdev->debug_mutex);
80 return 0;
81}
82
83DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, pm8xxx_debug_data_get,
84 pm8xxx_debug_data_set, "0x%02llX\n");
85
86static int pm8xxx_debug_addr_set(void *data, u64 val)
87{
88 struct pm8xxx_debug_device *debugdev = data;
89
90 if (pm8xxx_debug_addr_is_valid(val)) {
91 mutex_lock(&debugdev->debug_mutex);
92 debugdev->addr = val;
93 mutex_unlock(&debugdev->debug_mutex);
94 }
95
96 return 0;
97}
98
99static int pm8xxx_debug_addr_get(void *data, u64 *val)
100{
101 struct pm8xxx_debug_device *debugdev = data;
102
103 mutex_lock(&debugdev->debug_mutex);
104
105 if (pm8xxx_debug_addr_is_valid(debugdev->addr))
106 *val = debugdev->addr;
107
108 mutex_unlock(&debugdev->debug_mutex);
109 return 0;
110}
111
112DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, pm8xxx_debug_addr_get,
113 pm8xxx_debug_addr_set, "0x%03llX\n");
114
115static int __devinit pm8xxx_debug_probe(struct platform_device *pdev)
116{
117 char *name = pdev->dev.platform_data;
118 struct pm8xxx_debug_device *debugdev;
119 struct dentry *dir;
120 struct dentry *temp;
121 int rc;
122
123 if (name == NULL) {
124 pr_err("debugfs directory name must be specified in "
125 "platform_data pointer\n");
126 return -EINVAL;
127 }
128
129 debugdev = kzalloc(sizeof(struct pm8xxx_debug_device), GFP_KERNEL);
130 if (debugdev == NULL) {
131 pr_err("kzalloc failed\n");
132 return -ENOMEM;
133 }
134
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700135 debugdev->parent = pdev->dev.parent;
136 debugdev->addr = -1;
137
138 dir = debugfs_create_dir(name, NULL);
139 if (dir == NULL || IS_ERR(dir)) {
140 pr_err("debugfs_create_dir failed: rc=%ld\n", PTR_ERR(dir));
141 rc = PTR_ERR(dir);
142 goto dir_error;
143 }
144
145 temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, dir, debugdev,
146 &debug_addr_fops);
147 if (temp == NULL || IS_ERR(temp)) {
148 pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
149 rc = PTR_ERR(temp);
150 goto file_error;
151 }
152
153 temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, dir, debugdev,
154 &debug_data_fops);
155 if (temp == NULL || IS_ERR(temp)) {
156 pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
157 rc = PTR_ERR(temp);
158 goto file_error;
159 }
160
Jay Chokshi610f5d12011-09-29 17:16:14 -0700161 mutex_init(&debugdev->debug_mutex);
162
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700163 debugdev->dir = dir;
164 platform_set_drvdata(pdev, debugdev);
165
166 return 0;
167
168file_error:
169 debugfs_remove_recursive(dir);
170dir_error:
171 kfree(debugdev);
172
173 return rc;
174}
175
176static int __devexit pm8xxx_debug_remove(struct platform_device *pdev)
177{
178 struct pm8xxx_debug_device *debugdev = platform_get_drvdata(pdev);
179
180 if (debugdev) {
181 debugfs_remove_recursive(debugdev->dir);
Jay Chokshi610f5d12011-09-29 17:16:14 -0700182 mutex_destroy(&debugdev->debug_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700183 kfree(debugdev);
184 }
185
186 platform_set_drvdata(pdev, NULL);
187
188 return 0;
189}
190
191static struct platform_driver pm8xxx_debug_driver = {
192 .probe = pm8xxx_debug_probe,
193 .remove = __devexit_p(pm8xxx_debug_remove),
194 .driver = {
195 .name = PM8XXX_DEBUG_DEV_NAME,
196 .owner = THIS_MODULE,
197 },
198};
199
200static int __init pm8xxx_debug_init(void)
201{
202 return platform_driver_register(&pm8xxx_debug_driver);
203}
204subsys_initcall(pm8xxx_debug_init);
205
206static void __exit pm8xxx_debug_exit(void)
207{
208 platform_driver_unregister(&pm8xxx_debug_driver);
209}
210module_exit(pm8xxx_debug_exit);
211
212MODULE_LICENSE("GPL v2");
213MODULE_DESCRIPTION("PM8XXX Debug driver");
214MODULE_VERSION("1.0");
215MODULE_ALIAS("platform:" PM8XXX_DEBUG_DEV_NAME);