2 * HWMON Driver for Dialog DA9055
4 * Copyright(c) 2012 Dialog Semiconductor Ltd.
6 * Author: David Dajun Chen <dchen@diasemi.com>
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.
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/platform_device.h>
23 #include <linux/completion.h>
25 #include <linux/mfd/da9055/core.h>
26 #include <linux/mfd/da9055/reg.h>
28 #define DA9055_ADCIN_DIV 102
29 #define DA9055_VSYS_DIV 85
31 #define DA9055_ADC_VSYS 0
32 #define DA9055_ADC_ADCIN1 1
33 #define DA9055_ADC_ADCIN2 2
34 #define DA9055_ADC_ADCIN3 3
35 #define DA9055_ADC_TJUNC 4
38 struct da9055
*da9055
;
39 struct mutex hwmon_lock
;
40 struct mutex irq_lock
;
41 struct completion done
;
44 static const char * const input_names
[] = {
45 [DA9055_ADC_VSYS
] = "VSYS",
46 [DA9055_ADC_ADCIN1
] = "ADC IN1",
47 [DA9055_ADC_ADCIN2
] = "ADC IN2",
48 [DA9055_ADC_ADCIN3
] = "ADC IN3",
49 [DA9055_ADC_TJUNC
] = "CHIP TEMP",
52 static const u8 chan_mux
[DA9055_ADC_TJUNC
+ 1] = {
53 [DA9055_ADC_VSYS
] = DA9055_ADC_MUX_VSYS
,
54 [DA9055_ADC_ADCIN1
] = DA9055_ADC_MUX_ADCIN1
,
55 [DA9055_ADC_ADCIN2
] = DA9055_ADC_MUX_ADCIN2
,
56 [DA9055_ADC_ADCIN3
] = DA9055_ADC_MUX_ADCIN3
,
57 [DA9055_ADC_TJUNC
] = DA9055_ADC_MUX_T_SENSE
,
60 static int da9055_adc_manual_read(struct da9055_hwmon
*hwmon
,
61 unsigned char channel
)
64 unsigned short calc_data
;
66 unsigned char mux_sel
;
67 struct da9055
*da9055
= hwmon
->da9055
;
69 if (channel
> DA9055_ADC_TJUNC
)
72 mutex_lock(&hwmon
->irq_lock
);
74 /* Selects desired MUX for manual conversion */
75 mux_sel
= chan_mux
[channel
] | DA9055_ADC_MAN_CONV
;
77 ret
= da9055_reg_write(da9055
, DA9055_REG_ADC_MAN
, mux_sel
);
81 /* Wait for an interrupt */
82 if (!wait_for_completion_timeout(&hwmon
->done
,
83 msecs_to_jiffies(500))) {
85 "timeout waiting for ADC conversion interrupt\n");
90 ret
= da9055_reg_read(da9055
, DA9055_REG_ADC_RES_H
);
94 calc_data
= (unsigned short)ret
;
95 data
= calc_data
<< 2;
97 ret
= da9055_reg_read(da9055
, DA9055_REG_ADC_RES_L
);
101 calc_data
= (unsigned short)(ret
& DA9055_ADC_LSB_MASK
);
107 mutex_unlock(&hwmon
->irq_lock
);
111 static irqreturn_t
da9055_auxadc_irq(int irq
, void *irq_data
)
113 struct da9055_hwmon
*hwmon
= irq_data
;
115 complete(&hwmon
->done
);
120 /* Conversion function for VSYS and ADCINx */
121 static inline int volt_reg_to_mv(int value
, int channel
)
123 if (channel
== DA9055_ADC_VSYS
)
124 return DIV_ROUND_CLOSEST(value
* 1000, DA9055_VSYS_DIV
) + 2500;
126 return DIV_ROUND_CLOSEST(value
* 1000, DA9055_ADCIN_DIV
);
129 static int da9055_enable_auto_mode(struct da9055
*da9055
, int channel
)
132 return da9055_reg_update(da9055
, DA9055_REG_ADC_CONT
, 1 << channel
,
137 static int da9055_disable_auto_mode(struct da9055
*da9055
, int channel
)
140 return da9055_reg_update(da9055
, DA9055_REG_ADC_CONT
, 1 << channel
, 0);
143 static ssize_t
da9055_read_auto_ch(struct device
*dev
,
144 struct device_attribute
*devattr
, char *buf
)
146 struct da9055_hwmon
*hwmon
= dev_get_drvdata(dev
);
148 int channel
= to_sensor_dev_attr(devattr
)->index
;
150 mutex_lock(&hwmon
->hwmon_lock
);
152 ret
= da9055_enable_auto_mode(hwmon
->da9055
, channel
);
156 usleep_range(10000, 10500);
158 adc
= da9055_reg_read(hwmon
->da9055
, DA9055_REG_VSYS_RES
+ channel
);
161 goto hwmon_err_release
;
164 ret
= da9055_disable_auto_mode(hwmon
->da9055
, channel
);
168 mutex_unlock(&hwmon
->hwmon_lock
);
170 return sprintf(buf
, "%d\n", volt_reg_to_mv(adc
, channel
));
173 da9055_disable_auto_mode(hwmon
->da9055
, channel
);
175 mutex_unlock(&hwmon
->hwmon_lock
);
179 static ssize_t
da9055_read_tjunc(struct device
*dev
,
180 struct device_attribute
*devattr
, char *buf
)
182 struct da9055_hwmon
*hwmon
= dev_get_drvdata(dev
);
186 tjunc
= da9055_adc_manual_read(hwmon
, DA9055_ADC_TJUNC
);
190 toffset
= da9055_reg_read(hwmon
->da9055
, DA9055_REG_T_OFFSET
);
195 * Degrees celsius = -0.4084 * (ADC_RES - T_OFFSET) + 307.6332
196 * T_OFFSET is a trim value used to improve accuracy of the result
198 return sprintf(buf
, "%d\n", DIV_ROUND_CLOSEST(-4084 * (tjunc
- toffset
)
202 static ssize_t
show_label(struct device
*dev
,
203 struct device_attribute
*devattr
, char *buf
)
205 return sprintf(buf
, "%s\n",
206 input_names
[to_sensor_dev_attr(devattr
)->index
]);
209 static SENSOR_DEVICE_ATTR(in0_input
, S_IRUGO
, da9055_read_auto_ch
, NULL
,
211 static SENSOR_DEVICE_ATTR(in0_label
, S_IRUGO
, show_label
, NULL
,
213 static SENSOR_DEVICE_ATTR(in1_input
, S_IRUGO
, da9055_read_auto_ch
, NULL
,
215 static SENSOR_DEVICE_ATTR(in1_label
, S_IRUGO
, show_label
, NULL
,
217 static SENSOR_DEVICE_ATTR(in2_input
, S_IRUGO
, da9055_read_auto_ch
, NULL
,
219 static SENSOR_DEVICE_ATTR(in2_label
, S_IRUGO
, show_label
, NULL
,
221 static SENSOR_DEVICE_ATTR(in3_input
, S_IRUGO
, da9055_read_auto_ch
, NULL
,
223 static SENSOR_DEVICE_ATTR(in3_label
, S_IRUGO
, show_label
, NULL
,
226 static SENSOR_DEVICE_ATTR(temp1_input
, S_IRUGO
, da9055_read_tjunc
, NULL
,
228 static SENSOR_DEVICE_ATTR(temp1_label
, S_IRUGO
, show_label
, NULL
,
231 static struct attribute
*da9055_attrs
[] = {
232 &sensor_dev_attr_in0_input
.dev_attr
.attr
,
233 &sensor_dev_attr_in0_label
.dev_attr
.attr
,
234 &sensor_dev_attr_in1_input
.dev_attr
.attr
,
235 &sensor_dev_attr_in1_label
.dev_attr
.attr
,
236 &sensor_dev_attr_in2_input
.dev_attr
.attr
,
237 &sensor_dev_attr_in2_label
.dev_attr
.attr
,
238 &sensor_dev_attr_in3_input
.dev_attr
.attr
,
239 &sensor_dev_attr_in3_label
.dev_attr
.attr
,
241 &sensor_dev_attr_temp1_input
.dev_attr
.attr
,
242 &sensor_dev_attr_temp1_label
.dev_attr
.attr
,
246 ATTRIBUTE_GROUPS(da9055
);
248 static int da9055_hwmon_probe(struct platform_device
*pdev
)
250 struct device
*dev
= &pdev
->dev
;
251 struct da9055_hwmon
*hwmon
;
252 struct device
*hwmon_dev
;
255 hwmon
= devm_kzalloc(dev
, sizeof(struct da9055_hwmon
), GFP_KERNEL
);
259 mutex_init(&hwmon
->hwmon_lock
);
260 mutex_init(&hwmon
->irq_lock
);
262 init_completion(&hwmon
->done
);
263 hwmon
->da9055
= dev_get_drvdata(pdev
->dev
.parent
);
265 hwmon_irq
= platform_get_irq_byname(pdev
, "HWMON");
269 ret
= devm_request_threaded_irq(&pdev
->dev
, hwmon_irq
,
270 NULL
, da9055_auxadc_irq
,
271 IRQF_TRIGGER_HIGH
| IRQF_ONESHOT
,
274 dev_err(hwmon
->da9055
->dev
, "DA9055 ADC IRQ failed ret=%d\n",
279 hwmon_dev
= devm_hwmon_device_register_with_groups(dev
, "da9055",
282 return PTR_ERR_OR_ZERO(hwmon_dev
);
285 static struct platform_driver da9055_hwmon_driver
= {
286 .probe
= da9055_hwmon_probe
,
288 .name
= "da9055-hwmon",
292 module_platform_driver(da9055_hwmon_driver
);
294 MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
295 MODULE_DESCRIPTION("DA9055 HWMON driver");
296 MODULE_LICENSE("GPL");
297 MODULE_ALIAS("platform:da9055-hwmon");