]>
Commit | Line | Data |
---|---|---|
66533b48 JC |
1 | /* |
2 | * lis3l02dq.c support STMicroelectronics LISD02DQ | |
3 | * 3d 2g Linear Accelerometers via SPI | |
4 | * | |
5 | * Copyright (c) 2007 Jonathan Cameron <jic23@cam.ac.uk> | |
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 | * Settings: | |
12 | * 16 bit left justified mode used. | |
13 | */ | |
14 | ||
15 | #include <linux/interrupt.h> | |
16 | #include <linux/irq.h> | |
17 | #include <linux/gpio.h> | |
18 | #include <linux/workqueue.h> | |
19 | #include <linux/mutex.h> | |
20 | #include <linux/device.h> | |
21 | #include <linux/kernel.h> | |
22 | #include <linux/spi/spi.h> | |
5a0e3ad6 | 23 | #include <linux/slab.h> |
66533b48 JC |
24 | |
25 | #include <linux/sysfs.h> | |
26 | #include <linux/list.h> | |
27 | ||
28 | #include "../iio.h" | |
29 | #include "../sysfs.h" | |
30 | #include "accel.h" | |
31 | ||
32 | #include "lis3l02dq.h" | |
33 | ||
34 | /* At the moment the spi framework doesn't allow global setting of cs_change. | |
35 | * It's in the likely to be added comment at the top of spi.h. | |
36 | * This means that use cannot be made of spi_write etc. | |
37 | */ | |
38 | ||
39 | /** | |
40 | * lis3l02dq_spi_read_reg_8() - read single byte from a single register | |
41 | * @dev: device asosciated with child of actual device (iio_dev or iio_trig) | |
42 | * @reg_address: the address of the register to be read | |
43 | * @val: pass back the resulting value | |
44 | **/ | |
45 | int lis3l02dq_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val) | |
46 | { | |
47 | int ret; | |
48 | struct spi_message msg; | |
49 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
50 | struct lis3l02dq_state *st = iio_dev_get_devdata(indio_dev); | |
51 | struct spi_transfer xfer = { | |
52 | .tx_buf = st->tx, | |
53 | .rx_buf = st->rx, | |
54 | .bits_per_word = 8, | |
55 | .len = 2, | |
56 | .cs_change = 1, | |
57 | }; | |
58 | ||
59 | mutex_lock(&st->buf_lock); | |
60 | st->tx[0] = LIS3L02DQ_READ_REG(reg_address); | |
61 | st->tx[1] = 0; | |
62 | ||
63 | spi_message_init(&msg); | |
64 | spi_message_add_tail(&xfer, &msg); | |
65 | ret = spi_sync(st->us, &msg); | |
66 | *val = st->rx[1]; | |
67 | mutex_unlock(&st->buf_lock); | |
68 | ||
69 | return ret; | |
70 | } | |
71 | ||
72 | /** | |
73 | * lis3l02dq_spi_write_reg_8() - write single byte to a register | |
74 | * @dev: device associated with child of actual device (iio_dev or iio_trig) | |
75 | * @reg_address: the address of the register to be writen | |
76 | * @val: the value to write | |
77 | **/ | |
78 | int lis3l02dq_spi_write_reg_8(struct device *dev, | |
79 | u8 reg_address, | |
80 | u8 *val) | |
81 | { | |
82 | int ret; | |
83 | struct spi_message msg; | |
84 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
85 | struct lis3l02dq_state *st = iio_dev_get_devdata(indio_dev); | |
86 | struct spi_transfer xfer = { | |
87 | .tx_buf = st->tx, | |
88 | .bits_per_word = 8, | |
89 | .len = 2, | |
90 | .cs_change = 1, | |
91 | }; | |
92 | ||
93 | mutex_lock(&st->buf_lock); | |
94 | st->tx[0] = LIS3L02DQ_WRITE_REG(reg_address); | |
95 | st->tx[1] = *val; | |
96 | ||
97 | spi_message_init(&msg); | |
98 | spi_message_add_tail(&xfer, &msg); | |
99 | ret = spi_sync(st->us, &msg); | |
100 | mutex_unlock(&st->buf_lock); | |
101 | ||
102 | return ret; | |
103 | } | |
104 | ||
105 | /** | |
106 | * lisl302dq_spi_write_reg_s16() - write 2 bytes to a pair of registers | |
107 | * @dev: device associated with child of actual device (iio_dev or iio_trig) | |
108 | * @reg_address: the address of the lower of the two registers. Second register | |
109 | * is assumed to have address one greater. | |
110 | * @val: value to be written | |
111 | **/ | |
112 | static int lis3l02dq_spi_write_reg_s16(struct device *dev, | |
113 | u8 lower_reg_address, | |
114 | s16 value) | |
115 | { | |
116 | int ret; | |
117 | struct spi_message msg; | |
118 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
119 | struct lis3l02dq_state *st = iio_dev_get_devdata(indio_dev); | |
120 | struct spi_transfer xfers[] = { { | |
121 | .tx_buf = st->tx, | |
122 | .bits_per_word = 8, | |
123 | .len = 2, | |
124 | .cs_change = 1, | |
125 | }, { | |
126 | .tx_buf = st->tx + 2, | |
127 | .bits_per_word = 8, | |
128 | .len = 2, | |
129 | .cs_change = 1, | |
130 | }, | |
131 | }; | |
132 | ||
133 | mutex_lock(&st->buf_lock); | |
134 | st->tx[0] = LIS3L02DQ_WRITE_REG(lower_reg_address); | |
135 | st->tx[1] = value & 0xFF; | |
136 | st->tx[2] = LIS3L02DQ_WRITE_REG(lower_reg_address + 1); | |
137 | st->tx[3] = (value >> 8) & 0xFF; | |
138 | ||
139 | spi_message_init(&msg); | |
140 | spi_message_add_tail(&xfers[0], &msg); | |
141 | spi_message_add_tail(&xfers[1], &msg); | |
142 | ret = spi_sync(st->us, &msg); | |
143 | mutex_unlock(&st->buf_lock); | |
144 | ||
145 | return ret; | |
146 | } | |
147 | ||
148 | /** | |
149 | * lisl302dq_spi_read_reg_s16() - write 2 bytes to a pair of registers | |
150 | * @dev: device associated with child of actual device (iio_dev or iio_trig) | |
151 | * @reg_address: the address of the lower of the two registers. Second register | |
152 | * is assumed to have address one greater. | |
153 | * @val: somewhere to pass back the value read | |
154 | **/ | |
155 | static int lis3l02dq_spi_read_reg_s16(struct device *dev, | |
156 | u8 lower_reg_address, | |
157 | s16 *val) | |
158 | { | |
159 | struct spi_message msg; | |
160 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
161 | struct lis3l02dq_state *st = iio_dev_get_devdata(indio_dev); | |
162 | int ret; | |
163 | struct spi_transfer xfers[] = { { | |
164 | .tx_buf = st->tx, | |
165 | .rx_buf = st->rx, | |
166 | .bits_per_word = 8, | |
167 | .len = 2, | |
168 | .cs_change = 1, | |
169 | }, { | |
170 | .tx_buf = st->tx + 2, | |
171 | .rx_buf = st->rx + 2, | |
172 | .bits_per_word = 8, | |
173 | .len = 2, | |
174 | .cs_change = 1, | |
175 | ||
176 | }, | |
177 | }; | |
178 | ||
179 | mutex_lock(&st->buf_lock); | |
180 | st->tx[0] = LIS3L02DQ_READ_REG(lower_reg_address); | |
181 | st->tx[1] = 0; | |
182 | st->tx[2] = LIS3L02DQ_READ_REG(lower_reg_address+1); | |
183 | st->tx[3] = 0; | |
184 | ||
185 | spi_message_init(&msg); | |
186 | spi_message_add_tail(&xfers[0], &msg); | |
187 | spi_message_add_tail(&xfers[1], &msg); | |
188 | ret = spi_sync(st->us, &msg); | |
189 | if (ret) { | |
190 | dev_err(&st->us->dev, "problem when reading 16 bit register"); | |
191 | goto error_ret; | |
192 | } | |
193 | *val = (s16)(st->rx[1]) | ((s16)(st->rx[3]) << 8); | |
194 | ||
195 | error_ret: | |
196 | mutex_unlock(&st->buf_lock); | |
197 | return ret; | |
198 | } | |
199 | ||
200 | /** | |
201 | * lis3l02dq_read_signed() - attribute function used for 8 bit signed values | |
202 | * @dev: the child device associated with the iio_dev or iio_trigger | |
203 | * @attr: the attribute being processed | |
204 | * @buf: buffer into which put the output string | |
205 | **/ | |
206 | static ssize_t lis3l02dq_read_signed(struct device *dev, | |
207 | struct device_attribute *attr, | |
208 | char *buf) | |
209 | { | |
210 | int ret; | |
211 | s8 val; | |
212 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | |
213 | ||
214 | ret = lis3l02dq_spi_read_reg_8(dev, this_attr->address, (u8 *)&val); | |
215 | ||
216 | return ret ? ret : sprintf(buf, "%d\n", val); | |
217 | } | |
218 | ||
219 | static ssize_t lis3l02dq_read_unsigned(struct device *dev, | |
220 | struct device_attribute *attr, | |
221 | char *buf) | |
222 | { | |
223 | int ret; | |
224 | u8 val; | |
225 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | |
226 | ||
227 | ret = lis3l02dq_spi_read_reg_8(dev, this_attr->address, &val); | |
228 | ||
229 | return ret ? ret : sprintf(buf, "%d\n", val); | |
230 | } | |
231 | ||
232 | static ssize_t lis3l02dq_write_signed(struct device *dev, | |
233 | struct device_attribute *attr, | |
234 | const char *buf, | |
235 | size_t len) | |
236 | { | |
237 | long valin; | |
238 | s8 val; | |
239 | int ret; | |
240 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | |
241 | ||
242 | ret = strict_strtol(buf, 10, &valin); | |
243 | if (ret) | |
244 | goto error_ret; | |
245 | val = valin; | |
246 | ret = lis3l02dq_spi_write_reg_8(dev, this_attr->address, (u8 *)&val); | |
247 | ||
248 | error_ret: | |
249 | return ret ? ret : len; | |
250 | } | |
251 | ||
252 | static ssize_t lis3l02dq_write_unsigned(struct device *dev, | |
253 | struct device_attribute *attr, | |
254 | const char *buf, | |
255 | size_t len) | |
256 | { | |
257 | int ret; | |
258 | ulong valin; | |
259 | u8 val; | |
260 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | |
261 | ||
262 | ret = strict_strtoul(buf, 10, &valin); | |
263 | if (ret) | |
264 | goto err_ret; | |
265 | val = valin; | |
266 | ret = lis3l02dq_spi_write_reg_8(dev, this_attr->address, &val); | |
267 | ||
268 | err_ret: | |
269 | return ret ? ret : len; | |
270 | } | |
271 | ||
272 | static ssize_t lis3l02dq_read_16bit_signed(struct device *dev, | |
273 | struct device_attribute *attr, | |
274 | char *buf) | |
275 | { | |
276 | int ret; | |
277 | s16 val = 0; | |
278 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | |
279 | ||
280 | ret = lis3l02dq_spi_read_reg_s16(dev, this_attr->address, &val); | |
281 | ||
282 | if (ret) | |
283 | return ret; | |
284 | ||
285 | return sprintf(buf, "%d\n", val); | |
286 | } | |
287 | ||
288 | static ssize_t lis3l02dq_read_accel(struct device *dev, | |
289 | struct device_attribute *attr, | |
290 | char *buf) | |
291 | { | |
292 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
293 | ssize_t ret; | |
294 | ||
295 | /* Take the iio_dev status lock */ | |
296 | mutex_lock(&indio_dev->mlock); | |
297 | if (indio_dev->currentmode == INDIO_RING_TRIGGERED) | |
298 | ret = lis3l02dq_read_accel_from_ring(dev, attr, buf); | |
299 | else | |
300 | ret = lis3l02dq_read_16bit_signed(dev, attr, buf); | |
301 | mutex_unlock(&indio_dev->mlock); | |
302 | ||
303 | return ret; | |
304 | } | |
305 | ||
306 | static ssize_t lis3l02dq_write_16bit_signed(struct device *dev, | |
307 | struct device_attribute *attr, | |
308 | const char *buf, | |
309 | size_t len) | |
310 | { | |
311 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | |
312 | int ret; | |
313 | long val; | |
314 | ||
315 | ret = strict_strtol(buf, 10, &val); | |
316 | if (ret) | |
317 | goto error_ret; | |
318 | ret = lis3l02dq_spi_write_reg_s16(dev, this_attr->address, val); | |
319 | ||
320 | error_ret: | |
321 | return ret ? ret : len; | |
322 | } | |
323 | ||
324 | static ssize_t lis3l02dq_read_frequency(struct device *dev, | |
325 | struct device_attribute *attr, | |
326 | char *buf) | |
327 | { | |
328 | int ret, len = 0; | |
329 | s8 t; | |
330 | ret = lis3l02dq_spi_read_reg_8(dev, | |
331 | LIS3L02DQ_REG_CTRL_1_ADDR, | |
332 | (u8 *)&t); | |
333 | if (ret) | |
334 | return ret; | |
335 | t &= LIS3L02DQ_DEC_MASK; | |
336 | switch (t) { | |
337 | case LIS3L02DQ_REG_CTRL_1_DF_128: | |
338 | len = sprintf(buf, "280\n"); | |
339 | break; | |
340 | case LIS3L02DQ_REG_CTRL_1_DF_64: | |
341 | len = sprintf(buf, "560\n"); | |
342 | break; | |
343 | case LIS3L02DQ_REG_CTRL_1_DF_32: | |
344 | len = sprintf(buf, "1120\n"); | |
345 | break; | |
346 | case LIS3L02DQ_REG_CTRL_1_DF_8: | |
347 | len = sprintf(buf, "4480\n"); | |
348 | break; | |
349 | } | |
350 | return len; | |
351 | } | |
352 | ||
353 | static ssize_t lis3l02dq_write_frequency(struct device *dev, | |
354 | struct device_attribute *attr, | |
355 | const char *buf, | |
356 | size_t len) | |
357 | { | |
358 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
359 | long val; | |
360 | int ret; | |
361 | u8 t; | |
362 | ||
363 | ret = strict_strtol(buf, 10, &val); | |
364 | if (ret) | |
365 | return ret; | |
366 | ||
367 | mutex_lock(&indio_dev->mlock); | |
368 | ret = lis3l02dq_spi_read_reg_8(dev, | |
369 | LIS3L02DQ_REG_CTRL_1_ADDR, | |
370 | &t); | |
371 | if (ret) | |
372 | goto error_ret_mutex; | |
373 | /* Wipe the bits clean */ | |
374 | t &= ~LIS3L02DQ_DEC_MASK; | |
375 | switch (val) { | |
376 | case 280: | |
377 | t |= LIS3L02DQ_REG_CTRL_1_DF_128; | |
378 | break; | |
379 | case 560: | |
380 | t |= LIS3L02DQ_REG_CTRL_1_DF_64; | |
381 | break; | |
382 | case 1120: | |
383 | t |= LIS3L02DQ_REG_CTRL_1_DF_32; | |
384 | break; | |
385 | case 4480: | |
386 | t |= LIS3L02DQ_REG_CTRL_1_DF_8; | |
387 | break; | |
388 | default: | |
389 | ret = -EINVAL; | |
390 | goto error_ret_mutex; | |
391 | }; | |
392 | ||
393 | ret = lis3l02dq_spi_write_reg_8(dev, | |
394 | LIS3L02DQ_REG_CTRL_1_ADDR, | |
395 | &t); | |
396 | ||
397 | error_ret_mutex: | |
398 | mutex_unlock(&indio_dev->mlock); | |
399 | ||
400 | return ret ? ret : len; | |
401 | } | |
402 | ||
403 | static int lis3l02dq_initial_setup(struct lis3l02dq_state *st) | |
404 | { | |
405 | int ret; | |
406 | u8 val, valtest; | |
407 | ||
408 | st->us->mode = SPI_MODE_3; | |
409 | ||
410 | spi_setup(st->us); | |
411 | ||
412 | val = LIS3L02DQ_DEFAULT_CTRL1; | |
413 | /* Write suitable defaults to ctrl1 */ | |
414 | ret = lis3l02dq_spi_write_reg_8(&st->indio_dev->dev, | |
415 | LIS3L02DQ_REG_CTRL_1_ADDR, | |
416 | &val); | |
417 | if (ret) { | |
418 | dev_err(&st->us->dev, "problem with setup control register 1"); | |
419 | goto err_ret; | |
420 | } | |
421 | /* Repeat as sometimes doesn't work first time?*/ | |
422 | ret = lis3l02dq_spi_write_reg_8(&st->indio_dev->dev, | |
423 | LIS3L02DQ_REG_CTRL_1_ADDR, | |
424 | &val); | |
425 | if (ret) { | |
426 | dev_err(&st->us->dev, "problem with setup control register 1"); | |
427 | goto err_ret; | |
428 | } | |
429 | ||
430 | /* Read back to check this has worked acts as loose test of correct | |
431 | * chip */ | |
432 | ret = lis3l02dq_spi_read_reg_8(&st->indio_dev->dev, | |
433 | LIS3L02DQ_REG_CTRL_1_ADDR, | |
434 | &valtest); | |
435 | if (ret || (valtest != val)) { | |
436 | dev_err(&st->indio_dev->dev, "device not playing ball"); | |
437 | ret = -EINVAL; | |
438 | goto err_ret; | |
439 | } | |
440 | ||
441 | val = LIS3L02DQ_DEFAULT_CTRL2; | |
442 | ret = lis3l02dq_spi_write_reg_8(&st->indio_dev->dev, | |
443 | LIS3L02DQ_REG_CTRL_2_ADDR, | |
444 | &val); | |
445 | if (ret) { | |
446 | dev_err(&st->us->dev, "problem with setup control register 2"); | |
447 | goto err_ret; | |
448 | } | |
449 | ||
450 | val = LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC; | |
451 | ret = lis3l02dq_spi_write_reg_8(&st->indio_dev->dev, | |
452 | LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, | |
453 | &val); | |
454 | if (ret) | |
455 | dev_err(&st->us->dev, "problem with interrupt cfg register"); | |
456 | err_ret: | |
457 | ||
458 | return ret; | |
459 | } | |
460 | ||
461 | static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO, | |
462 | lis3l02dq_read_signed, | |
463 | lis3l02dq_write_signed, | |
464 | LIS3L02DQ_REG_OFFSET_X_ADDR); | |
465 | ||
466 | static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO, | |
467 | lis3l02dq_read_signed, | |
468 | lis3l02dq_write_signed, | |
469 | LIS3L02DQ_REG_OFFSET_Y_ADDR); | |
470 | ||
471 | static IIO_DEV_ATTR_ACCEL_Z_OFFSET(S_IWUSR | S_IRUGO, | |
472 | lis3l02dq_read_signed, | |
473 | lis3l02dq_write_signed, | |
474 | LIS3L02DQ_REG_OFFSET_Z_ADDR); | |
475 | ||
476 | static IIO_DEV_ATTR_ACCEL_X_GAIN(S_IWUSR | S_IRUGO, | |
477 | lis3l02dq_read_unsigned, | |
478 | lis3l02dq_write_unsigned, | |
479 | LIS3L02DQ_REG_GAIN_X_ADDR); | |
480 | ||
481 | static IIO_DEV_ATTR_ACCEL_Y_GAIN(S_IWUSR | S_IRUGO, | |
482 | lis3l02dq_read_unsigned, | |
483 | lis3l02dq_write_unsigned, | |
484 | LIS3L02DQ_REG_GAIN_Y_ADDR); | |
485 | ||
486 | static IIO_DEV_ATTR_ACCEL_Z_GAIN(S_IWUSR | S_IRUGO, | |
487 | lis3l02dq_read_unsigned, | |
488 | lis3l02dq_write_unsigned, | |
489 | LIS3L02DQ_REG_GAIN_Z_ADDR); | |
490 | ||
491 | static IIO_DEV_ATTR_ACCEL_THRESH(S_IWUSR | S_IRUGO, | |
492 | lis3l02dq_read_16bit_signed, | |
493 | lis3l02dq_write_16bit_signed, | |
494 | LIS3L02DQ_REG_THS_L_ADDR); | |
495 | ||
496 | /* RFC The reading method for these will change depending on whether | |
497 | * ring buffer capture is in use. Is it worth making these take two | |
498 | * functions and let the core handle which to call, or leave as in this | |
499 | * driver where it is the drivers problem to manage this? | |
500 | */ | |
501 | ||
502 | static IIO_DEV_ATTR_ACCEL_X(lis3l02dq_read_accel, | |
503 | LIS3L02DQ_REG_OUT_X_L_ADDR); | |
504 | ||
505 | static IIO_DEV_ATTR_ACCEL_Y(lis3l02dq_read_accel, | |
506 | LIS3L02DQ_REG_OUT_Y_L_ADDR); | |
507 | ||
508 | static IIO_DEV_ATTR_ACCEL_Z(lis3l02dq_read_accel, | |
509 | LIS3L02DQ_REG_OUT_Z_L_ADDR); | |
510 | ||
511 | static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, | |
512 | lis3l02dq_read_frequency, | |
513 | lis3l02dq_write_frequency); | |
514 | ||
515 | static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("280 560 1120 4480"); | |
516 | ||
517 | static ssize_t lis3l02dq_read_interrupt_config(struct device *dev, | |
518 | struct device_attribute *attr, | |
519 | char *buf) | |
520 | { | |
521 | int ret; | |
522 | s8 val; | |
523 | struct iio_event_attr *this_attr = to_iio_event_attr(attr); | |
524 | ||
525 | ret = lis3l02dq_spi_read_reg_8(dev, | |
526 | LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, | |
527 | (u8 *)&val); | |
528 | ||
529 | return ret ? ret : sprintf(buf, "%d\n", | |
530 | (val & this_attr->mask) ? 1 : 0);; | |
531 | } | |
532 | ||
533 | static ssize_t lis3l02dq_write_interrupt_config(struct device *dev, | |
534 | struct device_attribute *attr, | |
535 | const char *buf, | |
536 | size_t len) | |
537 | { | |
538 | struct iio_event_attr *this_attr = to_iio_event_attr(attr); | |
539 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
540 | int ret, currentlyset, changed = 0; | |
541 | u8 valold, controlold; | |
542 | bool val; | |
543 | ||
544 | val = !(buf[0] == '0'); | |
545 | ||
546 | mutex_lock(&indio_dev->mlock); | |
547 | /* read current value */ | |
548 | ret = lis3l02dq_spi_read_reg_8(dev, | |
549 | LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, | |
550 | &valold); | |
551 | if (ret) | |
552 | goto error_mutex_unlock; | |
553 | ||
554 | /* read current control */ | |
555 | ret = lis3l02dq_spi_read_reg_8(dev, | |
556 | LIS3L02DQ_REG_CTRL_2_ADDR, | |
557 | &controlold); | |
558 | if (ret) | |
559 | goto error_mutex_unlock; | |
560 | currentlyset = !!(valold & this_attr->mask); | |
561 | if (val == false && currentlyset) { | |
562 | valold &= ~this_attr->mask; | |
563 | changed = 1; | |
564 | iio_remove_event_from_list(this_attr->listel, | |
565 | &indio_dev->interrupts[0] | |
566 | ->ev_list); | |
567 | } else if (val == true && !currentlyset) { | |
568 | changed = 1; | |
569 | valold |= this_attr->mask; | |
570 | iio_add_event_to_list(this_attr->listel, | |
571 | &indio_dev->interrupts[0]->ev_list); | |
572 | } | |
573 | ||
574 | if (changed) { | |
575 | ret = lis3l02dq_spi_write_reg_8(dev, | |
576 | LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, | |
577 | &valold); | |
578 | if (ret) | |
579 | goto error_mutex_unlock; | |
580 | /* This always enables the interrupt, even if we've remove the | |
581 | * last thing using it. For this device we can use the reference | |
582 | * count on the handler to tell us if anyone wants the interrupt | |
583 | */ | |
584 | controlold = this_attr->listel->refcount ? | |
585 | (controlold | LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT) : | |
586 | (controlold & ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT); | |
587 | ret = lis3l02dq_spi_write_reg_8(dev, | |
588 | LIS3L02DQ_REG_CTRL_2_ADDR, | |
589 | &controlold); | |
590 | if (ret) | |
591 | goto error_mutex_unlock; | |
592 | } | |
593 | error_mutex_unlock: | |
594 | mutex_unlock(&indio_dev->mlock); | |
595 | ||
596 | return ret ? ret : len; | |
597 | } | |
598 | ||
599 | ||
600 | static int lis3l02dq_thresh_handler_th(struct iio_dev *dev_info, | |
601 | int index, | |
602 | s64 timestamp, | |
603 | int no_test) | |
604 | { | |
605 | struct lis3l02dq_state *st = dev_info->dev_data; | |
606 | ||
607 | /* Stash the timestamp somewhere convenient for the bh */ | |
608 | st->last_timestamp = timestamp; | |
609 | schedule_work(&st->work_cont_thresh.ws); | |
610 | ||
611 | return 0; | |
612 | } | |
613 | ||
614 | ||
615 | /* Unforunately it appears the interrupt won't clear unless you read from the | |
616 | * src register. | |
617 | */ | |
618 | static void lis3l02dq_thresh_handler_bh_no_check(struct work_struct *work_s) | |
619 | { | |
620 | struct iio_work_cont *wc | |
621 | = container_of(work_s, struct iio_work_cont, ws_nocheck); | |
622 | struct lis3l02dq_state *st = wc->st; | |
623 | u8 t; | |
624 | ||
625 | lis3l02dq_spi_read_reg_8(&st->indio_dev->dev, | |
626 | LIS3L02DQ_REG_WAKE_UP_SRC_ADDR, | |
627 | &t); | |
628 | ||
629 | if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH) | |
630 | iio_push_event(st->indio_dev, 0, | |
631 | IIO_EVENT_CODE_ACCEL_Z_HIGH, | |
632 | st->last_timestamp); | |
633 | ||
634 | if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW) | |
635 | iio_push_event(st->indio_dev, 0, | |
636 | IIO_EVENT_CODE_ACCEL_Z_LOW, | |
637 | st->last_timestamp); | |
638 | ||
639 | if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH) | |
640 | iio_push_event(st->indio_dev, 0, | |
641 | IIO_EVENT_CODE_ACCEL_Y_HIGH, | |
642 | st->last_timestamp); | |
643 | ||
644 | if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW) | |
645 | iio_push_event(st->indio_dev, 0, | |
646 | IIO_EVENT_CODE_ACCEL_Y_LOW, | |
647 | st->last_timestamp); | |
648 | ||
649 | if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH) | |
650 | iio_push_event(st->indio_dev, 0, | |
651 | IIO_EVENT_CODE_ACCEL_X_HIGH, | |
652 | st->last_timestamp); | |
653 | ||
654 | if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW) | |
655 | iio_push_event(st->indio_dev, 0, | |
656 | IIO_EVENT_CODE_ACCEL_X_LOW, | |
657 | st->last_timestamp); | |
658 | /* reenable the irq */ | |
659 | enable_irq(st->us->irq); | |
660 | /* Ack and allow for new interrupts */ | |
661 | lis3l02dq_spi_read_reg_8(&st->indio_dev->dev, | |
662 | LIS3L02DQ_REG_WAKE_UP_ACK_ADDR, | |
663 | &t); | |
664 | ||
665 | return; | |
666 | } | |
667 | ||
668 | /* A shared handler for a number of threshold types */ | |
669 | IIO_EVENT_SH(threshold, &lis3l02dq_thresh_handler_th); | |
670 | ||
671 | IIO_EVENT_ATTR_ACCEL_X_HIGH_SH(iio_event_threshold, | |
672 | lis3l02dq_read_interrupt_config, | |
673 | lis3l02dq_write_interrupt_config, | |
674 | LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_HIGH); | |
675 | ||
676 | IIO_EVENT_ATTR_ACCEL_Y_HIGH_SH(iio_event_threshold, | |
677 | lis3l02dq_read_interrupt_config, | |
678 | lis3l02dq_write_interrupt_config, | |
679 | LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_HIGH); | |
680 | ||
681 | IIO_EVENT_ATTR_ACCEL_Z_HIGH_SH(iio_event_threshold, | |
682 | lis3l02dq_read_interrupt_config, | |
683 | lis3l02dq_write_interrupt_config, | |
684 | LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH); | |
685 | ||
686 | IIO_EVENT_ATTR_ACCEL_X_LOW_SH(iio_event_threshold, | |
687 | lis3l02dq_read_interrupt_config, | |
688 | lis3l02dq_write_interrupt_config, | |
689 | LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_LOW); | |
690 | ||
691 | IIO_EVENT_ATTR_ACCEL_Y_LOW_SH(iio_event_threshold, | |
692 | lis3l02dq_read_interrupt_config, | |
693 | lis3l02dq_write_interrupt_config, | |
694 | LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_LOW); | |
695 | ||
696 | IIO_EVENT_ATTR_ACCEL_Z_LOW_SH(iio_event_threshold, | |
697 | lis3l02dq_read_interrupt_config, | |
698 | lis3l02dq_write_interrupt_config, | |
699 | LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW); | |
700 | ||
701 | static struct attribute *lis3l02dq_event_attributes[] = { | |
702 | &iio_event_attr_accel_x_high.dev_attr.attr, | |
703 | &iio_event_attr_accel_y_high.dev_attr.attr, | |
704 | &iio_event_attr_accel_z_high.dev_attr.attr, | |
705 | &iio_event_attr_accel_x_low.dev_attr.attr, | |
706 | &iio_event_attr_accel_y_low.dev_attr.attr, | |
707 | &iio_event_attr_accel_z_low.dev_attr.attr, | |
708 | NULL | |
709 | }; | |
710 | ||
711 | static struct attribute_group lis3l02dq_event_attribute_group = { | |
712 | .attrs = lis3l02dq_event_attributes, | |
713 | }; | |
714 | ||
715 | static IIO_CONST_ATTR(name, "lis3l02dq"); | |
716 | ||
717 | static struct attribute *lis3l02dq_attributes[] = { | |
718 | &iio_dev_attr_accel_x_offset.dev_attr.attr, | |
719 | &iio_dev_attr_accel_y_offset.dev_attr.attr, | |
720 | &iio_dev_attr_accel_z_offset.dev_attr.attr, | |
721 | &iio_dev_attr_accel_x_gain.dev_attr.attr, | |
722 | &iio_dev_attr_accel_y_gain.dev_attr.attr, | |
723 | &iio_dev_attr_accel_z_gain.dev_attr.attr, | |
724 | &iio_dev_attr_thresh.dev_attr.attr, | |
725 | &iio_dev_attr_accel_x.dev_attr.attr, | |
726 | &iio_dev_attr_accel_y.dev_attr.attr, | |
727 | &iio_dev_attr_accel_z.dev_attr.attr, | |
728 | &iio_dev_attr_sampling_frequency.dev_attr.attr, | |
729 | &iio_const_attr_available_sampling_frequency.dev_attr.attr, | |
730 | &iio_const_attr_name.dev_attr.attr, | |
731 | NULL | |
732 | }; | |
733 | ||
734 | static const struct attribute_group lis3l02dq_attribute_group = { | |
735 | .attrs = lis3l02dq_attributes, | |
736 | }; | |
737 | ||
738 | static int __devinit lis3l02dq_probe(struct spi_device *spi) | |
739 | { | |
740 | int ret, regdone = 0; | |
741 | struct lis3l02dq_state *st = kzalloc(sizeof *st, GFP_KERNEL); | |
742 | if (!st) { | |
743 | ret = -ENOMEM; | |
744 | goto error_ret; | |
745 | } | |
746 | /* this is only used tor removal purposes */ | |
747 | spi_set_drvdata(spi, st); | |
748 | ||
749 | /* Allocate the comms buffers */ | |
750 | st->rx = kzalloc(sizeof(*st->rx)*LIS3L02DQ_MAX_RX, GFP_KERNEL); | |
751 | if (st->rx == NULL) { | |
752 | ret = -ENOMEM; | |
753 | goto error_free_st; | |
754 | } | |
755 | st->tx = kzalloc(sizeof(*st->tx)*LIS3L02DQ_MAX_TX, GFP_KERNEL); | |
756 | if (st->tx == NULL) { | |
757 | ret = -ENOMEM; | |
758 | goto error_free_rx; | |
759 | } | |
760 | st->us = spi; | |
761 | mutex_init(&st->buf_lock); | |
762 | /* setup the industrialio driver allocated elements */ | |
763 | st->indio_dev = iio_allocate_device(); | |
764 | if (st->indio_dev == NULL) { | |
765 | ret = -ENOMEM; | |
766 | goto error_free_tx; | |
767 | } | |
768 | ||
769 | st->indio_dev->dev.parent = &spi->dev; | |
770 | st->indio_dev->num_interrupt_lines = 1; | |
771 | st->indio_dev->event_attrs = &lis3l02dq_event_attribute_group; | |
772 | st->indio_dev->attrs = &lis3l02dq_attribute_group; | |
773 | st->indio_dev->dev_data = (void *)(st); | |
774 | st->indio_dev->driver_module = THIS_MODULE; | |
775 | st->indio_dev->modes = INDIO_DIRECT_MODE; | |
776 | ||
777 | ret = lis3l02dq_configure_ring(st->indio_dev); | |
778 | if (ret) | |
779 | goto error_free_dev; | |
780 | ||
781 | ret = iio_device_register(st->indio_dev); | |
782 | if (ret) | |
783 | goto error_unreg_ring_funcs; | |
784 | regdone = 1; | |
785 | ||
786 | ret = lis3l02dq_initialize_ring(st->indio_dev->ring); | |
787 | if (ret) { | |
788 | printk(KERN_ERR "failed to initialize the ring\n"); | |
789 | goto error_unreg_ring_funcs; | |
790 | } | |
791 | ||
792 | if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) { | |
793 | /* This is a little unusual, in that the device seems | |
794 | to need a full read of the interrupt source reg before | |
795 | the interrupt will reset. | |
796 | Hence the two handlers are the same */ | |
797 | iio_init_work_cont(&st->work_cont_thresh, | |
798 | lis3l02dq_thresh_handler_bh_no_check, | |
799 | lis3l02dq_thresh_handler_bh_no_check, | |
800 | LIS3L02DQ_REG_WAKE_UP_SRC_ADDR, | |
801 | 0, | |
802 | st); | |
803 | st->inter = 0; | |
804 | ret = iio_register_interrupt_line(spi->irq, | |
805 | st->indio_dev, | |
806 | 0, | |
807 | IRQF_TRIGGER_RISING, | |
808 | "lis3l02dq"); | |
809 | if (ret) | |
810 | goto error_uninitialize_ring; | |
811 | ||
812 | ret = lis3l02dq_probe_trigger(st->indio_dev); | |
813 | if (ret) | |
814 | goto error_unregister_line; | |
815 | } | |
816 | ||
817 | /* Get the device into a sane initial state */ | |
818 | ret = lis3l02dq_initial_setup(st); | |
819 | if (ret) | |
820 | goto error_remove_trigger; | |
821 | return 0; | |
822 | ||
823 | error_remove_trigger: | |
824 | if (st->indio_dev->modes & INDIO_RING_TRIGGERED) | |
825 | lis3l02dq_remove_trigger(st->indio_dev); | |
826 | error_unregister_line: | |
827 | if (st->indio_dev->modes & INDIO_RING_TRIGGERED) | |
828 | iio_unregister_interrupt_line(st->indio_dev, 0); | |
829 | error_uninitialize_ring: | |
830 | lis3l02dq_uninitialize_ring(st->indio_dev->ring); | |
831 | error_unreg_ring_funcs: | |
832 | lis3l02dq_unconfigure_ring(st->indio_dev); | |
833 | error_free_dev: | |
834 | if (regdone) | |
835 | iio_device_unregister(st->indio_dev); | |
836 | else | |
837 | iio_free_device(st->indio_dev); | |
838 | error_free_tx: | |
839 | kfree(st->tx); | |
840 | error_free_rx: | |
841 | kfree(st->rx); | |
842 | error_free_st: | |
843 | kfree(st); | |
844 | error_ret: | |
845 | return ret; | |
846 | } | |
847 | ||
848 | /* Power down the device */ | |
849 | static int lis3l02dq_stop_device(struct iio_dev *indio_dev) | |
850 | { | |
851 | int ret; | |
852 | struct lis3l02dq_state *st = indio_dev->dev_data; | |
853 | u8 val = 0; | |
854 | ||
855 | mutex_lock(&indio_dev->mlock); | |
856 | ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev, | |
857 | LIS3L02DQ_REG_CTRL_1_ADDR, | |
858 | &val); | |
859 | if (ret) { | |
860 | dev_err(&st->us->dev, "problem with turning device off: ctrl1"); | |
861 | goto err_ret; | |
862 | } | |
863 | ||
864 | ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev, | |
865 | LIS3L02DQ_REG_CTRL_2_ADDR, | |
866 | &val); | |
867 | if (ret) | |
868 | dev_err(&st->us->dev, "problem with turning device off: ctrl2"); | |
869 | err_ret: | |
870 | mutex_unlock(&indio_dev->mlock); | |
871 | return ret; | |
872 | } | |
873 | ||
874 | /* fixme, confirm ordering in this function */ | |
875 | static int lis3l02dq_remove(struct spi_device *spi) | |
876 | { | |
877 | int ret; | |
878 | struct lis3l02dq_state *st = spi_get_drvdata(spi); | |
879 | struct iio_dev *indio_dev = st->indio_dev; | |
880 | ||
881 | ret = lis3l02dq_stop_device(indio_dev); | |
882 | if (ret) | |
883 | goto err_ret; | |
884 | ||
885 | flush_scheduled_work(); | |
886 | ||
887 | lis3l02dq_remove_trigger(indio_dev); | |
888 | if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) | |
889 | iio_unregister_interrupt_line(indio_dev, 0); | |
890 | ||
891 | lis3l02dq_uninitialize_ring(indio_dev->ring); | |
892 | lis3l02dq_unconfigure_ring(indio_dev); | |
893 | iio_device_unregister(indio_dev); | |
894 | kfree(st->tx); | |
895 | kfree(st->rx); | |
896 | kfree(st); | |
897 | ||
898 | return 0; | |
899 | ||
900 | err_ret: | |
901 | return ret; | |
902 | } | |
903 | ||
904 | static struct spi_driver lis3l02dq_driver = { | |
905 | .driver = { | |
906 | .name = "lis3l02dq", | |
907 | .owner = THIS_MODULE, | |
908 | }, | |
909 | .probe = lis3l02dq_probe, | |
910 | .remove = __devexit_p(lis3l02dq_remove), | |
911 | }; | |
912 | ||
913 | static __init int lis3l02dq_init(void) | |
914 | { | |
915 | return spi_register_driver(&lis3l02dq_driver); | |
916 | } | |
917 | module_init(lis3l02dq_init); | |
918 | ||
919 | static __exit void lis3l02dq_exit(void) | |
920 | { | |
921 | spi_unregister_driver(&lis3l02dq_driver); | |
922 | } | |
923 | module_exit(lis3l02dq_exit); | |
924 | ||
925 | MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>"); | |
926 | MODULE_DESCRIPTION("ST LIS3L02DQ Accelerometer SPI driver"); | |
927 | MODULE_LICENSE("GPL v2"); |