aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/da9052-hwmon.c
diff options
context:
space:
mode:
authorAshish Jangam <ashish.jangam@kpitcummins.com>2012-03-17 06:04:41 -0400
committerGuenter Roeck <linux@roeck-us.net>2012-07-22 00:48:37 -0400
commite41f6432d1ca757d8e4a2dccfd772e421d640e98 (patch)
tree30a336cdb790c5253b8504656dcd168dccdfcdf6 /drivers/hwmon/da9052-hwmon.c
parent62867d491a27affee36194d4856564f2f4e12b3c (diff)
hwmon: Driver for DA9052/53 PMIC
The DA9052 PMIC provides an Analogue to Digital Converter with 10 bits resolution and 10 channels. This patch monitors the DA9052 PMIC's ADC channels mostly for battery parameters like battery temperature, junction temperature, battery current etc. This patch is functionally tested on Samsung SMDKV6410 Signed-off-by: David Dajun Chen <dchen@diasemi.com> Signed-off-by: Ashish Jangam <ashish.jangam@kpitcummins.com> [Guenter Roeck: __init --> __devinit for probe function] Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Diffstat (limited to 'drivers/hwmon/da9052-hwmon.c')
-rw-r--r--drivers/hwmon/da9052-hwmon.c344
1 files changed, 344 insertions, 0 deletions
diff --git a/drivers/hwmon/da9052-hwmon.c b/drivers/hwmon/da9052-hwmon.c
new file mode 100644
index 000000000000..fc65f2d3ec91
--- /dev/null
+++ b/drivers/hwmon/da9052-hwmon.c
@@ -0,0 +1,344 @@
1/*
2 * HWMON Driver for Dialog DA9052
3 *
4 * Copyright(c) 2012 Dialog Semiconductor Ltd.
5 *
6 * Author: David Dajun Chen <dchen@diasemi.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 */
14
15#include <linux/delay.h>
16#include <linux/err.h>
17#include <linux/hwmon.h>
18#include <linux/hwmon-sysfs.h>
19#include <linux/init.h>
20#include <linux/kernel.h>
21#include <linux/module.h>
22#include <linux/slab.h>
23#include <linux/platform_device.h>
24
25#include <linux/mfd/da9052/da9052.h>
26#include <linux/mfd/da9052/reg.h>
27
28struct da9052_hwmon {
29 struct da9052 *da9052;
30 struct device *class_device;
31 struct mutex hwmon_lock;
32};
33
34static const char * const input_names[] = {
35 [DA9052_ADC_VDDOUT] = "VDDOUT",
36 [DA9052_ADC_ICH] = "CHARGING CURRENT",
37 [DA9052_ADC_TBAT] = "BATTERY TEMP",
38 [DA9052_ADC_VBAT] = "BATTERY VOLTAGE",
39 [DA9052_ADC_IN4] = "ADC IN4",
40 [DA9052_ADC_IN5] = "ADC IN5",
41 [DA9052_ADC_IN6] = "ADC IN6",
42 [DA9052_ADC_TJUNC] = "BATTERY JUNCTION TEMP",
43 [DA9052_ADC_VBBAT] = "BACK-UP BATTERY VOLTAGE",
44};
45
46/* Conversion function for VDDOUT and VBAT */
47static inline int volt_reg_to_mV(int value)
48{
49 return DIV_ROUND_CLOSEST(value * 1000, 512) + 2500;
50}
51
52/* Conversion function for ADC channels 4, 5 and 6 */
53static inline int input_reg_to_mV(int value)
54{
55 return DIV_ROUND_CLOSEST(value * 2500, 1023);
56}
57
58/* Conversion function for VBBAT */
59static inline int vbbat_reg_to_mV(int value)
60{
61 return DIV_ROUND_CLOSEST(value * 2500, 512);
62}
63
64static int da9052_enable_vddout_channel(struct da9052 *da9052)
65{
66 int ret;
67
68 ret = da9052_reg_read(da9052, DA9052_ADC_CONT_REG);
69 if (ret < 0)
70 return ret;
71
72 ret |= DA9052_ADCCONT_AUTOVDDEN;
73
74 return da9052_reg_write(da9052, DA9052_ADC_CONT_REG, ret);
75}
76
77static int da9052_disable_vddout_channel(struct da9052 *da9052)
78{
79 int ret;
80
81 ret = da9052_reg_read(da9052, DA9052_ADC_CONT_REG);
82 if (ret < 0)
83 return ret;
84
85 ret &= ~DA9052_ADCCONT_AUTOVDDEN;
86
87 return da9052_reg_write(da9052, DA9052_ADC_CONT_REG, ret);
88}
89
90static ssize_t da9052_read_vddout(struct device *dev,
91 struct device_attribute *devattr, char *buf)
92{
93 struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
94 int ret, vdd;
95
96 mutex_lock(&hwmon->hwmon_lock);
97
98 ret = da9052_enable_vddout_channel(hwmon->da9052);
99 if (ret < 0)
100 goto hwmon_err;
101
102 vdd = da9052_reg_read(hwmon->da9052, DA9052_VDD_RES_REG);
103 if (vdd < 0) {
104 ret = vdd;
105 goto hwmon_err_release;
106 }
107
108 ret = da9052_disable_vddout_channel(hwmon->da9052);
109 if (ret < 0)
110 goto hwmon_err;
111
112 mutex_unlock(&hwmon->hwmon_lock);
113 return sprintf(buf, "%d\n", volt_reg_to_mV(vdd));
114
115hwmon_err_release:
116 da9052_disable_vddout_channel(hwmon->da9052);
117hwmon_err:
118 mutex_unlock(&hwmon->hwmon_lock);
119 return ret;
120}
121
122static ssize_t da9052_read_ich(struct device *dev,
123 struct device_attribute *devattr, char *buf)
124{
125 struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
126 int ret;
127
128 ret = da9052_reg_read(hwmon->da9052, DA9052_ICHG_AV_REG);
129 if (ret < 0)
130 return ret;
131
132 /* Equivalent to 3.9mA/bit in register ICHG_AV */
133 return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(ret * 39, 10));
134}
135
136static ssize_t da9052_read_tbat(struct device *dev,
137 struct device_attribute *devattr, char *buf)
138{
139 struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
140
141 return sprintf(buf, "%d\n", da9052_adc_read_temp(hwmon->da9052));
142}
143
144static ssize_t da9052_read_vbat(struct device *dev,
145 struct device_attribute *devattr, char *buf)
146{
147 struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
148 int ret;
149
150 ret = da9052_adc_manual_read(hwmon->da9052, DA9052_ADC_VBAT);
151 if (ret < 0)
152 return ret;
153
154 return sprintf(buf, "%d\n", volt_reg_to_mV(ret));
155}
156
157static ssize_t da9052_read_misc_channel(struct device *dev,
158 struct device_attribute *devattr,
159 char *buf)
160{
161 struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
162 int channel = to_sensor_dev_attr(devattr)->index;
163 int ret;
164
165 ret = da9052_adc_manual_read(hwmon->da9052, channel);
166 if (ret < 0)
167 return ret;
168
169 return sprintf(buf, "%d\n", input_reg_to_mV(ret));
170}
171
172static ssize_t da9052_read_tjunc(struct device *dev,
173 struct device_attribute *devattr, char *buf)
174{
175 struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
176 int tjunc;
177 int toffset;
178
179 tjunc = da9052_reg_read(hwmon->da9052, DA9052_TJUNC_RES_REG);
180 if (tjunc < 0)
181 return tjunc;
182
183 toffset = da9052_reg_read(hwmon->da9052, DA9052_T_OFFSET_REG);
184 if (toffset < 0)
185 return toffset;
186
187 /*
188 * Degrees celsius = 1.708 * (TJUNC_RES - T_OFFSET) - 108.8
189 * T_OFFSET is a trim value used to improve accuracy of the result
190 */
191 return sprintf(buf, "%d\n", 1708 * (tjunc - toffset) - 108800);
192}
193
194static ssize_t da9052_read_vbbat(struct device *dev,
195 struct device_attribute *devattr, char *buf)
196{
197 struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
198 int ret;
199
200 ret = da9052_adc_manual_read(hwmon->da9052, DA9052_ADC_VBBAT);
201 if (ret < 0)
202 return ret;
203
204 return sprintf(buf, "%d\n", vbbat_reg_to_mV(ret));
205}
206
207static ssize_t da9052_hwmon_show_name(struct device *dev,
208 struct device_attribute *devattr,
209 char *buf)
210{
211 return sprintf(buf, "da9052-hwmon\n");
212}
213
214static ssize_t show_label(struct device *dev,
215 struct device_attribute *devattr, char *buf)
216{
217 return sprintf(buf, "%s\n",
218 input_names[to_sensor_dev_attr(devattr)->index]);
219}
220
221static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, da9052_read_vddout, NULL,
222 DA9052_ADC_VDDOUT);
223static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_label, NULL,
224 DA9052_ADC_VDDOUT);
225static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, da9052_read_vbat, NULL,
226 DA9052_ADC_VBAT);
227static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL,
228 DA9052_ADC_VBAT);
229static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, da9052_read_misc_channel, NULL,
230 DA9052_ADC_IN4);
231static SENSOR_DEVICE_ATTR(in4_label, S_IRUGO, show_label, NULL,
232 DA9052_ADC_IN4);
233static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, da9052_read_misc_channel, NULL,
234 DA9052_ADC_IN5);
235static SENSOR_DEVICE_ATTR(in5_label, S_IRUGO, show_label, NULL,
236 DA9052_ADC_IN5);
237static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, da9052_read_misc_channel, NULL,
238 DA9052_ADC_IN6);
239static SENSOR_DEVICE_ATTR(in6_label, S_IRUGO, show_label, NULL,
240 DA9052_ADC_IN6);
241static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, da9052_read_vbbat, NULL,
242 DA9052_ADC_VBBAT);
243static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL,
244 DA9052_ADC_VBBAT);
245
246static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, da9052_read_ich, NULL,
247 DA9052_ADC_ICH);
248static SENSOR_DEVICE_ATTR(curr1_label, S_IRUGO, show_label, NULL,
249 DA9052_ADC_ICH);
250
251static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, da9052_read_tbat, NULL,
252 DA9052_ADC_TBAT);
253static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL,
254 DA9052_ADC_TBAT);
255static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, da9052_read_tjunc, NULL,
256 DA9052_ADC_TJUNC);
257static SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO, show_label, NULL,
258 DA9052_ADC_TJUNC);
259
260static DEVICE_ATTR(name, S_IRUGO, da9052_hwmon_show_name, NULL);
261
262static struct attribute *da9052_attr[] = {
263 &dev_attr_name.attr,
264 &sensor_dev_attr_in0_input.dev_attr.attr,
265 &sensor_dev_attr_in0_label.dev_attr.attr,
266 &sensor_dev_attr_in3_input.dev_attr.attr,
267 &sensor_dev_attr_in3_label.dev_attr.attr,
268 &sensor_dev_attr_in4_input.dev_attr.attr,
269 &sensor_dev_attr_in4_label.dev_attr.attr,
270 &sensor_dev_attr_in5_input.dev_attr.attr,
271 &sensor_dev_attr_in5_label.dev_attr.attr,
272 &sensor_dev_attr_in6_input.dev_attr.attr,
273 &sensor_dev_attr_in6_label.dev_attr.attr,
274 &sensor_dev_attr_in9_input.dev_attr.attr,
275 &sensor_dev_attr_in9_label.dev_attr.attr,
276 &sensor_dev_attr_curr1_input.dev_attr.attr,
277 &sensor_dev_attr_curr1_label.dev_attr.attr,
278 &sensor_dev_attr_temp2_input.dev_attr.attr,
279 &sensor_dev_attr_temp2_label.dev_attr.attr,
280 &sensor_dev_attr_temp8_input.dev_attr.attr,
281 &sensor_dev_attr_temp8_label.dev_attr.attr,
282 NULL
283};
284
285static const struct attribute_group da9052_attr_group = {.attrs = da9052_attr};
286
287static int __devinit da9052_hwmon_probe(struct platform_device *pdev)
288{
289 struct da9052_hwmon *hwmon;
290 int ret;
291
292 hwmon = devm_kzalloc(&pdev->dev, sizeof(struct da9052_hwmon),
293 GFP_KERNEL);
294 if (!hwmon)
295 return -ENOMEM;
296
297 mutex_init(&hwmon->hwmon_lock);
298 hwmon->da9052 = dev_get_drvdata(pdev->dev.parent);
299
300 platform_set_drvdata(pdev, hwmon);
301
302 ret = sysfs_create_group(&pdev->dev.kobj, &da9052_attr_group);
303 if (ret)
304 goto err_mem;
305
306 hwmon->class_device = hwmon_device_register(&pdev->dev);
307 if (IS_ERR(hwmon->class_device)) {
308 ret = PTR_ERR(hwmon->class_device);
309 goto err_sysfs;
310 }
311
312 return 0;
313
314err_sysfs:
315 sysfs_remove_group(&pdev->dev.kobj, &da9052_attr_group);
316err_mem:
317 return ret;
318}
319
320static int __devexit da9052_hwmon_remove(struct platform_device *pdev)
321{
322 struct da9052_hwmon *hwmon = platform_get_drvdata(pdev);
323
324 hwmon_device_unregister(hwmon->class_device);
325 sysfs_remove_group(&pdev->dev.kobj, &da9052_attr_group);
326
327 return 0;
328}
329
330static struct platform_driver da9052_hwmon_driver = {
331 .probe = da9052_hwmon_probe,
332 .remove = __devexit_p(da9052_hwmon_remove),
333 .driver = {
334 .name = "da9052-hwmon",
335 .owner = THIS_MODULE,
336 },
337};
338
339module_platform_driver(da9052_hwmon_driver);
340
341MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
342MODULE_DESCRIPTION("DA9052 HWMON driver");
343MODULE_LICENSE("GPL");
344MODULE_ALIAS("platform:da9052-hwmon");