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> | |
66533b48 JC |
18 | #include <linux/mutex.h> |
19 | #include <linux/device.h> | |
20 | #include <linux/kernel.h> | |
21 | #include <linux/spi/spi.h> | |
5a0e3ad6 | 22 | #include <linux/slab.h> |
66533b48 | 23 | #include <linux/sysfs.h> |
99c97852 | 24 | #include <linux/module.h> |
66533b48 JC |
25 | |
26 | #include "../iio.h" | |
27 | #include "../sysfs.h" | |
2662051e | 28 | #include "../ring_generic.h" |
73bce12e | 29 | |
66533b48 JC |
30 | #include "lis3l02dq.h" |
31 | ||
32 | /* At the moment the spi framework doesn't allow global setting of cs_change. | |
33 | * It's in the likely to be added comment at the top of spi.h. | |
34 | * This means that use cannot be made of spi_write etc. | |
35 | */ | |
d731aea0 JC |
36 | /* direct copy of the irq_default_primary_handler */ |
37 | #ifndef CONFIG_IIO_RING_BUFFER | |
38 | static irqreturn_t lis3l02dq_noring(int irq, void *private) | |
39 | { | |
40 | return IRQ_WAKE_THREAD; | |
41 | } | |
42 | #endif | |
66533b48 | 43 | |
1b076b52 JC |
44 | /** |
45 | * lis3l02dq_spi_read_reg_8() - read single byte from a single register | |
46 | * @indio_dev: iio_dev for this actual device | |
47 | * @reg_address: the address of the register to be read | |
48 | * @val: pass back the resulting value | |
49 | **/ | |
50 | int lis3l02dq_spi_read_reg_8(struct iio_dev *indio_dev, | |
51 | u8 reg_address, u8 *val) | |
66533b48 | 52 | { |
7b2fdd19 | 53 | struct lis3l02dq_state *st = iio_priv(indio_dev); |
66533b48 | 54 | struct spi_message msg; |
f3736416 | 55 | int ret; |
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, | |
66533b48 JC |
61 | }; |
62 | ||
63 | mutex_lock(&st->buf_lock); | |
64 | st->tx[0] = LIS3L02DQ_READ_REG(reg_address); | |
65 | st->tx[1] = 0; | |
66 | ||
67 | spi_message_init(&msg); | |
68 | spi_message_add_tail(&xfer, &msg); | |
69 | ret = spi_sync(st->us, &msg); | |
70 | *val = st->rx[1]; | |
71 | mutex_unlock(&st->buf_lock); | |
72 | ||
73 | return ret; | |
74 | } | |
75 | ||
76 | /** | |
77 | * lis3l02dq_spi_write_reg_8() - write single byte to a register | |
1b076b52 | 78 | * @indio_dev: iio_dev for this device |
25985edc | 79 | * @reg_address: the address of the register to be written |
66533b48 JC |
80 | * @val: the value to write |
81 | **/ | |
1b076b52 | 82 | int lis3l02dq_spi_write_reg_8(struct iio_dev *indio_dev, |
66533b48 | 83 | u8 reg_address, |
7df86302 | 84 | u8 val) |
66533b48 JC |
85 | { |
86 | int ret; | |
7b2fdd19 | 87 | struct lis3l02dq_state *st = iio_priv(indio_dev); |
66533b48 JC |
88 | |
89 | mutex_lock(&st->buf_lock); | |
90 | st->tx[0] = LIS3L02DQ_WRITE_REG(reg_address); | |
7df86302 | 91 | st->tx[1] = val; |
1b076b52 | 92 | ret = spi_write(st->us, st->tx, 2); |
66533b48 JC |
93 | mutex_unlock(&st->buf_lock); |
94 | ||
95 | return ret; | |
96 | } | |
97 | ||
98 | /** | |
99 | * lisl302dq_spi_write_reg_s16() - write 2 bytes to a pair of registers | |
1b076b52 JC |
100 | * @indio_dev: iio_dev for this device |
101 | * @lower_reg_address: the address of the lower of the two registers. | |
102 | * Second register is assumed to have address one greater. | |
103 | * @value: value to be written | |
66533b48 | 104 | **/ |
1b076b52 | 105 | static int lis3l02dq_spi_write_reg_s16(struct iio_dev *indio_dev, |
66533b48 JC |
106 | u8 lower_reg_address, |
107 | s16 value) | |
108 | { | |
109 | int ret; | |
110 | struct spi_message msg; | |
7b2fdd19 | 111 | struct lis3l02dq_state *st = iio_priv(indio_dev); |
66533b48 JC |
112 | struct spi_transfer xfers[] = { { |
113 | .tx_buf = st->tx, | |
114 | .bits_per_word = 8, | |
115 | .len = 2, | |
116 | .cs_change = 1, | |
117 | }, { | |
118 | .tx_buf = st->tx + 2, | |
119 | .bits_per_word = 8, | |
120 | .len = 2, | |
66533b48 JC |
121 | }, |
122 | }; | |
123 | ||
124 | mutex_lock(&st->buf_lock); | |
125 | st->tx[0] = LIS3L02DQ_WRITE_REG(lower_reg_address); | |
126 | st->tx[1] = value & 0xFF; | |
127 | st->tx[2] = LIS3L02DQ_WRITE_REG(lower_reg_address + 1); | |
128 | st->tx[3] = (value >> 8) & 0xFF; | |
129 | ||
130 | spi_message_init(&msg); | |
131 | spi_message_add_tail(&xfers[0], &msg); | |
132 | spi_message_add_tail(&xfers[1], &msg); | |
133 | ret = spi_sync(st->us, &msg); | |
134 | mutex_unlock(&st->buf_lock); | |
135 | ||
136 | return ret; | |
137 | } | |
138 | ||
1b076b52 | 139 | static int lis3l02dq_read_reg_s16(struct iio_dev *indio_dev, |
f3736416 JC |
140 | u8 lower_reg_address, |
141 | int *val) | |
66533b48 | 142 | { |
7b2fdd19 | 143 | struct lis3l02dq_state *st = iio_priv(indio_dev); |
1b076b52 | 144 | |
66533b48 | 145 | struct spi_message msg; |
66533b48 | 146 | int ret; |
f3736416 | 147 | s16 tempval; |
66533b48 JC |
148 | struct spi_transfer xfers[] = { { |
149 | .tx_buf = st->tx, | |
150 | .rx_buf = st->rx, | |
151 | .bits_per_word = 8, | |
152 | .len = 2, | |
153 | .cs_change = 1, | |
154 | }, { | |
155 | .tx_buf = st->tx + 2, | |
156 | .rx_buf = st->rx + 2, | |
157 | .bits_per_word = 8, | |
158 | .len = 2, | |
66533b48 JC |
159 | }, |
160 | }; | |
161 | ||
162 | mutex_lock(&st->buf_lock); | |
163 | st->tx[0] = LIS3L02DQ_READ_REG(lower_reg_address); | |
164 | st->tx[1] = 0; | |
f3736416 | 165 | st->tx[2] = LIS3L02DQ_READ_REG(lower_reg_address + 1); |
66533b48 JC |
166 | st->tx[3] = 0; |
167 | ||
168 | spi_message_init(&msg); | |
169 | spi_message_add_tail(&xfers[0], &msg); | |
170 | spi_message_add_tail(&xfers[1], &msg); | |
171 | ret = spi_sync(st->us, &msg); | |
172 | if (ret) { | |
173 | dev_err(&st->us->dev, "problem when reading 16 bit register"); | |
174 | goto error_ret; | |
175 | } | |
f3736416 | 176 | tempval = (s16)(st->rx[1]) | ((s16)(st->rx[3]) << 8); |
66533b48 | 177 | |
f3736416 | 178 | *val = tempval; |
66533b48 JC |
179 | error_ret: |
180 | mutex_unlock(&st->buf_lock); | |
181 | return ret; | |
182 | } | |
183 | ||
f3736416 JC |
184 | enum lis3l02dq_rm_ind { |
185 | LIS3L02DQ_ACCEL, | |
186 | LIS3L02DQ_GAIN, | |
187 | LIS3L02DQ_BIAS, | |
188 | }; | |
66533b48 | 189 | |
f3736416 JC |
190 | static u8 lis3l02dq_axis_map[3][3] = { |
191 | [LIS3L02DQ_ACCEL] = { LIS3L02DQ_REG_OUT_X_L_ADDR, | |
192 | LIS3L02DQ_REG_OUT_Y_L_ADDR, | |
193 | LIS3L02DQ_REG_OUT_Z_L_ADDR }, | |
194 | [LIS3L02DQ_GAIN] = { LIS3L02DQ_REG_GAIN_X_ADDR, | |
195 | LIS3L02DQ_REG_GAIN_Y_ADDR, | |
196 | LIS3L02DQ_REG_GAIN_Z_ADDR }, | |
197 | [LIS3L02DQ_BIAS] = { LIS3L02DQ_REG_OFFSET_X_ADDR, | |
198 | LIS3L02DQ_REG_OFFSET_Y_ADDR, | |
199 | LIS3L02DQ_REG_OFFSET_Z_ADDR } | |
200 | }; | |
66533b48 | 201 | |
f3736416 JC |
202 | static int lis3l02dq_read_thresh(struct iio_dev *indio_dev, |
203 | int e, | |
204 | int *val) | |
66533b48 | 205 | { |
1b076b52 | 206 | return lis3l02dq_read_reg_s16(indio_dev, LIS3L02DQ_REG_THS_L_ADDR, val); |
66533b48 JC |
207 | } |
208 | ||
f3736416 JC |
209 | static int lis3l02dq_write_thresh(struct iio_dev *indio_dev, |
210 | int event_code, | |
211 | int val) | |
66533b48 | 212 | { |
f3736416 | 213 | u16 value = val; |
1b076b52 | 214 | return lis3l02dq_spi_write_reg_s16(indio_dev, |
f3736416 JC |
215 | LIS3L02DQ_REG_THS_L_ADDR, |
216 | value); | |
66533b48 JC |
217 | } |
218 | ||
1e8fa5b8 JC |
219 | static int lis3l02dq_write_raw(struct iio_dev *indio_dev, |
220 | struct iio_chan_spec const *chan, | |
221 | int val, | |
222 | int val2, | |
223 | long mask) | |
224 | { | |
225 | int ret = -EINVAL, reg; | |
226 | u8 uval; | |
227 | s8 sval; | |
228 | switch (mask) { | |
229 | case (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE): | |
230 | if (val > 255 || val < -256) | |
231 | return -EINVAL; | |
232 | sval = val; | |
233 | reg = lis3l02dq_axis_map[LIS3L02DQ_BIAS][chan->address]; | |
7df86302 | 234 | ret = lis3l02dq_spi_write_reg_8(indio_dev, reg, sval); |
1e8fa5b8 JC |
235 | break; |
236 | case (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE): | |
237 | if (val & ~0xFF) | |
238 | return -EINVAL; | |
239 | uval = val; | |
240 | reg = lis3l02dq_axis_map[LIS3L02DQ_GAIN][chan->address]; | |
7df86302 | 241 | ret = lis3l02dq_spi_write_reg_8(indio_dev, reg, uval); |
1e8fa5b8 JC |
242 | break; |
243 | } | |
244 | return ret; | |
245 | } | |
246 | ||
f3736416 JC |
247 | static int lis3l02dq_read_raw(struct iio_dev *indio_dev, |
248 | struct iio_chan_spec const *chan, | |
249 | int *val, | |
250 | int *val2, | |
251 | long mask) | |
66533b48 | 252 | { |
f3736416 JC |
253 | u8 utemp; |
254 | s8 stemp; | |
255 | ssize_t ret = 0; | |
f3736416 | 256 | u8 reg; |
1b076b52 | 257 | |
f3736416 JC |
258 | switch (mask) { |
259 | case 0: | |
260 | /* Take the iio_dev status lock */ | |
261 | mutex_lock(&indio_dev->mlock); | |
262 | if (indio_dev->currentmode == INDIO_RING_TRIGGERED) | |
263 | ret = lis3l02dq_read_accel_from_ring(indio_dev->ring, | |
264 | chan->scan_index, | |
265 | val); | |
266 | else { | |
267 | reg = lis3l02dq_axis_map | |
268 | [LIS3L02DQ_ACCEL][chan->address]; | |
1b076b52 | 269 | ret = lis3l02dq_read_reg_s16(indio_dev, reg, val); |
f3736416 JC |
270 | } |
271 | mutex_unlock(&indio_dev->mlock); | |
272 | return IIO_VAL_INT; | |
273 | case (1 << IIO_CHAN_INFO_SCALE_SHARED): | |
274 | *val = 0; | |
275 | *val2 = 9580; | |
276 | return IIO_VAL_INT_PLUS_MICRO; | |
277 | case (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE): | |
278 | reg = lis3l02dq_axis_map[LIS3L02DQ_GAIN][chan->address]; | |
1b076b52 | 279 | ret = lis3l02dq_spi_read_reg_8(indio_dev, reg, &utemp); |
f3736416 JC |
280 | if (ret) |
281 | goto error_ret; | |
282 | /* to match with what previous code does */ | |
283 | *val = utemp; | |
284 | return IIO_VAL_INT; | |
285 | ||
286 | case (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE): | |
287 | reg = lis3l02dq_axis_map[LIS3L02DQ_BIAS][chan->address]; | |
1b076b52 | 288 | ret = lis3l02dq_spi_read_reg_8(indio_dev, reg, (u8 *)&stemp); |
f3736416 JC |
289 | /* to match with what previous code does */ |
290 | *val = stemp; | |
291 | return IIO_VAL_INT; | |
292 | } | |
66533b48 | 293 | error_ret: |
f3736416 | 294 | return ret; |
66533b48 JC |
295 | } |
296 | ||
297 | static ssize_t lis3l02dq_read_frequency(struct device *dev, | |
298 | struct device_attribute *attr, | |
299 | char *buf) | |
300 | { | |
1b076b52 | 301 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
66533b48 JC |
302 | int ret, len = 0; |
303 | s8 t; | |
1b076b52 | 304 | ret = lis3l02dq_spi_read_reg_8(indio_dev, |
66533b48 JC |
305 | LIS3L02DQ_REG_CTRL_1_ADDR, |
306 | (u8 *)&t); | |
307 | if (ret) | |
308 | return ret; | |
309 | t &= LIS3L02DQ_DEC_MASK; | |
310 | switch (t) { | |
311 | case LIS3L02DQ_REG_CTRL_1_DF_128: | |
312 | len = sprintf(buf, "280\n"); | |
313 | break; | |
314 | case LIS3L02DQ_REG_CTRL_1_DF_64: | |
315 | len = sprintf(buf, "560\n"); | |
316 | break; | |
317 | case LIS3L02DQ_REG_CTRL_1_DF_32: | |
318 | len = sprintf(buf, "1120\n"); | |
319 | break; | |
320 | case LIS3L02DQ_REG_CTRL_1_DF_8: | |
321 | len = sprintf(buf, "4480\n"); | |
322 | break; | |
323 | } | |
324 | return len; | |
325 | } | |
326 | ||
327 | static ssize_t lis3l02dq_write_frequency(struct device *dev, | |
328 | struct device_attribute *attr, | |
329 | const char *buf, | |
330 | size_t len) | |
331 | { | |
332 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
333 | long val; | |
334 | int ret; | |
335 | u8 t; | |
336 | ||
337 | ret = strict_strtol(buf, 10, &val); | |
338 | if (ret) | |
339 | return ret; | |
340 | ||
341 | mutex_lock(&indio_dev->mlock); | |
1b076b52 | 342 | ret = lis3l02dq_spi_read_reg_8(indio_dev, |
66533b48 JC |
343 | LIS3L02DQ_REG_CTRL_1_ADDR, |
344 | &t); | |
345 | if (ret) | |
346 | goto error_ret_mutex; | |
347 | /* Wipe the bits clean */ | |
348 | t &= ~LIS3L02DQ_DEC_MASK; | |
349 | switch (val) { | |
350 | case 280: | |
351 | t |= LIS3L02DQ_REG_CTRL_1_DF_128; | |
352 | break; | |
353 | case 560: | |
354 | t |= LIS3L02DQ_REG_CTRL_1_DF_64; | |
355 | break; | |
356 | case 1120: | |
357 | t |= LIS3L02DQ_REG_CTRL_1_DF_32; | |
358 | break; | |
359 | case 4480: | |
360 | t |= LIS3L02DQ_REG_CTRL_1_DF_8; | |
361 | break; | |
362 | default: | |
363 | ret = -EINVAL; | |
364 | goto error_ret_mutex; | |
95cd17c9 | 365 | } |
66533b48 | 366 | |
1b076b52 | 367 | ret = lis3l02dq_spi_write_reg_8(indio_dev, |
66533b48 | 368 | LIS3L02DQ_REG_CTRL_1_ADDR, |
7df86302 | 369 | t); |
66533b48 JC |
370 | |
371 | error_ret_mutex: | |
372 | mutex_unlock(&indio_dev->mlock); | |
373 | ||
374 | return ret ? ret : len; | |
375 | } | |
376 | ||
7b2fdd19 | 377 | static int lis3l02dq_initial_setup(struct iio_dev *indio_dev) |
66533b48 | 378 | { |
7b2fdd19 | 379 | struct lis3l02dq_state *st = iio_priv(indio_dev); |
66533b48 JC |
380 | int ret; |
381 | u8 val, valtest; | |
382 | ||
383 | st->us->mode = SPI_MODE_3; | |
384 | ||
385 | spi_setup(st->us); | |
386 | ||
387 | val = LIS3L02DQ_DEFAULT_CTRL1; | |
388 | /* Write suitable defaults to ctrl1 */ | |
7b2fdd19 | 389 | ret = lis3l02dq_spi_write_reg_8(indio_dev, |
66533b48 | 390 | LIS3L02DQ_REG_CTRL_1_ADDR, |
7df86302 | 391 | val); |
66533b48 JC |
392 | if (ret) { |
393 | dev_err(&st->us->dev, "problem with setup control register 1"); | |
394 | goto err_ret; | |
395 | } | |
396 | /* Repeat as sometimes doesn't work first time?*/ | |
7b2fdd19 | 397 | ret = lis3l02dq_spi_write_reg_8(indio_dev, |
66533b48 | 398 | LIS3L02DQ_REG_CTRL_1_ADDR, |
7df86302 | 399 | val); |
66533b48 JC |
400 | if (ret) { |
401 | dev_err(&st->us->dev, "problem with setup control register 1"); | |
402 | goto err_ret; | |
403 | } | |
404 | ||
405 | /* Read back to check this has worked acts as loose test of correct | |
406 | * chip */ | |
7b2fdd19 | 407 | ret = lis3l02dq_spi_read_reg_8(indio_dev, |
66533b48 JC |
408 | LIS3L02DQ_REG_CTRL_1_ADDR, |
409 | &valtest); | |
410 | if (ret || (valtest != val)) { | |
7b2fdd19 | 411 | dev_err(&indio_dev->dev, |
1b076b52 | 412 | "device not playing ball %d %d\n", valtest, val); |
66533b48 JC |
413 | ret = -EINVAL; |
414 | goto err_ret; | |
415 | } | |
416 | ||
417 | val = LIS3L02DQ_DEFAULT_CTRL2; | |
7b2fdd19 | 418 | ret = lis3l02dq_spi_write_reg_8(indio_dev, |
66533b48 | 419 | LIS3L02DQ_REG_CTRL_2_ADDR, |
7df86302 | 420 | val); |
66533b48 JC |
421 | if (ret) { |
422 | dev_err(&st->us->dev, "problem with setup control register 2"); | |
423 | goto err_ret; | |
424 | } | |
425 | ||
426 | val = LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC; | |
7b2fdd19 | 427 | ret = lis3l02dq_spi_write_reg_8(indio_dev, |
66533b48 | 428 | LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, |
7df86302 | 429 | val); |
66533b48 JC |
430 | if (ret) |
431 | dev_err(&st->us->dev, "problem with interrupt cfg register"); | |
432 | err_ret: | |
433 | ||
434 | return ret; | |
435 | } | |
436 | ||
66533b48 JC |
437 | static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, |
438 | lis3l02dq_read_frequency, | |
439 | lis3l02dq_write_frequency); | |
440 | ||
f3fb0011 | 441 | static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("280 560 1120 4480"); |
66533b48 | 442 | |
aaf370db | 443 | static irqreturn_t lis3l02dq_event_handler(int irq, void *private) |
66533b48 | 444 | { |
aaf370db | 445 | struct iio_dev *indio_dev = private; |
461be806 JC |
446 | u8 t; |
447 | ||
448 | s64 timestamp = iio_get_time_ns(); | |
449 | ||
7b2fdd19 | 450 | lis3l02dq_spi_read_reg_8(indio_dev, |
461be806 JC |
451 | LIS3L02DQ_REG_WAKE_UP_SRC_ADDR, |
452 | &t); | |
66533b48 | 453 | |
461be806 | 454 | if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH) |
7b2fdd19 | 455 | iio_push_event(indio_dev, 0, |
c4b14d99 | 456 | IIO_MOD_EVENT_CODE(IIO_ACCEL, |
461be806 | 457 | 0, |
c4b14d99 | 458 | IIO_MOD_Z, |
461be806 JC |
459 | IIO_EV_TYPE_THRESH, |
460 | IIO_EV_DIR_RISING), | |
461 | timestamp); | |
462 | ||
463 | if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW) | |
7b2fdd19 | 464 | iio_push_event(indio_dev, 0, |
c4b14d99 | 465 | IIO_MOD_EVENT_CODE(IIO_ACCEL, |
461be806 | 466 | 0, |
c4b14d99 | 467 | IIO_MOD_Z, |
461be806 JC |
468 | IIO_EV_TYPE_THRESH, |
469 | IIO_EV_DIR_FALLING), | |
470 | timestamp); | |
471 | ||
472 | if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH) | |
7b2fdd19 | 473 | iio_push_event(indio_dev, 0, |
c4b14d99 | 474 | IIO_MOD_EVENT_CODE(IIO_ACCEL, |
461be806 | 475 | 0, |
c4b14d99 | 476 | IIO_MOD_Y, |
461be806 JC |
477 | IIO_EV_TYPE_THRESH, |
478 | IIO_EV_DIR_RISING), | |
479 | timestamp); | |
480 | ||
481 | if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW) | |
7b2fdd19 | 482 | iio_push_event(indio_dev, 0, |
c4b14d99 | 483 | IIO_MOD_EVENT_CODE(IIO_ACCEL, |
461be806 | 484 | 0, |
c4b14d99 | 485 | IIO_MOD_Y, |
461be806 JC |
486 | IIO_EV_TYPE_THRESH, |
487 | IIO_EV_DIR_FALLING), | |
488 | timestamp); | |
489 | ||
490 | if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH) | |
7b2fdd19 | 491 | iio_push_event(indio_dev, 0, |
c4b14d99 | 492 | IIO_MOD_EVENT_CODE(IIO_ACCEL, |
461be806 | 493 | 0, |
c4b14d99 | 494 | IIO_MOD_X, |
461be806 JC |
495 | IIO_EV_TYPE_THRESH, |
496 | IIO_EV_DIR_RISING), | |
497 | timestamp); | |
498 | ||
499 | if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW) | |
7b2fdd19 | 500 | iio_push_event(indio_dev, 0, |
c4b14d99 | 501 | IIO_MOD_EVENT_CODE(IIO_ACCEL, |
461be806 | 502 | 0, |
c4b14d99 | 503 | IIO_MOD_X, |
461be806 JC |
504 | IIO_EV_TYPE_THRESH, |
505 | IIO_EV_DIR_FALLING), | |
506 | timestamp); | |
507 | ||
508 | /* Ack and allow for new interrupts */ | |
7b2fdd19 | 509 | lis3l02dq_spi_read_reg_8(indio_dev, |
461be806 JC |
510 | LIS3L02DQ_REG_WAKE_UP_ACK_ADDR, |
511 | &t); | |
66533b48 | 512 | |
1e3345bc | 513 | return IRQ_HANDLED; |
66533b48 JC |
514 | } |
515 | ||
f3736416 JC |
516 | #define LIS3L02DQ_INFO_MASK \ |
517 | ((1 << IIO_CHAN_INFO_SCALE_SHARED) | \ | |
518 | (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE) | \ | |
519 | (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE)) | |
520 | ||
521 | #define LIS3L02DQ_EVENT_MASK \ | |
522 | (IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | \ | |
523 | IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING)) | |
524 | ||
525 | static struct iio_chan_spec lis3l02dq_channels[] = { | |
526 | IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_X, LIS3L02DQ_INFO_MASK, | |
aaf370db | 527 | 0, 0, IIO_ST('s', 12, 16, 0), LIS3L02DQ_EVENT_MASK), |
f3736416 | 528 | IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_Y, LIS3L02DQ_INFO_MASK, |
aaf370db | 529 | 1, 1, IIO_ST('s', 12, 16, 0), LIS3L02DQ_EVENT_MASK), |
f3736416 | 530 | IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_Z, LIS3L02DQ_INFO_MASK, |
aaf370db | 531 | 2, 2, IIO_ST('s', 12, 16, 0), LIS3L02DQ_EVENT_MASK), |
f3736416 JC |
532 | IIO_CHAN_SOFT_TIMESTAMP(3) |
533 | }; | |
534 | ||
535 | ||
536 | static ssize_t lis3l02dq_read_event_config(struct iio_dev *indio_dev, | |
537 | int event_code) | |
538 | { | |
539 | ||
540 | u8 val; | |
541 | int ret; | |
542 | u8 mask = (1 << (IIO_EVENT_CODE_EXTRACT_MODIFIER(event_code)*2 + | |
543 | (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == | |
544 | IIO_EV_DIR_RISING))); | |
1b076b52 | 545 | ret = lis3l02dq_spi_read_reg_8(indio_dev, |
66533b48 | 546 | LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, |
f3736416 JC |
547 | &val); |
548 | if (ret < 0) | |
549 | return ret; | |
550 | ||
551 | return !!(val & mask); | |
552 | } | |
66533b48 | 553 | |
1e3345bc JC |
554 | int lis3l02dq_disable_all_events(struct iio_dev *indio_dev) |
555 | { | |
1e3345bc JC |
556 | int ret; |
557 | u8 control, val; | |
1e3345bc JC |
558 | |
559 | ret = lis3l02dq_spi_read_reg_8(indio_dev, | |
560 | LIS3L02DQ_REG_CTRL_2_ADDR, | |
561 | &control); | |
562 | ||
1e3345bc JC |
563 | control &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT; |
564 | ret = lis3l02dq_spi_write_reg_8(indio_dev, | |
565 | LIS3L02DQ_REG_CTRL_2_ADDR, | |
7df86302 | 566 | control); |
1e3345bc JC |
567 | if (ret) |
568 | goto error_ret; | |
569 | /* Also for consistency clear the mask */ | |
570 | ret = lis3l02dq_spi_read_reg_8(indio_dev, | |
571 | LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, | |
572 | &val); | |
573 | if (ret) | |
574 | goto error_ret; | |
575 | val &= ~0x3f; | |
576 | ||
577 | ret = lis3l02dq_spi_write_reg_8(indio_dev, | |
578 | LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, | |
7df86302 | 579 | val); |
1e3345bc JC |
580 | if (ret) |
581 | goto error_ret; | |
582 | ||
1e3345bc JC |
583 | ret = control; |
584 | error_ret: | |
585 | return ret; | |
586 | } | |
587 | ||
f3736416 JC |
588 | static int lis3l02dq_write_event_config(struct iio_dev *indio_dev, |
589 | int event_code, | |
f3736416 JC |
590 | int state) |
591 | { | |
592 | int ret = 0; | |
593 | u8 val, control; | |
594 | u8 currentlyset; | |
595 | bool changed = false; | |
596 | u8 mask = (1 << (IIO_EVENT_CODE_EXTRACT_MODIFIER(event_code)*2 + | |
597 | (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == | |
598 | IIO_EV_DIR_RISING))); | |
599 | ||
600 | mutex_lock(&indio_dev->mlock); | |
66533b48 | 601 | /* read current control */ |
1b076b52 | 602 | ret = lis3l02dq_spi_read_reg_8(indio_dev, |
66533b48 | 603 | LIS3L02DQ_REG_CTRL_2_ADDR, |
f3736416 | 604 | &control); |
66533b48 | 605 | if (ret) |
f3736416 | 606 | goto error_ret; |
1b076b52 | 607 | ret = lis3l02dq_spi_read_reg_8(indio_dev, |
f3736416 JC |
608 | LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, |
609 | &val); | |
610 | if (ret < 0) | |
611 | goto error_ret; | |
612 | currentlyset = val & mask; | |
613 | ||
614 | if (!currentlyset && state) { | |
615 | changed = true; | |
616 | val |= mask; | |
f3736416 JC |
617 | } else if (currentlyset && !state) { |
618 | changed = true; | |
619 | val &= ~mask; | |
66533b48 | 620 | } |
1e3345bc | 621 | |
66533b48 | 622 | if (changed) { |
1b076b52 | 623 | ret = lis3l02dq_spi_write_reg_8(indio_dev, |
66533b48 | 624 | LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, |
7df86302 | 625 | val); |
66533b48 | 626 | if (ret) |
f3736416 | 627 | goto error_ret; |
1e3345bc | 628 | control = val & 0x3f ? |
f3736416 JC |
629 | (control | LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT) : |
630 | (control & ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT); | |
1b076b52 | 631 | ret = lis3l02dq_spi_write_reg_8(indio_dev, |
f3736416 | 632 | LIS3L02DQ_REG_CTRL_2_ADDR, |
7df86302 | 633 | control); |
1e3345bc JC |
634 | if (ret) |
635 | goto error_ret; | |
66533b48 | 636 | } |
66533b48 | 637 | |
f3736416 JC |
638 | error_ret: |
639 | mutex_unlock(&indio_dev->mlock); | |
640 | return ret; | |
66533b48 JC |
641 | } |
642 | ||
66533b48 | 643 | static struct attribute *lis3l02dq_attributes[] = { |
66533b48 | 644 | &iio_dev_attr_sampling_frequency.dev_attr.attr, |
f3fb0011 | 645 | &iio_const_attr_sampling_frequency_available.dev_attr.attr, |
66533b48 JC |
646 | NULL |
647 | }; | |
648 | ||
649 | static const struct attribute_group lis3l02dq_attribute_group = { | |
650 | .attrs = lis3l02dq_attributes, | |
651 | }; | |
652 | ||
6fe8135f JC |
653 | static const struct iio_info lis3l02dq_info = { |
654 | .num_interrupt_lines = 1, | |
655 | .read_raw = &lis3l02dq_read_raw, | |
656 | .write_raw = &lis3l02dq_write_raw, | |
657 | .read_event_value = &lis3l02dq_read_thresh, | |
658 | .write_event_value = &lis3l02dq_write_thresh, | |
659 | .write_event_config = &lis3l02dq_write_event_config, | |
660 | .read_event_config = &lis3l02dq_read_event_config, | |
661 | .driver_module = THIS_MODULE, | |
662 | .attrs = &lis3l02dq_attribute_group, | |
663 | }; | |
664 | ||
66533b48 JC |
665 | static int __devinit lis3l02dq_probe(struct spi_device *spi) |
666 | { | |
667 | int ret, regdone = 0; | |
7b2fdd19 JC |
668 | struct lis3l02dq_state *st; |
669 | struct iio_dev *indio_dev; | |
670 | ||
671 | indio_dev = iio_allocate_device(sizeof *st); | |
672 | if (indio_dev == NULL) { | |
673 | ret = -ENOMEM; | |
66533b48 JC |
674 | goto error_ret; |
675 | } | |
7b2fdd19 | 676 | st = iio_priv(indio_dev); |
66533b48 | 677 | /* this is only used tor removal purposes */ |
8016934c | 678 | spi_set_drvdata(spi, indio_dev); |
66533b48 | 679 | |
66533b48 JC |
680 | st->us = spi; |
681 | mutex_init(&st->buf_lock); | |
7b2fdd19 JC |
682 | indio_dev->name = spi->dev.driver->name; |
683 | indio_dev->dev.parent = &spi->dev; | |
6fe8135f | 684 | indio_dev->info = &lis3l02dq_info; |
7b2fdd19 JC |
685 | indio_dev->channels = lis3l02dq_channels; |
686 | indio_dev->num_channels = ARRAY_SIZE(lis3l02dq_channels); | |
6fe8135f | 687 | |
7b2fdd19 JC |
688 | indio_dev->modes = INDIO_DIRECT_MODE; |
689 | ||
690 | ret = lis3l02dq_configure_ring(indio_dev); | |
66533b48 JC |
691 | if (ret) |
692 | goto error_free_dev; | |
693 | ||
7b2fdd19 | 694 | ret = iio_device_register(indio_dev); |
66533b48 JC |
695 | if (ret) |
696 | goto error_unreg_ring_funcs; | |
697 | regdone = 1; | |
698 | ||
1aa04278 | 699 | ret = iio_ring_buffer_register_ex(indio_dev, 0, |
f3736416 JC |
700 | lis3l02dq_channels, |
701 | ARRAY_SIZE(lis3l02dq_channels)); | |
66533b48 JC |
702 | if (ret) { |
703 | printk(KERN_ERR "failed to initialize the ring\n"); | |
704 | goto error_unreg_ring_funcs; | |
705 | } | |
706 | ||
707 | if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) { | |
d731aea0 JC |
708 | ret = request_threaded_irq(st->us->irq, |
709 | &lis3l02dq_th, | |
710 | &lis3l02dq_event_handler, | |
711 | IRQF_TRIGGER_RISING, | |
712 | "lis3l02dq", | |
7b2fdd19 | 713 | indio_dev); |
66533b48 | 714 | if (ret) |
aaf370db | 715 | goto error_uninitialize_ring; |
d731aea0 | 716 | |
7b2fdd19 | 717 | ret = lis3l02dq_probe_trigger(indio_dev); |
d731aea0 JC |
718 | if (ret) |
719 | goto error_free_interrupt; | |
66533b48 JC |
720 | } |
721 | ||
722 | /* Get the device into a sane initial state */ | |
7b2fdd19 | 723 | ret = lis3l02dq_initial_setup(indio_dev); |
66533b48 JC |
724 | if (ret) |
725 | goto error_remove_trigger; | |
726 | return 0; | |
727 | ||
728 | error_remove_trigger: | |
7b2fdd19 JC |
729 | if (indio_dev->modes & INDIO_RING_TRIGGERED) |
730 | lis3l02dq_remove_trigger(indio_dev); | |
d731aea0 JC |
731 | error_free_interrupt: |
732 | if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) | |
7b2fdd19 | 733 | free_irq(st->us->irq, indio_dev); |
66533b48 | 734 | error_uninitialize_ring: |
1aa04278 | 735 | iio_ring_buffer_unregister(indio_dev); |
66533b48 | 736 | error_unreg_ring_funcs: |
7b2fdd19 | 737 | lis3l02dq_unconfigure_ring(indio_dev); |
66533b48 JC |
738 | error_free_dev: |
739 | if (regdone) | |
7b2fdd19 | 740 | iio_device_unregister(indio_dev); |
66533b48 | 741 | else |
7b2fdd19 | 742 | iio_free_device(indio_dev); |
66533b48 JC |
743 | error_ret: |
744 | return ret; | |
745 | } | |
746 | ||
747 | /* Power down the device */ | |
748 | static int lis3l02dq_stop_device(struct iio_dev *indio_dev) | |
749 | { | |
750 | int ret; | |
7b2fdd19 | 751 | struct lis3l02dq_state *st = iio_priv(indio_dev); |
66533b48 JC |
752 | u8 val = 0; |
753 | ||
754 | mutex_lock(&indio_dev->mlock); | |
1b076b52 | 755 | ret = lis3l02dq_spi_write_reg_8(indio_dev, |
66533b48 | 756 | LIS3L02DQ_REG_CTRL_1_ADDR, |
7df86302 | 757 | val); |
66533b48 JC |
758 | if (ret) { |
759 | dev_err(&st->us->dev, "problem with turning device off: ctrl1"); | |
760 | goto err_ret; | |
761 | } | |
762 | ||
1b076b52 | 763 | ret = lis3l02dq_spi_write_reg_8(indio_dev, |
66533b48 | 764 | LIS3L02DQ_REG_CTRL_2_ADDR, |
7df86302 | 765 | val); |
66533b48 JC |
766 | if (ret) |
767 | dev_err(&st->us->dev, "problem with turning device off: ctrl2"); | |
768 | err_ret: | |
769 | mutex_unlock(&indio_dev->mlock); | |
770 | return ret; | |
771 | } | |
772 | ||
773 | /* fixme, confirm ordering in this function */ | |
774 | static int lis3l02dq_remove(struct spi_device *spi) | |
775 | { | |
776 | int ret; | |
7b2fdd19 JC |
777 | struct iio_dev *indio_dev = spi_get_drvdata(spi); |
778 | struct lis3l02dq_state *st = iio_priv(indio_dev); | |
779 | ||
1e3345bc JC |
780 | ret = lis3l02dq_disable_all_events(indio_dev); |
781 | if (ret) | |
782 | goto err_ret; | |
66533b48 JC |
783 | |
784 | ret = lis3l02dq_stop_device(indio_dev); | |
785 | if (ret) | |
786 | goto err_ret; | |
787 | ||
d731aea0 JC |
788 | if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) |
789 | free_irq(st->us->irq, indio_dev); | |
790 | ||
66533b48 | 791 | lis3l02dq_remove_trigger(indio_dev); |
1aa04278 | 792 | iio_ring_buffer_unregister(indio_dev); |
66533b48 JC |
793 | lis3l02dq_unconfigure_ring(indio_dev); |
794 | iio_device_unregister(indio_dev); | |
66533b48 JC |
795 | |
796 | return 0; | |
797 | ||
798 | err_ret: | |
799 | return ret; | |
800 | } | |
801 | ||
802 | static struct spi_driver lis3l02dq_driver = { | |
803 | .driver = { | |
804 | .name = "lis3l02dq", | |
805 | .owner = THIS_MODULE, | |
806 | }, | |
807 | .probe = lis3l02dq_probe, | |
808 | .remove = __devexit_p(lis3l02dq_remove), | |
809 | }; | |
810 | ||
811 | static __init int lis3l02dq_init(void) | |
812 | { | |
813 | return spi_register_driver(&lis3l02dq_driver); | |
814 | } | |
815 | module_init(lis3l02dq_init); | |
816 | ||
817 | static __exit void lis3l02dq_exit(void) | |
818 | { | |
819 | spi_unregister_driver(&lis3l02dq_driver); | |
820 | } | |
821 | module_exit(lis3l02dq_exit); | |
822 | ||
823 | MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>"); | |
824 | MODULE_DESCRIPTION("ST LIS3L02DQ Accelerometer SPI driver"); | |
825 | MODULE_LICENSE("GPL v2"); |