Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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/err.h>
 16#include <linux/hwmon.h>
 17#include <linux/hwmon-sysfs.h>
 18#include <linux/init.h>
 19#include <linux/kernel.h>
 20#include <linux/module.h>
 21#include <linux/slab.h>
 22#include <linux/platform_device.h>
 23
 24#include <linux/mfd/da9052/da9052.h>
 25#include <linux/mfd/da9052/reg.h>
 26
 27struct da9052_hwmon {
 28	struct da9052	*da9052;
 29	struct device	*class_device;
 30	struct mutex	hwmon_lock;
 31};
 32
 33static const char * const input_names[] = {
 34	[DA9052_ADC_VDDOUT]	=	"VDDOUT",
 35	[DA9052_ADC_ICH]	=	"CHARGING CURRENT",
 36	[DA9052_ADC_TBAT]	=	"BATTERY TEMP",
 37	[DA9052_ADC_VBAT]	=	"BATTERY VOLTAGE",
 38	[DA9052_ADC_IN4]	=	"ADC IN4",
 39	[DA9052_ADC_IN5]	=	"ADC IN5",
 40	[DA9052_ADC_IN6]	=	"ADC IN6",
 41	[DA9052_ADC_TJUNC]	=	"BATTERY JUNCTION TEMP",
 42	[DA9052_ADC_VBBAT]	=	"BACK-UP BATTERY VOLTAGE",
 43};
 44
 45/* Conversion function for VDDOUT and VBAT */
 46static inline int volt_reg_to_mv(int value)
 47{
 48	return DIV_ROUND_CLOSEST(value * 2000, 1023) + 2500;
 49}
 50
 51/* Conversion function for ADC channels 4, 5 and 6 */
 52static inline int input_reg_to_mv(int value)
 53{
 54	return DIV_ROUND_CLOSEST(value * 2500, 1023);
 55}
 56
 57/* Conversion function for VBBAT */
 58static inline int vbbat_reg_to_mv(int value)
 59{
 60	return DIV_ROUND_CLOSEST(value * 5000, 1023);
 61}
 62
 63static inline int da9052_enable_vddout_channel(struct da9052 *da9052)
 64{
 65	return da9052_reg_update(da9052, DA9052_ADC_CONT_REG,
 66				 DA9052_ADCCONT_AUTOVDDEN,
 67				 DA9052_ADCCONT_AUTOVDDEN);
 68}
 69
 70static inline int da9052_disable_vddout_channel(struct da9052 *da9052)
 71{
 72	return da9052_reg_update(da9052, DA9052_ADC_CONT_REG,
 73				 DA9052_ADCCONT_AUTOVDDEN, 0);
 74}
 75
 76static ssize_t da9052_read_vddout(struct device *dev,
 77				  struct device_attribute *devattr, char *buf)
 78{
 79	struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
 80	int ret, vdd;
 81
 82	mutex_lock(&hwmon->hwmon_lock);
 83
 84	ret = da9052_enable_vddout_channel(hwmon->da9052);
 85	if (ret < 0)
 86		goto hwmon_err;
 87
 88	vdd = da9052_reg_read(hwmon->da9052, DA9052_VDD_RES_REG);
 89	if (vdd < 0) {
 90		ret = vdd;
 91		goto hwmon_err_release;
 92	}
 93
 94	ret = da9052_disable_vddout_channel(hwmon->da9052);
 95	if (ret < 0)
 96		goto hwmon_err;
 97
 98	mutex_unlock(&hwmon->hwmon_lock);
 99	return sprintf(buf, "%d\n", volt_reg_to_mv(vdd));
100
101hwmon_err_release:
102	da9052_disable_vddout_channel(hwmon->da9052);
103hwmon_err:
104	mutex_unlock(&hwmon->hwmon_lock);
105	return ret;
106}
107
108static ssize_t da9052_read_ich(struct device *dev,
109			       struct device_attribute *devattr, char *buf)
110{
111	struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
112	int ret;
113
114	ret = da9052_reg_read(hwmon->da9052, DA9052_ICHG_AV_REG);
115	if (ret < 0)
116		return ret;
117
118	/* Equivalent to 3.9mA/bit in register ICHG_AV */
119	return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(ret * 39, 10));
120}
121
122static ssize_t da9052_read_tbat(struct device *dev,
123				struct device_attribute *devattr, char *buf)
124{
125	struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
126
127	return sprintf(buf, "%d\n", da9052_adc_read_temp(hwmon->da9052));
128}
129
130static ssize_t da9052_read_vbat(struct device *dev,
131				struct device_attribute *devattr, char *buf)
132{
133	struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
134	int ret;
135
136	ret = da9052_adc_manual_read(hwmon->da9052, DA9052_ADC_VBAT);
137	if (ret < 0)
138		return ret;
139
140	return sprintf(buf, "%d\n", volt_reg_to_mv(ret));
141}
142
143static ssize_t da9052_read_misc_channel(struct device *dev,
144					struct device_attribute *devattr,
145					char *buf)
146{
147	struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
148	int channel = to_sensor_dev_attr(devattr)->index;
149	int ret;
150
151	ret = da9052_adc_manual_read(hwmon->da9052, channel);
152	if (ret < 0)
153		return ret;
154
155	return sprintf(buf, "%d\n", input_reg_to_mv(ret));
156}
157
158static ssize_t da9052_read_tjunc(struct device *dev,
159				 struct device_attribute *devattr, char *buf)
160{
161	struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
162	int tjunc;
163	int toffset;
164
165	tjunc = da9052_reg_read(hwmon->da9052, DA9052_TJUNC_RES_REG);
166	if (tjunc < 0)
167		return tjunc;
168
169	toffset = da9052_reg_read(hwmon->da9052, DA9052_T_OFFSET_REG);
170	if (toffset < 0)
171		return toffset;
172
173	/*
174	 * Degrees celsius = 1.708 * (TJUNC_RES - T_OFFSET) - 108.8
175	 * T_OFFSET is a trim value used to improve accuracy of the result
176	 */
177	return sprintf(buf, "%d\n", 1708 * (tjunc - toffset) - 108800);
178}
179
180static ssize_t da9052_read_vbbat(struct device *dev,
181				 struct device_attribute *devattr, char *buf)
182{
183	struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
184	int ret;
185
186	ret = da9052_adc_manual_read(hwmon->da9052, DA9052_ADC_VBBAT);
187	if (ret < 0)
188		return ret;
189
190	return sprintf(buf, "%d\n", vbbat_reg_to_mv(ret));
191}
192
193static ssize_t da9052_hwmon_show_name(struct device *dev,
194				      struct device_attribute *devattr,
195				      char *buf)
196{
197	return sprintf(buf, "da9052-hwmon\n");
198}
199
200static ssize_t show_label(struct device *dev,
201			  struct device_attribute *devattr, char *buf)
202{
203	return sprintf(buf, "%s\n",
204		       input_names[to_sensor_dev_attr(devattr)->index]);
205}
206
207static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, da9052_read_vddout, NULL,
208			  DA9052_ADC_VDDOUT);
209static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_label, NULL,
210			  DA9052_ADC_VDDOUT);
211static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, da9052_read_vbat, NULL,
212			  DA9052_ADC_VBAT);
213static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL,
214			  DA9052_ADC_VBAT);
215static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, da9052_read_misc_channel, NULL,
216			  DA9052_ADC_IN4);
217static SENSOR_DEVICE_ATTR(in4_label, S_IRUGO, show_label, NULL,
218			  DA9052_ADC_IN4);
219static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, da9052_read_misc_channel, NULL,
220			  DA9052_ADC_IN5);
221static SENSOR_DEVICE_ATTR(in5_label, S_IRUGO, show_label, NULL,
222			  DA9052_ADC_IN5);
223static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, da9052_read_misc_channel, NULL,
224			  DA9052_ADC_IN6);
225static SENSOR_DEVICE_ATTR(in6_label, S_IRUGO, show_label, NULL,
226			  DA9052_ADC_IN6);
227static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, da9052_read_vbbat, NULL,
228			  DA9052_ADC_VBBAT);
229static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL,
230			  DA9052_ADC_VBBAT);
231
232static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, da9052_read_ich, NULL,
233			  DA9052_ADC_ICH);
234static SENSOR_DEVICE_ATTR(curr1_label, S_IRUGO, show_label, NULL,
235			  DA9052_ADC_ICH);
236
237static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, da9052_read_tbat, NULL,
238			  DA9052_ADC_TBAT);
239static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL,
240			  DA9052_ADC_TBAT);
241static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, da9052_read_tjunc, NULL,
242			  DA9052_ADC_TJUNC);
243static SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO, show_label, NULL,
244			  DA9052_ADC_TJUNC);
245
246static DEVICE_ATTR(name, S_IRUGO, da9052_hwmon_show_name, NULL);
247
248static struct attribute *da9052_attr[] = {
249	&dev_attr_name.attr,
250	&sensor_dev_attr_in0_input.dev_attr.attr,
251	&sensor_dev_attr_in0_label.dev_attr.attr,
252	&sensor_dev_attr_in3_input.dev_attr.attr,
253	&sensor_dev_attr_in3_label.dev_attr.attr,
254	&sensor_dev_attr_in4_input.dev_attr.attr,
255	&sensor_dev_attr_in4_label.dev_attr.attr,
256	&sensor_dev_attr_in5_input.dev_attr.attr,
257	&sensor_dev_attr_in5_label.dev_attr.attr,
258	&sensor_dev_attr_in6_input.dev_attr.attr,
259	&sensor_dev_attr_in6_label.dev_attr.attr,
260	&sensor_dev_attr_in9_input.dev_attr.attr,
261	&sensor_dev_attr_in9_label.dev_attr.attr,
262	&sensor_dev_attr_curr1_input.dev_attr.attr,
263	&sensor_dev_attr_curr1_label.dev_attr.attr,
264	&sensor_dev_attr_temp2_input.dev_attr.attr,
265	&sensor_dev_attr_temp2_label.dev_attr.attr,
266	&sensor_dev_attr_temp8_input.dev_attr.attr,
267	&sensor_dev_attr_temp8_label.dev_attr.attr,
268	NULL
269};
270
271static const struct attribute_group da9052_attr_group = {.attrs = da9052_attr};
272
273static int da9052_hwmon_probe(struct platform_device *pdev)
274{
275	struct da9052_hwmon *hwmon;
276	int ret;
277
278	hwmon = devm_kzalloc(&pdev->dev, sizeof(struct da9052_hwmon),
279			     GFP_KERNEL);
280	if (!hwmon)
281		return -ENOMEM;
282
283	mutex_init(&hwmon->hwmon_lock);
284	hwmon->da9052 = dev_get_drvdata(pdev->dev.parent);
285
286	platform_set_drvdata(pdev, hwmon);
287
288	ret = sysfs_create_group(&pdev->dev.kobj, &da9052_attr_group);
289	if (ret)
290		goto err_mem;
291
292	hwmon->class_device = hwmon_device_register(&pdev->dev);
293	if (IS_ERR(hwmon->class_device)) {
294		ret = PTR_ERR(hwmon->class_device);
295		goto err_sysfs;
296	}
297
298	return 0;
299
300err_sysfs:
301	sysfs_remove_group(&pdev->dev.kobj, &da9052_attr_group);
302err_mem:
303	return ret;
304}
305
306static int da9052_hwmon_remove(struct platform_device *pdev)
307{
308	struct da9052_hwmon *hwmon = platform_get_drvdata(pdev);
309
310	hwmon_device_unregister(hwmon->class_device);
311	sysfs_remove_group(&pdev->dev.kobj, &da9052_attr_group);
312
313	return 0;
314}
315
316static struct platform_driver da9052_hwmon_driver = {
317	.probe = da9052_hwmon_probe,
318	.remove = da9052_hwmon_remove,
319	.driver = {
320		.name = "da9052-hwmon",
321		.owner = THIS_MODULE,
322	},
323};
324
325module_platform_driver(da9052_hwmon_driver);
326
327MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
328MODULE_DESCRIPTION("DA9052 HWMON driver");
329MODULE_LICENSE("GPL");
330MODULE_ALIAS("platform:da9052-hwmon");