Skip to content

Commit ccd2b52

Browse files
larsclausenjic23
authored andcommitted
staging:iio: Add common ADIS library
A lot of the devices from the ADIS family use the same methods for accessing registers, sampling data and trigger handling. They also have similar register layout for the control registers. This patch adds a common library for these devices. The library implements functions for reading and writing registers as buffer and trigger management. It also provides a set functions for accessing the control registers and for running the devices internal self-test. Having this common library code will allow us to remove a lot of duplicated code. Signed-off-by: Lars-Peter Clausen <[email protected]> Signed-off-by: Jonathan Cameron <[email protected]>
1 parent 6dc973d commit ccd2b52

File tree

6 files changed

+824
-0
lines changed

6 files changed

+824
-0
lines changed

drivers/staging/iio/imu/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,16 @@ config ADIS16400
1515
(adis16400 series also have magnetometers).
1616

1717
endmenu
18+
19+
config IIO_ADIS_LIB
20+
tristate
21+
help
22+
A set of IO helper functions for the Analog Devices ADIS* device family.
23+
24+
config IIO_ADIS_LIB_BUFFER
25+
bool
26+
select IIO_TRIGGER
27+
select IIO_SW_RING
28+
help
29+
A set of buffer helper functions for the Analog Devices ADIS* device
30+
family.

drivers/staging/iio/imu/Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,8 @@
55
adis16400-y := adis16400_core.o
66
adis16400-$(CONFIG_IIO_BUFFER) += adis16400_ring.o adis16400_trigger.o
77
obj-$(CONFIG_ADIS16400) += adis16400.o
8+
9+
adis_lib-y += adis.o
10+
adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_trigger.o
11+
adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
12+
obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o

