blob: 5a1b36f9c9fcbd9a6f4d6119c41399fa0a5a1fec [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2010,2011 The Linux Foundation. All rights reserved.
Anirudh Ghayalc2019332011-11-12 06:29:10 +05302 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13/*
14 * Qualcomm PM8XXX UPL driver
15 *
16 */
17
18#include <linux/module.h>
19#include <linux/platform_device.h>
20#include <linux/err.h>
21#include <linux/debugfs.h>
22#include <linux/slab.h>
23#include <linux/mfd/pm8xxx/core.h>
24#include <linux/mfd/pm8xxx/upl.h>
25
26/* PMIC8XXX UPL registers */
27#define SSBI_REG_UPL_CTRL 0x17B
28#define SSBI_REG_UPL_TRUTHTABLE1 0x17C
29#define SSBI_REG_UPL_TRUTHTABLE2 0x17D
30
31struct pm8xxx_upl_device {
32 struct device *dev;
33 struct mutex upl_mutex;
34#if defined(CONFIG_DEBUG_FS)
35 struct dentry *dent;
36#endif
37};
38static struct pm8xxx_upl_device *upl_dev;
39
40/* APIs */
41
42/*
43 * pm8xxx_upl_request - request a handle to access UPL device
44 */
45struct pm8xxx_upl_device *pm8xxx_upl_request(void)
46{
47 return upl_dev;
48}
49EXPORT_SYMBOL(pm8xxx_upl_request);
50
51/*
52 * pm8xxx_upl_read_truthtable - read value currently stored in UPL truth table
53 *
54 * @upldev: the UPL device
55 * @truthtable: value read from UPL truth table
56 */
57int pm8xxx_upl_read_truthtable(struct pm8xxx_upl_device *upldev,
58 u16 *truthtable)
59{
60 int rc = 0;
61 u8 table[2];
62
63 if (upldev == NULL || IS_ERR(upldev))
64 return -EINVAL;
65
66 mutex_lock(&upldev->upl_mutex);
67
68 rc = pm8xxx_readb(upldev->dev->parent, SSBI_REG_UPL_TRUTHTABLE1,
69 &(table[0]));
70 if (rc) {
71 pr_err("%s: FAIL pm8xxx_readb(0x%X)=0x%02X: rc=%d\n",
72 __func__, SSBI_REG_UPL_TRUTHTABLE1, table[0], rc);
73 goto upl_read_done;
74 }
75
76 rc = pm8xxx_readb(upldev->dev->parent, SSBI_REG_UPL_TRUTHTABLE2,
77 &(table[1]));
78 if (rc)
79 pr_err("%s: FAIL pm8xxx_readb(0x%X)=0x%02X: rc=%d\n",
80 __func__, SSBI_REG_UPL_TRUTHTABLE2, table[1], rc);
81upl_read_done:
82 mutex_unlock(&upldev->upl_mutex);
83 *truthtable = (((u16)table[1]) << 8) | table[0];
84 return rc;
85}
86EXPORT_SYMBOL(pm8xxx_upl_read_truthtable);
87
88/*
89 * pm8xxx_upl_writes_truthtable - write value into UPL truth table
90 *
91 * @upldev: the UPL device
92 * @truthtable: value written to UPL truth table
93 *
94 * Each bit in parameter "truthtable" corresponds to the UPL output for a given
95 * set of input pin values. For example, if the input pins have the following
96 * values: A=1, B=1, C=1, D=0, then the UPL would output the value of bit 14
97 * (0b1110) in parameter "truthtable".
98 */
99int pm8xxx_upl_write_truthtable(struct pm8xxx_upl_device *upldev,
100 u16 truthtable)
101{
102 int rc = 0;
103 u8 table[2];
104
105 if (upldev == NULL || IS_ERR(upldev))
106 return -EINVAL;
107
108 table[0] = truthtable & 0xFF;
109 table[1] = (truthtable >> 8) & 0xFF;
110
111 mutex_lock(&upldev->upl_mutex);
112
113 rc = pm8xxx_writeb(upldev->dev->parent, SSBI_REG_UPL_TRUTHTABLE1,
114 table[0]);
115 if (rc) {
116 pr_err("%s: FAIL pm8xxx_writeb(0x%X)=0x%04X: rc=%d\n",
117 __func__, SSBI_REG_UPL_TRUTHTABLE1, table[0], rc);
118 goto upl_write_done;
119 }
120
121 rc = pm8xxx_writeb(upldev->dev->parent, SSBI_REG_UPL_TRUTHTABLE2,
122 table[1]);
123 if (rc)
124 pr_err("%s: FAIL pm8xxx_writeb(0x%X)=0x%04X: rc=%d\n",
125 __func__, SSBI_REG_UPL_TRUTHTABLE2, table[1], rc);
126upl_write_done:
127 mutex_unlock(&upldev->upl_mutex);
128 return rc;
129}
130EXPORT_SYMBOL(pm8xxx_upl_write_truthtable);
131
132/*
133 * pm8xxx_upl_config - configure UPL I/O settings and UPL enable/disable
134 *
135 * @upldev: the UPL device
136 * @mask: setting mask to configure
137 * @flags: setting flags
138 */
139int pm8xxx_upl_config(struct pm8xxx_upl_device *upldev, u32 mask, u32 flags)
140{
141 int rc;
142 u8 upl_ctrl, m, f;
143
144 if (upldev == NULL || IS_ERR(upldev))
145 return -EINVAL;
146
147 mutex_lock(&upldev->upl_mutex);
148
149 rc = pm8xxx_readb(upldev->dev->parent, SSBI_REG_UPL_CTRL, &upl_ctrl);
150 if (rc) {
151 pr_err("%s: FAIL pm8xxx_readb(0x%X)=0x%02X: rc=%d\n",
152 __func__, SSBI_REG_UPL_CTRL, upl_ctrl, rc);
153 goto upl_config_done;
154 }
155
156 m = mask & 0x00ff;
157 f = flags & 0x00ff;
158 upl_ctrl &= ~m;
159 upl_ctrl |= m & f;
160
161 rc = pm8xxx_writeb(upldev->dev->parent, SSBI_REG_UPL_CTRL, upl_ctrl);
162 if (rc)
163 pr_err("%s: FAIL pm8xxx_writeb(0x%X)=0x%02X: rc=%d\n",
164 __func__, SSBI_REG_UPL_CTRL, upl_ctrl, rc);
165upl_config_done:
166 mutex_unlock(&upldev->upl_mutex);
167 return rc;
168}
169EXPORT_SYMBOL(pm8xxx_upl_config);
170
171#if defined(CONFIG_DEBUG_FS)
172
173static int truthtable_set(void *data, u64 val)
174{
175 int rc;
176
177 rc = pm8xxx_upl_write_truthtable(data, val);
178 if (rc)
179 pr_err("%s: pm8xxx_upl_write_truthtable: rc=%d, "
180 "truthtable=0x%llX\n", __func__, rc, val);
181 return rc;
182}
183
184static int truthtable_get(void *data, u64 *val)
185{
186 int rc;
187 u16 truthtable;
188
189 rc = pm8xxx_upl_read_truthtable(data, &truthtable);
190 if (rc)
191 pr_err("%s: pm8xxx_upl_read_truthtable: rc=%d, "
192 "truthtable=0x%X\n", __func__, rc, truthtable);
193 if (val)
194 *val = truthtable;
195
196 return rc;
197}
198
199DEFINE_SIMPLE_ATTRIBUTE(upl_truthtable_fops, truthtable_get,
200 truthtable_set, "0x%04llX\n");
201
202/* enter values as 0xMMMMFFFF where MMMM is the mask and FFFF is the flags */
203static int control_set(void *data, u64 val)
204{
205 u8 mask, flags;
206 int rc;
207
208 flags = val & 0xFFFF;
209 mask = (val >> 16) & 0xFFFF;
210
211 rc = pm8xxx_upl_config(data, mask, flags);
212 if (rc)
213 pr_err("%s: pm8xxx_upl_config: rc=%d, mask = 0x%X, "
214 "flags = 0x%X\n", __func__, rc, mask, flags);
215 return rc;
216}
217
218static int control_get(void *data, u64 *val)
219{
220 struct pm8xxx_upl_device *upldev;
221 int rc = 0;
222 u8 ctrl;
223
224 upldev = data;
225
226 mutex_lock(&upldev->upl_mutex);
227
228 rc = pm8xxx_readb(upldev->dev->parent, SSBI_REG_UPL_CTRL, &ctrl);
229 if (rc)
230 pr_err("%s: FAIL pm8xxx_readb(): rc=%d (ctrl=0x%02X)\n",
231 __func__, rc, ctrl);
232
233 mutex_unlock(&upldev->upl_mutex);
234
235 *val = ctrl;
236
237 return rc;
238}
239
240DEFINE_SIMPLE_ATTRIBUTE(upl_control_fops, control_get,
241 control_set, "0x%02llX\n");
242
243static int pm8xxx_upl_debug_init(struct pm8xxx_upl_device *upldev)
244{
245 struct dentry *dent;
246 struct dentry *temp;
247
248 dent = debugfs_create_dir("pm8xxx-upl", NULL);
249 if (dent == NULL || IS_ERR(dent)) {
250 pr_err("%s: ERR debugfs_create_dir: dent=0x%X\n",
251 __func__, (unsigned)dent);
252 return -ENOMEM;
253 }
254
255 temp = debugfs_create_file("truthtable", S_IRUSR | S_IWUSR, dent,
256 upldev, &upl_truthtable_fops);
257 if (temp == NULL || IS_ERR(temp)) {
258 pr_err("%s: ERR debugfs_create_file: dent=0x%X\n",
259 __func__, (unsigned)dent);
260 goto debug_error;
261 }
262
263 temp = debugfs_create_file("control", S_IRUSR | S_IWUSR, dent,
264 upldev, &upl_control_fops);
265 if (temp == NULL || IS_ERR(temp)) {
266 pr_err("%s: ERR debugfs_create_file: dent=0x%X\n",
267 __func__, (unsigned)dent);
268 goto debug_error;
269 }
270
271 upldev->dent = dent;
272 return 0;
273
274debug_error:
275 debugfs_remove_recursive(dent);
276 return -ENOMEM;
277}
278
279static int __devexit pm8xxx_upl_debug_remove(struct pm8xxx_upl_device *upldev)
280{
281 debugfs_remove_recursive(upldev->dent);
282 return 0;
283}
284
285#endif /* CONFIG_DEBUG_FS */
286
287static int __devinit pm8xxx_upl_probe(struct platform_device *pdev)
288{
289 struct pm8xxx_upl_device *upldev;
290
291 upldev = kzalloc(sizeof *upldev, GFP_KERNEL);
292 if (upldev == NULL) {
293 pr_err("%s: kzalloc() failed.\n", __func__);
294 return -ENOMEM;
295 }
296
297 mutex_init(&upldev->upl_mutex);
298
299 upl_dev = upldev;
300 upldev->dev = &pdev->dev;
301 platform_set_drvdata(pdev, upldev);
302
303#if defined(CONFIG_DEBUG_FS)
304 pm8xxx_upl_debug_init(upl_dev);
305#endif
306 pr_notice("%s: OK\n", __func__);
307 return 0;
308}
309
310static int __devexit pm8xxx_upl_remove(struct platform_device *pdev)
311{
312 struct pm8xxx_upl_device *upldev = platform_get_drvdata(pdev);
313
314#if defined(CONFIG_DEBUG_FS)
315 pm8xxx_upl_debug_remove(upldev);
316#endif
317
318 platform_set_drvdata(pdev, NULL);
319 kfree(upldev);
320 pr_notice("%s: OK\n", __func__);
321
322 return 0;
323}
324
325static struct platform_driver pm8xxx_upl_driver = {
326 .probe = pm8xxx_upl_probe,
327 .remove = __devexit_p(pm8xxx_upl_remove),
328 .driver = {
329 .name = PM8XXX_UPL_DEV_NAME,
330 .owner = THIS_MODULE,
331 },
332};
333
334static int __init pm8xxx_upl_init(void)
335{
336 return platform_driver_register(&pm8xxx_upl_driver);
337}
338
339static void __exit pm8xxx_upl_exit(void)
340{
341 platform_driver_unregister(&pm8xxx_upl_driver);
342}
343
344module_init(pm8xxx_upl_init);
345module_exit(pm8xxx_upl_exit);
346
347MODULE_LICENSE("GPL v2");
348MODULE_DESCRIPTION("PM8XXX UPL driver");
349MODULE_VERSION("1.0");
350MODULE_ALIAS("platform:" PM8XXX_UPL_DEV_NAME);