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