blob: b58b6fe339d4025ec062631471ca841fa1f25735 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
Duy Truong790f06d2013-02-13 16:38:12 -08002 * Copyright (c) 2011, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003 *
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>
Steve Mucklef132c6c2012-06-06 18:30:57 -070017#include <linux/module.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070018#include <linux/platform_device.h>
19#include <linux/slab.h>
20#include <linux/err.h>
21#include <linux/mfd/pm8xxx/core.h>
22#include <linux/debugfs.h>
23
24#define PM8XXX_DEBUG_DEV_NAME "pm8xxx-debug"
25
26struct pm8xxx_debug_device {
27 struct mutex debug_mutex;
28 struct device *parent;
29 struct dentry *dir;
30 int addr;
31};
32
33static bool pm8xxx_debug_addr_is_valid(int addr)
34{
35 if (addr < 0 || addr > 0x3FF) {
36 pr_err("PMIC register address is invalid: %d\n", addr);
37 return false;
38 }
39 return true;
40}
41
42static int pm8xxx_debug_data_set(void *data, u64 val)
43{
44 struct pm8xxx_debug_device *debugdev = data;
45 u8 reg = val;
46 int rc;
47
48 mutex_lock(&debugdev->debug_mutex);
49
50 if (pm8xxx_debug_addr_is_valid(debugdev->addr)) {
51 rc = pm8xxx_writeb(debugdev->parent, debugdev->addr, reg);
52
53 if (rc)
54 pr_err("pm8xxx_writeb(0x%03X)=0x%02X failed: rc=%d\n",
55 debugdev->addr, reg, rc);
56 }
57
58 mutex_unlock(&debugdev->debug_mutex);
59 return 0;
60}
61
62static int pm8xxx_debug_data_get(void *data, u64 *val)
63{
64 struct pm8xxx_debug_device *debugdev = data;
65 int rc;
66 u8 reg;
67
68 mutex_lock(&debugdev->debug_mutex);
69
70 if (pm8xxx_debug_addr_is_valid(debugdev->addr)) {
71 rc = pm8xxx_readb(debugdev->parent, debugdev->addr, &reg);
72
73 if (rc)
74 pr_err("pm8xxx_readb(0x%03X) failed: rc=%d\n",
75 debugdev->addr, rc);
76 else
77 *val = reg;
78 }
79
80 mutex_unlock(&debugdev->debug_mutex);
81 return 0;
82}
83
84DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, pm8xxx_debug_data_get,
85 pm8xxx_debug_data_set, "0x%02llX\n");
86
87static int pm8xxx_debug_addr_set(void *data, u64 val)
88{
89 struct pm8xxx_debug_device *debugdev = data;
90
91 if (pm8xxx_debug_addr_is_valid(val)) {
92 mutex_lock(&debugdev->debug_mutex);
93 debugdev->addr = val;
94 mutex_unlock(&debugdev->debug_mutex);
95 }
96
97 return 0;
98}
99
100static int pm8xxx_debug_addr_get(void *data, u64 *val)
101{
102 struct pm8xxx_debug_device *debugdev = data;
103
104 mutex_lock(&debugdev->debug_mutex);
105
106 if (pm8xxx_debug_addr_is_valid(debugdev->addr))
107 *val = debugdev->addr;
108
109 mutex_unlock(&debugdev->debug_mutex);
110 return 0;
111}
112
113DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, pm8xxx_debug_addr_get,
114 pm8xxx_debug_addr_set, "0x%03llX\n");
115
116static int __devinit pm8xxx_debug_probe(struct platform_device *pdev)
117{
118 char *name = pdev->dev.platform_data;
119 struct pm8xxx_debug_device *debugdev;
120 struct dentry *dir;
121 struct dentry *temp;
122 int rc;
123
124 if (name == NULL) {
125 pr_err("debugfs directory name must be specified in "
126 "platform_data pointer\n");
127 return -EINVAL;
128 }
129
130 debugdev = kzalloc(sizeof(struct pm8xxx_debug_device), GFP_KERNEL);
131 if (debugdev == NULL) {
132 pr_err("kzalloc failed\n");
133 return -ENOMEM;
134 }
135
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700136 debugdev->parent = pdev->dev.parent;
137 debugdev->addr = -1;
138
139 dir = debugfs_create_dir(name, NULL);
140 if (dir == NULL || IS_ERR(dir)) {
141 pr_err("debugfs_create_dir failed: rc=%ld\n", PTR_ERR(dir));
142 rc = PTR_ERR(dir);
143 goto dir_error;
144 }
145
146 temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, dir, debugdev,
147 &debug_addr_fops);
148 if (temp == NULL || IS_ERR(temp)) {
149 pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
150 rc = PTR_ERR(temp);
151 goto file_error;
152 }
153
154 temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, dir, debugdev,
155 &debug_data_fops);
156 if (temp == NULL || IS_ERR(temp)) {
157 pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
158 rc = PTR_ERR(temp);
159 goto file_error;
160 }
161
Jay Chokshi610f5d12011-09-29 17:16:14 -0700162 mutex_init(&debugdev->debug_mutex);
163
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700164 debugdev->dir = dir;
165 platform_set_drvdata(pdev, debugdev);
166
167 return 0;
168
169file_error:
170 debugfs_remove_recursive(dir);
171dir_error:
172 kfree(debugdev);
173
174 return rc;
175}
176
177static int __devexit pm8xxx_debug_remove(struct platform_device *pdev)
178{
179 struct pm8xxx_debug_device *debugdev = platform_get_drvdata(pdev);
180
181 if (debugdev) {
182 debugfs_remove_recursive(debugdev->dir);
Jay Chokshi610f5d12011-09-29 17:16:14 -0700183 mutex_destroy(&debugdev->debug_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184 kfree(debugdev);
185 }
186
187 platform_set_drvdata(pdev, NULL);
188
189 return 0;
190}
191
192static struct platform_driver pm8xxx_debug_driver = {
193 .probe = pm8xxx_debug_probe,
194 .remove = __devexit_p(pm8xxx_debug_remove),
195 .driver = {
196 .name = PM8XXX_DEBUG_DEV_NAME,
197 .owner = THIS_MODULE,
198 },
199};
200
201static int __init pm8xxx_debug_init(void)
202{
203 return platform_driver_register(&pm8xxx_debug_driver);
204}
205subsys_initcall(pm8xxx_debug_init);
206
207static void __exit pm8xxx_debug_exit(void)
208{
209 platform_driver_unregister(&pm8xxx_debug_driver);
210}
211module_exit(pm8xxx_debug_exit);
212
213MODULE_LICENSE("GPL v2");
214MODULE_DESCRIPTION("PM8XXX Debug driver");
215MODULE_VERSION("1.0");
216MODULE_ALIAS("platform:" PM8XXX_DEBUG_DEV_NAME);