1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * HWMON Driver for Dialog DA9055
5 * Copyright(c) 2012 Dialog Semiconductor Ltd.
7 * Author: David Dajun Chen <dchen@diasemi.com>
10 #include <linux/delay.h>
11 #include <linux/err.h>
12 #include <linux/hwmon.h>
13 #include <linux/hwmon-sysfs.h>
14 #include <linux/init.h>
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <linux/platform_device.h>
18 #include <linux/completion.h>
20 #include <linux/mfd/da9055/core.h>
21 #include <linux/mfd/da9055/reg.h>
23 #define DA9055_ADCIN_DIV 102
24 #define DA9055_VSYS_DIV 85
26 #define DA9055_ADC_VSYS 0
27 #define DA9055_ADC_ADCIN1 1
28 #define DA9055_ADC_ADCIN2 2
29 #define DA9055_ADC_ADCIN3 3
30 #define DA9055_ADC_TJUNC 4
33 struct da9055
*da9055
;
34 struct mutex hwmon_lock
;
35 struct mutex irq_lock
;
36 struct completion done
;
39 static const char * const input_names
[] = {
40 [DA9055_ADC_VSYS
] = "VSYS",
41 [DA9055_ADC_ADCIN1
] = "ADC IN1",
42 [DA9055_ADC_ADCIN2
] = "ADC IN2",
43 [DA9055_ADC_ADCIN3
] = "ADC IN3",
44 [DA9055_ADC_TJUNC
] = "CHIP TEMP",
47 static const u8 chan_mux
[DA9055_ADC_TJUNC
+ 1] = {
48 [DA9055_ADC_VSYS
] = DA9055_ADC_MUX_VSYS
,
49 [DA9055_ADC_ADCIN1
] = DA9055_ADC_MUX_ADCIN1
,
50 [DA9055_ADC_ADCIN2
] = DA9055_ADC_MUX_ADCIN2
,
51 [DA9055_ADC_ADCIN3
] = DA9055_ADC_MUX_ADCIN3
,
52 [DA9055_ADC_TJUNC
] = DA9055_ADC_MUX_T_SENSE
,
55 static int da9055_adc_manual_read(struct da9055_hwmon
*hwmon
,
56 unsigned char channel
)
59 unsigned short calc_data
;
61 unsigned char mux_sel
;
62 struct da9055
*da9055
= hwmon
->da9055
;
64 if (channel
> DA9055_ADC_TJUNC
)
67 mutex_lock(&hwmon
->irq_lock
);
69 /* Selects desired MUX for manual conversion */
70 mux_sel
= chan_mux
[channel
] | DA9055_ADC_MAN_CONV
;
72 ret
= da9055_reg_write(da9055
, DA9055_REG_ADC_MAN
, mux_sel
);
76 /* Wait for an interrupt */
77 if (!wait_for_completion_timeout(&hwmon
->done
,
78 msecs_to_jiffies(500))) {
80 "timeout waiting for ADC conversion interrupt\n");
85 ret
= da9055_reg_read(da9055
, DA9055_REG_ADC_RES_H
);
89 calc_data
= (unsigned short)ret
;
90 data
= calc_data
<< 2;
92 ret
= da9055_reg_read(da9055
, DA9055_REG_ADC_RES_L
);
96 calc_data
= (unsigned short)(ret
& DA9055_ADC_LSB_MASK
);
102 mutex_unlock(&hwmon
->irq_lock
);
106 static irqreturn_t
da9055_auxadc_irq(int irq
, void *irq_data
)
108 struct da9055_hwmon
*hwmon
= irq_data
;
110 complete(&hwmon
->done
);
115 /* Conversion function for VSYS and ADCINx */
116 static inline int volt_reg_to_mv(int value
, int channel
)
118 if (channel
== DA9055_ADC_VSYS
)
119 return DIV_ROUND_CLOSEST(value
* 1000, DA9055_VSYS_DIV
) + 2500;
121 return DIV_ROUND_CLOSEST(value
* 1000, DA9055_ADCIN_DIV
);
124 static int da9055_enable_auto_mode(struct da9055
*da9055
, int channel
)
127 return da9055_reg_update(da9055
, DA9055_REG_ADC_CONT
, 1 << channel
,
132 static int da9055_disable_auto_mode(struct da9055
*da9055
, int channel
)
135 return da9055_reg_update(da9055
, DA9055_REG_ADC_CONT
, 1 << channel
, 0);
138 static ssize_t
da9055_auto_ch_show(struct device
*dev
,
139 struct device_attribute
*devattr
,
142 struct da9055_hwmon
*hwmon
= dev_get_drvdata(dev
);
144 int channel
= to_sensor_dev_attr(devattr
)->index
;
146 mutex_lock(&hwmon
->hwmon_lock
);
148 ret
= da9055_enable_auto_mode(hwmon
->da9055
, channel
);
152 usleep_range(10000, 10500);
154 adc
= da9055_reg_read(hwmon
->da9055
, DA9055_REG_VSYS_RES
+ channel
);
157 goto hwmon_err_release
;
160 ret
= da9055_disable_auto_mode(hwmon
->da9055
, channel
);
164 mutex_unlock(&hwmon
->hwmon_lock
);
166 return sprintf(buf
, "%d\n", volt_reg_to_mv(adc
, channel
));
169 da9055_disable_auto_mode(hwmon
->da9055
, channel
);
171 mutex_unlock(&hwmon
->hwmon_lock
);
175 static ssize_t
da9055_tjunc_show(struct device
*dev
,
176 struct device_attribute
*devattr
, char *buf
)
178 struct da9055_hwmon
*hwmon
= dev_get_drvdata(dev
);
182 tjunc
= da9055_adc_manual_read(hwmon
, DA9055_ADC_TJUNC
);
186 toffset
= da9055_reg_read(hwmon
->da9055
, DA9055_REG_T_OFFSET
);
191 * Degrees celsius = -0.4084 * (ADC_RES - T_OFFSET) + 307.6332
192 * T_OFFSET is a trim value used to improve accuracy of the result
194 return sprintf(buf
, "%d\n", DIV_ROUND_CLOSEST(-4084 * (tjunc
- toffset
)
198 static ssize_t
label_show(struct device
*dev
,
199 struct device_attribute
*devattr
, char *buf
)
201 return sprintf(buf
, "%s\n",
202 input_names
[to_sensor_dev_attr(devattr
)->index
]);
205 static SENSOR_DEVICE_ATTR_RO(in0_input
, da9055_auto_ch
, DA9055_ADC_VSYS
);
206 static SENSOR_DEVICE_ATTR_RO(in0_label
, label
, DA9055_ADC_VSYS
);
207 static SENSOR_DEVICE_ATTR_RO(in1_input
, da9055_auto_ch
, DA9055_ADC_ADCIN1
);
208 static SENSOR_DEVICE_ATTR_RO(in1_label
, label
, DA9055_ADC_ADCIN1
);
209 static SENSOR_DEVICE_ATTR_RO(in2_input
, da9055_auto_ch
, DA9055_ADC_ADCIN2
);
210 static SENSOR_DEVICE_ATTR_RO(in2_label
, label
, DA9055_ADC_ADCIN2
);
211 static SENSOR_DEVICE_ATTR_RO(in3_input
, da9055_auto_ch
, DA9055_ADC_ADCIN3
);
212 static SENSOR_DEVICE_ATTR_RO(in3_label
, label
, DA9055_ADC_ADCIN3
);
214 static SENSOR_DEVICE_ATTR_RO(temp1_input
, da9055_tjunc
, DA9055_ADC_TJUNC
);
215 static SENSOR_DEVICE_ATTR_RO(temp1_label
, label
, DA9055_ADC_TJUNC
);
217 static struct attribute
*da9055_attrs
[] = {
218 &sensor_dev_attr_in0_input
.dev_attr
.attr
,
219 &sensor_dev_attr_in0_label
.dev_attr
.attr
,
220 &sensor_dev_attr_in1_input
.dev_attr
.attr
,
221 &sensor_dev_attr_in1_label
.dev_attr
.attr
,
222 &sensor_dev_attr_in2_input
.dev_attr
.attr
,
223 &sensor_dev_attr_in2_label
.dev_attr
.attr
,
224 &sensor_dev_attr_in3_input
.dev_attr
.attr
,
225 &sensor_dev_attr_in3_label
.dev_attr
.attr
,
227 &sensor_dev_attr_temp1_input
.dev_attr
.attr
,
228 &sensor_dev_attr_temp1_label
.dev_attr
.attr
,
232 ATTRIBUTE_GROUPS(da9055
);
234 static int da9055_hwmon_probe(struct platform_device
*pdev
)
236 struct device
*dev
= &pdev
->dev
;
237 struct da9055_hwmon
*hwmon
;
238 struct device
*hwmon_dev
;
241 hwmon
= devm_kzalloc(dev
, sizeof(struct da9055_hwmon
), GFP_KERNEL
);
245 mutex_init(&hwmon
->hwmon_lock
);
246 mutex_init(&hwmon
->irq_lock
);
248 init_completion(&hwmon
->done
);
249 hwmon
->da9055
= dev_get_drvdata(pdev
->dev
.parent
);
251 hwmon_irq
= platform_get_irq_byname(pdev
, "HWMON");
255 ret
= devm_request_threaded_irq(&pdev
->dev
, hwmon_irq
,
256 NULL
, da9055_auxadc_irq
,
257 IRQF_TRIGGER_HIGH
| IRQF_ONESHOT
,
260 dev_err(hwmon
->da9055
->dev
, "DA9055 ADC IRQ failed ret=%d\n",
265 hwmon_dev
= devm_hwmon_device_register_with_groups(dev
, "da9055",
268 return PTR_ERR_OR_ZERO(hwmon_dev
);
271 static struct platform_driver da9055_hwmon_driver
= {
272 .probe
= da9055_hwmon_probe
,
274 .name
= "da9055-hwmon",
278 module_platform_driver(da9055_hwmon_driver
);
280 MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
281 MODULE_DESCRIPTION("DA9055 HWMON driver");
282 MODULE_LICENSE("GPL");
283 MODULE_ALIAS("platform:da9055-hwmon");