diff options
author | Lars-Peter Clausen <lars@metafoo.de> | 2012-11-20 08:36:00 -0500 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2012-11-20 16:28:51 -0500 |
commit | 2f3abe6cbb6c963ac790b40936b6761c9f0497b4 (patch) | |
tree | 1050db75a5d9c8040c500e10e5a308ab5d2ed5df /drivers/iio/imu | |
parent | 6807d7211327dbdd8df3692f3d26ca711514ba71 (diff) |
iio:imu: Add support for the ADIS16480 and similar IMUs
This patch adds support for the ADIS16375, ADIS16480, ADIS16485, ADIS16488 6
degree to 10 degree of freedom IMUs.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers/iio/imu')
-rw-r--r-- | drivers/iio/imu/Kconfig | 16 | ||||
-rw-r--r-- | drivers/iio/imu/Makefile | 2 | ||||
-rw-r--r-- | drivers/iio/imu/adis.c | 3 | ||||
-rw-r--r-- | drivers/iio/imu/adis16480.c | 925 |
4 files changed, 946 insertions, 0 deletions
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index c24410c873c7..3d79a40e916b 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig | |||
@@ -1,3 +1,19 @@ | |||
1 | # | ||
2 | # IIO imu drivers configuration | ||
3 | # | ||
4 | menu "Inertial measurement units" | ||
5 | |||
6 | config ADIS16480 | ||
7 | tristate "Analog Devices ADIS16480 and similar IMU driver" | ||
8 | depends on SPI | ||
9 | select IIO_ADIS_LIB | ||
10 | select IIO_ADIS_LIB_BUFFER if IIO_BUFFER | ||
11 | help | ||
12 | Say yes here to build support for Analog Devices ADIS16375, ADIS16480, | ||
13 | ADIS16485, ADIS16488 inertial sensors. | ||
14 | |||
15 | endmenu | ||
16 | |||
1 | config IIO_ADIS_LIB | 17 | config IIO_ADIS_LIB |
2 | tristate | 18 | tristate |
3 | help | 19 | help |
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index 97676ab5723d..cfe57638f6f9 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile | |||
@@ -2,6 +2,8 @@ | |||
2 | # Makefile for Inertial Measurement Units | 2 | # Makefile for Inertial Measurement Units |
3 | # | 3 | # |
4 | 4 | ||
5 | obj-$(CONFIG_ADIS16480) += adis16480.o | ||
6 | |||
5 | adis_lib-y += adis.o | 7 | adis_lib-y += adis.o |
6 | adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_trigger.o | 8 | adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_trigger.o |
7 | adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o | 9 | adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o |
diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index c4ea04ffa60e..911255d41c1a 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c | |||
@@ -238,6 +238,9 @@ int adis_enable_irq(struct adis *adis, bool enable) | |||
238 | int ret = 0; | 238 | int ret = 0; |
239 | uint16_t msc; | 239 | uint16_t msc; |
240 | 240 | ||
241 | if (adis->data->enable_irq) | ||
242 | return adis->data->enable_irq(adis, enable); | ||
243 | |||
241 | ret = adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc); | 244 | ret = adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc); |
242 | if (ret) | 245 | if (ret) |
243 | goto error_ret; | 246 | goto error_ret; |
diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c new file mode 100644 index 000000000000..a080b3515015 --- /dev/null +++ b/drivers/iio/imu/adis16480.c | |||
@@ -0,0 +1,925 @@ | |||
1 | /* | ||
2 | * ADIS16480 and similar IMUs driver | ||
3 | * | ||
4 | * Copyright 2012 Analog Devices Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/mutex.h> | ||
15 | #include <linux/device.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/spi/spi.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/sysfs.h> | ||
20 | #include <linux/module.h> | ||
21 | |||
22 | #include <linux/iio/iio.h> | ||
23 | #include <linux/iio/sysfs.h> | ||
24 | #include <linux/iio/buffer.h> | ||
25 | #include <linux/iio/imu/adis.h> | ||
26 | |||
27 | #include <linux/iio/iio.h> | ||
28 | #include <linux/debugfs.h> | ||
29 | |||
30 | #define ADIS16480_PAGE_SIZE 0x80 | ||
31 | |||
32 | #define ADIS16480_REG(page, reg) ((page) * ADIS16480_PAGE_SIZE + (reg)) | ||
33 | |||
34 | #define ADIS16480_REG_PAGE_ID 0x00 /* Same address on each page */ | ||
35 | #define ADIS16480_REG_SEQ_CNT ADIS16480_REG(0x00, 0x06) | ||
36 | #define ADIS16480_REG_SYS_E_FLA ADIS16480_REG(0x00, 0x08) | ||
37 | #define ADIS16480_REG_DIAG_STS ADIS16480_REG(0x00, 0x0A) | ||
38 | #define ADIS16480_REG_ALM_STS ADIS16480_REG(0x00, 0x0C) | ||
39 | #define ADIS16480_REG_TEMP_OUT ADIS16480_REG(0x00, 0x0E) | ||
40 | #define ADIS16480_REG_X_GYRO_OUT ADIS16480_REG(0x00, 0x10) | ||
41 | #define ADIS16480_REG_Y_GYRO_OUT ADIS16480_REG(0x00, 0x14) | ||
42 | #define ADIS16480_REG_Z_GYRO_OUT ADIS16480_REG(0x00, 0x18) | ||
43 | #define ADIS16480_REG_X_ACCEL_OUT ADIS16480_REG(0x00, 0x1C) | ||
44 | #define ADIS16480_REG_Y_ACCEL_OUT ADIS16480_REG(0x00, 0x20) | ||
45 | #define ADIS16480_REG_Z_ACCEL_OUT ADIS16480_REG(0x00, 0x24) | ||
46 | #define ADIS16480_REG_X_MAGN_OUT ADIS16480_REG(0x00, 0x28) | ||
47 | #define ADIS16480_REG_Y_MAGN_OUT ADIS16480_REG(0x00, 0x2A) | ||
48 | #define ADIS16480_REG_Z_MAGN_OUT ADIS16480_REG(0x00, 0x2C) | ||
49 | #define ADIS16480_REG_BAROM_OUT ADIS16480_REG(0x00, 0x2E) | ||
50 | #define ADIS16480_REG_X_DELTAANG_OUT ADIS16480_REG(0x00, 0x40) | ||
51 | #define ADIS16480_REG_Y_DELTAANG_OUT ADIS16480_REG(0x00, 0x44) | ||
52 | #define ADIS16480_REG_Z_DELTAANG_OUT ADIS16480_REG(0x00, 0x48) | ||
53 | #define ADIS16480_REG_X_DELTAVEL_OUT ADIS16480_REG(0x00, 0x4C) | ||
54 | #define ADIS16480_REG_Y_DELTAVEL_OUT ADIS16480_REG(0x00, 0x50) | ||
55 | #define ADIS16480_REG_Z_DELTAVEL_OUT ADIS16480_REG(0x00, 0x54) | ||
56 | #define ADIS16480_REG_PROD_ID ADIS16480_REG(0x00, 0x7E) | ||
57 | |||
58 | #define ADIS16480_REG_X_GYRO_SCALE ADIS16480_REG(0x02, 0x04) | ||
59 | #define ADIS16480_REG_Y_GYRO_SCALE ADIS16480_REG(0x02, 0x06) | ||
60 | #define ADIS16480_REG_Z_GYRO_SCALE ADIS16480_REG(0x02, 0x08) | ||
61 | #define ADIS16480_REG_X_ACCEL_SCALE ADIS16480_REG(0x02, 0x0A) | ||
62 | #define ADIS16480_REG_Y_ACCEL_SCALE ADIS16480_REG(0x02, 0x0C) | ||
63 | #define ADIS16480_REG_Z_ACCEL_SCALE ADIS16480_REG(0x02, 0x0E) | ||
64 | #define ADIS16480_REG_X_GYRO_BIAS ADIS16480_REG(0x02, 0x10) | ||
65 | #define ADIS16480_REG_Y_GYRO_BIAS ADIS16480_REG(0x02, 0x14) | ||
66 | #define ADIS16480_REG_Z_GYRO_BIAS ADIS16480_REG(0x02, 0x18) | ||
67 | #define ADIS16480_REG_X_ACCEL_BIAS ADIS16480_REG(0x02, 0x1C) | ||
68 | #define ADIS16480_REG_Y_ACCEL_BIAS ADIS16480_REG(0x02, 0x20) | ||
69 | #define ADIS16480_REG_Z_ACCEL_BIAS ADIS16480_REG(0x02, 0x24) | ||
70 | #define ADIS16480_REG_X_HARD_IRON ADIS16480_REG(0x02, 0x28) | ||
71 | #define ADIS16480_REG_Y_HARD_IRON ADIS16480_REG(0x02, 0x2A) | ||
72 | #define ADIS16480_REG_Z_HARD_IRON ADIS16480_REG(0x02, 0x2C) | ||
73 | #define ADIS16480_REG_BAROM_BIAS ADIS16480_REG(0x02, 0x40) | ||
74 | #define ADIS16480_REG_FLASH_CNT ADIS16480_REG(0x02, 0x7C) | ||
75 | |||
76 | #define ADIS16480_REG_GLOB_CMD ADIS16480_REG(0x03, 0x02) | ||
77 | #define ADIS16480_REG_FNCTIO_CTRL ADIS16480_REG(0x03, 0x06) | ||
78 | #define ADIS16480_REG_GPIO_CTRL ADIS16480_REG(0x03, 0x08) | ||
79 | #define ADIS16480_REG_CONFIG ADIS16480_REG(0x03, 0x0A) | ||
80 | #define ADIS16480_REG_DEC_RATE ADIS16480_REG(0x03, 0x0C) | ||
81 | #define ADIS16480_REG_SLP_CNT ADIS16480_REG(0x03, 0x10) | ||
82 | #define ADIS16480_REG_FILTER_BNK0 ADIS16480_REG(0x03, 0x16) | ||
83 | #define ADIS16480_REG_FILTER_BNK1 ADIS16480_REG(0x03, 0x18) | ||
84 | #define ADIS16480_REG_ALM_CNFG0 ADIS16480_REG(0x03, 0x20) | ||
85 | #define ADIS16480_REG_ALM_CNFG1 ADIS16480_REG(0x03, 0x22) | ||
86 | #define ADIS16480_REG_ALM_CNFG2 ADIS16480_REG(0x03, 0x24) | ||
87 | #define ADIS16480_REG_XG_ALM_MAGN ADIS16480_REG(0x03, 0x28) | ||
88 | #define ADIS16480_REG_YG_ALM_MAGN ADIS16480_REG(0x03, 0x2A) | ||
89 | #define ADIS16480_REG_ZG_ALM_MAGN ADIS16480_REG(0x03, 0x2C) | ||
90 | #define ADIS16480_REG_XA_ALM_MAGN ADIS16480_REG(0x03, 0x2E) | ||
91 | #define ADIS16480_REG_YA_ALM_MAGN ADIS16480_REG(0x03, 0x30) | ||
92 | #define ADIS16480_REG_ZA_ALM_MAGN ADIS16480_REG(0x03, 0x32) | ||
93 | #define ADIS16480_REG_XM_ALM_MAGN ADIS16480_REG(0x03, 0x34) | ||
94 | #define ADIS16480_REG_YM_ALM_MAGN ADIS16480_REG(0x03, 0x36) | ||
95 | #define ADIS16480_REG_ZM_ALM_MAGN ADIS16480_REG(0x03, 0x38) | ||
96 | #define ADIS16480_REG_BR_ALM_MAGN ADIS16480_REG(0x03, 0x3A) | ||
97 | #define ADIS16480_REG_FIRM_REV ADIS16480_REG(0x03, 0x78) | ||
98 | #define ADIS16480_REG_FIRM_DM ADIS16480_REG(0x03, 0x7A) | ||
99 | #define ADIS16480_REG_FIRM_Y ADIS16480_REG(0x03, 0x7C) | ||
100 | |||
101 | #define ADIS16480_REG_SERIAL_NUM ADIS16480_REG(0x04, 0x20) | ||
102 | |||
103 | /* Each filter coefficent bank spans two pages */ | ||
104 | #define ADIS16480_FIR_COEF(page) (x < 60 ? ADIS16480_REG(page, (x) + 8) : \ | ||
105 | ADIS16480_REG((page) + 1, (x) - 60 + 8)) | ||
106 | #define ADIS16480_FIR_COEF_A(x) ADIS16480_FIR_COEF(0x05, (x)) | ||
107 | #define ADIS16480_FIR_COEF_B(x) ADIS16480_FIR_COEF(0x07, (x)) | ||
108 | #define ADIS16480_FIR_COEF_C(x) ADIS16480_FIR_COEF(0x09, (x)) | ||
109 | #define ADIS16480_FIR_COEF_D(x) ADIS16480_FIR_COEF(0x0B, (x)) | ||
110 | |||
111 | struct adis16480_chip_info { | ||
112 | unsigned int num_channels; | ||
113 | const struct iio_chan_spec *channels; | ||
114 | }; | ||
115 | |||
116 | struct adis16480 { | ||
117 | const struct adis16480_chip_info *chip_info; | ||
118 | |||
119 | struct adis adis; | ||
120 | }; | ||
121 | |||
122 | #ifdef CONFIG_DEBUG_FS | ||
123 | |||
124 | static ssize_t adis16480_show_firmware_revision(struct file *file, | ||
125 | char __user *userbuf, size_t count, loff_t *ppos) | ||
126 | { | ||
127 | struct adis16480 *adis16480 = file->private_data; | ||
128 | char buf[6]; | ||
129 | size_t len; | ||
130 | u16 rev; | ||
131 | int ret; | ||
132 | |||
133 | ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_REV, &rev); | ||
134 | if (ret < 0) | ||
135 | return ret; | ||
136 | |||
137 | len = snprintf(buf, sizeof(buf), "%x.%x\n", rev >> 8, rev & 0xff); | ||
138 | |||
139 | return simple_read_from_buffer(userbuf, count, ppos, buf, len); | ||
140 | } | ||
141 | |||
142 | static const struct file_operations adis16480_firmware_revision_fops = { | ||
143 | .open = simple_open, | ||
144 | .read = adis16480_show_firmware_revision, | ||
145 | .llseek = default_llseek, | ||
146 | .owner = THIS_MODULE, | ||
147 | }; | ||
148 | |||
149 | static ssize_t adis16480_show_firmware_date(struct file *file, | ||
150 | char __user *userbuf, size_t count, loff_t *ppos) | ||
151 | { | ||
152 | struct adis16480 *adis16480 = file->private_data; | ||
153 | u16 md, year; | ||
154 | char buf[12]; | ||
155 | size_t len; | ||
156 | int ret; | ||
157 | |||
158 | ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_Y, &year); | ||
159 | if (ret < 0) | ||
160 | return ret; | ||
161 | |||
162 | ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_DM, &md); | ||
163 | if (ret < 0) | ||
164 | return ret; | ||
165 | |||
166 | len = snprintf(buf, sizeof(buf), "%.2x-%.2x-%.4x\n", | ||
167 | md >> 8, md & 0xff, year); | ||
168 | |||
169 | return simple_read_from_buffer(userbuf, count, ppos, buf, len); | ||
170 | } | ||
171 | |||
172 | static const struct file_operations adis16480_firmware_date_fops = { | ||
173 | .open = simple_open, | ||
174 | .read = adis16480_show_firmware_date, | ||
175 | .llseek = default_llseek, | ||
176 | .owner = THIS_MODULE, | ||
177 | }; | ||
178 | |||
179 | static int adis16480_show_serial_number(void *arg, u64 *val) | ||
180 | { | ||
181 | struct adis16480 *adis16480 = arg; | ||
182 | u16 serial; | ||
183 | int ret; | ||
184 | |||
185 | ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_SERIAL_NUM, | ||
186 | &serial); | ||
187 | if (ret < 0) | ||
188 | return ret; | ||
189 | |||
190 | *val = serial; | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | DEFINE_SIMPLE_ATTRIBUTE(adis16480_serial_number_fops, | ||
195 | adis16480_show_serial_number, NULL, "0x%.4llx\n"); | ||
196 | |||
197 | static int adis16480_show_product_id(void *arg, u64 *val) | ||
198 | { | ||
199 | struct adis16480 *adis16480 = arg; | ||
200 | u16 prod_id; | ||
201 | int ret; | ||
202 | |||
203 | ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_PROD_ID, | ||
204 | &prod_id); | ||
205 | if (ret < 0) | ||
206 | return ret; | ||
207 | |||
208 | *val = prod_id; | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | DEFINE_SIMPLE_ATTRIBUTE(adis16480_product_id_fops, | ||
213 | adis16480_show_product_id, NULL, "%llu\n"); | ||
214 | |||
215 | static int adis16480_show_flash_count(void *arg, u64 *val) | ||
216 | { | ||
217 | struct adis16480 *adis16480 = arg; | ||
218 | u32 flash_count; | ||
219 | int ret; | ||
220 | |||
221 | ret = adis_read_reg_32(&adis16480->adis, ADIS16480_REG_FLASH_CNT, | ||
222 | &flash_count); | ||
223 | if (ret < 0) | ||
224 | return ret; | ||
225 | |||
226 | *val = flash_count; | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | DEFINE_SIMPLE_ATTRIBUTE(adis16480_flash_count_fops, | ||
231 | adis16480_show_flash_count, NULL, "%lld\n"); | ||
232 | |||
233 | static int adis16480_debugfs_init(struct iio_dev *indio_dev) | ||
234 | { | ||
235 | struct adis16480 *adis16480 = iio_priv(indio_dev); | ||
236 | |||
237 | debugfs_create_file("firmware_revision", 0400, | ||
238 | indio_dev->debugfs_dentry, adis16480, | ||
239 | &adis16480_firmware_revision_fops); | ||
240 | debugfs_create_file("firmware_date", 0400, indio_dev->debugfs_dentry, | ||
241 | adis16480, &adis16480_firmware_date_fops); | ||
242 | debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry, | ||
243 | adis16480, &adis16480_serial_number_fops); | ||
244 | debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry, | ||
245 | adis16480, &adis16480_product_id_fops); | ||
246 | debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry, | ||
247 | adis16480, &adis16480_flash_count_fops); | ||
248 | |||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | #else | ||
253 | |||
254 | static int adis16480_debugfs_init(struct iio_dev *indio_dev) | ||
255 | { | ||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | #endif | ||
260 | |||
261 | static int adis16480_set_freq(struct adis16480 *st, unsigned int freq) | ||
262 | { | ||
263 | unsigned int t; | ||
264 | |||
265 | t = 2460000 / freq; | ||
266 | if (t > 2048) | ||
267 | t = 2048; | ||
268 | |||
269 | if (t != 0) | ||
270 | t--; | ||
271 | |||
272 | return adis_write_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, t); | ||
273 | } | ||
274 | |||
275 | static int adis16480_get_freq(struct adis16480 *st, unsigned int *freq) | ||
276 | { | ||
277 | uint16_t t; | ||
278 | int ret; | ||
279 | |||
280 | ret = adis_read_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, &t); | ||
281 | if (ret < 0) | ||
282 | return ret; | ||
283 | |||
284 | *freq = 2460000 / (t + 1); | ||
285 | |||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | static ssize_t adis16480_read_frequency(struct device *dev, | ||
290 | struct device_attribute *attr, | ||
291 | char *buf) | ||
292 | { | ||
293 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
294 | struct adis16480 *st = iio_priv(indio_dev); | ||
295 | unsigned int freq; | ||
296 | int ret; | ||
297 | |||
298 | ret = adis16480_get_freq(st, &freq); | ||
299 | if (ret < 0) | ||
300 | return ret; | ||
301 | |||
302 | return sprintf(buf, "%d.%.3d\n", freq / 1000, freq % 1000); | ||
303 | } | ||
304 | |||
305 | static ssize_t adis16480_write_frequency(struct device *dev, | ||
306 | struct device_attribute *attr, | ||
307 | const char *buf, | ||
308 | size_t len) | ||
309 | { | ||
310 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
311 | struct adis16480 *st = iio_priv(indio_dev); | ||
312 | int freq_int, freq_fract; | ||
313 | long val; | ||
314 | int ret; | ||
315 | |||
316 | ret = iio_str_to_fixpoint(buf, 100, &freq_int, &freq_fract); | ||
317 | if (ret) | ||
318 | return ret; | ||
319 | |||
320 | val = freq_int * 1000 + freq_fract; | ||
321 | |||
322 | if (val <= 0) | ||
323 | return -EINVAL; | ||
324 | |||
325 | ret = adis16480_set_freq(st, val); | ||
326 | |||
327 | return ret ? ret : len; | ||
328 | } | ||
329 | |||
330 | static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, | ||
331 | adis16480_read_frequency, | ||
332 | adis16480_write_frequency); | ||
333 | |||
334 | enum { | ||
335 | ADIS16480_SCAN_GYRO_X, | ||
336 | ADIS16480_SCAN_GYRO_Y, | ||
337 | ADIS16480_SCAN_GYRO_Z, | ||
338 | ADIS16480_SCAN_ACCEL_X, | ||
339 | ADIS16480_SCAN_ACCEL_Y, | ||
340 | ADIS16480_SCAN_ACCEL_Z, | ||
341 | ADIS16480_SCAN_MAGN_X, | ||
342 | ADIS16480_SCAN_MAGN_Y, | ||
343 | ADIS16480_SCAN_MAGN_Z, | ||
344 | ADIS16480_SCAN_BARO, | ||
345 | ADIS16480_SCAN_TEMP, | ||
346 | }; | ||
347 | |||
348 | static const unsigned int adis16480_calibbias_regs[] = { | ||
349 | [ADIS16480_SCAN_GYRO_X] = ADIS16480_REG_X_GYRO_BIAS, | ||
350 | [ADIS16480_SCAN_GYRO_Y] = ADIS16480_REG_Y_GYRO_BIAS, | ||
351 | [ADIS16480_SCAN_GYRO_Z] = ADIS16480_REG_Z_GYRO_BIAS, | ||
352 | [ADIS16480_SCAN_ACCEL_X] = ADIS16480_REG_X_ACCEL_BIAS, | ||
353 | [ADIS16480_SCAN_ACCEL_Y] = ADIS16480_REG_Y_ACCEL_BIAS, | ||
354 | [ADIS16480_SCAN_ACCEL_Z] = ADIS16480_REG_Z_ACCEL_BIAS, | ||
355 | [ADIS16480_SCAN_MAGN_X] = ADIS16480_REG_X_HARD_IRON, | ||
356 | [ADIS16480_SCAN_MAGN_Y] = ADIS16480_REG_Y_HARD_IRON, | ||
357 | [ADIS16480_SCAN_MAGN_Z] = ADIS16480_REG_Z_HARD_IRON, | ||
358 | [ADIS16480_SCAN_BARO] = ADIS16480_REG_BAROM_BIAS, | ||
359 | }; | ||
360 | |||
361 | static const unsigned int adis16480_calibscale_regs[] = { | ||
362 | [ADIS16480_SCAN_GYRO_X] = ADIS16480_REG_X_GYRO_SCALE, | ||
363 | [ADIS16480_SCAN_GYRO_Y] = ADIS16480_REG_Y_GYRO_SCALE, | ||
364 | [ADIS16480_SCAN_GYRO_Z] = ADIS16480_REG_Z_GYRO_SCALE, | ||
365 | [ADIS16480_SCAN_ACCEL_X] = ADIS16480_REG_X_ACCEL_SCALE, | ||
366 | [ADIS16480_SCAN_ACCEL_Y] = ADIS16480_REG_Y_ACCEL_SCALE, | ||
367 | [ADIS16480_SCAN_ACCEL_Z] = ADIS16480_REG_Z_ACCEL_SCALE, | ||
368 | }; | ||
369 | |||
370 | static int adis16480_set_calibbias(struct iio_dev *indio_dev, | ||
371 | const struct iio_chan_spec *chan, int bias) | ||
372 | { | ||
373 | unsigned int reg = adis16480_calibbias_regs[chan->scan_index]; | ||
374 | struct adis16480 *st = iio_priv(indio_dev); | ||
375 | |||
376 | switch (chan->type) { | ||
377 | case IIO_MAGN: | ||
378 | case IIO_PRESSURE: | ||
379 | if (bias < -0x8000 || bias >= 0x8000) | ||
380 | return -EINVAL; | ||
381 | return adis_write_reg_16(&st->adis, reg, bias); | ||
382 | case IIO_ANGL_VEL: | ||
383 | case IIO_ACCEL: | ||
384 | return adis_write_reg_32(&st->adis, reg, bias); | ||
385 | default: | ||
386 | break; | ||
387 | } | ||
388 | |||
389 | return -EINVAL; | ||
390 | } | ||
391 | |||
392 | static int adis16480_get_calibbias(struct iio_dev *indio_dev, | ||
393 | const struct iio_chan_spec *chan, int *bias) | ||
394 | { | ||
395 | unsigned int reg = adis16480_calibbias_regs[chan->scan_index]; | ||
396 | struct adis16480 *st = iio_priv(indio_dev); | ||
397 | uint16_t val16; | ||
398 | uint32_t val32; | ||
399 | int ret; | ||
400 | |||
401 | switch (chan->type) { | ||
402 | case IIO_MAGN: | ||
403 | case IIO_PRESSURE: | ||
404 | ret = adis_read_reg_16(&st->adis, reg, &val16); | ||
405 | *bias = sign_extend32(val16, 15); | ||
406 | break; | ||
407 | case IIO_ANGL_VEL: | ||
408 | case IIO_ACCEL: | ||
409 | ret = adis_read_reg_32(&st->adis, reg, &val32); | ||
410 | *bias = sign_extend32(val32, 31); | ||
411 | break; | ||
412 | default: | ||
413 | ret = -EINVAL; | ||
414 | } | ||
415 | |||
416 | if (ret < 0) | ||
417 | return ret; | ||
418 | |||
419 | return IIO_VAL_INT; | ||
420 | } | ||
421 | |||
422 | static int adis16480_set_calibscale(struct iio_dev *indio_dev, | ||
423 | const struct iio_chan_spec *chan, int scale) | ||
424 | { | ||
425 | unsigned int reg = adis16480_calibscale_regs[chan->scan_index]; | ||
426 | struct adis16480 *st = iio_priv(indio_dev); | ||
427 | |||
428 | if (scale < -0x8000 || scale >= 0x8000) | ||
429 | return -EINVAL; | ||
430 | |||
431 | return adis_write_reg_16(&st->adis, reg, scale); | ||
432 | } | ||
433 | |||
434 | static int adis16480_get_calibscale(struct iio_dev *indio_dev, | ||
435 | const struct iio_chan_spec *chan, int *scale) | ||
436 | { | ||
437 | unsigned int reg = adis16480_calibscale_regs[chan->scan_index]; | ||
438 | struct adis16480 *st = iio_priv(indio_dev); | ||
439 | uint16_t val16; | ||
440 | int ret; | ||
441 | |||
442 | ret = adis_read_reg_16(&st->adis, reg, &val16); | ||
443 | if (ret < 0) | ||
444 | return ret; | ||
445 | |||
446 | *scale = sign_extend32(val16, 15); | ||
447 | return IIO_VAL_INT; | ||
448 | } | ||
449 | |||
450 | static const unsigned int adis16480_def_filter_freqs[] = { | ||
451 | 310, | ||
452 | 55, | ||
453 | 275, | ||
454 | 63, | ||
455 | }; | ||
456 | |||
457 | static const unsigned int ad16480_filter_data[][2] = { | ||
458 | [ADIS16480_SCAN_GYRO_X] = { ADIS16480_REG_FILTER_BNK0, 0 }, | ||
459 | [ADIS16480_SCAN_GYRO_Y] = { ADIS16480_REG_FILTER_BNK0, 3 }, | ||
460 | [ADIS16480_SCAN_GYRO_Z] = { ADIS16480_REG_FILTER_BNK0, 6 }, | ||
461 | [ADIS16480_SCAN_ACCEL_X] = { ADIS16480_REG_FILTER_BNK0, 9 }, | ||
462 | [ADIS16480_SCAN_ACCEL_Y] = { ADIS16480_REG_FILTER_BNK0, 12 }, | ||
463 | [ADIS16480_SCAN_ACCEL_Z] = { ADIS16480_REG_FILTER_BNK1, 0 }, | ||
464 | [ADIS16480_SCAN_MAGN_X] = { ADIS16480_REG_FILTER_BNK1, 3 }, | ||
465 | [ADIS16480_SCAN_MAGN_Y] = { ADIS16480_REG_FILTER_BNK1, 6 }, | ||
466 | [ADIS16480_SCAN_MAGN_Z] = { ADIS16480_REG_FILTER_BNK1, 9 }, | ||
467 | }; | ||
468 | |||
469 | static int adis16480_get_filter_freq(struct iio_dev *indio_dev, | ||
470 | const struct iio_chan_spec *chan, int *freq) | ||
471 | { | ||
472 | struct adis16480 *st = iio_priv(indio_dev); | ||
473 | unsigned int enable_mask, offset, reg; | ||
474 | uint16_t val; | ||
475 | int ret; | ||
476 | |||
477 | reg = ad16480_filter_data[chan->scan_index][0]; | ||
478 | offset = ad16480_filter_data[chan->scan_index][1]; | ||
479 | enable_mask = BIT(offset + 2); | ||
480 | |||
481 | ret = adis_read_reg_16(&st->adis, reg, &val); | ||
482 | if (ret < 0) | ||
483 | return ret; | ||
484 | |||
485 | if (!(val & enable_mask)) | ||
486 | *freq = 0; | ||
487 | else | ||
488 | *freq = adis16480_def_filter_freqs[(val >> offset) & 0x3]; | ||
489 | |||
490 | return IIO_VAL_INT; | ||
491 | } | ||
492 | |||
493 | static int adis16480_set_filter_freq(struct iio_dev *indio_dev, | ||
494 | const struct iio_chan_spec *chan, unsigned int freq) | ||
495 | { | ||
496 | struct adis16480 *st = iio_priv(indio_dev); | ||
497 | unsigned int enable_mask, offset, reg; | ||
498 | unsigned int diff, best_diff; | ||
499 | unsigned int i, best_freq; | ||
500 | uint16_t val; | ||
501 | int ret; | ||
502 | |||
503 | reg = ad16480_filter_data[chan->scan_index][0]; | ||
504 | offset = ad16480_filter_data[chan->scan_index][1]; | ||
505 | enable_mask = BIT(offset + 2); | ||
506 | |||
507 | ret = adis_read_reg_16(&st->adis, reg, &val); | ||
508 | if (ret < 0) | ||
509 | return ret; | ||
510 | |||
511 | if (freq == 0) { | ||
512 | val &= ~enable_mask; | ||
513 | } else { | ||
514 | best_freq = 0; | ||
515 | best_diff = 310; | ||
516 | for (i = 0; i < ARRAY_SIZE(adis16480_def_filter_freqs); i++) { | ||
517 | if (adis16480_def_filter_freqs[i] >= freq) { | ||
518 | diff = adis16480_def_filter_freqs[i] - freq; | ||
519 | if (diff < best_diff) { | ||
520 | best_diff = diff; | ||
521 | best_freq = i; | ||
522 | } | ||
523 | } | ||
524 | } | ||
525 | |||
526 | val &= ~(0x3 << offset); | ||
527 | val |= best_freq << offset; | ||
528 | val |= enable_mask; | ||
529 | } | ||
530 | |||
531 | return adis_write_reg_16(&st->adis, reg, val); | ||
532 | } | ||
533 | |||
534 | static int adis16480_read_raw(struct iio_dev *indio_dev, | ||
535 | const struct iio_chan_spec *chan, int *val, int *val2, long info) | ||
536 | { | ||
537 | switch (info) { | ||
538 | case IIO_CHAN_INFO_RAW: | ||
539 | return adis_single_conversion(indio_dev, chan, 0, val); | ||
540 | case IIO_CHAN_INFO_SCALE: | ||
541 | switch (chan->type) { | ||
542 | case IIO_ANGL_VEL: | ||
543 | *val = 0; | ||
544 | *val2 = IIO_DEGREE_TO_RAD(20000); /* 0.02 degree/sec */ | ||
545 | return IIO_VAL_INT_PLUS_MICRO; | ||
546 | case IIO_ACCEL: | ||
547 | *val = 0; | ||
548 | *val2 = IIO_G_TO_M_S_2(800); /* 0.8 mg */ | ||
549 | return IIO_VAL_INT_PLUS_MICRO; | ||
550 | case IIO_MAGN: | ||
551 | *val = 0; | ||
552 | *val2 = 100; /* 0.0001 gauss */ | ||
553 | return IIO_VAL_INT_PLUS_MICRO; | ||
554 | case IIO_TEMP: | ||
555 | *val = 5; | ||
556 | *val2 = 650000; /* 5.65 milli degree Celsius */ | ||
557 | return IIO_VAL_INT_PLUS_MICRO; | ||
558 | case IIO_PRESSURE: | ||
559 | *val = 0; | ||
560 | *val2 = 4000; /* 40ubar = 0.004 kPa */ | ||
561 | return IIO_VAL_INT_PLUS_MICRO; | ||
562 | default: | ||
563 | return -EINVAL; | ||
564 | } | ||
565 | case IIO_CHAN_INFO_OFFSET: | ||
566 | /* Only the temperature channel has a offset */ | ||
567 | *val = 4425; /* 25 degree Celsius = 0x0000 */ | ||
568 | return IIO_VAL_INT; | ||
569 | case IIO_CHAN_INFO_CALIBBIAS: | ||
570 | return adis16480_get_calibbias(indio_dev, chan, val); | ||
571 | case IIO_CHAN_INFO_CALIBSCALE: | ||
572 | return adis16480_get_calibscale(indio_dev, chan, val); | ||
573 | case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: | ||
574 | return adis16480_get_filter_freq(indio_dev, chan, val); | ||
575 | default: | ||
576 | return -EINVAL; | ||
577 | } | ||
578 | } | ||
579 | |||
580 | static int adis16480_write_raw(struct iio_dev *indio_dev, | ||
581 | const struct iio_chan_spec *chan, int val, int val2, long info) | ||
582 | { | ||
583 | switch (info) { | ||
584 | case IIO_CHAN_INFO_CALIBBIAS: | ||
585 | return adis16480_set_calibbias(indio_dev, chan, val); | ||
586 | case IIO_CHAN_INFO_CALIBSCALE: | ||
587 | return adis16480_set_calibscale(indio_dev, chan, val); | ||
588 | case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: | ||
589 | return adis16480_set_filter_freq(indio_dev, chan, val); | ||
590 | default: | ||
591 | return -EINVAL; | ||
592 | } | ||
593 | } | ||
594 | |||
595 | #define ADIS16480_MOD_CHANNEL(_type, _mod, _address, _si, _info, _bits) \ | ||
596 | { \ | ||
597 | .type = (_type), \ | ||
598 | .modified = 1, \ | ||
599 | .channel2 = (_mod), \ | ||
600 | .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ | ||
601 | IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \ | ||
602 | IIO_CHAN_INFO_SCALE_SHARED_BIT | \ | ||
603 | _info, \ | ||
604 | .address = (_address), \ | ||
605 | .scan_index = (_si), \ | ||
606 | .scan_type = { \ | ||
607 | .sign = 's', \ | ||
608 | .realbits = (_bits), \ | ||
609 | .storagebits = (_bits), \ | ||
610 | .endianness = IIO_BE, \ | ||
611 | }, \ | ||
612 | } | ||
613 | |||
614 | #define ADIS16480_GYRO_CHANNEL(_mod) \ | ||
615 | ADIS16480_MOD_CHANNEL(IIO_ANGL_VEL, IIO_MOD_ ## _mod, \ | ||
616 | ADIS16480_REG_ ## _mod ## _GYRO_OUT, ADIS16480_SCAN_GYRO_ ## _mod, \ | ||
617 | IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT | \ | ||
618 | IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, \ | ||
619 | 32) | ||
620 | |||
621 | #define ADIS16480_ACCEL_CHANNEL(_mod) \ | ||
622 | ADIS16480_MOD_CHANNEL(IIO_ACCEL, IIO_MOD_ ## _mod, \ | ||
623 | ADIS16480_REG_ ## _mod ## _ACCEL_OUT, ADIS16480_SCAN_ACCEL_ ## _mod, \ | ||
624 | IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT | \ | ||
625 | IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, \ | ||
626 | 32) | ||
627 | |||
628 | #define ADIS16480_MAGN_CHANNEL(_mod) \ | ||
629 | ADIS16480_MOD_CHANNEL(IIO_MAGN, IIO_MOD_ ## _mod, \ | ||
630 | ADIS16480_REG_ ## _mod ## _MAGN_OUT, ADIS16480_SCAN_MAGN_ ## _mod, \ | ||
631 | IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT, \ | ||
632 | 16) | ||
633 | |||
634 | #define ADIS16480_PRESSURE_CHANNEL() \ | ||
635 | { \ | ||
636 | .type = IIO_PRESSURE, \ | ||
637 | .indexed = 1, \ | ||
638 | .channel = 0, \ | ||
639 | .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ | ||
640 | IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \ | ||
641 | IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ | ||
642 | .address = ADIS16480_REG_BAROM_OUT, \ | ||
643 | .scan_index = ADIS16480_SCAN_BARO, \ | ||
644 | .scan_type = { \ | ||
645 | .sign = 's', \ | ||
646 | .realbits = 32, \ | ||
647 | .storagebits = 32, \ | ||
648 | .endianness = IIO_BE, \ | ||
649 | }, \ | ||
650 | } | ||
651 | |||
652 | #define ADIS16480_TEMP_CHANNEL() { \ | ||
653 | .type = IIO_TEMP, \ | ||
654 | .indexed = 1, \ | ||
655 | .channel = 0, \ | ||
656 | .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ | ||
657 | IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \ | ||
658 | IIO_CHAN_INFO_OFFSET_SEPARATE_BIT, \ | ||
659 | .address = ADIS16480_REG_TEMP_OUT, \ | ||
660 | .scan_index = ADIS16480_SCAN_TEMP, \ | ||
661 | .scan_type = { \ | ||
662 | .sign = 's', \ | ||
663 | .realbits = 16, \ | ||
664 | .storagebits = 16, \ | ||
665 | .endianness = IIO_BE, \ | ||
666 | }, \ | ||
667 | } | ||
668 | |||
669 | static const struct iio_chan_spec adis16480_channels[] = { | ||
670 | ADIS16480_GYRO_CHANNEL(X), | ||
671 | ADIS16480_GYRO_CHANNEL(Y), | ||
672 | ADIS16480_GYRO_CHANNEL(Z), | ||
673 | ADIS16480_ACCEL_CHANNEL(X), | ||
674 | ADIS16480_ACCEL_CHANNEL(Y), | ||
675 | ADIS16480_ACCEL_CHANNEL(Z), | ||
676 | ADIS16480_MAGN_CHANNEL(X), | ||
677 | ADIS16480_MAGN_CHANNEL(Y), | ||
678 | ADIS16480_MAGN_CHANNEL(Z), | ||
679 | ADIS16480_PRESSURE_CHANNEL(), | ||
680 | ADIS16480_TEMP_CHANNEL(), | ||
681 | IIO_CHAN_SOFT_TIMESTAMP(11) | ||
682 | }; | ||
683 | |||
684 | static const struct iio_chan_spec adis16485_channels[] = { | ||
685 | ADIS16480_GYRO_CHANNEL(X), | ||
686 | ADIS16480_GYRO_CHANNEL(Y), | ||
687 | ADIS16480_GYRO_CHANNEL(Z), | ||
688 | ADIS16480_ACCEL_CHANNEL(X), | ||
689 | ADIS16480_ACCEL_CHANNEL(Y), | ||
690 | ADIS16480_ACCEL_CHANNEL(Z), | ||
691 | ADIS16480_TEMP_CHANNEL(), | ||
692 | IIO_CHAN_SOFT_TIMESTAMP(7) | ||
693 | }; | ||
694 | |||
695 | enum adis16480_variant { | ||
696 | ADIS16375, | ||
697 | ADIS16480, | ||
698 | ADIS16485, | ||
699 | ADIS16488, | ||
700 | }; | ||
701 | |||
702 | static const struct adis16480_chip_info adis16480_chip_info[] = { | ||
703 | [ADIS16375] = { | ||
704 | .channels = adis16485_channels, | ||
705 | .num_channels = ARRAY_SIZE(adis16485_channels), | ||
706 | }, | ||
707 | [ADIS16480] = { | ||
708 | .channels = adis16480_channels, | ||
709 | .num_channels = ARRAY_SIZE(adis16480_channels), | ||
710 | }, | ||
711 | [ADIS16485] = { | ||
712 | .channels = adis16485_channels, | ||
713 | .num_channels = ARRAY_SIZE(adis16485_channels), | ||
714 | }, | ||
715 | [ADIS16488] = { | ||
716 | .channels = adis16480_channels, | ||
717 | .num_channels = ARRAY_SIZE(adis16480_channels), | ||
718 | }, | ||
719 | }; | ||
720 | |||
721 | static struct attribute *adis16480_attributes[] = { | ||
722 | &iio_dev_attr_sampling_frequency.dev_attr.attr, | ||
723 | NULL | ||
724 | }; | ||
725 | |||
726 | static const struct attribute_group adis16480_attribute_group = { | ||
727 | .attrs = adis16480_attributes, | ||
728 | }; | ||
729 | |||
730 | static const struct iio_info adis16480_info = { | ||
731 | .attrs = &adis16480_attribute_group, | ||
732 | .read_raw = &adis16480_read_raw, | ||
733 | .write_raw = &adis16480_write_raw, | ||
734 | .update_scan_mode = adis_update_scan_mode, | ||
735 | .driver_module = THIS_MODULE, | ||
736 | }; | ||
737 | |||
738 | static int adis16480_stop_device(struct iio_dev *indio_dev) | ||
739 | { | ||
740 | struct adis16480 *st = iio_priv(indio_dev); | ||
741 | int ret; | ||
742 | |||
743 | ret = adis_write_reg_16(&st->adis, ADIS16480_REG_SLP_CNT, BIT(9)); | ||
744 | if (ret) | ||
745 | dev_err(&indio_dev->dev, | ||
746 | "Could not power down device: %d\n", ret); | ||
747 | |||
748 | return ret; | ||
749 | } | ||
750 | |||
751 | static int adis16480_enable_irq(struct adis *adis, bool enable) | ||
752 | { | ||
753 | return adis_write_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL, | ||
754 | enable ? BIT(3) : 0); | ||
755 | } | ||
756 | |||
757 | static int adis16480_initial_setup(struct iio_dev *indio_dev) | ||
758 | { | ||
759 | struct adis16480 *st = iio_priv(indio_dev); | ||
760 | uint16_t prod_id; | ||
761 | unsigned int device_id; | ||
762 | int ret; | ||
763 | |||
764 | adis_reset(&st->adis); | ||
765 | msleep(70); | ||
766 | |||
767 | ret = adis_write_reg_16(&st->adis, ADIS16480_REG_GLOB_CMD, BIT(1)); | ||
768 | if (ret) | ||
769 | return ret; | ||
770 | msleep(30); | ||
771 | |||
772 | ret = adis_check_status(&st->adis); | ||
773 | if (ret) | ||
774 | return ret; | ||
775 | |||
776 | ret = adis_read_reg_16(&st->adis, ADIS16480_REG_PROD_ID, &prod_id); | ||
777 | if (ret) | ||
778 | return ret; | ||
779 | |||
780 | sscanf(indio_dev->name, "adis%u\n", &device_id); | ||
781 | |||
782 | if (prod_id != device_id) | ||
783 | dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.", | ||
784 | device_id, prod_id); | ||
785 | |||
786 | return 0; | ||
787 | } | ||
788 | |||
789 | #define ADIS16480_DIAG_STAT_XGYRO_FAIL 0 | ||
790 | #define ADIS16480_DIAG_STAT_YGYRO_FAIL 1 | ||
791 | #define ADIS16480_DIAG_STAT_ZGYRO_FAIL 2 | ||
792 | #define ADIS16480_DIAG_STAT_XACCL_FAIL 3 | ||
793 | #define ADIS16480_DIAG_STAT_YACCL_FAIL 4 | ||
794 | #define ADIS16480_DIAG_STAT_ZACCL_FAIL 5 | ||
795 | #define ADIS16480_DIAG_STAT_XMAGN_FAIL 8 | ||
796 | #define ADIS16480_DIAG_STAT_YMAGN_FAIL 9 | ||
797 | #define ADIS16480_DIAG_STAT_ZMAGN_FAIL 10 | ||
798 | #define ADIS16480_DIAG_STAT_BARO_FAIL 11 | ||
799 | |||
800 | static const char * const adis16480_status_error_msgs[] = { | ||
801 | [ADIS16480_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure", | ||
802 | [ADIS16480_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure", | ||
803 | [ADIS16480_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure", | ||
804 | [ADIS16480_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure", | ||
805 | [ADIS16480_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure", | ||
806 | [ADIS16480_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure", | ||
807 | [ADIS16480_DIAG_STAT_XMAGN_FAIL] = "X-axis magnetometer self-test failure", | ||
808 | [ADIS16480_DIAG_STAT_YMAGN_FAIL] = "Y-axis magnetometer self-test failure", | ||
809 | [ADIS16480_DIAG_STAT_ZMAGN_FAIL] = "Z-axis magnetometer self-test failure", | ||
810 | [ADIS16480_DIAG_STAT_BARO_FAIL] = "Barometer self-test failure", | ||
811 | }; | ||
812 | |||
813 | static const struct adis_data adis16480_data = { | ||
814 | .diag_stat_reg = ADIS16480_REG_DIAG_STS, | ||
815 | .glob_cmd_reg = ADIS16480_REG_GLOB_CMD, | ||
816 | .has_paging = true, | ||
817 | |||
818 | .read_delay = 5, | ||
819 | .write_delay = 5, | ||
820 | |||
821 | .status_error_msgs = adis16480_status_error_msgs, | ||
822 | .status_error_mask = BIT(ADIS16480_DIAG_STAT_XGYRO_FAIL) | | ||
823 | BIT(ADIS16480_DIAG_STAT_YGYRO_FAIL) | | ||
824 | BIT(ADIS16480_DIAG_STAT_ZGYRO_FAIL) | | ||
825 | BIT(ADIS16480_DIAG_STAT_XACCL_FAIL) | | ||
826 | BIT(ADIS16480_DIAG_STAT_YACCL_FAIL) | | ||
827 | BIT(ADIS16480_DIAG_STAT_ZACCL_FAIL) | | ||
828 | BIT(ADIS16480_DIAG_STAT_XMAGN_FAIL) | | ||
829 | BIT(ADIS16480_DIAG_STAT_YMAGN_FAIL) | | ||
830 | BIT(ADIS16480_DIAG_STAT_ZMAGN_FAIL) | | ||
831 | BIT(ADIS16480_DIAG_STAT_BARO_FAIL), | ||
832 | |||
833 | .enable_irq = adis16480_enable_irq, | ||
834 | }; | ||
835 | |||
836 | static int adis16480_probe(struct spi_device *spi) | ||
837 | { | ||
838 | const struct spi_device_id *id = spi_get_device_id(spi); | ||
839 | struct iio_dev *indio_dev; | ||
840 | struct adis16480 *st; | ||
841 | int ret; | ||
842 | |||
843 | indio_dev = iio_device_alloc(sizeof(*st)); | ||
844 | if (indio_dev == NULL) | ||
845 | return -ENOMEM; | ||
846 | |||
847 | spi_set_drvdata(spi, indio_dev); | ||
848 | |||
849 | st = iio_priv(indio_dev); | ||
850 | |||
851 | st->chip_info = &adis16480_chip_info[id->driver_data]; | ||
852 | indio_dev->dev.parent = &spi->dev; | ||
853 | indio_dev->name = spi_get_device_id(spi)->name; | ||
854 | indio_dev->channels = st->chip_info->channels; | ||
855 | indio_dev->num_channels = st->chip_info->num_channels; | ||
856 | indio_dev->info = &adis16480_info; | ||
857 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
858 | |||
859 | ret = adis_init(&st->adis, indio_dev, spi, &adis16480_data); | ||
860 | if (ret) | ||
861 | goto error_free_dev; | ||
862 | |||
863 | ret = adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL); | ||
864 | if (ret) | ||
865 | goto error_free_dev; | ||
866 | |||
867 | ret = adis16480_initial_setup(indio_dev); | ||
868 | if (ret) | ||
869 | goto error_cleanup_buffer; | ||
870 | |||
871 | ret = iio_device_register(indio_dev); | ||
872 | if (ret) | ||
873 | goto error_stop_device; | ||
874 | |||
875 | adis16480_debugfs_init(indio_dev); | ||
876 | |||
877 | return 0; | ||
878 | |||
879 | error_stop_device: | ||
880 | adis16480_stop_device(indio_dev); | ||
881 | error_cleanup_buffer: | ||
882 | adis_cleanup_buffer_and_trigger(&st->adis, indio_dev); | ||
883 | error_free_dev: | ||
884 | iio_device_free(indio_dev); | ||
885 | return ret; | ||
886 | } | ||
887 | |||
888 | static int adis16480_remove(struct spi_device *spi) | ||
889 | { | ||
890 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | ||
891 | struct adis16480 *st = iio_priv(indio_dev); | ||
892 | |||
893 | iio_device_unregister(indio_dev); | ||
894 | adis16480_stop_device(indio_dev); | ||
895 | |||
896 | adis_cleanup_buffer_and_trigger(&st->adis, indio_dev); | ||
897 | |||
898 | iio_device_free(indio_dev); | ||
899 | |||
900 | return 0; | ||
901 | } | ||
902 | |||
903 | static const struct spi_device_id adis16480_ids[] = { | ||
904 | { "adis16375", ADIS16375 }, | ||
905 | { "adis16480", ADIS16480 }, | ||
906 | { "adis16485", ADIS16485 }, | ||
907 | { "adis16488", ADIS16488 }, | ||
908 | { } | ||
909 | }; | ||
910 | MODULE_DEVICE_TABLE(spi, adis16480_ids); | ||
911 | |||
912 | static struct spi_driver adis16480_driver = { | ||
913 | .driver = { | ||
914 | .name = "adis16480", | ||
915 | .owner = THIS_MODULE, | ||
916 | }, | ||
917 | .id_table = adis16480_ids, | ||
918 | .probe = adis16480_probe, | ||
919 | .remove = adis16480_remove, | ||
920 | }; | ||
921 | module_spi_driver(adis16480_driver); | ||
922 | |||
923 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | ||
924 | MODULE_DESCRIPTION("Analog Devices ADIS16480 IMU driver"); | ||
925 | MODULE_LICENSE("GPL v2"); | ||