blob: 1a41b8034405ea18955d17e0c68fe00e1bce7605 [file] [log] [blame]
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -04001/*
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>
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -040010#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>
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -040016
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
81struct adt7310_chip_info {
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -040082 struct spi_device *spi_dev;
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -040083 u8 config;
84};
85
86/*
87 * adt7310 register access by SPI
88 */
89
90static 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
114static 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
133static 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
155static 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
173static 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);
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100178 struct adt7310_chip_info *chip = iio_priv(dev_info);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400179 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
195static 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);
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100201 struct adt7310_chip_info *chip = iio_priv(dev_info);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400202 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
226static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
227 adt7310_show_mode,
228 adt7310_store_mode,
229 0);
230
231static 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
238static IIO_DEVICE_ATTR(available_modes, S_IRUGO, adt7310_show_available_modes, NULL, 0);
239
240static 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);
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100245 struct adt7310_chip_info *chip = iio_priv(dev_info);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400246 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
261static 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);
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100267 struct adt7310_chip_info *chip = iio_priv(dev_info);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400268 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
293static IIO_DEVICE_ATTR(resolution, S_IRUGO | S_IWUSR,
294 adt7310_show_resolution,
295 adt7310_store_resolution,
296 0);
297
298static 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);
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100303 struct adt7310_chip_info *chip = iio_priv(dev_info);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400304 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
316static IIO_DEVICE_ATTR(id, S_IRUGO | S_IWUSR,
317 adt7310_show_id,
318 NULL,
319 0);
320
321static 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
348static 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);
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100353 struct adt7310_chip_info *chip = iio_priv(dev_info);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400354 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
374static IIO_DEVICE_ATTR(value, S_IRUGO, adt7310_show_value, NULL, 0);
375
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400376static 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,
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400382 NULL,
383};
384
385static const struct attribute_group adt7310_attribute_group = {
386 .attrs = adt7310_attributes,
387};
388
Jonathan Cameroncda0b2a2011-05-18 14:41:13 +0100389static irqreturn_t adt7310_event_handler(int irq, void *private)
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400390{
Jonathan Cameroncda0b2a2011-05-18 14:41:13 +0100391 struct iio_dev *indio_dev = private;
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100392 struct adt7310_chip_info *chip = iio_priv(indio_dev);
Jonathan Cameroncda0b2a2011-05-18 14:41:13 +0100393 s64 timestamp = iio_get_time_ns();
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400394 u8 status;
Jonathan Cameroncda0b2a2011-05-18 14:41:13 +0100395 int ret;
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400396
Jonathan Cameroncda0b2a2011-05-18 14:41:13 +0100397 ret = adt7310_spi_read_byte(chip, ADT7310_STATUS, &status);
398 if (ret)
399 return ret;
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400400
401 if (status & ADT7310_STAT_T_HIGH)
Jonathan Cameroncda0b2a2011-05-18 14:41:13 +0100402 iio_push_event(indio_dev, 0,
Jonathan Camerone7a2c322011-05-18 14:42:16 +0100403 IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
404 IIO_EV_TYPE_THRESH,
405 IIO_EV_DIR_RISING),
406 timestamp);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400407 if (status & ADT7310_STAT_T_LOW)
Jonathan Cameroncda0b2a2011-05-18 14:41:13 +0100408 iio_push_event(indio_dev, 0,
Jonathan Camerone7a2c322011-05-18 14:42:16 +0100409 IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
410 IIO_EV_TYPE_THRESH,
411 IIO_EV_DIR_FALLING),
412 timestamp);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400413 if (status & ADT7310_STAT_T_CRIT)
Jonathan Cameroncda0b2a2011-05-18 14:41:13 +0100414 iio_push_event(indio_dev, 0,
Jonathan Camerone7a2c322011-05-18 14:42:16 +0100415 IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
416 IIO_EV_TYPE_THRESH,
417 IIO_EV_DIR_RISING),
Jonathan Cameroncda0b2a2011-05-18 14:41:13 +0100418 timestamp);
419 return IRQ_HANDLED;
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400420}
421
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400422static 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);
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100427 struct adt7310_chip_info *chip = iio_priv(dev_info);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400428 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
440static 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);
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100446 struct adt7310_chip_info *chip = iio_priv(dev_info);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400447 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
467static 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
474static 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);
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100479 struct adt7310_chip_info *chip = iio_priv(dev_info);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400480 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
489static 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);
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100495 struct adt7310_chip_info *chip = iio_priv(dev_info);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400496 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
519static 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);
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100525 struct adt7310_chip_info *chip = iio_priv(dev_info);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400526 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
536static 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);
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100543 struct adt7310_chip_info *chip = iio_priv(dev_info);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400544 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
607static 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
615static 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
624static 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
632static 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
641static 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
649static 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
658static 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);
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100663 struct adt7310_chip_info *chip = iio_priv(dev_info);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400664 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
674static 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);
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100680 struct adt7310_chip_info *chip = iio_priv(dev_info);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400681 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
Jonathan Cameroncda0b2a2011-05-18 14:41:13 +0100699static IIO_DEVICE_ATTR(event_mode,
700 S_IRUGO | S_IWUSR,
701 adt7310_show_event_mode, adt7310_set_event_mode, 0);
702static IIO_DEVICE_ATTR(available_event_modes,
703 S_IRUGO | S_IWUSR,
704 adt7310_show_available_event_modes, NULL, 0);
705static IIO_DEVICE_ATTR(fault_queue,
706 S_IRUGO | S_IWUSR,
707 adt7310_show_fault_queue, adt7310_set_fault_queue, 0);
708static IIO_DEVICE_ATTR(t_alarm_high,
709 S_IRUGO | S_IWUSR,
710 adt7310_show_t_alarm_high, adt7310_set_t_alarm_high, 0);
711static IIO_DEVICE_ATTR(t_alarm_low,
712 S_IRUGO | S_IWUSR,
713 adt7310_show_t_alarm_low, adt7310_set_t_alarm_low, 0);
714static IIO_DEVICE_ATTR(t_crit,
715 S_IRUGO | S_IWUSR,
716 adt7310_show_t_crit, adt7310_set_t_crit, 0);
717static IIO_DEVICE_ATTR(t_hyst,
718 S_IRUGO | S_IWUSR,
719 adt7310_show_t_hyst, adt7310_set_t_hyst, 0);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400720
721static struct attribute *adt7310_event_int_attributes[] = {
Jonathan Cameroncda0b2a2011-05-18 14:41:13 +0100722 &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,
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400728 NULL,
729};
730
731static struct attribute *adt7310_event_ct_attributes[] = {
Jonathan Cameroncda0b2a2011-05-18 14:41:13 +0100732 &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,
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400737 NULL,
738};
739
740static struct attribute_group adt7310_event_attribute_group[ADT7310_IRQS] = {
741 {
742 .attrs = adt7310_event_int_attributes,
Jonathan Cameroncda0b2a2011-05-18 14:41:13 +0100743 }, {
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400744 .attrs = adt7310_event_ct_attributes,
745 }
746};
747
Jonathan Cameron6fe81352011-05-18 14:42:37 +0100748static 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
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400755/*
756 * device probe and remove
757 */
758
759static int __devinit adt7310_probe(struct spi_device *spi_dev)
760{
761 struct adt7310_chip_info *chip;
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100762 struct iio_dev *indio_dev;
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400763 int ret = 0;
764 unsigned long *adt7310_platform_data = spi_dev->dev.platform_data;
765 unsigned long irq_flags;
766
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100767 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);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400773 /* this is only used for device removal purposes */
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100774 dev_set_drvdata(&spi_dev->dev, indio_dev);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400775
776 chip->spi_dev = spi_dev;
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400777
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100778 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;
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400782
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100783 ret = iio_device_register(indio_dev);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400784 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;
Jonathan Cameroncda0b2a2011-05-18 14:41:13 +0100793 ret = request_threaded_irq(spi_dev->irq,
794 NULL,
795 &adt7310_event_handler,
796 irq_flags,
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100797 indio_dev->name,
798 indio_dev);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400799 if (ret)
800 goto error_unreg_dev;
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400801 }
802
803 /* INT bound temperature alarm event. line 1 */
804 if (adt7310_platform_data[0]) {
Jonathan Cameroncda0b2a2011-05-18 14:41:13 +0100805 ret = request_threaded_irq(adt7310_platform_data[0],
806 NULL,
807 &adt7310_event_handler,
808 adt7310_platform_data[1],
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100809 indio_dev->name,
810 indio_dev);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400811 if (ret)
812 goto error_unreg_ct_irq;
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400813 }
814
815 if (spi_dev->irq && adt7310_platform_data[0]) {
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400816 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",
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100838 indio_dev->name);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400839
840 return 0;
841
842error_unreg_int_irq:
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100843 free_irq(adt7310_platform_data[0], indio_dev);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400844error_unreg_ct_irq:
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100845 free_irq(spi_dev->irq, indio_dev);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400846error_unreg_dev:
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100847 iio_device_unregister(indio_dev);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400848error_free_dev:
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100849 iio_free_device(indio_dev);
850error_ret:
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400851 return ret;
852}
853
854static int __devexit adt7310_remove(struct spi_device *spi_dev)
855{
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100856 struct iio_dev *indio_dev = dev_get_drvdata(&spi_dev->dev);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400857 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])
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100861 free_irq(adt7310_platform_data[0], indio_dev);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400862 if (spi_dev->irq)
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100863 free_irq(spi_dev->irq, indio_dev);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400864 iio_device_unregister(indio_dev);
Jonathan Cameron7978f9f2011-06-27 13:07:28 +0100865 iio_free_device(indio_dev);
Sonic Zhanga5d8c6b2010-10-27 21:44:01 -0400866
867 return 0;
868}
869
870static const struct spi_device_id adt7310_id[] = {
871 { "adt7310", 0 },
872 {}
873};
874
875MODULE_DEVICE_TABLE(spi, adt7310_id);
876
877static 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
888static __init int adt7310_init(void)
889{
890 return spi_register_driver(&adt7310_driver);
891}
892
893static __exit void adt7310_exit(void)
894{
895 spi_unregister_driver(&adt7310_driver);
896}
897
898MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
899MODULE_DESCRIPTION("Analog Devices ADT7310 digital"
900 " temperature sensor driver");
901MODULE_LICENSE("GPL v2");
902
903module_init(adt7310_init);
904module_exit(adt7310_exit);