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