blob: ae0abd864237fce6c2d188c705f0c9cd13763d7a [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2 *
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 PMIC8058 UPL driver
15 *
16 */
17
18#include <linux/module.h>
19#include <linux/platform_device.h>
20#include <linux/err.h>
21#include <linux/mfd/pmic8058.h>
22#include <linux/pmic8058-upl.h>
23#include <linux/debugfs.h>
24#include <linux/slab.h>
25
26/* PMIC8058 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 pm8058_upl_device {
32 struct mutex upl_mutex;
33 struct pm8058_chip *pm_chip;
34#if defined(CONFIG_DEBUG_FS)
35 struct dentry *dent;
36#endif
37};
38static struct pm8058_upl_device *upl_dev;
39
40/* APIs */
41
42/*
43 * pm8058_upl_request - request a handle to access UPL device
44 */
45struct pm8058_upl_device *pm8058_upl_request(void)
46{
47 return upl_dev;
48}
49EXPORT_SYMBOL(pm8058_upl_request);
50
51/*
52 * pm8058_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 pm8058_upl_read_truthtable(struct pm8058_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 if (upldev->pm_chip == NULL)
66 return -ENODEV;
67
68 mutex_lock(&upldev->upl_mutex);
69
70 rc = pm8058_read(upldev->pm_chip, SSBI_REG_UPL_TRUTHTABLE1,
71 &(table[0]), 1);
72 if (rc) {
73 pr_err("%s: FAIL pm8058_read(0x%X)=0x%02X: rc=%d\n",
74 __func__, SSBI_REG_UPL_TRUTHTABLE1, table[0], rc);
75 goto upl_read_done;
76 }
77
78 rc = pm8058_read(upldev->pm_chip, SSBI_REG_UPL_TRUTHTABLE2,
79 &(table[1]), 1);
80 if (rc)
81 pr_err("%s: FAIL pm8058_read(0x%X)=0x%02X: rc=%d\n",
82 __func__, SSBI_REG_UPL_TRUTHTABLE2, table[1], rc);
83upl_read_done:
84 mutex_unlock(&upldev->upl_mutex);
85 *truthtable = (((u16)table[1]) << 8) | table[0];
86 return rc;
87}
88EXPORT_SYMBOL(pm8058_upl_read_truthtable);
89
90/*
91 * pm8058_upl_writes_truthtable - write value into UPL truth table
92 *
93 * @upldev: the UPL device
94 * @truthtable: value written to UPL truth table
95 *
96 * Each bit in parameter "truthtable" corresponds to the UPL output for a given
97 * set of input pin values. For example, if the input pins have the following
98 * values: A=1, B=1, C=1, D=0, then the UPL would output the value of bit 14
99 * (0b1110) in parameter "truthtable".
100 */
101int pm8058_upl_write_truthtable(struct pm8058_upl_device *upldev,
102 u16 truthtable)
103{
104 int rc = 0;
105 u8 table[2];
106
107 if (upldev == NULL || IS_ERR(upldev))
108 return -EINVAL;
109 if (upldev->pm_chip == NULL)
110 return -ENODEV;
111
112 table[0] = truthtable & 0xFF;
113 table[1] = (truthtable >> 8) & 0xFF;
114
115 mutex_lock(&upldev->upl_mutex);
116
117 rc = pm8058_write(upldev->pm_chip, SSBI_REG_UPL_TRUTHTABLE1,
118 &(table[0]), 1);
119 if (rc) {
120 pr_err("%s: FAIL pm8058_write(0x%X)=0x%04X: rc=%d\n",
121 __func__, SSBI_REG_UPL_TRUTHTABLE1, table[0], rc);
122 goto upl_write_done;
123 }
124
125 rc = pm8058_write(upldev->pm_chip, SSBI_REG_UPL_TRUTHTABLE2,
126 &(table[1]), 1);
127 if (rc)
128 pr_err("%s: FAIL pm8058_write(0x%X)=0x%04X: rc=%d\n",
129 __func__, SSBI_REG_UPL_TRUTHTABLE2, table[1], rc);
130upl_write_done:
131 mutex_unlock(&upldev->upl_mutex);
132 return rc;
133}
134EXPORT_SYMBOL(pm8058_upl_write_truthtable);
135
136/*
137 * pm8058_upl_config - configure UPL I/O settings and UPL enable/disable
138 *
139 * @upldev: the UPL device
140 * @mask: setting mask to configure
141 * @flags: setting flags
142 */
143int pm8058_upl_config(struct pm8058_upl_device *upldev, u32 mask, u32 flags)
144{
145 int rc;
146 u8 upl_ctrl, m, f;
147
148 if (upldev == NULL || IS_ERR(upldev))
149 return -EINVAL;
150 if (upldev->pm_chip == NULL)
151 return -ENODEV;
152
153 mutex_lock(&upldev->upl_mutex);
154
155 rc = pm8058_read(upldev->pm_chip, SSBI_REG_UPL_CTRL, &upl_ctrl, 1);
156 if (rc) {
157 pr_err("%s: FAIL pm8058_read(0x%X)=0x%02X: rc=%d\n",
158 __func__, SSBI_REG_UPL_CTRL, upl_ctrl, rc);
159 goto upl_config_done;
160 }
161
162 m = mask & 0x00ff;
163 f = flags & 0x00ff;
164 upl_ctrl &= ~m;
165 upl_ctrl |= m & f;
166
167 rc = pm8058_write(upldev->pm_chip, SSBI_REG_UPL_CTRL, &upl_ctrl, 1);
168 if (rc)
169 pr_err("%s: FAIL pm8058_write(0x%X)=0x%02X: rc=%d\n",
170 __func__, SSBI_REG_UPL_CTRL, upl_ctrl, rc);
171upl_config_done:
172 mutex_unlock(&upldev->upl_mutex);
173 return rc;
174}
175EXPORT_SYMBOL(pm8058_upl_config);
176
177#if defined(CONFIG_DEBUG_FS)
178
179static int truthtable_set(void *data, u64 val)
180{
181 int rc;
182
183 rc = pm8058_upl_write_truthtable(data, val);
184 if (rc)
185 pr_err("%s: pm8058_upl_write_truthtable: rc=%d, "
186 "truthtable=0x%llX\n", __func__, rc, val);
187 return rc;
188}
189
190static int truthtable_get(void *data, u64 *val)
191{
192 int rc;
193 u16 truthtable;
194
195 rc = pm8058_upl_read_truthtable(data, &truthtable);
196 if (rc)
197 pr_err("%s: pm8058_upl_read_truthtable: rc=%d, "
198 "truthtable=0x%X\n", __func__, rc, truthtable);
199 if (val)
200 *val = truthtable;
201
202 return rc;
203}
204
205DEFINE_SIMPLE_ATTRIBUTE(upl_truthtable_fops, truthtable_get,
206 truthtable_set, "0x%04llX\n");
207
208/* enter values as 0xMMMMFFFF where MMMM is the mask and FFFF is the flags */
209static int control_set(void *data, u64 val)
210{
211 u8 mask, flags;
212 int rc;
213
214 flags = val & 0xFFFF;
215 mask = (val >> 16) & 0xFFFF;
216
217 rc = pm8058_upl_config(data, mask, flags);
218 if (rc)
219 pr_err("%s: pm8058_upl_config: rc=%d, mask = 0x%X, "
220 "flags = 0x%X\n", __func__, rc, mask, flags);
221 return rc;
222}
223
224static int control_get(void *data, u64 *val)
225{
226 struct pm8058_upl_device *upldev;
227 int rc = 0;
228 u8 ctrl;
229
230 upldev = data;
231
232 mutex_lock(&upldev->upl_mutex);
233
234 rc = pm8058_read(upldev->pm_chip, SSBI_REG_UPL_CTRL, &ctrl, 1);
235 if (rc)
236 pr_err("%s: FAIL pm8058_read(): rc=%d (ctrl=0x%02X)\n",
237 __func__, rc, ctrl);
238
239 mutex_unlock(&upldev->upl_mutex);
240
241 *val = ctrl;
242
243 return rc;
244}
245
246DEFINE_SIMPLE_ATTRIBUTE(upl_control_fops, control_get,
247 control_set, "0x%02llX\n");
248
249static int pm8058_upl_debug_init(struct pm8058_upl_device *upldev)
250{
251 struct dentry *dent;
252 struct dentry *temp;
253
254 dent = debugfs_create_dir("pm8058-upl", NULL);
255 if (dent == NULL || IS_ERR(dent)) {
256 pr_err("%s: ERR debugfs_create_dir: dent=0x%X\n",
257 __func__, (unsigned)dent);
258 return -ENOMEM;
259 }
260
261 temp = debugfs_create_file("truthtable", S_IRUSR | S_IWUSR, dent,
262 upldev, &upl_truthtable_fops);
263 if (temp == NULL || IS_ERR(temp)) {
264 pr_err("%s: ERR debugfs_create_file: dent=0x%X\n",
265 __func__, (unsigned)dent);
266 goto debug_error;
267 }
268
269 temp = debugfs_create_file("control", S_IRUSR | S_IWUSR, dent,
270 upldev, &upl_control_fops);
271 if (temp == NULL || IS_ERR(temp)) {
272 pr_err("%s: ERR debugfs_create_file: dent=0x%X\n",
273 __func__, (unsigned)dent);
274 goto debug_error;
275 }
276
277 upldev->dent = dent;
278 return 0;
279
280debug_error:
281 debugfs_remove_recursive(dent);
282 return -ENOMEM;
283}
284
285static int __devexit pm8058_upl_debug_remove(struct pm8058_upl_device *upldev)
286{
287 debugfs_remove_recursive(upldev->dent);
288 return 0;
289}
290
291#endif /* CONFIG_DEBUG_FS */
292
293static int __devinit pmic8058_upl_probe(struct platform_device *pdev)
294{
295 struct pm8058_chip *pm_chip;
296 struct pm8058_upl_device *upldev;
297
298 pm_chip = dev_get_drvdata(pdev->dev.parent);
299 if (pm_chip == NULL) {
300 pr_err("%s: no parent data passed in.\n", __func__);
301 return -EFAULT;
302 }
303
304 upldev = kzalloc(sizeof *upldev, GFP_KERNEL);
305 if (upldev == NULL) {
306 pr_err("%s: kzalloc() failed.\n", __func__);
307 return -ENOMEM;
308 }
309
310 mutex_init(&upldev->upl_mutex);
311
312 upldev->pm_chip = pm_chip;
313 upl_dev = upldev;
314 platform_set_drvdata(pdev, upldev);
315
316#if defined(CONFIG_DEBUG_FS)
317 pm8058_upl_debug_init(upl_dev);
318#endif
319 pr_notice("%s: OK\n", __func__);
320 return 0;
321}
322
323static int __devexit pmic8058_upl_remove(struct platform_device *pdev)
324{
325 struct pm8058_upl_device *upldev = platform_get_drvdata(pdev);
326
327#if defined(CONFIG_DEBUG_FS)
328 pm8058_upl_debug_remove(upldev);
329#endif
330
331 platform_set_drvdata(pdev, upldev->pm_chip);
332 kfree(upldev);
333 pr_notice("%s: OK\n", __func__);
334
335 return 0;
336}
337
338static struct platform_driver pmic8058_upl_driver = {
339 .probe = pmic8058_upl_probe,
340 .remove = __devexit_p(pmic8058_upl_remove),
341 .driver = {
342 .name = "pm8058-upl",
343 .owner = THIS_MODULE,
344 },
345};
346
347static int __init pm8058_upl_init(void)
348{
349 return platform_driver_register(&pmic8058_upl_driver);
350}
351
352static void __exit pm8058_upl_exit(void)
353{
354 platform_driver_unregister(&pmic8058_upl_driver);
355}
356
357module_init(pm8058_upl_init);
358module_exit(pm8058_upl_exit);
359
360MODULE_LICENSE("GPL v2");
361MODULE_DESCRIPTION("PMIC8058 UPL driver");
362MODULE_VERSION("1.0");
363MODULE_ALIAS("platform:pmic8058-upl");