blob: 85de565a4e806c02f89aa681f850d2641d52fa9c [file] [log] [blame]
Thomas Gleixner80503b22019-05-24 12:04:09 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +00002/*
3 * Common library for ADIS16XXX devices
4 *
5 * Copyright 2012 Analog Devices Inc.
6 * Author: Lars-Peter Clausen <lars@metafoo.de>
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +00007 */
8
9#include <linux/delay.h>
10#include <linux/mutex.h>
11#include <linux/device.h>
12#include <linux/kernel.h>
13#include <linux/spi/spi.h>
14#include <linux/slab.h>
15#include <linux/sysfs.h>
16#include <linux/module.h>
17#include <asm/unaligned.h>
18
19#include <linux/iio/iio.h>
20#include <linux/iio/sysfs.h>
21#include <linux/iio/buffer.h>
Lars-Peter Clausenec04cb02012-11-13 13:28:00 +000022#include <linux/iio/imu/adis.h>
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +000023
24#define ADIS_MSC_CTRL_DATA_RDY_EN BIT(2)
25#define ADIS_MSC_CTRL_DATA_RDY_POL_HIGH BIT(1)
26#define ADIS_MSC_CTRL_DATA_RDY_DIO2 BIT(0)
27#define ADIS_GLOB_CMD_SW_RESET BIT(7)
28
Lars-Peter Clausen57a12282012-11-20 13:36:00 +000029int adis_write_reg(struct adis *adis, unsigned int reg,
30 unsigned int value, unsigned int size)
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +000031{
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +000032 unsigned int page = reg / ADIS_PAGE_SIZE;
Lars-Peter Clausen57a12282012-11-20 13:36:00 +000033 int ret, i;
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +000034 struct spi_message msg;
35 struct spi_transfer xfers[] = {
36 {
37 .tx_buf = adis->tx,
38 .bits_per_word = 8,
39 .len = 2,
40 .cs_change = 1,
41 .delay_usecs = adis->data->write_delay,
Alexandru Ardeleandca39af2019-07-23 10:36:38 +030042 .cs_change_delay = adis->data->cs_change_delay,
43 .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +000044 }, {
45 .tx_buf = adis->tx + 2,
46 .bits_per_word = 8,
47 .len = 2,
Lars-Peter Clausen57a12282012-11-20 13:36:00 +000048 .cs_change = 1,
49 .delay_usecs = adis->data->write_delay,
Alexandru Ardeleandca39af2019-07-23 10:36:38 +030050 .cs_change_delay = adis->data->cs_change_delay,
51 .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
Lars-Peter Clausen57a12282012-11-20 13:36:00 +000052 }, {
53 .tx_buf = adis->tx + 4,
54 .bits_per_word = 8,
55 .len = 2,
56 .cs_change = 1,
57 .delay_usecs = adis->data->write_delay,
Alexandru Ardeleandca39af2019-07-23 10:36:38 +030058 .cs_change_delay = adis->data->cs_change_delay,
59 .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
Lars-Peter Clausen57a12282012-11-20 13:36:00 +000060 }, {
61 .tx_buf = adis->tx + 6,
62 .bits_per_word = 8,
63 .len = 2,
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +000064 .delay_usecs = adis->data->write_delay,
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +000065 }, {
66 .tx_buf = adis->tx + 8,
67 .bits_per_word = 8,
68 .len = 2,
69 .delay_usecs = adis->data->write_delay,
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +000070 },
71 };
72
73 mutex_lock(&adis->txrx_lock);
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +000074
75 spi_message_init(&msg);
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +000076
77 if (adis->current_page != page) {
78 adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
79 adis->tx[1] = page;
80 spi_message_add_tail(&xfers[0], &msg);
81 }
82
Lars-Peter Clausen57a12282012-11-20 13:36:00 +000083 switch (size) {
84 case 4:
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +000085 adis->tx[8] = ADIS_WRITE_REG(reg + 3);
86 adis->tx[9] = (value >> 24) & 0xff;
87 adis->tx[6] = ADIS_WRITE_REG(reg + 2);
88 adis->tx[7] = (value >> 16) & 0xff;
Gustavo A. R. Silva82d65f92018-07-03 14:35:50 -050089 /* fall through */
Lars-Peter Clausen57a12282012-11-20 13:36:00 +000090 case 2:
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +000091 adis->tx[4] = ADIS_WRITE_REG(reg + 1);
92 adis->tx[5] = (value >> 8) & 0xff;
Gustavo A. R. Silva82d65f92018-07-03 14:35:50 -050093 /* fall through */
Lars-Peter Clausen57a12282012-11-20 13:36:00 +000094 case 1:
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +000095 adis->tx[2] = ADIS_WRITE_REG(reg);
96 adis->tx[3] = value & 0xff;
Lars-Peter Clausen57a12282012-11-20 13:36:00 +000097 break;
98 default:
99 ret = -EINVAL;
100 goto out_unlock;
101 }
102
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +0000103 xfers[size].cs_change = 0;
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000104
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +0000105 for (i = 1; i <= size; i++)
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000106 spi_message_add_tail(&xfers[i], &msg);
107
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000108 ret = spi_sync(adis->spi, &msg);
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000109 if (ret) {
110 dev_err(&adis->spi->dev, "Failed to write register 0x%02X: %d\n",
111 reg, ret);
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +0000112 } else {
113 adis->current_page = page;
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000114 }
115
116out_unlock:
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000117 mutex_unlock(&adis->txrx_lock);
118
119 return ret;
120}
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000121EXPORT_SYMBOL_GPL(adis_write_reg);
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000122
123/**
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000124 * adis_read_reg() - read 2 bytes from a 16-bit register
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000125 * @adis: The adis device
126 * @reg: The address of the lower of the two registers
127 * @val: The value read back from the device
128 */
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000129int adis_read_reg(struct adis *adis, unsigned int reg,
130 unsigned int *val, unsigned int size)
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000131{
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +0000132 unsigned int page = reg / ADIS_PAGE_SIZE;
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000133 struct spi_message msg;
134 int ret;
135 struct spi_transfer xfers[] = {
136 {
137 .tx_buf = adis->tx,
138 .bits_per_word = 8,
139 .len = 2,
140 .cs_change = 1,
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +0000141 .delay_usecs = adis->data->write_delay,
Alexandru Ardeleandca39af2019-07-23 10:36:38 +0300142 .cs_change_delay = adis->data->cs_change_delay,
143 .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000144 }, {
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000145 .tx_buf = adis->tx + 2,
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +0000146 .bits_per_word = 8,
147 .len = 2,
148 .cs_change = 1,
149 .delay_usecs = adis->data->read_delay,
Alexandru Ardeleandca39af2019-07-23 10:36:38 +0300150 .cs_change_delay = adis->data->cs_change_delay,
151 .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +0000152 }, {
153 .tx_buf = adis->tx + 4,
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000154 .rx_buf = adis->rx,
155 .bits_per_word = 8,
156 .len = 2,
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000157 .cs_change = 1,
158 .delay_usecs = adis->data->read_delay,
Alexandru Ardeleandca39af2019-07-23 10:36:38 +0300159 .cs_change_delay = adis->data->cs_change_delay,
160 .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000161 }, {
162 .rx_buf = adis->rx + 2,
163 .bits_per_word = 8,
164 .len = 2,
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000165 .delay_usecs = adis->data->read_delay,
166 },
167 };
168
169 mutex_lock(&adis->txrx_lock);
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000170 spi_message_init(&msg);
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000171
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +0000172 if (adis->current_page != page) {
173 adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
174 adis->tx[1] = page;
175 spi_message_add_tail(&xfers[0], &msg);
176 }
177
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000178 switch (size) {
179 case 4:
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +0000180 adis->tx[2] = ADIS_READ_REG(reg + 2);
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000181 adis->tx[3] = 0;
182 spi_message_add_tail(&xfers[1], &msg);
Gustavo A. R. Silva82d65f92018-07-03 14:35:50 -0500183 /* fall through */
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +0000184 case 2:
185 adis->tx[4] = ADIS_READ_REG(reg);
186 adis->tx[5] = 0;
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000187 spi_message_add_tail(&xfers[2], &msg);
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +0000188 spi_message_add_tail(&xfers[3], &msg);
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000189 break;
190 default:
191 ret = -EINVAL;
192 goto out_unlock;
193 }
194
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000195 ret = spi_sync(adis->spi, &msg);
196 if (ret) {
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000197 dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n",
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000198 reg, ret);
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000199 goto out_unlock;
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +0000200 } else {
201 adis->current_page = page;
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000202 }
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000203
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000204 switch (size) {
205 case 4:
206 *val = get_unaligned_be32(adis->rx);
207 break;
208 case 2:
209 *val = get_unaligned_be16(adis->rx + 2);
210 break;
211 }
212
213out_unlock:
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000214 mutex_unlock(&adis->txrx_lock);
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000215
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000216 return ret;
217}
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000218EXPORT_SYMBOL_GPL(adis_read_reg);
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000219
Lars-Peter Clausen78026a62012-11-20 13:36:00 +0000220#ifdef CONFIG_DEBUG_FS
221
222int adis_debugfs_reg_access(struct iio_dev *indio_dev,
223 unsigned int reg, unsigned int writeval, unsigned int *readval)
224{
225 struct adis *adis = iio_device_get_drvdata(indio_dev);
226
227 if (readval) {
228 uint16_t val16;
229 int ret;
230
231 ret = adis_read_reg_16(adis, reg, &val16);
Alexandru Ardelean38262c02019-11-01 11:35:04 +0200232 if (ret == 0)
233 *readval = val16;
Lars-Peter Clausen78026a62012-11-20 13:36:00 +0000234
235 return ret;
236 } else {
237 return adis_write_reg_16(adis, reg, writeval);
238 }
239}
240EXPORT_SYMBOL(adis_debugfs_reg_access);
241
242#endif
243
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000244/**
245 * adis_enable_irq() - Enable or disable data ready IRQ
246 * @adis: The adis device
247 * @enable: Whether to enable the IRQ
248 *
249 * Returns 0 on success, negative error code otherwise
250 */
251int adis_enable_irq(struct adis *adis, bool enable)
252{
253 int ret = 0;
254 uint16_t msc;
255
Lars-Peter Clausen2f3abe62012-11-20 13:36:00 +0000256 if (adis->data->enable_irq)
257 return adis->data->enable_irq(adis, enable);
258
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000259 ret = adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc);
260 if (ret)
261 goto error_ret;
262
263 msc |= ADIS_MSC_CTRL_DATA_RDY_POL_HIGH;
264 msc &= ~ADIS_MSC_CTRL_DATA_RDY_DIO2;
265 if (enable)
266 msc |= ADIS_MSC_CTRL_DATA_RDY_EN;
267 else
268 msc &= ~ADIS_MSC_CTRL_DATA_RDY_EN;
269
270 ret = adis_write_reg_16(adis, adis->data->msc_ctrl_reg, msc);
271
272error_ret:
273 return ret;
274}
275EXPORT_SYMBOL(adis_enable_irq);
276
277/**
278 * adis_check_status() - Check the device for error conditions
279 * @adis: The adis device
280 *
281 * Returns 0 on success, a negative error code otherwise
282 */
283int adis_check_status(struct adis *adis)
284{
285 uint16_t status;
286 int ret;
287 int i;
288
289 ret = adis_read_reg_16(adis, adis->data->diag_stat_reg, &status);
Alexandru Ardelean6a39ab32019-11-01 11:35:00 +0200290 if (ret)
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000291 return ret;
292
293 status &= adis->data->status_error_mask;
294
295 if (status == 0)
296 return 0;
297
298 for (i = 0; i < 16; ++i) {
299 if (status & BIT(i)) {
300 dev_err(&adis->spi->dev, "%s.\n",
301 adis->data->status_error_msgs[i]);
302 }
303 }
304
305 return -EIO;
306}
307EXPORT_SYMBOL_GPL(adis_check_status);
308
309/**
310 * adis_reset() - Reset the device
311 * @adis: The adis device
312 *
313 * Returns 0 on success, a negative error code otherwise
314 */
315int adis_reset(struct adis *adis)
316{
317 int ret;
318
319 ret = adis_write_reg_8(adis, adis->data->glob_cmd_reg,
320 ADIS_GLOB_CMD_SW_RESET);
321 if (ret)
322 dev_err(&adis->spi->dev, "Failed to reset device: %d\n", ret);
323
324 return ret;
325}
326EXPORT_SYMBOL_GPL(adis_reset);
327
328static int adis_self_test(struct adis *adis)
329{
330 int ret;
331
332 ret = adis_write_reg_16(adis, adis->data->msc_ctrl_reg,
333 adis->data->self_test_mask);
334 if (ret) {
335 dev_err(&adis->spi->dev, "Failed to initiate self test: %d\n",
336 ret);
337 return ret;
338 }
339
340 msleep(adis->data->startup_delay);
341
Lars-Peter Clausenaf8a4122016-04-15 16:59:38 +0200342 ret = adis_check_status(adis);
343
344 if (adis->data->self_test_no_autoclear)
345 adis_write_reg_16(adis, adis->data->msc_ctrl_reg, 0x00);
346
347 return ret;
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000348}
349
350/**
351 * adis_inital_startup() - Performs device self-test
352 * @adis: The adis device
353 *
354 * Returns 0 if the device is operational, a negative error code otherwise.
355 *
356 * This function should be called early on in the device initialization sequence
357 * to ensure that the device is in a sane and known state and that it is usable.
358 */
359int adis_initial_startup(struct adis *adis)
360{
361 int ret;
362
363 ret = adis_self_test(adis);
364 if (ret) {
365 dev_err(&adis->spi->dev, "Self-test failed, trying reset.\n");
366 adis_reset(adis);
367 msleep(adis->data->startup_delay);
368 ret = adis_self_test(adis);
369 if (ret) {
370 dev_err(&adis->spi->dev, "Second self-test failed, giving up.\n");
371 return ret;
372 }
373 }
374
375 return 0;
376}
377EXPORT_SYMBOL_GPL(adis_initial_startup);
378
379/**
380 * adis_single_conversion() - Performs a single sample conversion
381 * @indio_dev: The IIO device
382 * @chan: The IIO channel
383 * @error_mask: Mask for the error bit
384 * @val: Result of the conversion
385 *
386 * Returns IIO_VAL_INT on success, a negative error code otherwise.
387 *
388 * The function performs a single conversion on a given channel and post
389 * processes the value accordingly to the channel spec. If a error_mask is given
390 * the function will check if the mask is set in the returned raw value. If it
391 * is set the function will perform a self-check. If the device does not report
392 * a error bit in the channels raw value set error_mask to 0.
393 */
394int adis_single_conversion(struct iio_dev *indio_dev,
395 const struct iio_chan_spec *chan, unsigned int error_mask, int *val)
396{
397 struct adis *adis = iio_device_get_drvdata(indio_dev);
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000398 unsigned int uval;
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000399 int ret;
400
401 mutex_lock(&indio_dev->mlock);
402
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000403 ret = adis_read_reg(adis, chan->address, &uval,
404 chan->scan_type.storagebits / 8);
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000405 if (ret)
406 goto err_unlock;
407
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000408 if (uval & error_mask) {
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000409 ret = adis_check_status(adis);
410 if (ret)
411 goto err_unlock;
412 }
413
414 if (chan->scan_type.sign == 's')
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000415 *val = sign_extend32(uval, chan->scan_type.realbits - 1);
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000416 else
Lars-Peter Clausen57a12282012-11-20 13:36:00 +0000417 *val = uval & ((1 << chan->scan_type.realbits) - 1);
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000418
419 ret = IIO_VAL_INT;
420err_unlock:
421 mutex_unlock(&indio_dev->mlock);
422 return ret;
423}
424EXPORT_SYMBOL_GPL(adis_single_conversion);
425
426/**
427 * adis_init() - Initialize adis device structure
428 * @adis: The adis device
429 * @indio_dev: The iio device
430 * @spi: The spi device
431 * @data: Chip specific data
432 *
433 * Returns 0 on success, a negative error code otherwise.
434 *
435 * This function must be called, before any other adis helper function may be
436 * called.
437 */
438int adis_init(struct adis *adis, struct iio_dev *indio_dev,
439 struct spi_device *spi, const struct adis_data *data)
440{
441 mutex_init(&adis->txrx_lock);
442 adis->spi = spi;
443 adis->data = data;
444 iio_device_set_drvdata(indio_dev, adis);
445
Lars-Peter Clausen484a0bf2012-11-20 13:36:00 +0000446 if (data->has_paging) {
447 /* Need to set the page before first read/write */
448 adis->current_page = -1;
449 } else {
450 /* Page will always be 0 */
451 adis->current_page = 0;
452 }
453
Lars-Peter Clausenccd2b522012-11-13 13:28:00 +0000454 return adis_enable_irq(adis, false);
455}
456EXPORT_SYMBOL_GPL(adis_init);
457
458MODULE_LICENSE("GPL");
459MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
460MODULE_DESCRIPTION("Common library code for ADIS16XXX devices");