drivers/staging/iio/imu/adis.c

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
/*
2+
* Common library for ADIS16XXX devices
3+
*
4+
* Copyright 2012 Analog Devices Inc.
5+
* Author: Lars-Peter Clausen <[email protected]>
6+
*
7+
* Licensed under the GPL-2 or later.
8+
*/
9+
10+
#include <linux/delay.h>
11+
#include <linux/mutex.h>
12+
#include <linux/device.h>
13+
#include <linux/kernel.h>
14+
#include <linux/spi/spi.h>
15+
#include <linux/slab.h>
16+
#include <linux/sysfs.h>
17+
#include <linux/module.h>
18+
#include <asm/unaligned.h>
19+
20+
#include <linux/iio/iio.h>
21+
#include <linux/iio/sysfs.h>
22+
#include <linux/iio/buffer.h>
23+
24+
#include "adis.h"
25+
26+
#define ADIS_MSC_CTRL_DATA_RDY_EN BIT(2)
27+
#define ADIS_MSC_CTRL_DATA_RDY_POL_HIGH BIT(1)
28+
#define ADIS_MSC_CTRL_DATA_RDY_DIO2 BIT(0)
29+
#define ADIS_GLOB_CMD_SW_RESET BIT(7)
30+
31+
/**
32+
* adis_write_reg_8() - Write single byte to a register
33+
* @adis: The adis device
34+
* @reg: The address of the register to be written
35+
* @val: The value to write
36+
*/
37+
int adis_write_reg_8(struct adis *adis, unsigned int reg, uint8_t val)
38+
{
39+
int ret;
40+
41+
mutex_lock(&adis->txrx_lock);
42+
adis->tx[0] = ADIS_WRITE_REG(reg);
43+
adis->tx[1] = val;
44+
45+
ret = spi_write(adis->spi, adis->tx, 2);
46+
mutex_unlock(&adis->txrx_lock);
47+
48+
return ret;
49+
}
50+
EXPORT_SYMBOL_GPL(adis_write_reg_8);
51+
52+
/**
53+
* adis_write_reg_16() - Write 2 bytes to a pair of registers
54+
* @adis: The adis device
55+
* @reg: The address of the lower of the two registers
56+
* @val: Value to be written
57+
*/
58+
int adis_write_reg_16(struct adis *adis, unsigned int reg, uint16_t value)
59+
{
60+
int ret;
61+
struct spi_message msg;
62+
struct spi_transfer xfers[] = {
63+
{
64+
.tx_buf = adis->tx,
65+
.bits_per_word = 8,
66+
.len = 2,
67+
.cs_change = 1,
68+
.delay_usecs = adis->data->write_delay,
69+
}, {
70+
.tx_buf = adis->tx + 2,
71+
.bits_per_word = 8,
72+
.len = 2,
73+
.delay_usecs = adis->data->write_delay,
74+
},
75+
};
76+
77+
mutex_lock(&adis->txrx_lock);
78+
adis->tx[0] = ADIS_WRITE_REG(reg);
79+
adis->tx[1] = value & 0xff;
80+
adis->tx[2] = ADIS_WRITE_REG(reg + 1);
81+
adis->tx[3] = (value >> 8) & 0xff;
82+
83+
spi_message_init(&msg);
84+
spi_message_add_tail(&xfers[0], &msg);
85+
spi_message_add_tail(&xfers[1], &msg);
86+
ret = spi_sync(adis->spi, &msg);
87+
mutex_unlock(&adis->txrx_lock);
88+
89+
return ret;
90+
}
91+
EXPORT_SYMBOL_GPL(adis_write_reg_16);
92+
93+
/**
94+
* adis_read_reg_16() - read 2 bytes from a 16-bit register
95+
* @adis: The adis device
96+
* @reg: The address of the lower of the two registers
97+
* @val: The value read back from the device
98+
*/
99+
int adis_read_reg_16(struct adis *adis, unsigned int reg, uint16_t *val)
100+
{
101+
struct spi_message msg;
102+
int ret;
103+
struct spi_transfer xfers[] = {
104+
{
105+
.tx_buf = adis->tx,
106+
.bits_per_word = 8,
107+
.len = 2,
108+
.cs_change = 1,
109+
.delay_usecs = adis->data->read_delay,
110+
}, {
111+
.rx_buf = adis->rx,
112+
.bits_per_word = 8,
113+
.len = 2,
114+
.delay_usecs = adis->data->read_delay,
115+
},
116+
};
117+
118+
mutex_lock(&adis->txrx_lock);
119+
adis->tx[0] = ADIS_READ_REG(reg);
120+
adis->tx[1] = 0;
121+
122+
spi_message_init(&msg);
123+
spi_message_add_tail(&xfers[0], &msg);
124+
spi_message_add_tail(&xfers[1], &msg);
125+
ret = spi_sync(adis->spi, &msg);
126+
if (ret) {
127+
dev_err(&adis->spi->dev, "Failed to read 16 bit register 0x%02X: %d\n",
128+
reg, ret);
129+
goto error_ret;
130+
}
131+
*val = get_unaligned_be16(adis->rx);
132+
133+
error_ret:
134+
mutex_unlock(&adis->txrx_lock);
135+
return ret;
136+
}
137+
EXPORT_SYMBOL_GPL(adis_read_reg_16);
138+
139+
/**
140+
* adis_enable_irq() - Enable or disable data ready IRQ
141+
* @adis: The adis device
142+
* @enable: Whether to enable the IRQ
143+
*
144+
* Returns 0 on success, negative error code otherwise
145+
*/
146+
int adis_enable_irq(struct adis *adis, bool enable)
147+
{
148+
int ret = 0;
149+
uint16_t msc;
150+
151+
ret = adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc);
152+
if (ret)
153+
goto error_ret;
154+
155+
msc |= ADIS_MSC_CTRL_DATA_RDY_POL_HIGH;
156+
msc &= ~ADIS_MSC_CTRL_DATA_RDY_DIO2;
157+
if (enable)
158+
msc |= ADIS_MSC_CTRL_DATA_RDY_EN;
159+
else
160+
msc &= ~ADIS_MSC_CTRL_DATA_RDY_EN;
161+
162+
ret = adis_write_reg_16(adis, adis->data->msc_ctrl_reg, msc);
163+
164+
error_ret:
165+
return ret;
166+
}
167+
EXPORT_SYMBOL(adis_enable_irq);
168+
169+
/**
170+
* adis_check_status() - Check the device for error conditions
171+
* @adis: The adis device
172+
*
173+
* Returns 0 on success, a negative error code otherwise
174+
*/
175+
int adis_check_status(struct adis *adis)
176+
{
177+
uint16_t status;
178+
int ret;
179+
int i;
180+
181+
ret = adis_read_reg_16(adis, adis->data->diag_stat_reg, &status);
182+
if (ret < 0)
183+
return ret;
184+
185+
status &= adis->data->status_error_mask;
186+
187+
if (status == 0)
188+
return 0;
189+
190+
for (i = 0; i < 16; ++i) {
191+
if (status & BIT(i)) {
192+
dev_err(&adis->spi->dev, "%s.\n",
193+
adis->data->status_error_msgs[i]);
194+
}
195+
}
196+
197+
return -EIO;
198+
}
199+
EXPORT_SYMBOL_GPL(adis_check_status);
200+
201+
/**
202+
* adis_reset() - Reset the device
203+
* @adis: The adis device
204+
*
205+
* Returns 0 on success, a negative error code otherwise
206+
*/
207+
int adis_reset(struct adis *adis)
208+
{
209+
int ret;
210+
211+
ret = adis_write_reg_8(adis, adis->data->glob_cmd_reg,
212+
ADIS_GLOB_CMD_SW_RESET);
213+
if (ret)
214+
dev_err(&adis->spi->dev, "Failed to reset device: %d\n", ret);
215+
216+
return ret;
217+
}
218+
EXPORT_SYMBOL_GPL(adis_reset);
219+
220+
static int adis_self_test(struct adis *adis)
221+
{
222+
int ret;
223+
224+
ret = adis_write_reg_16(adis, adis->data->msc_ctrl_reg,
225+
adis->data->self_test_mask);
226+
if (ret) {
227+
dev_err(&adis->spi->dev, "Failed to initiate self test: %d\n",
228+
ret);
229+
return ret;
230+
}
231+
232+
msleep(adis->data->startup_delay);
233+
234+
return adis_check_status(adis);
235+
}
236+
237+
/**
238+
* adis_inital_startup() - Performs device self-test
239+
* @adis: The adis device
240+
*
241+
* Returns 0 if the device is operational, a negative error code otherwise.
242+
*
243+
* This function should be called early on in the device initialization sequence
244+
* to ensure that the device is in a sane and known state and that it is usable.
245+
*/
246+
int adis_initial_startup(struct adis *adis)
247+
{
248+
int ret;
249+
250+
ret = adis_self_test(adis);
251+
if (ret) {
252+
dev_err(&adis->spi->dev, "Self-test failed, trying reset.\n");
253+
adis_reset(adis);
254+
msleep(adis->data->startup_delay);
255+
ret = adis_self_test(adis);
256+
if (ret) {
257+
dev_err(&adis->spi->dev, "Second self-test failed, giving up.\n");
258+
return ret;
259+
}
260+
}
261+
262+
return 0;
263+
}
264+
EXPORT_SYMBOL_GPL(adis_initial_startup);
265+
266+
/**
267+
* adis_single_conversion() - Performs a single sample conversion
268+
* @indio_dev: The IIO device
269+
* @chan: The IIO channel
270+
* @error_mask: Mask for the error bit
271+
* @val: Result of the conversion
272+
*
273+
* Returns IIO_VAL_INT on success, a negative error code otherwise.
274+
*
275+
* The function performs a single conversion on a given channel and post
276+
* processes the value accordingly to the channel spec. If a error_mask is given
277+
* the function will check if the mask is set in the returned raw value. If it
278+
* is set the function will perform a self-check. If the device does not report
279+
* a error bit in the channels raw value set error_mask to 0.
280+
*/
281+
int adis_single_conversion(struct iio_dev *indio_dev,
282+
const struct iio_chan_spec *chan, unsigned int error_mask, int *val)
283+
{
284+
struct adis *adis = iio_device_get_drvdata(indio_dev);
285+
uint16_t val16;
286+
int ret;
287+
288+
mutex_lock(&indio_dev->mlock);
289+
290+
ret = adis_read_reg_16(adis, chan->address, &val16);
291+
if (ret)
292+
goto err_unlock;
293+
294+
if (val16 & error_mask) {
295+
ret = adis_check_status(adis);
296+
if (ret)
297+
goto err_unlock;
298+
}
299+
300+
if (chan->scan_type.sign == 's')
301+
*val = sign_extend32(val16, chan->scan_type.realbits - 1);
302+
else
303+
*val = val16 & ((1 << chan->scan_type.realbits) - 1);
304+
305+
ret = IIO_VAL_INT;
306+
err_unlock:
307+
mutex_unlock(&indio_dev->mlock);
308+
return ret;
309+
}
310+
EXPORT_SYMBOL_GPL(adis_single_conversion);
311+
312+
/**
313+
* adis_init() - Initialize adis device structure
314+
* @adis: The adis device
315+
* @indio_dev: The iio device
316+
* @spi: The spi device
317+
* @data: Chip specific data
318+
*
319+
* Returns 0 on success, a negative error code otherwise.
320+
*
321+
* This function must be called, before any other adis helper function may be
322+
* called.
323+
*/
324+
int adis_init(struct adis *adis, struct iio_dev *indio_dev,
325+
struct spi_device *spi, const struct adis_data *data)
326+
{
327+
mutex_init(&adis->txrx_lock);
328+
adis->spi = spi;
329+
adis->data = data;
330+
iio_device_set_drvdata(indio_dev, adis);
331+
332+
return adis_enable_irq(adis, false);
333+
}
334+
EXPORT_SYMBOL_GPL(adis_init);
335+
336+
MODULE_LICENSE("GPL");
337+
MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
338+
MODULE_DESCRIPTION("Common library code for ADIS16XXX devices");

0 commit comments

Comments
 (0)