]>
Commit | Line | Data |
---|---|---|
6c1fe725 VD |
1 | /* |
2 | * Maxim MAX197 A/D Converter driver | |
3 | * | |
4 | * Copyright (c) 2012 Savoir-faire Linux Inc. | |
5 | * Vivien Didelot <[email protected]> | |
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> | |
ac316725 | 16 | #include <linux/mod_devicetable.h> |
6c1fe725 VD |
17 | #include <linux/init.h> |
18 | #include <linux/err.h> | |
19 | #include <linux/slab.h> | |
20 | #include <linux/mutex.h> | |
21 | #include <linux/device.h> | |
22 | #include <linux/sysfs.h> | |
23 | #include <linux/hwmon.h> | |
24 | #include <linux/hwmon-sysfs.h> | |
25 | #include <linux/platform_device.h> | |
26 | #include <linux/platform_data/max197.h> | |
27 | ||
28 | #define MAX199_LIMIT 4000 /* 4V */ | |
29 | #define MAX197_LIMIT 10000 /* 10V */ | |
30 | ||
31 | #define MAX197_NUM_CH 8 /* 8 Analog Input Channels */ | |
32 | ||
33 | /* Control byte format */ | |
34 | #define MAX197_BIP (1 << 3) /* Bipolarity */ | |
35 | #define MAX197_RNG (1 << 4) /* Full range */ | |
36 | ||
37 | #define MAX197_SCALE 12207 /* Scale coefficient for raw data */ | |
38 | ||
39 | /* List of supported chips */ | |
40 | enum max197_chips { max197, max199 }; | |
41 | ||
42 | /** | |
43 | * struct max197_data - device instance specific data | |
44 | * @pdata: Platform data. | |
45 | * @hwmon_dev: The hwmon device. | |
46 | * @lock: Read/Write mutex. | |
47 | * @limit: Max range value (10V for MAX197, 4V for MAX199). | |
48 | * @scale: Need to scale. | |
49 | * @ctrl_bytes: Channels control byte. | |
50 | */ | |
51 | struct max197_data { | |
52 | struct max197_platform_data *pdata; | |
53 | struct device *hwmon_dev; | |
54 | struct mutex lock; | |
55 | int limit; | |
56 | bool scale; | |
57 | u8 ctrl_bytes[MAX197_NUM_CH]; | |
58 | }; | |
59 | ||
60 | static inline void max197_set_unipolarity(struct max197_data *data, int channel) | |
61 | { | |
62 | data->ctrl_bytes[channel] &= ~MAX197_BIP; | |
63 | } | |
64 | ||
65 | static inline void max197_set_bipolarity(struct max197_data *data, int channel) | |
66 | { | |
67 | data->ctrl_bytes[channel] |= MAX197_BIP; | |
68 | } | |
69 | ||
70 | static inline void max197_set_half_range(struct max197_data *data, int channel) | |
71 | { | |
72 | data->ctrl_bytes[channel] &= ~MAX197_RNG; | |
73 | } | |
74 | ||
75 | static inline void max197_set_full_range(struct max197_data *data, int channel) | |
76 | { | |
77 | data->ctrl_bytes[channel] |= MAX197_RNG; | |
78 | } | |
79 | ||
80 | static inline bool max197_is_bipolar(struct max197_data *data, int channel) | |
81 | { | |
82 | return data->ctrl_bytes[channel] & MAX197_BIP; | |
83 | } | |
84 | ||
85 | static inline bool max197_is_full_range(struct max197_data *data, int channel) | |
86 | { | |
87 | return data->ctrl_bytes[channel] & MAX197_RNG; | |
88 | } | |
89 | ||
90 | /* Function called on read access on in{0,1,2,3,4,5,6,7}_{min,max} */ | |
91 | static ssize_t max197_show_range(struct device *dev, | |
92 | struct device_attribute *devattr, char *buf) | |
93 | { | |
94 | struct max197_data *data = dev_get_drvdata(dev); | |
95 | struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); | |
96 | int channel = attr->index; | |
97 | bool is_min = attr->nr; | |
98 | int range; | |
99 | ||
100 | if (mutex_lock_interruptible(&data->lock)) | |
101 | return -ERESTARTSYS; | |
102 | ||
103 | range = max197_is_full_range(data, channel) ? | |
104 | data->limit : data->limit / 2; | |
105 | if (is_min) { | |
106 | if (max197_is_bipolar(data, channel)) | |
107 | range = -range; | |
108 | else | |
109 | range = 0; | |
110 | } | |
111 | ||
112 | mutex_unlock(&data->lock); | |
113 | ||
114 | return sprintf(buf, "%d\n", range); | |
115 | } | |
116 | ||
117 | /* Function called on write access on in{0,1,2,3,4,5,6,7}_{min,max} */ | |
118 | static ssize_t max197_store_range(struct device *dev, | |
119 | struct device_attribute *devattr, | |
120 | const char *buf, size_t count) | |
121 | { | |
122 | struct max197_data *data = dev_get_drvdata(dev); | |
123 | struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); | |
124 | int channel = attr->index; | |
125 | bool is_min = attr->nr; | |
126 | long value; | |
127 | int half = data->limit / 2; | |
128 | int full = data->limit; | |
129 | ||
130 | if (kstrtol(buf, 10, &value)) | |
131 | return -EINVAL; | |
132 | ||
133 | if (is_min) { | |
134 | if (value <= -full) | |
135 | value = -full; | |
136 | else if (value < 0) | |
137 | value = -half; | |
138 | else | |
139 | value = 0; | |
140 | } else { | |
141 | if (value >= full) | |
142 | value = full; | |
143 | else | |
144 | value = half; | |
145 | } | |
146 | ||
147 | if (mutex_lock_interruptible(&data->lock)) | |
148 | return -ERESTARTSYS; | |
149 | ||
150 | if (value == 0) { | |
151 | /* We can deduce only the polarity */ | |
152 | max197_set_unipolarity(data, channel); | |
153 | } else if (value == -half) { | |
154 | max197_set_bipolarity(data, channel); | |
155 | max197_set_half_range(data, channel); | |
156 | } else if (value == -full) { | |
157 | max197_set_bipolarity(data, channel); | |
158 | max197_set_full_range(data, channel); | |
159 | } else if (value == half) { | |
160 | /* We can deduce only the range */ | |
161 | max197_set_half_range(data, channel); | |
162 | } else if (value == full) { | |
163 | /* We can deduce only the range */ | |
164 | max197_set_full_range(data, channel); | |
165 | } | |
166 | ||
167 | mutex_unlock(&data->lock); | |
168 | ||
169 | return count; | |
170 | } | |
171 | ||
172 | /* Function called on read access on in{0,1,2,3,4,5,6,7}_input */ | |
173 | static ssize_t max197_show_input(struct device *dev, | |
174 | struct device_attribute *devattr, | |
175 | char *buf) | |
176 | { | |
177 | struct max197_data *data = dev_get_drvdata(dev); | |
178 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | |
179 | int channel = attr->index; | |
180 | s32 value; | |
181 | int ret; | |
182 | ||
183 | if (mutex_lock_interruptible(&data->lock)) | |
184 | return -ERESTARTSYS; | |
185 | ||
186 | ret = data->pdata->convert(data->ctrl_bytes[channel]); | |
187 | if (ret < 0) { | |
188 | dev_err(dev, "conversion failed\n"); | |
189 | goto unlock; | |
190 | } | |
191 | value = ret; | |
192 | ||
193 | /* | |
194 | * Coefficient to apply on raw value. | |
195 | * See Table 1. Full Scale and Zero Scale in the MAX197 datasheet. | |
196 | */ | |
197 | if (data->scale) { | |
198 | value *= MAX197_SCALE; | |
199 | if (max197_is_full_range(data, channel)) | |
200 | value *= 2; | |
201 | value /= 10000; | |
202 | } | |
203 | ||
204 | ret = sprintf(buf, "%d\n", value); | |
205 | ||
206 | unlock: | |
207 | mutex_unlock(&data->lock); | |
208 | return ret; | |
209 | } | |
210 | ||
a93c843b JL |
211 | static ssize_t name_show(struct device *dev, struct device_attribute *attr, |
212 | char *buf) | |
6c1fe725 VD |
213 | { |
214 | struct platform_device *pdev = to_platform_device(dev); | |
215 | return sprintf(buf, "%s\n", pdev->name); | |
216 | } | |
217 | ||
218 | #define MAX197_SENSOR_DEVICE_ATTR_CH(chan) \ | |
219 | static SENSOR_DEVICE_ATTR(in##chan##_input, S_IRUGO, \ | |
220 | max197_show_input, NULL, chan); \ | |
221 | static SENSOR_DEVICE_ATTR_2(in##chan##_min, S_IRUGO | S_IWUSR, \ | |
222 | max197_show_range, \ | |
223 | max197_store_range, \ | |
224 | true, chan); \ | |
225 | static SENSOR_DEVICE_ATTR_2(in##chan##_max, S_IRUGO | S_IWUSR, \ | |
226 | max197_show_range, \ | |
227 | max197_store_range, \ | |
228 | false, chan) | |
229 | ||
230 | #define MAX197_SENSOR_DEV_ATTR_IN(chan) \ | |
231 | &sensor_dev_attr_in##chan##_input.dev_attr.attr, \ | |
232 | &sensor_dev_attr_in##chan##_max.dev_attr.attr, \ | |
233 | &sensor_dev_attr_in##chan##_min.dev_attr.attr | |
234 | ||
a93c843b | 235 | static DEVICE_ATTR_RO(name); |
6c1fe725 VD |
236 | |
237 | MAX197_SENSOR_DEVICE_ATTR_CH(0); | |
238 | MAX197_SENSOR_DEVICE_ATTR_CH(1); | |
239 | MAX197_SENSOR_DEVICE_ATTR_CH(2); | |
240 | MAX197_SENSOR_DEVICE_ATTR_CH(3); | |
241 | MAX197_SENSOR_DEVICE_ATTR_CH(4); | |
242 | MAX197_SENSOR_DEVICE_ATTR_CH(5); | |
243 | MAX197_SENSOR_DEVICE_ATTR_CH(6); | |
244 | MAX197_SENSOR_DEVICE_ATTR_CH(7); | |
245 | ||
246 | static const struct attribute_group max197_sysfs_group = { | |
247 | .attrs = (struct attribute *[]) { | |
248 | &dev_attr_name.attr, | |
249 | MAX197_SENSOR_DEV_ATTR_IN(0), | |
250 | MAX197_SENSOR_DEV_ATTR_IN(1), | |
251 | MAX197_SENSOR_DEV_ATTR_IN(2), | |
252 | MAX197_SENSOR_DEV_ATTR_IN(3), | |
253 | MAX197_SENSOR_DEV_ATTR_IN(4), | |
254 | MAX197_SENSOR_DEV_ATTR_IN(5), | |
255 | MAX197_SENSOR_DEV_ATTR_IN(6), | |
256 | MAX197_SENSOR_DEV_ATTR_IN(7), | |
257 | NULL | |
258 | }, | |
259 | }; | |
260 | ||
6c931ae1 | 261 | static int max197_probe(struct platform_device *pdev) |
6c1fe725 VD |
262 | { |
263 | int ch, ret; | |
264 | struct max197_data *data; | |
a8b3a3a5 | 265 | struct max197_platform_data *pdata = dev_get_platdata(&pdev->dev); |
6c1fe725 VD |
266 | enum max197_chips chip = platform_get_device_id(pdev)->driver_data; |
267 | ||
268 | if (pdata == NULL) { | |
269 | dev_err(&pdev->dev, "no platform data supplied\n"); | |
270 | return -EINVAL; | |
271 | } | |
272 | ||
273 | if (pdata->convert == NULL) { | |
274 | dev_err(&pdev->dev, "no convert function supplied\n"); | |
275 | return -EINVAL; | |
276 | } | |
277 | ||
278 | data = devm_kzalloc(&pdev->dev, sizeof(struct max197_data), GFP_KERNEL); | |
6122de8f | 279 | if (!data) |
6c1fe725 | 280 | return -ENOMEM; |
6c1fe725 VD |
281 | |
282 | data->pdata = pdata; | |
283 | mutex_init(&data->lock); | |
284 | ||
285 | if (chip == max197) { | |
286 | data->limit = MAX197_LIMIT; | |
287 | data->scale = true; | |
288 | } else { | |
289 | data->limit = MAX199_LIMIT; | |
290 | data->scale = false; | |
291 | } | |
292 | ||
293 | for (ch = 0; ch < MAX197_NUM_CH; ch++) | |
294 | data->ctrl_bytes[ch] = (u8) ch; | |
295 | ||
296 | platform_set_drvdata(pdev, data); | |
297 | ||
298 | ret = sysfs_create_group(&pdev->dev.kobj, &max197_sysfs_group); | |
299 | if (ret) { | |
300 | dev_err(&pdev->dev, "sysfs create group failed\n"); | |
301 | return ret; | |
302 | } | |
303 | ||
304 | data->hwmon_dev = hwmon_device_register(&pdev->dev); | |
305 | if (IS_ERR(data->hwmon_dev)) { | |
306 | ret = PTR_ERR(data->hwmon_dev); | |
307 | dev_err(&pdev->dev, "hwmon device register failed\n"); | |
308 | goto error; | |
309 | } | |
310 | ||
311 | return 0; | |
312 | ||
313 | error: | |
314 | sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group); | |
315 | return ret; | |
316 | } | |
317 | ||
281dfd0b | 318 | static int max197_remove(struct platform_device *pdev) |
6c1fe725 VD |
319 | { |
320 | struct max197_data *data = platform_get_drvdata(pdev); | |
321 | ||
322 | hwmon_device_unregister(data->hwmon_dev); | |
323 | sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group); | |
324 | ||
325 | return 0; | |
326 | } | |
327 | ||
9c2cbcec | 328 | static const struct platform_device_id max197_device_ids[] = { |
6c1fe725 VD |
329 | { "max197", max197 }, |
330 | { "max199", max199 }, | |
331 | { } | |
332 | }; | |
333 | MODULE_DEVICE_TABLE(platform, max197_device_ids); | |
334 | ||
335 | static struct platform_driver max197_driver = { | |
336 | .driver = { | |
337 | .name = "max197", | |
6c1fe725 VD |
338 | }, |
339 | .probe = max197_probe, | |
9e5e9b7a | 340 | .remove = max197_remove, |
6c1fe725 VD |
341 | .id_table = max197_device_ids, |
342 | }; | |
343 | module_platform_driver(max197_driver); | |
344 | ||
345 | MODULE_LICENSE("GPL"); | |
346 | MODULE_AUTHOR("Savoir-faire Linux Inc. <[email protected]>"); | |
347 | MODULE_DESCRIPTION("Maxim MAX197 A/D Converter driver"); |