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 device
*class_device
;
40 struct mutex hwmon_lock
;
41 struct mutex irq_lock
;
42 struct completion done
;
45 static const char * const input_names
[] = {
46 [DA9055_ADC_VSYS
] = "VSYS",
47 [DA9055_ADC_ADCIN1
] = "ADC IN1",
48 [DA9055_ADC_ADCIN2
] = "ADC IN2",
49 [DA9055_ADC_ADCIN3
] = "ADC IN3",
50 [DA9055_ADC_TJUNC
] = "CHIP TEMP",
53 static const u8 chan_mux
[DA9055_ADC_TJUNC
+ 1] = {
54 [DA9055_ADC_VSYS
] = DA9055_ADC_MUX_VSYS
,
55 [DA9055_ADC_ADCIN1
] = DA9055_ADC_MUX_ADCIN1
,
56 [DA9055_ADC_ADCIN2
] = DA9055_ADC_MUX_ADCIN2
,
57 [DA9055_ADC_ADCIN3
] = DA9055_ADC_MUX_ADCIN3
,
58 [DA9055_ADC_TJUNC
] = DA9055_ADC_MUX_T_SENSE
,
61 static int da9055_adc_manual_read(struct da9055_hwmon
*hwmon
,
62 unsigned char channel
)
65 unsigned short calc_data
;
67 unsigned char mux_sel
;
68 struct da9055
*da9055
= hwmon
->da9055
;
70 if (channel
> DA9055_ADC_TJUNC
)
73 mutex_lock(&hwmon
->irq_lock
);
75 /* Selects desired MUX for manual conversion */
76 mux_sel
= chan_mux
[channel
] | DA9055_ADC_MAN_CONV
;
78 ret
= da9055_reg_write(da9055
, DA9055_REG_ADC_MAN
, mux_sel
);
82 /* Wait for an interrupt */
83 if (!wait_for_completion_timeout(&hwmon
->done
,
84 msecs_to_jiffies(500))) {
86 "timeout waiting for ADC conversion interrupt\n");
91 ret
= da9055_reg_read(da9055
, DA9055_REG_ADC_RES_H
);
95 calc_data
= (unsigned short)ret
;
96 data
= calc_data
<< 2;
98 ret
= da9055_reg_read(da9055
, DA9055_REG_ADC_RES_L
);
102 calc_data
= (unsigned short)(ret
& DA9055_ADC_LSB_MASK
);
108 mutex_unlock(&hwmon
->irq_lock
);
112 static irqreturn_t
da9055_auxadc_irq(int irq
, void *irq_data
)
114 struct da9055_hwmon
*hwmon
= irq_data
;
116 complete(&hwmon
->done
);
121 /* Conversion function for VSYS and ADCINx */
122 static inline int volt_reg_to_mV(int value
, int channel
)
124 if (channel
== DA9055_ADC_VSYS
)
125 return DIV_ROUND_CLOSEST(value
* 1000, DA9055_VSYS_DIV
) + 2500;
127 return DIV_ROUND_CLOSEST(value
* 1000, DA9055_ADCIN_DIV
);
130 static int da9055_enable_auto_mode(struct da9055
*da9055
, int channel
)
133 return da9055_reg_update(da9055
, DA9055_REG_ADC_CONT
, 1 << channel
,
138 static int da9055_disable_auto_mode(struct da9055
*da9055
, int channel
)
141 return da9055_reg_update(da9055
, DA9055_REG_ADC_CONT
, 1 << channel
, 0);
144 static ssize_t
da9055_read_auto_ch(struct device
*dev
,
145 struct device_attribute
*devattr
, char *buf
)
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_read_tjunc(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
da9055_hwmon_show_name(struct device
*dev
,
204 struct device_attribute
*devattr
,
207 return sprintf(buf
, "da9055-hwmon\n");
210 static ssize_t
show_label(struct device
*dev
,
211 struct device_attribute
*devattr
, char *buf
)
213 return sprintf(buf
, "%s\n",
214 input_names
[to_sensor_dev_attr(devattr
)->index
]);
217 static SENSOR_DEVICE_ATTR(in0_input
, S_IRUGO
, da9055_read_auto_ch
, NULL
,
219 static SENSOR_DEVICE_ATTR(in0_label
, S_IRUGO
, show_label
, NULL
,
221 static SENSOR_DEVICE_ATTR(in1_input
, S_IRUGO
, da9055_read_auto_ch
, NULL
,
223 static SENSOR_DEVICE_ATTR(in1_label
, S_IRUGO
, show_label
, NULL
,
225 static SENSOR_DEVICE_ATTR(in2_input
, S_IRUGO
, da9055_read_auto_ch
, NULL
,
227 static SENSOR_DEVICE_ATTR(in2_label
, S_IRUGO
, show_label
, NULL
,
229 static SENSOR_DEVICE_ATTR(in3_input
, S_IRUGO
, da9055_read_auto_ch
, NULL
,
231 static SENSOR_DEVICE_ATTR(in3_label
, S_IRUGO
, show_label
, NULL
,
234 static SENSOR_DEVICE_ATTR(temp1_input
, S_IRUGO
, da9055_read_tjunc
, NULL
,
236 static SENSOR_DEVICE_ATTR(temp1_label
, S_IRUGO
, show_label
, NULL
,
239 static DEVICE_ATTR(name
, S_IRUGO
, da9055_hwmon_show_name
, NULL
);
241 static struct attribute
*da9055_attr
[] = {
243 &sensor_dev_attr_in0_input
.dev_attr
.attr
,
244 &sensor_dev_attr_in0_label
.dev_attr
.attr
,
245 &sensor_dev_attr_in1_input
.dev_attr
.attr
,
246 &sensor_dev_attr_in1_label
.dev_attr
.attr
,
247 &sensor_dev_attr_in2_input
.dev_attr
.attr
,
248 &sensor_dev_attr_in2_label
.dev_attr
.attr
,
249 &sensor_dev_attr_in3_input
.dev_attr
.attr
,
250 &sensor_dev_attr_in3_label
.dev_attr
.attr
,
252 &sensor_dev_attr_temp1_input
.dev_attr
.attr
,
253 &sensor_dev_attr_temp1_label
.dev_attr
.attr
,
257 static const struct attribute_group da9055_attr_group
= {.attrs
= da9055_attr
};
259 static int da9055_hwmon_probe(struct platform_device
*pdev
)
261 struct da9055_hwmon
*hwmon
;
264 hwmon
= devm_kzalloc(&pdev
->dev
, sizeof(struct da9055_hwmon
),
269 mutex_init(&hwmon
->hwmon_lock
);
270 mutex_init(&hwmon
->irq_lock
);
272 init_completion(&hwmon
->done
);
273 hwmon
->da9055
= dev_get_drvdata(pdev
->dev
.parent
);
275 platform_set_drvdata(pdev
, hwmon
);
277 hwmon_irq
= platform_get_irq_byname(pdev
, "HWMON");
281 hwmon_irq
= regmap_irq_get_virq(hwmon
->da9055
->irq_data
, hwmon_irq
);
285 ret
= devm_request_threaded_irq(&pdev
->dev
, hwmon_irq
,
286 NULL
, da9055_auxadc_irq
,
287 IRQF_TRIGGER_HIGH
| IRQF_ONESHOT
,
290 dev_err(hwmon
->da9055
->dev
, "DA9055 ADC IRQ failed ret=%d\n",
295 ret
= sysfs_create_group(&pdev
->dev
.kobj
, &da9055_attr_group
);
299 hwmon
->class_device
= hwmon_device_register(&pdev
->dev
);
300 if (IS_ERR(hwmon
->class_device
)) {
301 ret
= PTR_ERR(hwmon
->class_device
);
308 sysfs_remove_group(&pdev
->dev
.kobj
, &da9055_attr_group
);
312 static int da9055_hwmon_remove(struct platform_device
*pdev
)
314 struct da9055_hwmon
*hwmon
= platform_get_drvdata(pdev
);
316 sysfs_remove_group(&pdev
->dev
.kobj
, &da9055_attr_group
);
317 hwmon_device_unregister(hwmon
->class_device
);
322 static struct platform_driver da9055_hwmon_driver
= {
323 .probe
= da9055_hwmon_probe
,
324 .remove
= da9055_hwmon_remove
,
326 .name
= "da9055-hwmon",
327 .owner
= THIS_MODULE
,
331 module_platform_driver(da9055_hwmon_driver
);
333 MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
334 MODULE_DESCRIPTION("DA9055 HWMON driver");
335 MODULE_LICENSE("GPL");
336 MODULE_ALIAS("platform:da9055-hwmon");