diff options
Diffstat (limited to 'drivers/staging/iio/adc/adt7310.c')
-rw-r--r-- | drivers/staging/iio/adc/adt7310.c | 904 |
1 files changed, 904 insertions, 0 deletions
diff --git a/drivers/staging/iio/adc/adt7310.c b/drivers/staging/iio/adc/adt7310.c new file mode 100644 index 00000000000..1a41b803440 --- /dev/null +++ b/drivers/staging/iio/adc/adt7310.c | |||
@@ -0,0 +1,904 @@ | |||
1 | /* | ||
2 | * ADT7310 digital temperature sensor driver supporting ADT7310 | ||
3 | * | ||
4 | * Copyright 2010 Analog Devices Inc. | ||
5 | * | ||
6 | * Licensed under the GPL-2 or later. | ||
7 | */ | ||
8 | |||
9 | #include <linux/interrupt.h> | ||
10 | #include <linux/device.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/sysfs.h> | ||
14 | #include <linux/list.h> | ||
15 | #include <linux/spi/spi.h> | ||
16 | |||
17 | #include "../iio.h" | ||
18 | #include "../sysfs.h" | ||
19 | |||
20 | /* | ||
21 | * ADT7310 registers definition | ||
22 | */ | ||
23 | |||
24 | #define ADT7310_STATUS 0 | ||
25 | #define ADT7310_CONFIG 1 | ||
26 | #define ADT7310_TEMPERATURE 2 | ||
27 | #define ADT7310_ID 3 | ||
28 | #define ADT7310_T_CRIT 4 | ||
29 | #define ADT7310_T_HYST 5 | ||
30 | #define ADT7310_T_ALARM_HIGH 6 | ||
31 | #define ADT7310_T_ALARM_LOW 7 | ||
32 | |||
33 | /* | ||
34 | * ADT7310 status | ||
35 | */ | ||
36 | #define ADT7310_STAT_T_LOW 0x10 | ||
37 | #define ADT7310_STAT_T_HIGH 0x20 | ||
38 | #define ADT7310_STAT_T_CRIT 0x40 | ||
39 | #define ADT7310_STAT_NOT_RDY 0x80 | ||
40 | |||
41 | /* | ||
42 | * ADT7310 config | ||
43 | */ | ||
44 | #define ADT7310_FAULT_QUEUE_MASK 0x3 | ||
45 | #define ADT7310_CT_POLARITY 0x4 | ||
46 | #define ADT7310_INT_POLARITY 0x8 | ||
47 | #define ADT7310_EVENT_MODE 0x10 | ||
48 | #define ADT7310_MODE_MASK 0x60 | ||
49 | #define ADT7310_ONESHOT 0x20 | ||
50 | #define ADT7310_SPS 0x40 | ||
51 | #define ADT7310_PD 0x60 | ||
52 | #define ADT7310_RESOLUTION 0x80 | ||
53 | |||
54 | /* | ||
55 | * ADT7310 masks | ||
56 | */ | ||
57 | #define ADT7310_T16_VALUE_SIGN 0x8000 | ||
58 | #define ADT7310_T16_VALUE_FLOAT_OFFSET 7 | ||
59 | #define ADT7310_T16_VALUE_FLOAT_MASK 0x7F | ||
60 | #define ADT7310_T13_VALUE_SIGN 0x1000 | ||
61 | #define ADT7310_T13_VALUE_OFFSET 3 | ||
62 | #define ADT7310_T13_VALUE_FLOAT_OFFSET 4 | ||
63 | #define ADT7310_T13_VALUE_FLOAT_MASK 0xF | ||
64 | #define ADT7310_T_HYST_MASK 0xF | ||
65 | #define ADT7310_DEVICE_ID_MASK 0x7 | ||
66 | #define ADT7310_MANUFACTORY_ID_MASK 0xF8 | ||
67 | #define ADT7310_MANUFACTORY_ID_OFFSET 3 | ||
68 | |||
69 | |||
70 | #define ADT7310_CMD_REG_MASK 0x28 | ||
71 | #define ADT7310_CMD_REG_OFFSET 3 | ||
72 | #define ADT7310_CMD_READ 0x40 | ||
73 | #define ADT7310_CMD_CON_READ 0x4 | ||
74 | |||
75 | #define ADT7310_IRQS 2 | ||
76 | |||
77 | /* | ||
78 | * struct adt7310_chip_info - chip specifc information | ||
79 | */ | ||
80 | |||
81 | struct adt7310_chip_info { | ||
82 | struct spi_device *spi_dev; | ||
83 | u8 config; | ||
84 | }; | ||
85 | |||
86 | /* | ||
87 | * adt7310 register access by SPI | ||
88 | */ | ||
89 | |||
90 | static int adt7310_spi_read_word(struct adt7310_chip_info *chip, u8 reg, u16 *data) | ||
91 | { | ||
92 | struct spi_device *spi_dev = chip->spi_dev; | ||
93 | u8 command = (reg << ADT7310_CMD_REG_OFFSET) & ADT7310_CMD_REG_MASK; | ||
94 | int ret = 0; | ||
95 | |||
96 | command |= ADT7310_CMD_READ; | ||
97 | ret = spi_write(spi_dev, &command, sizeof(command)); | ||
98 | if (ret < 0) { | ||
99 | dev_err(&spi_dev->dev, "SPI write command error\n"); | ||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | ret = spi_read(spi_dev, (u8 *)data, sizeof(*data)); | ||
104 | if (ret < 0) { | ||
105 | dev_err(&spi_dev->dev, "SPI read word error\n"); | ||
106 | return ret; | ||
107 | } | ||
108 | |||
109 | *data = be16_to_cpu(*data); | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static int adt7310_spi_write_word(struct adt7310_chip_info *chip, u8 reg, u16 data) | ||
115 | { | ||
116 | struct spi_device *spi_dev = chip->spi_dev; | ||
117 | u8 buf[3]; | ||
118 | int ret = 0; | ||
119 | |||
120 | buf[0] = (reg << ADT7310_CMD_REG_OFFSET) & ADT7310_CMD_REG_MASK; | ||
121 | buf[1] = (u8)(data >> 8); | ||
122 | buf[2] = (u8)(data & 0xFF); | ||
123 | |||
124 | ret = spi_write(spi_dev, buf, 3); | ||
125 | if (ret < 0) { | ||
126 | dev_err(&spi_dev->dev, "SPI write word error\n"); | ||
127 | return ret; | ||
128 | } | ||
129 | |||
130 | return ret; | ||
131 | } | ||
132 | |||
133 | static int adt7310_spi_read_byte(struct adt7310_chip_info *chip, u8 reg, u8 *data) | ||
134 | { | ||
135 | struct spi_device *spi_dev = chip->spi_dev; | ||
136 | u8 command = (reg << ADT7310_CMD_REG_OFFSET) & ADT7310_CMD_REG_MASK; | ||
137 | int ret = 0; | ||
138 | |||
139 | command |= ADT7310_CMD_READ; | ||
140 | ret = spi_write(spi_dev, &command, sizeof(command)); | ||
141 | if (ret < 0) { | ||
142 | dev_err(&spi_dev->dev, "SPI write command error\n"); | ||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | ret = spi_read(spi_dev, data, sizeof(*data)); | ||
147 | if (ret < 0) { | ||
148 | dev_err(&spi_dev->dev, "SPI read byte error\n"); | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | static int adt7310_spi_write_byte(struct adt7310_chip_info *chip, u8 reg, u8 data) | ||
156 | { | ||
157 | struct spi_device *spi_dev = chip->spi_dev; | ||
158 | u8 buf[2]; | ||
159 | int ret = 0; | ||
160 | |||
161 | buf[0] = (reg << ADT7310_CMD_REG_OFFSET) & ADT7310_CMD_REG_MASK; | ||
162 | buf[1] = data; | ||
163 | |||
164 | ret = spi_write(spi_dev, buf, 2); | ||
165 | if (ret < 0) { | ||
166 | dev_err(&spi_dev->dev, "SPI write byte error\n"); | ||
167 | return ret; | ||
168 | } | ||
169 | |||
170 | return ret; | ||
171 | } | ||
172 | |||
173 | static ssize_t adt7310_show_mode(struct device *dev, | ||
174 | struct device_attribute *attr, | ||
175 | char *buf) | ||
176 | { | ||
177 | struct iio_dev *dev_info = dev_get_drvdata(dev); | ||
178 | struct adt7310_chip_info *chip = iio_priv(dev_info); | ||
179 | u8 config; | ||
180 | |||
181 | config = chip->config & ADT7310_MODE_MASK; | ||
182 | |||
183 | switch (config) { | ||
184 | case ADT7310_PD: | ||
185 | return sprintf(buf, "power-down\n"); | ||
186 | case ADT7310_ONESHOT: | ||
187 | return sprintf(buf, "one-shot\n"); | ||
188 | case ADT7310_SPS: | ||
189 | return sprintf(buf, "sps\n"); | ||
190 | default: | ||
191 | return sprintf(buf, "full\n"); | ||
192 | } | ||
193 | } | ||
194 | |||
195 | static ssize_t adt7310_store_mode(struct device *dev, | ||
196 | struct device_attribute *attr, | ||
197 | const char *buf, | ||
198 | size_t len) | ||
199 | { | ||
200 | struct iio_dev *dev_info = dev_get_drvdata(dev); | ||
201 | struct adt7310_chip_info *chip = iio_priv(dev_info); | ||
202 | u16 config; | ||
203 | int ret; | ||
204 | |||
205 | ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config); | ||
206 | if (ret) | ||
207 | return -EIO; | ||
208 | |||
209 | config = chip->config & (~ADT7310_MODE_MASK); | ||
210 | if (strcmp(buf, "power-down")) | ||
211 | config |= ADT7310_PD; | ||
212 | else if (strcmp(buf, "one-shot")) | ||
213 | config |= ADT7310_ONESHOT; | ||
214 | else if (strcmp(buf, "sps")) | ||
215 | config |= ADT7310_SPS; | ||
216 | |||
217 | ret = adt7310_spi_write_byte(chip, ADT7310_CONFIG, config); | ||
218 | if (ret) | ||
219 | return -EIO; | ||
220 | |||
221 | chip->config = config; | ||
222 | |||
223 | return len; | ||
224 | } | ||
225 | |||
226 | static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, | ||
227 | adt7310_show_mode, | ||
228 | adt7310_store_mode, | ||
229 | 0); | ||
230 | |||
231 | static ssize_t adt7310_show_available_modes(struct device *dev, | ||
232 | struct device_attribute *attr, | ||
233 | char *buf) | ||
234 | { | ||
235 | return sprintf(buf, "full\none-shot\nsps\npower-down\n"); | ||
236 | } | ||
237 | |||
238 | static IIO_DEVICE_ATTR(available_modes, S_IRUGO, adt7310_show_available_modes, NULL, 0); | ||
239 | |||
240 | static ssize_t adt7310_show_resolution(struct device *dev, | ||
241 | struct device_attribute *attr, | ||
242 | char *buf) | ||
243 | { | ||
244 | struct iio_dev *dev_info = dev_get_drvdata(dev); | ||
245 | struct adt7310_chip_info *chip = iio_priv(dev_info); | ||
246 | int ret; | ||
247 | int bits; | ||
248 | |||
249 | ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config); | ||
250 | if (ret) | ||
251 | return -EIO; | ||
252 | |||
253 | if (chip->config & ADT7310_RESOLUTION) | ||
254 | bits = 16; | ||
255 | else | ||
256 | bits = 13; | ||
257 | |||
258 | return sprintf(buf, "%d bits\n", bits); | ||
259 | } | ||
260 | |||
261 | static ssize_t adt7310_store_resolution(struct device *dev, | ||
262 | struct device_attribute *attr, | ||
263 | const char *buf, | ||
264 | size_t len) | ||
265 | { | ||
266 | struct iio_dev *dev_info = dev_get_drvdata(dev); | ||
267 | struct adt7310_chip_info *chip = iio_priv(dev_info); | ||
268 | unsigned long data; | ||
269 | u16 config; | ||
270 | int ret; | ||
271 | |||
272 | ret = strict_strtoul(buf, 10, &data); | ||
273 | if (ret) | ||
274 | return -EINVAL; | ||
275 | |||
276 | ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config); | ||
277 | if (ret) | ||
278 | return -EIO; | ||
279 | |||
280 | config = chip->config & (~ADT7310_RESOLUTION); | ||
281 | if (data) | ||
282 | config |= ADT7310_RESOLUTION; | ||
283 | |||
284 | ret = adt7310_spi_write_byte(chip, ADT7310_CONFIG, config); | ||
285 | if (ret) | ||
286 | return -EIO; | ||
287 | |||
288 | chip->config = config; | ||
289 | |||
290 | return len; | ||
291 | } | ||
292 | |||
293 | static IIO_DEVICE_ATTR(resolution, S_IRUGO | S_IWUSR, | ||
294 | adt7310_show_resolution, | ||
295 | adt7310_store_resolution, | ||
296 | 0); | ||
297 | |||
298 | static ssize_t adt7310_show_id(struct device *dev, | ||
299 | struct device_attribute *attr, | ||
300 | char *buf) | ||
301 | { | ||
302 | struct iio_dev *dev_info = dev_get_drvdata(dev); | ||
303 | struct adt7310_chip_info *chip = iio_priv(dev_info); | ||
304 | u8 id; | ||
305 | int ret; | ||
306 | |||
307 | ret = adt7310_spi_read_byte(chip, ADT7310_ID, &id); | ||
308 | if (ret) | ||
309 | return -EIO; | ||
310 | |||
311 | return sprintf(buf, "device id: 0x%x\nmanufactory id: 0x%x\n", | ||
312 | id & ADT7310_DEVICE_ID_MASK, | ||
313 | (id & ADT7310_MANUFACTORY_ID_MASK) >> ADT7310_MANUFACTORY_ID_OFFSET); | ||
314 | } | ||
315 | |||
316 | static IIO_DEVICE_ATTR(id, S_IRUGO | S_IWUSR, | ||
317 | adt7310_show_id, | ||
318 | NULL, | ||
319 | 0); | ||
320 | |||
321 | static ssize_t adt7310_convert_temperature(struct adt7310_chip_info *chip, | ||
322 | u16 data, char *buf) | ||
323 | { | ||
324 | char sign = ' '; | ||
325 | |||
326 | if (chip->config & ADT7310_RESOLUTION) { | ||
327 | if (data & ADT7310_T16_VALUE_SIGN) { | ||
328 | /* convert supplement to positive value */ | ||
329 | data = (u16)((ADT7310_T16_VALUE_SIGN << 1) - (u32)data); | ||
330 | sign = '-'; | ||
331 | } | ||
332 | return sprintf(buf, "%c%d.%.7d\n", sign, | ||
333 | (data >> ADT7310_T16_VALUE_FLOAT_OFFSET), | ||
334 | (data & ADT7310_T16_VALUE_FLOAT_MASK) * 78125); | ||
335 | } else { | ||
336 | if (data & ADT7310_T13_VALUE_SIGN) { | ||
337 | /* convert supplement to positive value */ | ||
338 | data >>= ADT7310_T13_VALUE_OFFSET; | ||
339 | data = (ADT7310_T13_VALUE_SIGN << 1) - data; | ||
340 | sign = '-'; | ||
341 | } | ||
342 | return sprintf(buf, "%c%d.%.4d\n", sign, | ||
343 | (data >> ADT7310_T13_VALUE_FLOAT_OFFSET), | ||
344 | (data & ADT7310_T13_VALUE_FLOAT_MASK) * 625); | ||
345 | } | ||
346 | } | ||
347 | |||
348 | static ssize_t adt7310_show_value(struct device *dev, | ||
349 | struct device_attribute *attr, | ||
350 | char *buf) | ||
351 | { | ||
352 | struct iio_dev *dev_info = dev_get_drvdata(dev); | ||
353 | struct adt7310_chip_info *chip = iio_priv(dev_info); | ||
354 | u8 status; | ||
355 | u16 data; | ||
356 | int ret, i = 0; | ||
357 | |||
358 | do { | ||
359 | ret = adt7310_spi_read_byte(chip, ADT7310_STATUS, &status); | ||
360 | if (ret) | ||
361 | return -EIO; | ||
362 | i++; | ||
363 | if (i == 10000) | ||
364 | return -EIO; | ||
365 | } while (status & ADT7310_STAT_NOT_RDY); | ||
366 | |||
367 | ret = adt7310_spi_read_word(chip, ADT7310_TEMPERATURE, &data); | ||
368 | if (ret) | ||
369 | return -EIO; | ||
370 | |||
371 | return adt7310_convert_temperature(chip, data, buf); | ||
372 | } | ||
373 | |||
374 | static IIO_DEVICE_ATTR(value, S_IRUGO, adt7310_show_value, NULL, 0); | ||
375 | |||
376 | static struct attribute *adt7310_attributes[] = { | ||
377 | &iio_dev_attr_available_modes.dev_attr.attr, | ||
378 | &iio_dev_attr_mode.dev_attr.attr, | ||
379 | &iio_dev_attr_resolution.dev_attr.attr, | ||
380 | &iio_dev_attr_id.dev_attr.attr, | ||
381 | &iio_dev_attr_value.dev_attr.attr, | ||
382 | NULL, | ||
383 | }; | ||
384 | |||
385 | static const struct attribute_group adt7310_attribute_group = { | ||
386 | .attrs = adt7310_attributes, | ||
387 | }; | ||
388 | |||
389 | static irqreturn_t adt7310_event_handler(int irq, void *private) | ||
390 | { | ||
391 | struct iio_dev *indio_dev = private; | ||
392 | struct adt7310_chip_info *chip = iio_priv(indio_dev); | ||
393 | s64 timestamp = iio_get_time_ns(); | ||
394 | u8 status; | ||
395 | int ret; | ||
396 | |||
397 | ret = adt7310_spi_read_byte(chip, ADT7310_STATUS, &status); | ||
398 | if (ret) | ||
399 | return ret; | ||
400 | |||
401 | if (status & ADT7310_STAT_T_HIGH) | ||
402 | iio_push_event(indio_dev, 0, | ||
403 | IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0, | ||
404 | IIO_EV_TYPE_THRESH, | ||
405 | IIO_EV_DIR_RISING), | ||
406 | timestamp); | ||
407 | if (status & ADT7310_STAT_T_LOW) | ||
408 | iio_push_event(indio_dev, 0, | ||
409 | IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0, | ||
410 | IIO_EV_TYPE_THRESH, | ||
411 | IIO_EV_DIR_FALLING), | ||
412 | timestamp); | ||
413 | if (status & ADT7310_STAT_T_CRIT) | ||
414 | iio_push_event(indio_dev, 0, | ||
415 | IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0, | ||
416 | IIO_EV_TYPE_THRESH, | ||
417 | IIO_EV_DIR_RISING), | ||
418 | timestamp); | ||
419 | return IRQ_HANDLED; | ||
420 | } | ||
421 | |||
422 | static ssize_t adt7310_show_event_mode(struct device *dev, | ||
423 | struct device_attribute *attr, | ||
424 | char *buf) | ||
425 | { | ||
426 | struct iio_dev *dev_info = dev_get_drvdata(dev); | ||
427 | struct adt7310_chip_info *chip = iio_priv(dev_info); | ||
428 | int ret; | ||
429 | |||
430 | ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config); | ||
431 | if (ret) | ||
432 | return -EIO; | ||
433 | |||
434 | if (chip->config & ADT7310_EVENT_MODE) | ||
435 | return sprintf(buf, "interrupt\n"); | ||
436 | else | ||
437 | return sprintf(buf, "comparator\n"); | ||
438 | } | ||
439 | |||
440 | static ssize_t adt7310_set_event_mode(struct device *dev, | ||
441 | struct device_attribute *attr, | ||
442 | const char *buf, | ||
443 | size_t len) | ||
444 | { | ||
445 | struct iio_dev *dev_info = dev_get_drvdata(dev); | ||
446 | struct adt7310_chip_info *chip = iio_priv(dev_info); | ||
447 | u16 config; | ||
448 | int ret; | ||
449 | |||
450 | ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config); | ||
451 | if (ret) | ||
452 | return -EIO; | ||
453 | |||
454 | config = chip->config &= ~ADT7310_EVENT_MODE; | ||
455 | if (strcmp(buf, "comparator") != 0) | ||
456 | config |= ADT7310_EVENT_MODE; | ||
457 | |||
458 | ret = adt7310_spi_write_byte(chip, ADT7310_CONFIG, config); | ||
459 | if (ret) | ||
460 | return -EIO; | ||
461 | |||
462 | chip->config = config; | ||
463 | |||
464 | return len; | ||
465 | } | ||
466 | |||
467 | static ssize_t adt7310_show_available_event_modes(struct device *dev, | ||
468 | struct device_attribute *attr, | ||
469 | char *buf) | ||
470 | { | ||
471 | return sprintf(buf, "comparator\ninterrupt\n"); | ||
472 | } | ||
473 | |||
474 | static ssize_t adt7310_show_fault_queue(struct device *dev, | ||
475 | struct device_attribute *attr, | ||
476 | char *buf) | ||
477 | { | ||
478 | struct iio_dev *dev_info = dev_get_drvdata(dev); | ||
479 | struct adt7310_chip_info *chip = iio_priv(dev_info); | ||
480 | int ret; | ||
481 | |||
482 | ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config); | ||
483 | if (ret) | ||
484 | return -EIO; | ||
485 | |||
486 | return sprintf(buf, "%d\n", chip->config & ADT7310_FAULT_QUEUE_MASK); | ||
487 | } | ||
488 | |||
489 | static ssize_t adt7310_set_fault_queue(struct device *dev, | ||
490 | struct device_attribute *attr, | ||
491 | const char *buf, | ||
492 | size_t len) | ||
493 | { | ||
494 | struct iio_dev *dev_info = dev_get_drvdata(dev); | ||
495 | struct adt7310_chip_info *chip = iio_priv(dev_info); | ||
496 | unsigned long data; | ||
497 | int ret; | ||
498 | u8 config; | ||
499 | |||
500 | ret = strict_strtoul(buf, 10, &data); | ||
501 | if (ret || data > 3) | ||
502 | return -EINVAL; | ||
503 | |||
504 | ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config); | ||
505 | if (ret) | ||
506 | return -EIO; | ||
507 | |||
508 | config = chip->config & ~ADT7310_FAULT_QUEUE_MASK; | ||
509 | config |= data; | ||
510 | ret = adt7310_spi_write_byte(chip, ADT7310_CONFIG, config); | ||
511 | if (ret) | ||
512 | return -EIO; | ||
513 | |||
514 | chip->config = config; | ||
515 | |||
516 | return len; | ||
517 | } | ||
518 | |||
519 | static inline ssize_t adt7310_show_t_bound(struct device *dev, | ||
520 | struct device_attribute *attr, | ||
521 | u8 bound_reg, | ||
522 | char *buf) | ||
523 | { | ||
524 | struct iio_dev *dev_info = dev_get_drvdata(dev); | ||
525 | struct adt7310_chip_info *chip = iio_priv(dev_info); | ||
526 | u16 data; | ||
527 | int ret; | ||
528 | |||
529 | ret = adt7310_spi_read_word(chip, bound_reg, &data); | ||
530 | if (ret) | ||
531 | return -EIO; | ||
532 | |||
533 | return adt7310_convert_temperature(chip, data, buf); | ||
534 | } | ||
535 | |||
536 | static inline ssize_t adt7310_set_t_bound(struct device *dev, | ||
537 | struct device_attribute *attr, | ||
538 | u8 bound_reg, | ||
539 | const char *buf, | ||
540 | size_t len) | ||
541 | { | ||
542 | struct iio_dev *dev_info = dev_get_drvdata(dev); | ||
543 | struct adt7310_chip_info *chip = iio_priv(dev_info); | ||
544 | long tmp1, tmp2; | ||
545 | u16 data; | ||
546 | char *pos; | ||
547 | int ret; | ||
548 | |||
549 | pos = strchr(buf, '.'); | ||
550 | |||
551 | ret = strict_strtol(buf, 10, &tmp1); | ||
552 | |||
553 | if (ret || tmp1 > 127 || tmp1 < -128) | ||
554 | return -EINVAL; | ||
555 | |||
556 | if (pos) { | ||
557 | len = strlen(pos); | ||
558 | |||
559 | if (chip->config & ADT7310_RESOLUTION) { | ||
560 | if (len > ADT7310_T16_VALUE_FLOAT_OFFSET) | ||
561 | len = ADT7310_T16_VALUE_FLOAT_OFFSET; | ||
562 | pos[len] = 0; | ||
563 | ret = strict_strtol(pos, 10, &tmp2); | ||
564 | |||
565 | if (!ret) | ||
566 | tmp2 = (tmp2 / 78125) * 78125; | ||
567 | } else { | ||
568 | if (len > ADT7310_T13_VALUE_FLOAT_OFFSET) | ||
569 | len = ADT7310_T13_VALUE_FLOAT_OFFSET; | ||
570 | pos[len] = 0; | ||
571 | ret = strict_strtol(pos, 10, &tmp2); | ||
572 | |||
573 | if (!ret) | ||
574 | tmp2 = (tmp2 / 625) * 625; | ||
575 | } | ||
576 | } | ||
577 | |||
578 | if (tmp1 < 0) | ||
579 | data = (u16)(-tmp1); | ||
580 | else | ||
581 | data = (u16)tmp1; | ||
582 | |||
583 | if (chip->config & ADT7310_RESOLUTION) { | ||
584 | data = (data << ADT7310_T16_VALUE_FLOAT_OFFSET) | | ||
585 | (tmp2 & ADT7310_T16_VALUE_FLOAT_MASK); | ||
586 | |||
587 | if (tmp1 < 0) | ||
588 | /* convert positive value to supplyment */ | ||
589 | data = (u16)((ADT7310_T16_VALUE_SIGN << 1) - (u32)data); | ||
590 | } else { | ||
591 | data = (data << ADT7310_T13_VALUE_FLOAT_OFFSET) | | ||
592 | (tmp2 & ADT7310_T13_VALUE_FLOAT_MASK); | ||
593 | |||
594 | if (tmp1 < 0) | ||
595 | /* convert positive value to supplyment */ | ||
596 | data = (ADT7310_T13_VALUE_SIGN << 1) - data; | ||
597 | data <<= ADT7310_T13_VALUE_OFFSET; | ||
598 | } | ||
599 | |||
600 | ret = adt7310_spi_write_word(chip, bound_reg, data); | ||
601 | if (ret) | ||
602 | return -EIO; | ||
603 | |||
604 | return len; | ||
605 | } | ||
606 | |||
607 | static ssize_t adt7310_show_t_alarm_high(struct device *dev, | ||
608 | struct device_attribute *attr, | ||
609 | char *buf) | ||
610 | { | ||
611 | return adt7310_show_t_bound(dev, attr, | ||
612 | ADT7310_T_ALARM_HIGH, buf); | ||
613 | } | ||
614 | |||
615 | static inline ssize_t adt7310_set_t_alarm_high(struct device *dev, | ||
616 | struct device_attribute *attr, | ||
617 | const char *buf, | ||
618 | size_t len) | ||
619 | { | ||
620 | return adt7310_set_t_bound(dev, attr, | ||
621 | ADT7310_T_ALARM_HIGH, buf, len); | ||
622 | } | ||
623 | |||
624 | static ssize_t adt7310_show_t_alarm_low(struct device *dev, | ||
625 | struct device_attribute *attr, | ||
626 | char *buf) | ||
627 | { | ||
628 | return adt7310_show_t_bound(dev, attr, | ||
629 | ADT7310_T_ALARM_LOW, buf); | ||
630 | } | ||
631 | |||
632 | static inline ssize_t adt7310_set_t_alarm_low(struct device *dev, | ||
633 | struct device_attribute *attr, | ||
634 | const char *buf, | ||
635 | size_t len) | ||
636 | { | ||
637 | return adt7310_set_t_bound(dev, attr, | ||
638 | ADT7310_T_ALARM_LOW, buf, len); | ||
639 | } | ||
640 | |||
641 | static ssize_t adt7310_show_t_crit(struct device *dev, | ||
642 | struct device_attribute *attr, | ||
643 | char *buf) | ||
644 | { | ||
645 | return adt7310_show_t_bound(dev, attr, | ||
646 | ADT7310_T_CRIT, buf); | ||
647 | } | ||
648 | |||
649 | static inline ssize_t adt7310_set_t_crit(struct device *dev, | ||
650 | struct device_attribute *attr, | ||
651 | const char *buf, | ||
652 | size_t len) | ||
653 | { | ||
654 | return adt7310_set_t_bound(dev, attr, | ||
655 | ADT7310_T_CRIT, buf, len); | ||
656 | } | ||
657 | |||
658 | static ssize_t adt7310_show_t_hyst(struct device *dev, | ||
659 | struct device_attribute *attr, | ||
660 | char *buf) | ||
661 | { | ||
662 | struct iio_dev *dev_info = dev_get_drvdata(dev); | ||
663 | struct adt7310_chip_info *chip = iio_priv(dev_info); | ||
664 | int ret; | ||
665 | u8 t_hyst; | ||
666 | |||
667 | ret = adt7310_spi_read_byte(chip, ADT7310_T_HYST, &t_hyst); | ||
668 | if (ret) | ||
669 | return -EIO; | ||
670 | |||
671 | return sprintf(buf, "%d\n", t_hyst & ADT7310_T_HYST_MASK); | ||
672 | } | ||
673 | |||
674 | static inline ssize_t adt7310_set_t_hyst(struct device *dev, | ||
675 | struct device_attribute *attr, | ||
676 | const char *buf, | ||
677 | size_t len) | ||
678 | { | ||
679 | struct iio_dev *dev_info = dev_get_drvdata(dev); | ||
680 | struct adt7310_chip_info *chip = iio_priv(dev_info); | ||
681 | int ret; | ||
682 | unsigned long data; | ||
683 | u8 t_hyst; | ||
684 | |||
685 | ret = strict_strtol(buf, 10, &data); | ||
686 | |||
687 | if (ret || data > ADT7310_T_HYST_MASK) | ||
688 | return -EINVAL; | ||
689 | |||
690 | t_hyst = (u8)data; | ||
691 | |||
692 | ret = adt7310_spi_write_byte(chip, ADT7310_T_HYST, t_hyst); | ||
693 | if (ret) | ||
694 | return -EIO; | ||
695 | |||
696 | return len; | ||
697 | } | ||
698 | |||
699 | static IIO_DEVICE_ATTR(event_mode, | ||
700 | S_IRUGO | S_IWUSR, | ||
701 | adt7310_show_event_mode, adt7310_set_event_mode, 0); | ||
702 | static IIO_DEVICE_ATTR(available_event_modes, | ||
703 | S_IRUGO | S_IWUSR, | ||
704 | adt7310_show_available_event_modes, NULL, 0); | ||
705 | static IIO_DEVICE_ATTR(fault_queue, | ||
706 | S_IRUGO | S_IWUSR, | ||
707 | adt7310_show_fault_queue, adt7310_set_fault_queue, 0); | ||
708 | static IIO_DEVICE_ATTR(t_alarm_high, | ||
709 | S_IRUGO | S_IWUSR, | ||
710 | adt7310_show_t_alarm_high, adt7310_set_t_alarm_high, 0); | ||
711 | static IIO_DEVICE_ATTR(t_alarm_low, | ||
712 | S_IRUGO | S_IWUSR, | ||
713 | adt7310_show_t_alarm_low, adt7310_set_t_alarm_low, 0); | ||
714 | static IIO_DEVICE_ATTR(t_crit, | ||
715 | S_IRUGO | S_IWUSR, | ||
716 | adt7310_show_t_crit, adt7310_set_t_crit, 0); | ||
717 | static IIO_DEVICE_ATTR(t_hyst, | ||
718 | S_IRUGO | S_IWUSR, | ||
719 | adt7310_show_t_hyst, adt7310_set_t_hyst, 0); | ||
720 | |||
721 | static struct attribute *adt7310_event_int_attributes[] = { | ||
722 | &iio_dev_attr_event_mode.dev_attr.attr, | ||
723 | &iio_dev_attr_available_event_modes.dev_attr.attr, | ||
724 | &iio_dev_attr_fault_queue.dev_attr.attr, | ||
725 | &iio_dev_attr_t_alarm_high.dev_attr.attr, | ||
726 | &iio_dev_attr_t_alarm_low.dev_attr.attr, | ||
727 | &iio_dev_attr_t_hyst.dev_attr.attr, | ||
728 | NULL, | ||
729 | }; | ||
730 | |||
731 | static struct attribute *adt7310_event_ct_attributes[] = { | ||
732 | &iio_dev_attr_event_mode.dev_attr.attr, | ||
733 | &iio_dev_attr_available_event_modes.dev_attr.attr, | ||
734 | &iio_dev_attr_fault_queue.dev_attr.attr, | ||
735 | &iio_dev_attr_t_crit.dev_attr.attr, | ||
736 | &iio_dev_attr_t_hyst.dev_attr.attr, | ||
737 | NULL, | ||
738 | }; | ||
739 | |||
740 | static struct attribute_group adt7310_event_attribute_group[ADT7310_IRQS] = { | ||
741 | { | ||
742 | .attrs = adt7310_event_int_attributes, | ||
743 | }, { | ||
744 | .attrs = adt7310_event_ct_attributes, | ||
745 | } | ||
746 | }; | ||
747 | |||
748 | static const struct iio_info adt7310_info = { | ||
749 | .attrs = &adt7310_attribute_group, | ||
750 | .num_interrupt_lines = ADT7310_IRQS, | ||
751 | .event_attrs = adt7310_event_attribute_group, | ||
752 | .driver_module = THIS_MODULE, | ||
753 | }; | ||
754 | |||
755 | /* | ||
756 | * device probe and remove | ||
757 | */ | ||
758 | |||
759 | static int __devinit adt7310_probe(struct spi_device *spi_dev) | ||
760 | { | ||
761 | struct adt7310_chip_info *chip; | ||
762 | struct iio_dev *indio_dev; | ||
763 | int ret = 0; | ||
764 | unsigned long *adt7310_platform_data = spi_dev->dev.platform_data; | ||
765 | unsigned long irq_flags; | ||
766 | |||
767 | indio_dev = iio_allocate_device(sizeof(*chip)); | ||
768 | if (indio_dev == NULL) { | ||
769 | ret = -ENOMEM; | ||
770 | goto error_ret; | ||
771 | } | ||
772 | chip = iio_priv(indio_dev); | ||
773 | /* this is only used for device removal purposes */ | ||
774 | dev_set_drvdata(&spi_dev->dev, indio_dev); | ||
775 | |||
776 | chip->spi_dev = spi_dev; | ||
777 | |||
778 | indio_dev->dev.parent = &spi_dev->dev; | ||
779 | indio_dev->name = spi_get_device_id(spi_dev)->name; | ||
780 | indio_dev->info = &adt7310_info; | ||
781 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
782 | |||
783 | ret = iio_device_register(indio_dev); | ||
784 | if (ret) | ||
785 | goto error_free_dev; | ||
786 | |||
787 | /* CT critcal temperature event. line 0 */ | ||
788 | if (spi_dev->irq) { | ||
789 | if (adt7310_platform_data[2]) | ||
790 | irq_flags = adt7310_platform_data[2]; | ||
791 | else | ||
792 | irq_flags = IRQF_TRIGGER_LOW; | ||
793 | ret = request_threaded_irq(spi_dev->irq, | ||
794 | NULL, | ||
795 | &adt7310_event_handler, | ||
796 | irq_flags, | ||
797 | indio_dev->name, | ||
798 | indio_dev); | ||
799 | if (ret) | ||
800 | goto error_unreg_dev; | ||
801 | } | ||
802 | |||
803 | /* INT bound temperature alarm event. line 1 */ | ||
804 | if (adt7310_platform_data[0]) { | ||
805 | ret = request_threaded_irq(adt7310_platform_data[0], | ||
806 | NULL, | ||
807 | &adt7310_event_handler, | ||
808 | adt7310_platform_data[1], | ||
809 | indio_dev->name, | ||
810 | indio_dev); | ||
811 | if (ret) | ||
812 | goto error_unreg_ct_irq; | ||
813 | } | ||
814 | |||
815 | if (spi_dev->irq && adt7310_platform_data[0]) { | ||
816 | ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config); | ||
817 | if (ret) { | ||
818 | ret = -EIO; | ||
819 | goto error_unreg_int_irq; | ||
820 | } | ||
821 | |||
822 | /* set irq polarity low level */ | ||
823 | chip->config &= ~ADT7310_CT_POLARITY; | ||
824 | |||
825 | if (adt7310_platform_data[1] & IRQF_TRIGGER_HIGH) | ||
826 | chip->config |= ADT7310_INT_POLARITY; | ||
827 | else | ||
828 | chip->config &= ~ADT7310_INT_POLARITY; | ||
829 | |||
830 | ret = adt7310_spi_write_byte(chip, ADT7310_CONFIG, chip->config); | ||
831 | if (ret) { | ||
832 | ret = -EIO; | ||
833 | goto error_unreg_int_irq; | ||
834 | } | ||
835 | } | ||
836 | |||
837 | dev_info(&spi_dev->dev, "%s temperature sensor registered.\n", | ||
838 | indio_dev->name); | ||
839 | |||
840 | return 0; | ||
841 | |||
842 | error_unreg_int_irq: | ||
843 | free_irq(adt7310_platform_data[0], indio_dev); | ||
844 | error_unreg_ct_irq: | ||
845 | free_irq(spi_dev->irq, indio_dev); | ||
846 | error_unreg_dev: | ||
847 | iio_device_unregister(indio_dev); | ||
848 | error_free_dev: | ||
849 | iio_free_device(indio_dev); | ||
850 | error_ret: | ||
851 | return ret; | ||
852 | } | ||
853 | |||
854 | static int __devexit adt7310_remove(struct spi_device *spi_dev) | ||
855 | { | ||
856 | struct iio_dev *indio_dev = dev_get_drvdata(&spi_dev->dev); | ||
857 | unsigned long *adt7310_platform_data = spi_dev->dev.platform_data; | ||
858 | |||
859 | dev_set_drvdata(&spi_dev->dev, NULL); | ||
860 | if (adt7310_platform_data[0]) | ||
861 | free_irq(adt7310_platform_data[0], indio_dev); | ||
862 | if (spi_dev->irq) | ||
863 | free_irq(spi_dev->irq, indio_dev); | ||
864 | iio_device_unregister(indio_dev); | ||
865 | iio_free_device(indio_dev); | ||
866 | |||
867 | return 0; | ||
868 | } | ||
869 | |||
870 | static const struct spi_device_id adt7310_id[] = { | ||
871 | { "adt7310", 0 }, | ||
872 | {} | ||
873 | }; | ||
874 | |||
875 | MODULE_DEVICE_TABLE(spi, adt7310_id); | ||
876 | |||
877 | static struct spi_driver adt7310_driver = { | ||
878 | .driver = { | ||
879 | .name = "adt7310", | ||
880 | .bus = &spi_bus_type, | ||
881 | .owner = THIS_MODULE, | ||
882 | }, | ||
883 | .probe = adt7310_probe, | ||
884 | .remove = __devexit_p(adt7310_remove), | ||
885 | .id_table = adt7310_id, | ||
886 | }; | ||
887 | |||
888 | static __init int adt7310_init(void) | ||
889 | { | ||
890 | return spi_register_driver(&adt7310_driver); | ||
891 | } | ||
892 | |||
893 | static __exit void adt7310_exit(void) | ||
894 | { | ||
895 | spi_unregister_driver(&adt7310_driver); | ||
896 | } | ||
897 | |||
898 | MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>"); | ||
899 | MODULE_DESCRIPTION("Analog Devices ADT7310 digital" | ||
900 | " temperature sensor driver"); | ||
901 | MODULE_LICENSE("GPL v2"); | ||
902 | |||
903 | module_init(adt7310_init); | ||
904 | module_exit(adt7310_exit); | ||