aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVivien Didelot <vivien.didelot@savoirfairelinux.com>2012-08-30 21:42:57 -0400
committerGuenter Roeck <linux@roeck-us.net>2012-09-24 00:08:33 -0400
commit6c1fe725fd76f4328e22c146d3a36513963a01ea (patch)
tree23c2fabf8c2e38beac375353dde793138366a37c
parent37f9648b2745fc3830f3715a601f7f94296de838 (diff)
hwmon: add Maxim MAX197 support
The MAX197 is an A/D converter, made by Maxim. This driver currently supports the MAX197, and MAX199. They are both 8-Channel, Multi-Range, 5V, 12-Bit DAS with 8+4 Bus Interface and Fault Protection. The available ranges for the MAX197 are {0,-5V} to 5V, and {0,-10V} to 10V, while they are {0,-2V} to 2V, and {0,-4V} to 4V on the MAX199. Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
-rw-r--r--Documentation/hwmon/max19760
-rw-r--r--drivers/hwmon/Kconfig9
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/max197.c349
-rw-r--r--include/linux/platform_data/max197.h21
5 files changed, 440 insertions, 0 deletions
diff --git a/Documentation/hwmon/max197 b/Documentation/hwmon/max197
new file mode 100644
index 000000000000..8d89b9009df8
--- /dev/null
+++ b/Documentation/hwmon/max197
@@ -0,0 +1,60 @@
1Maxim MAX197 driver
2===================
3
4Author:
5 * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
6
7Supported chips:
8 * Maxim MAX197
9 Prefix: 'max197'
10 Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX197.pdf
11
12 * Maxim MAX199
13 Prefix: 'max199'
14 Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX199.pdf
15
16Description
17-----------
18
19The A/D converters MAX197, and MAX199 are both 8-Channel, Multi-Range, 5V,
2012-Bit DAS with 8+4 Bus Interface and Fault Protection.
21
22The available ranges for the MAX197 are {0,-5V} to 5V, and {0,-10V} to 10V,
23while they are {0,-2V} to 2V, and {0,-4V} to 4V on the MAX199.
24
25Platform data
26-------------
27
28The MAX197 platform data (defined in linux/platform_data/max197.h) should be
29filled with a pointer to a conversion function, defined like:
30
31 int convert(u8 ctrl);
32
33ctrl is the control byte to write to start a new conversion.
34On success, the function must return the 12-bit raw value read from the chip,
35or a negative error code otherwise.
36
37Control byte format:
38
39Bit Name Description
407,6 PD1,PD0 Clock and Power-Down modes
415 ACQMOD Internal or External Controlled Acquisition
424 RNG Full-scale voltage magnitude at the input
433 BIP Unipolar or Bipolar conversion mode
442,1,0 A2,A1,A0 Channel
45
46Sysfs interface
47---------------
48
49* in[0-7]_input: The conversion value for the corresponding channel.
50 RO
51
52* in[0-7]_min: The lower limit (in mV) for the corresponding channel.
53 For the MAX197, it will be adjusted to -10000, -5000, or 0.
54 For the MAX199, it will be adjusted to -4000, -2000, or 0.
55 RW
56
57* in[0-7]_max: The higher limit (in mV) for the corresponding channel.
58 For the MAX197, it will be adjusted to 0, 5000, or 10000.
59 For the MAX199, it will be adjusted to 0, 2000, or 4000.
60 RW
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index f50cb440575d..6566ed6db3b0 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -813,6 +813,15 @@ config SENSORS_MAX1668
813 This driver can also be built as a module. If so, the module 813 This driver can also be built as a module. If so, the module
814 will be called max1668. 814 will be called max1668.
815 815
816config SENSORS_MAX197
817 tristate "Maxim MAX197 and compatibles"
818 help
819 Support for the Maxim MAX197 A/D converter.
820 Support will include, but not be limited to, MAX197, and MAX199.
821
822 This driver can also be built as a module. If so, the module
823 will be called max197.
824
816config SENSORS_MAX6639 825config SENSORS_MAX6639
817 tristate "Maxim MAX6639 sensor chip" 826 tristate "Maxim MAX6639 sensor chip"
818 depends on I2C && EXPERIMENTAL 827 depends on I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 8b3d4382f61e..a62ce17ddbfc 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -95,6 +95,7 @@ obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
95obj-$(CONFIG_SENSORS_MAX16065) += max16065.o 95obj-$(CONFIG_SENSORS_MAX16065) += max16065.o
96obj-$(CONFIG_SENSORS_MAX1619) += max1619.o 96obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
97obj-$(CONFIG_SENSORS_MAX1668) += max1668.o 97obj-$(CONFIG_SENSORS_MAX1668) += max1668.o
98obj-$(CONFIG_SENSORS_MAX197) += max197.o
98obj-$(CONFIG_SENSORS_MAX6639) += max6639.o 99obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
99obj-$(CONFIG_SENSORS_MAX6642) += max6642.o 100obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
100obj-$(CONFIG_SENSORS_MAX6650) += max6650.o 101obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
diff --git a/drivers/hwmon/max197.c b/drivers/hwmon/max197.c
new file mode 100644
index 000000000000..6304f2616fa7
--- /dev/null
+++ b/drivers/hwmon/max197.c
@@ -0,0 +1,349 @@
1/*
2 * Maxim MAX197 A/D Converter driver
3 *
4 * Copyright (c) 2012 Savoir-faire Linux Inc.
5 * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * For further information, see the Documentation/hwmon/max197 file.
12 */
13
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/err.h>
18#include <linux/slab.h>
19#include <linux/mutex.h>
20#include <linux/device.h>
21#include <linux/sysfs.h>
22#include <linux/hwmon.h>
23#include <linux/hwmon-sysfs.h>
24#include <linux/platform_device.h>
25#include <linux/platform_data/max197.h>
26
27#define MAX199_LIMIT 4000 /* 4V */
28#define MAX197_LIMIT 10000 /* 10V */
29
30#define MAX197_NUM_CH 8 /* 8 Analog Input Channels */
31
32/* Control byte format */
33#define MAX197_BIP (1 << 3) /* Bipolarity */
34#define MAX197_RNG (1 << 4) /* Full range */
35
36#define MAX197_SCALE 12207 /* Scale coefficient for raw data */
37
38/* List of supported chips */
39enum max197_chips { max197, max199 };
40
41/**
42 * struct max197_data - device instance specific data
43 * @pdata: Platform data.
44 * @hwmon_dev: The hwmon device.
45 * @lock: Read/Write mutex.
46 * @limit: Max range value (10V for MAX197, 4V for MAX199).
47 * @scale: Need to scale.
48 * @ctrl_bytes: Channels control byte.
49 */
50struct max197_data {
51 struct max197_platform_data *pdata;
52 struct device *hwmon_dev;
53 struct mutex lock;
54 int limit;
55 bool scale;
56 u8 ctrl_bytes[MAX197_NUM_CH];
57};
58
59static inline void max197_set_unipolarity(struct max197_data *data, int channel)
60{
61 data->ctrl_bytes[channel] &= ~MAX197_BIP;
62}
63
64static inline void max197_set_bipolarity(struct max197_data *data, int channel)
65{
66 data->ctrl_bytes[channel] |= MAX197_BIP;
67}
68
69static inline void max197_set_half_range(struct max197_data *data, int channel)
70{
71 data->ctrl_bytes[channel] &= ~MAX197_RNG;
72}
73
74static inline void max197_set_full_range(struct max197_data *data, int channel)
75{
76 data->ctrl_bytes[channel] |= MAX197_RNG;
77}
78
79static inline bool max197_is_bipolar(struct max197_data *data, int channel)
80{
81 return data->ctrl_bytes[channel] & MAX197_BIP;
82}
83
84static inline bool max197_is_full_range(struct max197_data *data, int channel)
85{
86 return data->ctrl_bytes[channel] & MAX197_RNG;
87}
88
89/* Function called on read access on in{0,1,2,3,4,5,6,7}_{min,max} */
90static ssize_t max197_show_range(struct device *dev,
91 struct device_attribute *devattr, char *buf)
92{
93 struct max197_data *data = dev_get_drvdata(dev);
94 struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
95 int channel = attr->index;
96 bool is_min = attr->nr;
97 int range;
98
99 if (mutex_lock_interruptible(&data->lock))
100 return -ERESTARTSYS;
101
102 range = max197_is_full_range(data, channel) ?
103 data->limit : data->limit / 2;
104 if (is_min) {
105 if (max197_is_bipolar(data, channel))
106 range = -range;
107 else
108 range = 0;
109 }
110
111 mutex_unlock(&data->lock);
112
113 return sprintf(buf, "%d\n", range);
114}
115
116/* Function called on write access on in{0,1,2,3,4,5,6,7}_{min,max} */
117static ssize_t max197_store_range(struct device *dev,
118 struct device_attribute *devattr,
119 const char *buf, size_t count)
120{
121 struct max197_data *data = dev_get_drvdata(dev);
122 struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
123 int channel = attr->index;
124 bool is_min = attr->nr;
125 long value;
126 int half = data->limit / 2;
127 int full = data->limit;
128
129 if (kstrtol(buf, 10, &value))
130 return -EINVAL;
131
132 if (is_min) {
133 if (value <= -full)
134 value = -full;
135 else if (value < 0)
136 value = -half;
137 else
138 value = 0;
139 } else {
140 if (value >= full)
141 value = full;
142 else
143 value = half;
144 }
145
146 if (mutex_lock_interruptible(&data->lock))
147 return -ERESTARTSYS;
148
149 if (value == 0) {
150 /* We can deduce only the polarity */
151 max197_set_unipolarity(data, channel);
152 } else if (value == -half) {
153 max197_set_bipolarity(data, channel);
154 max197_set_half_range(data, channel);
155 } else if (value == -full) {
156 max197_set_bipolarity(data, channel);
157 max197_set_full_range(data, channel);
158 } else if (value == half) {
159 /* We can deduce only the range */
160 max197_set_half_range(data, channel);
161 } else if (value == full) {
162 /* We can deduce only the range */
163 max197_set_full_range(data, channel);
164 }
165
166 mutex_unlock(&data->lock);
167
168 return count;
169}
170
171/* Function called on read access on in{0,1,2,3,4,5,6,7}_input */
172static ssize_t max197_show_input(struct device *dev,
173 struct device_attribute *devattr,
174 char *buf)
175{
176 struct max197_data *data = dev_get_drvdata(dev);
177 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
178 int channel = attr->index;
179 s32 value;
180 int ret;
181
182 if (mutex_lock_interruptible(&data->lock))
183 return -ERESTARTSYS;
184
185 ret = data->pdata->convert(data->ctrl_bytes[channel]);
186 if (ret < 0) {
187 dev_err(dev, "conversion failed\n");
188 goto unlock;
189 }
190 value = ret;
191
192 /*
193 * Coefficient to apply on raw value.
194 * See Table 1. Full Scale and Zero Scale in the MAX197 datasheet.
195 */
196 if (data->scale) {
197 value *= MAX197_SCALE;
198 if (max197_is_full_range(data, channel))
199 value *= 2;
200 value /= 10000;
201 }
202
203 ret = sprintf(buf, "%d\n", value);
204
205unlock:
206 mutex_unlock(&data->lock);
207 return ret;
208}
209
210static ssize_t max197_show_name(struct device *dev,
211 struct device_attribute *attr, char *buf)
212{
213 struct platform_device *pdev = to_platform_device(dev);
214 return sprintf(buf, "%s\n", pdev->name);
215}
216
217#define MAX197_SENSOR_DEVICE_ATTR_CH(chan) \
218 static SENSOR_DEVICE_ATTR(in##chan##_input, S_IRUGO, \
219 max197_show_input, NULL, chan); \
220 static SENSOR_DEVICE_ATTR_2(in##chan##_min, S_IRUGO | S_IWUSR, \
221 max197_show_range, \
222 max197_store_range, \
223 true, chan); \
224 static SENSOR_DEVICE_ATTR_2(in##chan##_max, S_IRUGO | S_IWUSR, \
225 max197_show_range, \
226 max197_store_range, \
227 false, chan)
228
229#define MAX197_SENSOR_DEV_ATTR_IN(chan) \
230 &sensor_dev_attr_in##chan##_input.dev_attr.attr, \
231 &sensor_dev_attr_in##chan##_max.dev_attr.attr, \
232 &sensor_dev_attr_in##chan##_min.dev_attr.attr
233
234static DEVICE_ATTR(name, S_IRUGO, max197_show_name, NULL);
235
236MAX197_SENSOR_DEVICE_ATTR_CH(0);
237MAX197_SENSOR_DEVICE_ATTR_CH(1);
238MAX197_SENSOR_DEVICE_ATTR_CH(2);
239MAX197_SENSOR_DEVICE_ATTR_CH(3);
240MAX197_SENSOR_DEVICE_ATTR_CH(4);
241MAX197_SENSOR_DEVICE_ATTR_CH(5);
242MAX197_SENSOR_DEVICE_ATTR_CH(6);
243MAX197_SENSOR_DEVICE_ATTR_CH(7);
244
245static const struct attribute_group max197_sysfs_group = {
246 .attrs = (struct attribute *[]) {
247 &dev_attr_name.attr,
248 MAX197_SENSOR_DEV_ATTR_IN(0),
249 MAX197_SENSOR_DEV_ATTR_IN(1),
250 MAX197_SENSOR_DEV_ATTR_IN(2),
251 MAX197_SENSOR_DEV_ATTR_IN(3),
252 MAX197_SENSOR_DEV_ATTR_IN(4),
253 MAX197_SENSOR_DEV_ATTR_IN(5),
254 MAX197_SENSOR_DEV_ATTR_IN(6),
255 MAX197_SENSOR_DEV_ATTR_IN(7),
256 NULL
257 },
258};
259
260static int __devinit max197_probe(struct platform_device *pdev)
261{
262 int ch, ret;
263 struct max197_data *data;
264 struct max197_platform_data *pdata = pdev->dev.platform_data;
265 enum max197_chips chip = platform_get_device_id(pdev)->driver_data;
266
267 if (pdata == NULL) {
268 dev_err(&pdev->dev, "no platform data supplied\n");
269 return -EINVAL;
270 }
271
272 if (pdata->convert == NULL) {
273 dev_err(&pdev->dev, "no convert function supplied\n");
274 return -EINVAL;
275 }
276
277 data = devm_kzalloc(&pdev->dev, sizeof(struct max197_data), GFP_KERNEL);
278 if (!data) {
279 dev_err(&pdev->dev, "devm_kzalloc failed\n");
280 return -ENOMEM;
281 }
282
283 data->pdata = pdata;
284 mutex_init(&data->lock);
285
286 if (chip == max197) {
287 data->limit = MAX197_LIMIT;
288 data->scale = true;
289 } else {
290 data->limit = MAX199_LIMIT;
291 data->scale = false;
292 }
293
294 for (ch = 0; ch < MAX197_NUM_CH; ch++)
295 data->ctrl_bytes[ch] = (u8) ch;
296
297 platform_set_drvdata(pdev, data);
298
299 ret = sysfs_create_group(&pdev->dev.kobj, &max197_sysfs_group);
300 if (ret) {
301 dev_err(&pdev->dev, "sysfs create group failed\n");
302 return ret;
303 }
304
305 data->hwmon_dev = hwmon_device_register(&pdev->dev);
306 if (IS_ERR(data->hwmon_dev)) {
307 ret = PTR_ERR(data->hwmon_dev);
308 dev_err(&pdev->dev, "hwmon device register failed\n");
309 goto error;
310 }
311
312 return 0;
313
314error:
315 sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group);
316 return ret;
317}
318
319static int __devexit max197_remove(struct platform_device *pdev)
320{
321 struct max197_data *data = platform_get_drvdata(pdev);
322
323 hwmon_device_unregister(data->hwmon_dev);
324 sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group);
325
326 return 0;
327}
328
329static struct platform_device_id max197_device_ids[] = {
330 { "max197", max197 },
331 { "max199", max199 },
332 { }
333};
334MODULE_DEVICE_TABLE(platform, max197_device_ids);
335
336static struct platform_driver max197_driver = {
337 .driver = {
338 .name = "max197",
339 .owner = THIS_MODULE,
340 },
341 .probe = max197_probe,
342 .remove = __devexit_p(max197_remove),
343 .id_table = max197_device_ids,
344};
345module_platform_driver(max197_driver);
346
347MODULE_LICENSE("GPL");
348MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
349MODULE_DESCRIPTION("Maxim MAX197 A/D Converter driver");
diff --git a/include/linux/platform_data/max197.h b/include/linux/platform_data/max197.h
new file mode 100644
index 000000000000..e2a41dd7690c
--- /dev/null
+++ b/include/linux/platform_data/max197.h
@@ -0,0 +1,21 @@
1/*
2 * Maxim MAX197 A/D Converter Driver
3 *
4 * Copyright (c) 2012 Savoir-faire Linux Inc.
5 * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * For further information, see the Documentation/hwmon/max197 file.
12 */
13
14/**
15 * struct max197_platform_data - MAX197 connectivity info
16 * @convert: Function used to start a conversion with control byte ctrl.
17 * It must return the raw data, or a negative error code.
18 */
19struct max197_platform_data {
20 int (*convert)(u8 ctrl);
21};