Commit | Line | Data |
---|---|---|
54c5be34 BS |
1 | /* |
2 | * AD7150 capacitive sensor driver supporting AD7150/1/6 | |
3 | * | |
4e687ddb | 4 | * Copyright 2010-2011 Analog Devices Inc. |
54c5be34 BS |
5 | * |
6 | * Licensed under the GPL-2 or later. | |
7 | */ | |
8 | ||
9 | #include <linux/interrupt.h> | |
54c5be34 BS |
10 | #include <linux/device.h> |
11 | #include <linux/kernel.h> | |
12 | #include <linux/slab.h> | |
54c5be34 | 13 | #include <linux/i2c.h> |
99c97852 | 14 | #include <linux/module.h> |
54c5be34 | 15 | |
06458e27 JC |
16 | #include <linux/iio/iio.h> |
17 | #include <linux/iio/sysfs.h> | |
18 | #include <linux/iio/events.h> | |
54c5be34 BS |
19 | /* |
20 | * AD7150 registers definition | |
21 | */ | |
22 | ||
23 | #define AD7150_STATUS 0 | |
24 | #define AD7150_STATUS_OUT1 (1 << 3) | |
25 | #define AD7150_STATUS_OUT2 (1 << 5) | |
26 | #define AD7150_CH1_DATA_HIGH 1 | |
54c5be34 | 27 | #define AD7150_CH2_DATA_HIGH 3 |
54c5be34 | 28 | #define AD7150_CH1_AVG_HIGH 5 |
54c5be34 | 29 | #define AD7150_CH2_AVG_HIGH 7 |
54c5be34 BS |
30 | #define AD7150_CH1_SENSITIVITY 9 |
31 | #define AD7150_CH1_THR_HOLD_H 9 | |
32 | #define AD7150_CH1_TIMEOUT 10 | |
54c5be34 BS |
33 | #define AD7150_CH1_SETUP 11 |
34 | #define AD7150_CH2_SENSITIVITY 12 | |
35 | #define AD7150_CH2_THR_HOLD_H 12 | |
36 | #define AD7150_CH2_TIMEOUT 13 | |
54c5be34 BS |
37 | #define AD7150_CH2_SETUP 14 |
38 | #define AD7150_CFG 15 | |
39 | #define AD7150_CFG_FIX (1 << 7) | |
40 | #define AD7150_PD_TIMER 16 | |
41 | #define AD7150_CH1_CAPDAC 17 | |
42 | #define AD7150_CH2_CAPDAC 18 | |
43 | #define AD7150_SN3 19 | |
44 | #define AD7150_SN2 20 | |
45 | #define AD7150_SN1 21 | |
46 | #define AD7150_SN0 22 | |
47 | #define AD7150_ID 23 | |
48 | ||
531efd6a JC |
49 | /** |
50 | * struct ad7150_chip_info - instance specific chip data | |
51 | * @client: i2c client for this device | |
52 | * @current_event: device always has one type of event enabled. | |
53 | * This element stores the event code of the current one. | |
54 | * @threshold: thresholds for simple capacitance value events | |
55 | * @thresh_sensitivity: threshold for simple capacitance offset | |
56 | * from 'average' value. | |
57 | * @mag_sensitity: threshold for magnitude of capacitance offset from | |
58 | * from 'average' value. | |
59 | * @thresh_timeout: a timeout, in samples from the moment an | |
60 | * adaptive threshold event occurs to when the average | |
61 | * value jumps to current value. | |
62 | * @mag_timeout: a timeout, in sample from the moment an | |
63 | * adaptive magnitude event occurs to when the average | |
64 | * value jumps to the current value. | |
65 | * @old_state: store state from previous event, allowing confirmation | |
66 | * of new condition. | |
67 | * @conversion_mode: the current conversion mode. | |
68 | * @state_lock: ensure consistent state of this structure wrt the | |
69 | * hardware. | |
70 | */ | |
54c5be34 | 71 | struct ad7150_chip_info { |
54c5be34 | 72 | struct i2c_client *client; |
531efd6a JC |
73 | u64 current_event; |
74 | u16 threshold[2][2]; | |
75 | u8 thresh_sensitivity[2][2]; | |
76 | u8 mag_sensitivity[2][2]; | |
77 | u8 thresh_timeout[2][2]; | |
78 | u8 mag_timeout[2][2]; | |
54c5be34 BS |
79 | int old_state; |
80 | char *conversion_mode; | |
531efd6a | 81 | struct mutex state_lock; |
54c5be34 BS |
82 | }; |
83 | ||
54c5be34 BS |
84 | /* |
85 | * sysfs nodes | |
86 | */ | |
87 | ||
531efd6a JC |
88 | static const u8 ad7150_addresses[][6] = { |
89 | { AD7150_CH1_DATA_HIGH, AD7150_CH1_AVG_HIGH, | |
90 | AD7150_CH1_SETUP, AD7150_CH1_THR_HOLD_H, | |
91 | AD7150_CH1_SENSITIVITY, AD7150_CH1_TIMEOUT }, | |
92 | { AD7150_CH2_DATA_HIGH, AD7150_CH2_AVG_HIGH, | |
93 | AD7150_CH2_SETUP, AD7150_CH2_THR_HOLD_H, | |
94 | AD7150_CH2_SENSITIVITY, AD7150_CH2_TIMEOUT }, | |
95 | }; | |
54c5be34 | 96 | |
531efd6a JC |
97 | static int ad7150_read_raw(struct iio_dev *indio_dev, |
98 | struct iio_chan_spec const *chan, | |
99 | int *val, | |
100 | int *val2, | |
101 | long mask) | |
54c5be34 | 102 | { |
54c5be34 | 103 | int ret; |
531efd6a | 104 | struct ad7150_chip_info *chip = iio_priv(indio_dev); |
54c5be34 | 105 | |
531efd6a | 106 | switch (mask) { |
e33e0750 | 107 | case IIO_CHAN_INFO_RAW: |
531efd6a JC |
108 | ret = i2c_smbus_read_word_data(chip->client, |
109 | ad7150_addresses[chan->channel][0]); | |
110 | if (ret < 0) | |
111 | return ret; | |
112 | *val = swab16(ret); | |
113 | return IIO_VAL_INT; | |
c8a9f805 | 114 | case IIO_CHAN_INFO_AVERAGE_RAW: |
531efd6a JC |
115 | ret = i2c_smbus_read_word_data(chip->client, |
116 | ad7150_addresses[chan->channel][1]); | |
117 | if (ret < 0) | |
118 | return ret; | |
119 | *val = swab16(ret); | |
120 | return IIO_VAL_INT; | |
121 | default: | |
122 | return -EINVAL; | |
54c5be34 | 123 | } |
54c5be34 BS |
124 | } |
125 | ||
531efd6a | 126 | static int ad7150_read_event_config(struct iio_dev *indio_dev, u64 event_code) |
54c5be34 | 127 | { |
54c5be34 | 128 | int ret; |
531efd6a JC |
129 | u8 threshtype; |
130 | bool adaptive; | |
131 | struct ad7150_chip_info *chip = iio_priv(indio_dev); | |
132 | int rising = !!(IIO_EVENT_CODE_EXTRACT_DIR(event_code) == | |
133 | IIO_EV_DIR_RISING); | |
54c5be34 | 134 | |
531efd6a JC |
135 | ret = i2c_smbus_read_byte_data(chip->client, AD7150_CFG); |
136 | if (ret < 0) | |
137 | return ret; | |
54c5be34 | 138 | |
531efd6a JC |
139 | threshtype = (ret >> 5) & 0x03; |
140 | adaptive = !!(ret & 0x80); | |
141 | ||
142 | switch (IIO_EVENT_CODE_EXTRACT_TYPE(event_code)) { | |
143 | case IIO_EV_TYPE_MAG_ADAPTIVE: | |
144 | if (rising) | |
145 | return adaptive && (threshtype == 0x1); | |
146 | else | |
147 | return adaptive && (threshtype == 0x0); | |
148 | case IIO_EV_TYPE_THRESH_ADAPTIVE: | |
149 | if (rising) | |
150 | return adaptive && (threshtype == 0x3); | |
151 | else | |
152 | return adaptive && (threshtype == 0x2); | |
153 | ||
154 | case IIO_EV_TYPE_THRESH: | |
155 | if (rising) | |
156 | return !adaptive && (threshtype == 0x1); | |
157 | else | |
158 | return !adaptive && (threshtype == 0x0); | |
159 | }; | |
54c5be34 BS |
160 | return -EINVAL; |
161 | } | |
162 | ||
531efd6a JC |
163 | /* lock should be held */ |
164 | static int ad7150_write_event_params(struct iio_dev *indio_dev, u64 event_code) | |
54c5be34 | 165 | { |
54c5be34 | 166 | int ret; |
531efd6a JC |
167 | u16 value; |
168 | u8 sens, timeout; | |
169 | struct ad7150_chip_info *chip = iio_priv(indio_dev); | |
da367160 | 170 | int chan = IIO_EVENT_CODE_EXTRACT_CHAN(event_code); |
531efd6a JC |
171 | int rising = !!(IIO_EVENT_CODE_EXTRACT_DIR(event_code) == |
172 | IIO_EV_DIR_RISING); | |
173 | ||
174 | if (event_code != chip->current_event) | |
175 | return 0; | |
176 | ||
177 | switch (IIO_EVENT_CODE_EXTRACT_TYPE(event_code)) { | |
178 | /* Note completely different from the adaptive versions */ | |
179 | case IIO_EV_TYPE_THRESH: | |
180 | value = chip->threshold[rising][chan]; | |
181 | ret = i2c_smbus_write_word_data(chip->client, | |
182 | ad7150_addresses[chan][3], | |
183 | swab16(value)); | |
184 | if (ret < 0) | |
185 | return ret; | |
186 | return 0; | |
187 | case IIO_EV_TYPE_MAG_ADAPTIVE: | |
188 | sens = chip->mag_sensitivity[rising][chan]; | |
189 | timeout = chip->mag_timeout[rising][chan]; | |
190 | break; | |
191 | case IIO_EV_TYPE_THRESH_ADAPTIVE: | |
192 | sens = chip->thresh_sensitivity[rising][chan]; | |
193 | timeout = chip->thresh_timeout[rising][chan]; | |
194 | break; | |
195 | default: | |
196 | return -EINVAL; | |
197 | }; | |
198 | ret = i2c_smbus_write_byte_data(chip->client, | |
199 | ad7150_addresses[chan][4], | |
200 | sens); | |
201 | if (ret < 0) | |
202 | return ret; | |
54c5be34 | 203 | |
531efd6a JC |
204 | ret = i2c_smbus_write_byte_data(chip->client, |
205 | ad7150_addresses[chan][5], | |
206 | timeout); | |
207 | if (ret < 0) | |
208 | return ret; | |
54c5be34 | 209 | |
531efd6a | 210 | return 0; |
54c5be34 BS |
211 | } |
212 | ||
531efd6a JC |
213 | static int ad7150_write_event_config(struct iio_dev *indio_dev, |
214 | u64 event_code, int state) | |
54c5be34 | 215 | { |
531efd6a | 216 | u8 thresh_type, cfg, adaptive; |
54c5be34 | 217 | int ret; |
531efd6a JC |
218 | struct ad7150_chip_info *chip = iio_priv(indio_dev); |
219 | int rising = !!(IIO_EVENT_CODE_EXTRACT_DIR(event_code) == | |
220 | IIO_EV_DIR_RISING); | |
54c5be34 | 221 | |
531efd6a JC |
222 | /* Something must always be turned on */ |
223 | if (state == 0) | |
224 | return -EINVAL; | |
54c5be34 | 225 | |
531efd6a JC |
226 | if (event_code == chip->current_event) |
227 | return 0; | |
228 | mutex_lock(&chip->state_lock); | |
229 | ret = i2c_smbus_read_byte_data(chip->client, AD7150_CFG); | |
230 | if (ret < 0) | |
231 | goto error_ret; | |
54c5be34 | 232 | |
531efd6a JC |
233 | cfg = ret & ~((0x03 << 5) | (0x1 << 7)); |
234 | ||
235 | switch (IIO_EVENT_CODE_EXTRACT_TYPE(event_code)) { | |
236 | case IIO_EV_TYPE_MAG_ADAPTIVE: | |
237 | adaptive = 1; | |
238 | if (rising) | |
239 | thresh_type = 0x1; | |
240 | else | |
241 | thresh_type = 0x0; | |
242 | break; | |
243 | case IIO_EV_TYPE_THRESH_ADAPTIVE: | |
244 | adaptive = 1; | |
245 | if (rising) | |
246 | thresh_type = 0x3; | |
247 | else | |
248 | thresh_type = 0x2; | |
249 | break; | |
250 | case IIO_EV_TYPE_THRESH: | |
251 | adaptive = 0; | |
252 | if (rising) | |
253 | thresh_type = 0x1; | |
254 | else | |
255 | thresh_type = 0x0; | |
256 | break; | |
257 | default: | |
258 | ret = -EINVAL; | |
259 | goto error_ret; | |
260 | }; | |
54c5be34 | 261 | |
531efd6a | 262 | cfg |= (!adaptive << 7) | (thresh_type << 5); |
54c5be34 | 263 | |
531efd6a JC |
264 | ret = i2c_smbus_write_byte_data(chip->client, AD7150_CFG, cfg); |
265 | if (ret < 0) | |
266 | goto error_ret; | |
54c5be34 | 267 | |
531efd6a | 268 | chip->current_event = event_code; |
54c5be34 | 269 | |
531efd6a JC |
270 | /* update control attributes */ |
271 | ret = ad7150_write_event_params(indio_dev, event_code); | |
272 | error_ret: | |
273 | mutex_unlock(&chip->state_lock); | |
54c5be34 | 274 | |
531efd6a | 275 | return 0; |
54c5be34 BS |
276 | } |
277 | ||
531efd6a JC |
278 | static int ad7150_read_event_value(struct iio_dev *indio_dev, |
279 | u64 event_code, | |
280 | int *val) | |
54c5be34 | 281 | { |
da367160 | 282 | int chan = IIO_EVENT_CODE_EXTRACT_CHAN(event_code); |
531efd6a JC |
283 | struct ad7150_chip_info *chip = iio_priv(indio_dev); |
284 | int rising = !!(IIO_EVENT_CODE_EXTRACT_DIR(event_code) == | |
285 | IIO_EV_DIR_RISING); | |
54c5be34 | 286 | |
531efd6a JC |
287 | /* Complex register sharing going on here */ |
288 | switch (IIO_EVENT_CODE_EXTRACT_TYPE(event_code)) { | |
289 | case IIO_EV_TYPE_MAG_ADAPTIVE: | |
290 | *val = chip->mag_sensitivity[rising][chan]; | |
291 | return 0; | |
54c5be34 | 292 | |
531efd6a JC |
293 | case IIO_EV_TYPE_THRESH_ADAPTIVE: |
294 | *val = chip->thresh_sensitivity[rising][chan]; | |
295 | return 0; | |
54c5be34 | 296 | |
531efd6a JC |
297 | case IIO_EV_TYPE_THRESH: |
298 | *val = chip->threshold[rising][chan]; | |
299 | return 0; | |
54c5be34 | 300 | |
531efd6a JC |
301 | default: |
302 | return -EINVAL; | |
303 | }; | |
54c5be34 BS |
304 | } |
305 | ||
531efd6a JC |
306 | static int ad7150_write_event_value(struct iio_dev *indio_dev, |
307 | u64 event_code, | |
308 | int val) | |
54c5be34 | 309 | { |
54c5be34 | 310 | int ret; |
531efd6a | 311 | struct ad7150_chip_info *chip = iio_priv(indio_dev); |
da367160 | 312 | int chan = IIO_EVENT_CODE_EXTRACT_CHAN(event_code); |
531efd6a JC |
313 | int rising = !!(IIO_EVENT_CODE_EXTRACT_DIR(event_code) == |
314 | IIO_EV_DIR_RISING); | |
315 | ||
316 | mutex_lock(&chip->state_lock); | |
317 | switch (IIO_EVENT_CODE_EXTRACT_TYPE(event_code)) { | |
318 | case IIO_EV_TYPE_MAG_ADAPTIVE: | |
319 | chip->mag_sensitivity[rising][chan] = val; | |
320 | break; | |
321 | case IIO_EV_TYPE_THRESH_ADAPTIVE: | |
322 | chip->thresh_sensitivity[rising][chan] = val; | |
323 | break; | |
324 | case IIO_EV_TYPE_THRESH: | |
325 | chip->threshold[rising][chan] = val; | |
326 | break; | |
327 | default: | |
328 | ret = -EINVAL; | |
329 | goto error_ret; | |
330 | }; | |
54c5be34 | 331 | |
531efd6a JC |
332 | /* write back if active */ |
333 | ret = ad7150_write_event_params(indio_dev, event_code); | |
54c5be34 | 334 | |
531efd6a | 335 | error_ret: |
ee760ab2 | 336 | mutex_unlock(&chip->state_lock); |
531efd6a | 337 | return ret; |
54c5be34 BS |
338 | } |
339 | ||
531efd6a JC |
340 | static ssize_t ad7150_show_timeout(struct device *dev, |
341 | struct device_attribute *attr, | |
342 | char *buf) | |
54c5be34 | 343 | { |
84f79ecb JC |
344 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
345 | struct ad7150_chip_info *chip = iio_priv(indio_dev); | |
531efd6a JC |
346 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
347 | u8 value; | |
348 | ||
349 | /* use the event code for consistency reasons */ | |
da367160 | 350 | int chan = IIO_EVENT_CODE_EXTRACT_CHAN(this_attr->address); |
531efd6a JC |
351 | int rising = !!(IIO_EVENT_CODE_EXTRACT_DIR(this_attr->address) |
352 | == IIO_EV_DIR_RISING); | |
353 | ||
354 | switch (IIO_EVENT_CODE_EXTRACT_TYPE(this_attr->address)) { | |
355 | case IIO_EV_TYPE_MAG_ADAPTIVE: | |
356 | value = chip->mag_timeout[rising][chan]; | |
357 | break; | |
358 | case IIO_EV_TYPE_THRESH_ADAPTIVE: | |
359 | value = chip->thresh_timeout[rising][chan]; | |
360 | break; | |
361 | default: | |
362 | return -EINVAL; | |
363 | }; | |
54c5be34 | 364 | |
531efd6a | 365 | return sprintf(buf, "%d\n", value); |
54c5be34 BS |
366 | } |
367 | ||
531efd6a | 368 | static ssize_t ad7150_store_timeout(struct device *dev, |
54c5be34 BS |
369 | struct device_attribute *attr, |
370 | const char *buf, | |
371 | size_t len) | |
372 | { | |
531efd6a JC |
373 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
374 | struct ad7150_chip_info *chip = iio_priv(indio_dev); | |
375 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | |
da367160 | 376 | int chan = IIO_EVENT_CODE_EXTRACT_CHAN(this_attr->address); |
531efd6a JC |
377 | int rising = !!(IIO_EVENT_CODE_EXTRACT_DIR(this_attr->address) == |
378 | IIO_EV_DIR_RISING); | |
379 | u8 data; | |
54c5be34 BS |
380 | int ret; |
381 | ||
531efd6a JC |
382 | ret = kstrtou8(buf, 10, &data); |
383 | if (ret < 0) | |
384 | return ret; | |
54c5be34 | 385 | |
531efd6a JC |
386 | mutex_lock(&chip->state_lock); |
387 | switch (IIO_EVENT_CODE_EXTRACT_TYPE(this_attr->address)) { | |
388 | case IIO_EV_TYPE_MAG_ADAPTIVE: | |
389 | chip->mag_timeout[rising][chan] = data; | |
390 | break; | |
391 | case IIO_EV_TYPE_THRESH_ADAPTIVE: | |
392 | chip->thresh_timeout[rising][chan] = data; | |
393 | break; | |
394 | default: | |
395 | ret = -EINVAL; | |
396 | goto error_ret; | |
397 | }; | |
54c5be34 | 398 | |
531efd6a JC |
399 | ret = ad7150_write_event_params(indio_dev, this_attr->address); |
400 | error_ret: | |
401 | mutex_unlock(&chip->state_lock); | |
54c5be34 | 402 | |
531efd6a JC |
403 | if (ret < 0) |
404 | return ret; | |
54c5be34 | 405 | |
531efd6a | 406 | return len; |
54c5be34 BS |
407 | } |
408 | ||
531efd6a JC |
409 | #define AD7150_TIMEOUT(chan, type, dir, ev_type, ev_dir) \ |
410 | IIO_DEVICE_ATTR(in_capacitance##chan##_##type##_##dir##_timeout, \ | |
411 | S_IRUGO | S_IWUSR, \ | |
412 | &ad7150_show_timeout, \ | |
413 | &ad7150_store_timeout, \ | |
414 | IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, \ | |
415 | chan, \ | |
416 | IIO_EV_TYPE_##ev_type, \ | |
417 | IIO_EV_DIR_##ev_dir)) | |
418 | static AD7150_TIMEOUT(0, mag_adaptive, rising, MAG_ADAPTIVE, RISING); | |
419 | static AD7150_TIMEOUT(0, mag_adaptive, falling, MAG_ADAPTIVE, FALLING); | |
420 | static AD7150_TIMEOUT(1, mag_adaptive, rising, MAG_ADAPTIVE, RISING); | |
421 | static AD7150_TIMEOUT(1, mag_adaptive, falling, MAG_ADAPTIVE, FALLING); | |
422 | static AD7150_TIMEOUT(0, thresh_adaptive, rising, THRESH_ADAPTIVE, RISING); | |
423 | static AD7150_TIMEOUT(0, thresh_adaptive, falling, THRESH_ADAPTIVE, FALLING); | |
424 | static AD7150_TIMEOUT(1, thresh_adaptive, rising, THRESH_ADAPTIVE, RISING); | |
425 | static AD7150_TIMEOUT(1, thresh_adaptive, falling, THRESH_ADAPTIVE, FALLING); | |
426 | ||
427 | static const struct iio_chan_spec ad7150_channels[] = { | |
428 | { | |
429 | .type = IIO_CAPACITANCE, | |
430 | .indexed = 1, | |
431 | .channel = 0, | |
e33e0750 JC |
432 | .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | |
433 | IIO_CHAN_INFO_AVERAGE_RAW_SEPARATE_BIT, | |
531efd6a JC |
434 | .event_mask = |
435 | IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | | |
436 | IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING) | | |
437 | IIO_EV_BIT(IIO_EV_TYPE_THRESH_ADAPTIVE, IIO_EV_DIR_RISING) | | |
438 | IIO_EV_BIT(IIO_EV_TYPE_THRESH_ADAPTIVE, IIO_EV_DIR_FALLING) | | |
439 | IIO_EV_BIT(IIO_EV_TYPE_MAG_ADAPTIVE, IIO_EV_DIR_RISING) | | |
440 | IIO_EV_BIT(IIO_EV_TYPE_MAG_ADAPTIVE, IIO_EV_DIR_FALLING) | |
441 | }, { | |
442 | .type = IIO_CAPACITANCE, | |
443 | .indexed = 1, | |
444 | .channel = 1, | |
e33e0750 JC |
445 | .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | |
446 | IIO_CHAN_INFO_AVERAGE_RAW_SEPARATE_BIT, | |
531efd6a JC |
447 | .event_mask = |
448 | IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | | |
449 | IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING) | | |
450 | IIO_EV_BIT(IIO_EV_TYPE_THRESH_ADAPTIVE, IIO_EV_DIR_RISING) | | |
451 | IIO_EV_BIT(IIO_EV_TYPE_THRESH_ADAPTIVE, IIO_EV_DIR_FALLING) | | |
452 | IIO_EV_BIT(IIO_EV_TYPE_MAG_ADAPTIVE, IIO_EV_DIR_RISING) | | |
453 | IIO_EV_BIT(IIO_EV_TYPE_MAG_ADAPTIVE, IIO_EV_DIR_FALLING) | |
454 | }, | |
455 | }; | |
54c5be34 | 456 | |
54c5be34 BS |
457 | /* |
458 | * threshold events | |
459 | */ | |
460 | ||
9b5d9b06 | 461 | static irqreturn_t ad7150_event_handler(int irq, void *private) |
54c5be34 | 462 | { |
9b5d9b06 | 463 | struct iio_dev *indio_dev = private; |
46a6af38 | 464 | struct ad7150_chip_info *chip = iio_priv(indio_dev); |
54c5be34 | 465 | u8 int_status; |
9b5d9b06 | 466 | s64 timestamp = iio_get_time_ns(); |
531efd6a | 467 | int ret; |
54c5be34 | 468 | |
531efd6a JC |
469 | ret = i2c_smbus_read_byte_data(chip->client, AD7150_STATUS); |
470 | if (ret < 0) | |
471 | return IRQ_HANDLED; | |
472 | ||
473 | int_status = ret; | |
54c5be34 | 474 | |
5aa96188 JC |
475 | if ((int_status & AD7150_STATUS_OUT1) && |
476 | !(chip->old_state & AD7150_STATUS_OUT1)) | |
477 | iio_push_event(indio_dev, | |
1b992320 | 478 | IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, |
d91a0ab0 JC |
479 | 0, |
480 | IIO_EV_TYPE_THRESH, | |
481 | IIO_EV_DIR_RISING), | |
9b5d9b06 | 482 | timestamp); |
5aa96188 JC |
483 | else if ((!(int_status & AD7150_STATUS_OUT1)) && |
484 | (chip->old_state & AD7150_STATUS_OUT1)) | |
485 | iio_push_event(indio_dev, | |
1b992320 | 486 | IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, |
d91a0ab0 JC |
487 | 0, |
488 | IIO_EV_TYPE_THRESH, | |
489 | IIO_EV_DIR_FALLING), | |
490 | timestamp); | |
54c5be34 | 491 | |
5aa96188 JC |
492 | if ((int_status & AD7150_STATUS_OUT2) && |
493 | !(chip->old_state & AD7150_STATUS_OUT2)) | |
494 | iio_push_event(indio_dev, | |
1b992320 | 495 | IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, |
d91a0ab0 JC |
496 | 1, |
497 | IIO_EV_TYPE_THRESH, | |
498 | IIO_EV_DIR_RISING), | |
499 | timestamp); | |
5aa96188 JC |
500 | else if ((!(int_status & AD7150_STATUS_OUT2)) && |
501 | (chip->old_state & AD7150_STATUS_OUT2)) | |
502 | iio_push_event(indio_dev, | |
1b992320 | 503 | IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, |
d91a0ab0 JC |
504 | 1, |
505 | IIO_EV_TYPE_THRESH, | |
506 | IIO_EV_DIR_FALLING), | |
507 | timestamp); | |
531efd6a JC |
508 | /* store the status to avoid repushing same events */ |
509 | chip->old_state = int_status; | |
510 | ||
9b5d9b06 | 511 | return IRQ_HANDLED; |
54c5be34 BS |
512 | } |
513 | ||
531efd6a | 514 | /* Timeouts not currently handled by core */ |
54c5be34 | 515 | static struct attribute *ad7150_event_attributes[] = { |
531efd6a JC |
516 | &iio_dev_attr_in_capacitance0_mag_adaptive_rising_timeout |
517 | .dev_attr.attr, | |
518 | &iio_dev_attr_in_capacitance0_mag_adaptive_falling_timeout | |
519 | .dev_attr.attr, | |
520 | &iio_dev_attr_in_capacitance1_mag_adaptive_rising_timeout | |
521 | .dev_attr.attr, | |
522 | &iio_dev_attr_in_capacitance1_mag_adaptive_falling_timeout | |
523 | .dev_attr.attr, | |
524 | &iio_dev_attr_in_capacitance0_thresh_adaptive_rising_timeout | |
525 | .dev_attr.attr, | |
526 | &iio_dev_attr_in_capacitance0_thresh_adaptive_falling_timeout | |
527 | .dev_attr.attr, | |
528 | &iio_dev_attr_in_capacitance1_thresh_adaptive_rising_timeout | |
529 | .dev_attr.attr, | |
530 | &iio_dev_attr_in_capacitance1_thresh_adaptive_falling_timeout | |
531 | .dev_attr.attr, | |
54c5be34 BS |
532 | NULL, |
533 | }; | |
534 | ||
535 | static struct attribute_group ad7150_event_attribute_group = { | |
536 | .attrs = ad7150_event_attributes, | |
8e7d9672 | 537 | .name = "events", |
54c5be34 BS |
538 | }; |
539 | ||
6fe8135f | 540 | static const struct iio_info ad7150_info = { |
6fe8135f JC |
541 | .event_attrs = &ad7150_event_attribute_group, |
542 | .driver_module = THIS_MODULE, | |
531efd6a JC |
543 | .read_raw = &ad7150_read_raw, |
544 | .read_event_config = &ad7150_read_event_config, | |
545 | .write_event_config = &ad7150_write_event_config, | |
546 | .read_event_value = &ad7150_read_event_value, | |
547 | .write_event_value = &ad7150_write_event_value, | |
6fe8135f | 548 | }; |
531efd6a | 549 | |
54c5be34 BS |
550 | /* |
551 | * device probe and remove | |
552 | */ | |
553 | ||
554 | static int __devinit ad7150_probe(struct i2c_client *client, | |
555 | const struct i2c_device_id *id) | |
556 | { | |
26d25ae3 | 557 | int ret; |
46a6af38 JC |
558 | struct ad7150_chip_info *chip; |
559 | struct iio_dev *indio_dev; | |
560 | ||
7cbb7537 | 561 | indio_dev = iio_device_alloc(sizeof(*chip)); |
46a6af38 | 562 | if (indio_dev == NULL) { |
54c5be34 BS |
563 | ret = -ENOMEM; |
564 | goto error_ret; | |
565 | } | |
46a6af38 | 566 | chip = iio_priv(indio_dev); |
531efd6a | 567 | mutex_init(&chip->state_lock); |
54c5be34 | 568 | /* this is only used for device removal purposes */ |
46a6af38 | 569 | i2c_set_clientdata(client, indio_dev); |
54c5be34 BS |
570 | |
571 | chip->client = client; | |
54c5be34 | 572 | |
46a6af38 | 573 | indio_dev->name = id->name; |
531efd6a JC |
574 | indio_dev->channels = ad7150_channels; |
575 | indio_dev->num_channels = ARRAY_SIZE(ad7150_channels); | |
576 | /* Establish that the iio_dev is a child of the i2c device */ | |
46a6af38 | 577 | indio_dev->dev.parent = &client->dev; |
6fe8135f | 578 | |
46a6af38 | 579 | indio_dev->info = &ad7150_info; |
6fe8135f | 580 | |
46a6af38 | 581 | indio_dev->modes = INDIO_DIRECT_MODE; |
54c5be34 | 582 | |
9b5d9b06 JC |
583 | if (client->irq) { |
584 | ret = request_threaded_irq(client->irq, | |
585 | NULL, | |
586 | &ad7150_event_handler, | |
587 | IRQF_TRIGGER_RISING | | |
588 | IRQF_TRIGGER_FALLING, | |
4e687ddb | 589 | "ad7150_irq1", |
46a6af38 | 590 | indio_dev); |
54c5be34 BS |
591 | if (ret) |
592 | goto error_free_dev; | |
54c5be34 BS |
593 | } |
594 | ||
4e687ddb MH |
595 | if (client->dev.platform_data) { |
596 | ret = request_threaded_irq(*(unsigned int *) | |
597 | client->dev.platform_data, | |
598 | NULL, | |
599 | &ad7150_event_handler, | |
600 | IRQF_TRIGGER_RISING | | |
601 | IRQF_TRIGGER_FALLING, | |
602 | "ad7150_irq2", | |
603 | indio_dev); | |
604 | if (ret) | |
605 | goto error_free_irq; | |
606 | } | |
607 | ||
26d25ae3 JC |
608 | ret = iio_device_register(indio_dev); |
609 | if (ret) | |
4e687ddb | 610 | goto error_free_irq2; |
26d25ae3 | 611 | |
531efd6a JC |
612 | dev_info(&client->dev, "%s capacitive sensor registered,irq: %d\n", |
613 | id->name, client->irq); | |
54c5be34 BS |
614 | |
615 | return 0; | |
4e687ddb MH |
616 | error_free_irq2: |
617 | if (client->dev.platform_data) | |
618 | free_irq(*(unsigned int *)client->dev.platform_data, | |
619 | indio_dev); | |
26d25ae3 JC |
620 | error_free_irq: |
621 | if (client->irq) | |
622 | free_irq(client->irq, indio_dev); | |
54c5be34 | 623 | error_free_dev: |
7cbb7537 | 624 | iio_device_free(indio_dev); |
54c5be34 BS |
625 | error_ret: |
626 | return ret; | |
627 | } | |
628 | ||
629 | static int __devexit ad7150_remove(struct i2c_client *client) | |
630 | { | |
46a6af38 | 631 | struct iio_dev *indio_dev = i2c_get_clientdata(client); |
54c5be34 | 632 | |
d2fffd6c | 633 | iio_device_unregister(indio_dev); |
9b5d9b06 JC |
634 | if (client->irq) |
635 | free_irq(client->irq, indio_dev); | |
4e687ddb MH |
636 | |
637 | if (client->dev.platform_data) | |
638 | free_irq(*(unsigned int *)client->dev.platform_data, indio_dev); | |
639 | ||
7cbb7537 | 640 | iio_device_free(indio_dev); |
54c5be34 BS |
641 | |
642 | return 0; | |
643 | } | |
644 | ||
645 | static const struct i2c_device_id ad7150_id[] = { | |
646 | { "ad7150", 0 }, | |
647 | { "ad7151", 0 }, | |
648 | { "ad7156", 0 }, | |
649 | {} | |
650 | }; | |
651 | ||
652 | MODULE_DEVICE_TABLE(i2c, ad7150_id); | |
653 | ||
654 | static struct i2c_driver ad7150_driver = { | |
655 | .driver = { | |
656 | .name = "ad7150", | |
657 | }, | |
658 | .probe = ad7150_probe, | |
659 | .remove = __devexit_p(ad7150_remove), | |
660 | .id_table = ad7150_id, | |
661 | }; | |
6e5af184 | 662 | module_i2c_driver(ad7150_driver); |
54c5be34 BS |
663 | |
664 | MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); | |
4e687ddb | 665 | MODULE_DESCRIPTION("Analog Devices AD7150/1/6 capacitive sensor driver"); |
54c5be34 | 666 | MODULE_LICENSE("GPL v2"); |