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_auto_ch_show(struct device
*dev
,
144 struct device_attribute
*devattr
,
147 struct da9055_hwmon
*hwmon
= dev_get_drvdata(dev
);
149 int channel
= to_sensor_dev_attr(devattr
)->index
;
151 mutex_lock(&hwmon
->hwmon_lock
);
153 ret
= da9055_enable_auto_mode(hwmon
->da9055
, channel
);
157 usleep_range(10000, 10500);
159 adc
= da9055_reg_read(hwmon
->da9055
, DA9055_REG_VSYS_RES
+ channel
);
162 goto hwmon_err_release
;
165 ret
= da9055_disable_auto_mode(hwmon
->da9055
, channel
);
169 mutex_unlock(&hwmon
->hwmon_lock
);
171 return sprintf(buf
, "%d\n", volt_reg_to_mv(adc
, channel
));
174 da9055_disable_auto_mode(hwmon
->da9055
, channel
);
176 mutex_unlock(&hwmon
->hwmon_lock
);
180 static ssize_t
da9055_tjunc_show(struct device
*dev
,
181 struct device_attribute
*devattr
, char *buf
)
183 struct da9055_hwmon
*hwmon
= dev_get_drvdata(dev
);
187 tjunc
= da9055_adc_manual_read(hwmon
, DA9055_ADC_TJUNC
);
191 toffset
= da9055_reg_read(hwmon
->da9055
, DA9055_REG_T_OFFSET
);
196 * Degrees celsius = -0.4084 * (ADC_RES - T_OFFSET) + 307.6332
197 * T_OFFSET is a trim value used to improve accuracy of the result
199 return sprintf(buf
, "%d\n", DIV_ROUND_CLOSEST(-4084 * (tjunc
- toffset
)
203 static ssize_t
label_show(struct device
*dev
,
204 struct device_attribute
*devattr
, char *buf
)
206 return sprintf(buf
, "%s\n",
207 input_names
[to_sensor_dev_attr(devattr
)->index
]);
210 static SENSOR_DEVICE_ATTR_RO(in0_input
, da9055_auto_ch
, DA9055_ADC_VSYS
);
211 static SENSOR_DEVICE_ATTR_RO(in0_label
, label
, DA9055_ADC_VSYS
);
212 static SENSOR_DEVICE_ATTR_RO(in1_input
, da9055_auto_ch
, DA9055_ADC_ADCIN1
);
213 static SENSOR_DEVICE_ATTR_RO(in1_label
, label
, DA9055_ADC_ADCIN1
);
214 static SENSOR_DEVICE_ATTR_RO(in2_input
, da9055_auto_ch
, DA9055_ADC_ADCIN2
);
215 static SENSOR_DEVICE_ATTR_RO(in2_label
, label
, DA9055_ADC_ADCIN2
);
216 static SENSOR_DEVICE_ATTR_RO(in3_input
, da9055_auto_ch
, DA9055_ADC_ADCIN3
);
217 static SENSOR_DEVICE_ATTR_RO(in3_label
, label
, DA9055_ADC_ADCIN3
);
219 static SENSOR_DEVICE_ATTR_RO(temp1_input
, da9055_tjunc
, DA9055_ADC_TJUNC
);
220 static SENSOR_DEVICE_ATTR_RO(temp1_label
, label
, DA9055_ADC_TJUNC
);
222 static struct attribute
*da9055_attrs
[] = {
223 &sensor_dev_attr_in0_input
.dev_attr
.attr
,
224 &sensor_dev_attr_in0_label
.dev_attr
.attr
,
225 &sensor_dev_attr_in1_input
.dev_attr
.attr
,
226 &sensor_dev_attr_in1_label
.dev_attr
.attr
,
227 &sensor_dev_attr_in2_input
.dev_attr
.attr
,
228 &sensor_dev_attr_in2_label
.dev_attr
.attr
,
229 &sensor_dev_attr_in3_input
.dev_attr
.attr
,
230 &sensor_dev_attr_in3_label
.dev_attr
.attr
,
232 &sensor_dev_attr_temp1_input
.dev_attr
.attr
,
233 &sensor_dev_attr_temp1_label
.dev_attr
.attr
,
237 ATTRIBUTE_GROUPS(da9055
);
239 static int da9055_hwmon_probe(struct platform_device
*pdev
)
241 struct device
*dev
= &pdev
->dev
;
242 struct da9055_hwmon
*hwmon
;
243 struct device
*hwmon_dev
;
246 hwmon
= devm_kzalloc(dev
, sizeof(struct da9055_hwmon
), GFP_KERNEL
);
250 mutex_init(&hwmon
->hwmon_lock
);
251 mutex_init(&hwmon
->irq_lock
);
253 init_completion(&hwmon
->done
);
254 hwmon
->da9055
= dev_get_drvdata(pdev
->dev
.parent
);
256 hwmon_irq
= platform_get_irq_byname(pdev
, "HWMON");
260 ret
= devm_request_threaded_irq(&pdev
->dev
, hwmon_irq
,
261 NULL
, da9055_auxadc_irq
,
262 IRQF_TRIGGER_HIGH
| IRQF_ONESHOT
,
265 dev_err(hwmon
->da9055
->dev
, "DA9055 ADC IRQ failed ret=%d\n",
270 hwmon_dev
= devm_hwmon_device_register_with_groups(dev
, "da9055",
273 return PTR_ERR_OR_ZERO(hwmon_dev
);
276 static struct platform_driver da9055_hwmon_driver
= {
277 .probe
= da9055_hwmon_probe
,
279 .name
= "da9055-hwmon",
283 module_platform_driver(da9055_hwmon_driver
);
285 MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
286 MODULE_DESCRIPTION("DA9055 HWMON driver");
287 MODULE_LICENSE("GPL");
288 MODULE_ALIAS("platform:da9055-hwmon");