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