1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ADIS16133/ADIS16135/ADIS16136 gyroscope driver |
4 | * |
5 | * Copyright 2012 Analog Devices Inc. |
6 | * Author: Lars-Peter Clausen <lars@metafoo.de> |
7 | */ |
8 | |
9 | #include <linux/device.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/spi/spi.h> |
12 | #include <linux/sysfs.h> |
13 | #include <linux/module.h> |
14 | |
15 | #include <linux/iio/iio.h> |
16 | #include <linux/iio/sysfs.h> |
17 | #include <linux/iio/imu/adis.h> |
18 | |
19 | #include <linux/debugfs.h> |
20 | |
21 | #define ADIS16136_REG_FLASH_CNT 0x00 |
22 | #define ADIS16136_REG_TEMP_OUT 0x02 |
23 | #define ADIS16136_REG_GYRO_OUT2 0x04 |
24 | #define ADIS16136_REG_GYRO_OUT 0x06 |
25 | #define ADIS16136_REG_GYRO_OFF2 0x08 |
26 | #define ADIS16136_REG_GYRO_OFF 0x0A |
27 | #define ADIS16136_REG_ALM_MAG1 0x10 |
28 | #define ADIS16136_REG_ALM_MAG2 0x12 |
29 | #define ADIS16136_REG_ALM_SAMPL1 0x14 |
30 | #define ADIS16136_REG_ALM_SAMPL2 0x16 |
31 | #define ADIS16136_REG_ALM_CTRL 0x18 |
32 | #define ADIS16136_REG_GPIO_CTRL 0x1A |
33 | #define ADIS16136_REG_MSC_CTRL 0x1C |
34 | #define ADIS16136_REG_SMPL_PRD 0x1E |
35 | #define ADIS16136_REG_AVG_CNT 0x20 |
36 | #define ADIS16136_REG_DEC_RATE 0x22 |
37 | #define ADIS16136_REG_SLP_CTRL 0x24 |
38 | #define ADIS16136_REG_DIAG_STAT 0x26 |
39 | #define ADIS16136_REG_GLOB_CMD 0x28 |
40 | #define ADIS16136_REG_LOT1 0x32 |
41 | #define ADIS16136_REG_LOT2 0x34 |
42 | #define ADIS16136_REG_LOT3 0x36 |
43 | #define ADIS16136_REG_PROD_ID 0x38 |
44 | #define ADIS16136_REG_SERIAL_NUM 0x3A |
45 | |
46 | #define ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL 2 |
47 | #define ADIS16136_DIAG_STAT_SPI_FAIL 3 |
48 | #define ADIS16136_DIAG_STAT_SELF_TEST_FAIL 5 |
49 | #define ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL 6 |
50 | |
51 | #define ADIS16136_MSC_CTRL_MEMORY_TEST BIT(11) |
52 | #define ADIS16136_MSC_CTRL_SELF_TEST BIT(10) |
53 | |
54 | struct adis16136_chip_info { |
55 | unsigned int precision; |
56 | unsigned int fullscale; |
57 | const struct adis_data adis_data; |
58 | }; |
59 | |
60 | struct adis16136 { |
61 | const struct adis16136_chip_info *chip_info; |
62 | |
63 | struct adis adis; |
64 | }; |
65 | |
66 | #ifdef CONFIG_DEBUG_FS |
67 | |
68 | static ssize_t adis16136_show_serial(struct file *file, |
69 | char __user *userbuf, size_t count, loff_t *ppos) |
70 | { |
71 | struct adis16136 *adis16136 = file->private_data; |
72 | uint16_t lot1, lot2, lot3, serial; |
73 | char buf[20]; |
74 | size_t len; |
75 | int ret; |
76 | |
77 | ret = adis_read_reg_16(adis: &adis16136->adis, ADIS16136_REG_SERIAL_NUM, |
78 | val: &serial); |
79 | if (ret) |
80 | return ret; |
81 | |
82 | ret = adis_read_reg_16(adis: &adis16136->adis, ADIS16136_REG_LOT1, val: &lot1); |
83 | if (ret) |
84 | return ret; |
85 | |
86 | ret = adis_read_reg_16(adis: &adis16136->adis, ADIS16136_REG_LOT2, val: &lot2); |
87 | if (ret) |
88 | return ret; |
89 | |
90 | ret = adis_read_reg_16(adis: &adis16136->adis, ADIS16136_REG_LOT3, val: &lot3); |
91 | if (ret) |
92 | return ret; |
93 | |
94 | len = snprintf(buf, size: sizeof(buf), fmt: "%.4x%.4x%.4x-%.4x\n" , lot1, lot2, |
95 | lot3, serial); |
96 | |
97 | return simple_read_from_buffer(to: userbuf, count, ppos, from: buf, available: len); |
98 | } |
99 | |
100 | static const struct file_operations adis16136_serial_fops = { |
101 | .open = simple_open, |
102 | .read = adis16136_show_serial, |
103 | .llseek = default_llseek, |
104 | .owner = THIS_MODULE, |
105 | }; |
106 | |
107 | static int adis16136_show_product_id(void *arg, u64 *val) |
108 | { |
109 | struct adis16136 *adis16136 = arg; |
110 | u16 prod_id; |
111 | int ret; |
112 | |
113 | ret = adis_read_reg_16(adis: &adis16136->adis, ADIS16136_REG_PROD_ID, |
114 | val: &prod_id); |
115 | if (ret) |
116 | return ret; |
117 | |
118 | *val = prod_id; |
119 | |
120 | return 0; |
121 | } |
122 | DEFINE_DEBUGFS_ATTRIBUTE(adis16136_product_id_fops, |
123 | adis16136_show_product_id, NULL, "%llu\n" ); |
124 | |
125 | static int adis16136_show_flash_count(void *arg, u64 *val) |
126 | { |
127 | struct adis16136 *adis16136 = arg; |
128 | uint16_t flash_count; |
129 | int ret; |
130 | |
131 | ret = adis_read_reg_16(adis: &adis16136->adis, ADIS16136_REG_FLASH_CNT, |
132 | val: &flash_count); |
133 | if (ret) |
134 | return ret; |
135 | |
136 | *val = flash_count; |
137 | |
138 | return 0; |
139 | } |
140 | DEFINE_DEBUGFS_ATTRIBUTE(adis16136_flash_count_fops, |
141 | adis16136_show_flash_count, NULL, "%lld\n" ); |
142 | |
143 | static int adis16136_debugfs_init(struct iio_dev *indio_dev) |
144 | { |
145 | struct adis16136 *adis16136 = iio_priv(indio_dev); |
146 | struct dentry *d = iio_get_debugfs_dentry(indio_dev); |
147 | |
148 | debugfs_create_file_unsafe(name: "serial_number" , mode: 0400, |
149 | parent: d, data: adis16136, fops: &adis16136_serial_fops); |
150 | debugfs_create_file_unsafe(name: "product_id" , mode: 0400, |
151 | parent: d, data: adis16136, fops: &adis16136_product_id_fops); |
152 | debugfs_create_file_unsafe(name: "flash_count" , mode: 0400, |
153 | parent: d, data: adis16136, fops: &adis16136_flash_count_fops); |
154 | |
155 | return 0; |
156 | } |
157 | |
158 | #else |
159 | |
160 | static int adis16136_debugfs_init(struct iio_dev *indio_dev) |
161 | { |
162 | return 0; |
163 | } |
164 | |
165 | #endif |
166 | |
167 | static int adis16136_set_freq(struct adis16136 *adis16136, unsigned int freq) |
168 | { |
169 | unsigned int t; |
170 | |
171 | t = 32768 / freq; |
172 | if (t < 0xf) |
173 | t = 0xf; |
174 | else if (t > 0xffff) |
175 | t = 0xffff; |
176 | else |
177 | t--; |
178 | |
179 | return adis_write_reg_16(adis: &adis16136->adis, ADIS16136_REG_SMPL_PRD, val: t); |
180 | } |
181 | |
182 | static int __adis16136_get_freq(struct adis16136 *adis16136, unsigned int *freq) |
183 | { |
184 | uint16_t t; |
185 | int ret; |
186 | |
187 | ret = __adis_read_reg_16(adis: &adis16136->adis, ADIS16136_REG_SMPL_PRD, val: &t); |
188 | if (ret) |
189 | return ret; |
190 | |
191 | *freq = 32768 / (t + 1); |
192 | |
193 | return 0; |
194 | } |
195 | |
196 | static ssize_t adis16136_write_frequency(struct device *dev, |
197 | struct device_attribute *attr, const char *buf, size_t len) |
198 | { |
199 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
200 | struct adis16136 *adis16136 = iio_priv(indio_dev); |
201 | unsigned int val; |
202 | int ret; |
203 | |
204 | ret = kstrtouint(s: buf, base: 10, res: &val); |
205 | if (ret) |
206 | return ret; |
207 | |
208 | if (val == 0) |
209 | return -EINVAL; |
210 | |
211 | ret = adis16136_set_freq(adis16136, freq: val); |
212 | |
213 | return ret ? ret : len; |
214 | } |
215 | |
216 | static ssize_t adis16136_read_frequency(struct device *dev, |
217 | struct device_attribute *attr, char *buf) |
218 | { |
219 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
220 | struct adis16136 *adis16136 = iio_priv(indio_dev); |
221 | unsigned int freq; |
222 | int ret; |
223 | |
224 | adis_dev_lock(adis: &adis16136->adis); |
225 | ret = __adis16136_get_freq(adis16136, freq: &freq); |
226 | adis_dev_unlock(adis: &adis16136->adis); |
227 | if (ret) |
228 | return ret; |
229 | |
230 | return sprintf(buf, fmt: "%d\n" , freq); |
231 | } |
232 | |
233 | static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, |
234 | adis16136_read_frequency, |
235 | adis16136_write_frequency); |
236 | |
237 | static const unsigned adis16136_3db_divisors[] = { |
238 | [0] = 2, /* Special case */ |
239 | [1] = 6, |
240 | [2] = 12, |
241 | [3] = 25, |
242 | [4] = 50, |
243 | [5] = 100, |
244 | [6] = 200, |
245 | [7] = 200, /* Not a valid setting */ |
246 | }; |
247 | |
248 | static int adis16136_set_filter(struct iio_dev *indio_dev, int val) |
249 | { |
250 | struct adis16136 *adis16136 = iio_priv(indio_dev); |
251 | unsigned int freq; |
252 | int i, ret; |
253 | |
254 | adis_dev_lock(adis: &adis16136->adis); |
255 | ret = __adis16136_get_freq(adis16136, freq: &freq); |
256 | if (ret) |
257 | goto out_unlock; |
258 | |
259 | for (i = ARRAY_SIZE(adis16136_3db_divisors) - 1; i >= 1; i--) { |
260 | if (freq / adis16136_3db_divisors[i] >= val) |
261 | break; |
262 | } |
263 | |
264 | ret = __adis_write_reg_16(adis: &adis16136->adis, ADIS16136_REG_AVG_CNT, val: i); |
265 | out_unlock: |
266 | adis_dev_unlock(adis: &adis16136->adis); |
267 | |
268 | return ret; |
269 | } |
270 | |
271 | static int adis16136_get_filter(struct iio_dev *indio_dev, int *val) |
272 | { |
273 | struct adis16136 *adis16136 = iio_priv(indio_dev); |
274 | unsigned int freq; |
275 | uint16_t val16; |
276 | int ret; |
277 | |
278 | adis_dev_lock(adis: &adis16136->adis); |
279 | |
280 | ret = __adis_read_reg_16(adis: &adis16136->adis, ADIS16136_REG_AVG_CNT, |
281 | val: &val16); |
282 | if (ret) |
283 | goto err_unlock; |
284 | |
285 | ret = __adis16136_get_freq(adis16136, freq: &freq); |
286 | if (ret) |
287 | goto err_unlock; |
288 | |
289 | *val = freq / adis16136_3db_divisors[val16 & 0x07]; |
290 | |
291 | err_unlock: |
292 | adis_dev_unlock(adis: &adis16136->adis); |
293 | |
294 | return ret ? ret : IIO_VAL_INT; |
295 | } |
296 | |
297 | static int adis16136_read_raw(struct iio_dev *indio_dev, |
298 | const struct iio_chan_spec *chan, int *val, int *val2, long info) |
299 | { |
300 | struct adis16136 *adis16136 = iio_priv(indio_dev); |
301 | uint32_t val32; |
302 | int ret; |
303 | |
304 | switch (info) { |
305 | case IIO_CHAN_INFO_RAW: |
306 | return adis_single_conversion(indio_dev, chan, error_mask: 0, val); |
307 | case IIO_CHAN_INFO_SCALE: |
308 | switch (chan->type) { |
309 | case IIO_ANGL_VEL: |
310 | *val = adis16136->chip_info->precision; |
311 | *val2 = (adis16136->chip_info->fullscale << 16); |
312 | return IIO_VAL_FRACTIONAL; |
313 | case IIO_TEMP: |
314 | *val = 10; |
315 | *val2 = 697000; /* 0.010697 degree Celsius */ |
316 | return IIO_VAL_INT_PLUS_MICRO; |
317 | default: |
318 | return -EINVAL; |
319 | } |
320 | case IIO_CHAN_INFO_CALIBBIAS: |
321 | ret = adis_read_reg_32(adis: &adis16136->adis, |
322 | ADIS16136_REG_GYRO_OFF2, val: &val32); |
323 | if (ret) |
324 | return ret; |
325 | |
326 | *val = sign_extend32(value: val32, index: 31); |
327 | |
328 | return IIO_VAL_INT; |
329 | case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: |
330 | return adis16136_get_filter(indio_dev, val); |
331 | default: |
332 | return -EINVAL; |
333 | } |
334 | } |
335 | |
336 | static int adis16136_write_raw(struct iio_dev *indio_dev, |
337 | const struct iio_chan_spec *chan, int val, int val2, long info) |
338 | { |
339 | struct adis16136 *adis16136 = iio_priv(indio_dev); |
340 | |
341 | switch (info) { |
342 | case IIO_CHAN_INFO_CALIBBIAS: |
343 | return adis_write_reg_32(adis: &adis16136->adis, |
344 | ADIS16136_REG_GYRO_OFF2, val); |
345 | case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: |
346 | return adis16136_set_filter(indio_dev, val); |
347 | default: |
348 | break; |
349 | } |
350 | |
351 | return -EINVAL; |
352 | } |
353 | |
354 | enum { |
355 | ADIS16136_SCAN_GYRO, |
356 | ADIS16136_SCAN_TEMP, |
357 | }; |
358 | |
359 | static const struct iio_chan_spec adis16136_channels[] = { |
360 | { |
361 | .type = IIO_ANGL_VEL, |
362 | .modified = 1, |
363 | .channel2 = IIO_MOD_X, |
364 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
365 | BIT(IIO_CHAN_INFO_CALIBBIAS) | |
366 | BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), |
367 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), |
368 | |
369 | .address = ADIS16136_REG_GYRO_OUT2, |
370 | .scan_index = ADIS16136_SCAN_GYRO, |
371 | .scan_type = { |
372 | .sign = 's', |
373 | .realbits = 32, |
374 | .storagebits = 32, |
375 | .endianness = IIO_BE, |
376 | }, |
377 | }, { |
378 | .type = IIO_TEMP, |
379 | .indexed = 1, |
380 | .channel = 0, |
381 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
382 | BIT(IIO_CHAN_INFO_SCALE), |
383 | .address = ADIS16136_REG_TEMP_OUT, |
384 | .scan_index = ADIS16136_SCAN_TEMP, |
385 | .scan_type = { |
386 | .sign = 's', |
387 | .realbits = 16, |
388 | .storagebits = 16, |
389 | .endianness = IIO_BE, |
390 | }, |
391 | }, |
392 | IIO_CHAN_SOFT_TIMESTAMP(2), |
393 | }; |
394 | |
395 | static struct attribute *adis16136_attributes[] = { |
396 | &iio_dev_attr_sampling_frequency.dev_attr.attr, |
397 | NULL |
398 | }; |
399 | |
400 | static const struct attribute_group adis16136_attribute_group = { |
401 | .attrs = adis16136_attributes, |
402 | }; |
403 | |
404 | static const struct iio_info adis16136_info = { |
405 | .attrs = &adis16136_attribute_group, |
406 | .read_raw = &adis16136_read_raw, |
407 | .write_raw = &adis16136_write_raw, |
408 | .update_scan_mode = adis_update_scan_mode, |
409 | .debugfs_reg_access = adis_debugfs_reg_access, |
410 | }; |
411 | |
412 | static int adis16136_stop_device(struct iio_dev *indio_dev) |
413 | { |
414 | struct adis16136 *adis16136 = iio_priv(indio_dev); |
415 | int ret; |
416 | |
417 | ret = adis_write_reg_16(adis: &adis16136->adis, ADIS16136_REG_SLP_CTRL, val: 0xff); |
418 | if (ret) |
419 | dev_err(&indio_dev->dev, |
420 | "Could not power down device: %d\n" , ret); |
421 | |
422 | return ret; |
423 | } |
424 | |
425 | static int adis16136_initial_setup(struct iio_dev *indio_dev) |
426 | { |
427 | struct adis16136 *adis16136 = iio_priv(indio_dev); |
428 | unsigned int device_id; |
429 | uint16_t prod_id; |
430 | int ret; |
431 | |
432 | ret = __adis_initial_startup(adis: &adis16136->adis); |
433 | if (ret) |
434 | return ret; |
435 | |
436 | ret = adis_read_reg_16(adis: &adis16136->adis, ADIS16136_REG_PROD_ID, |
437 | val: &prod_id); |
438 | if (ret) |
439 | return ret; |
440 | |
441 | ret = sscanf(indio_dev->name, "adis%u\n" , &device_id); |
442 | if (ret != 1) |
443 | return -EINVAL; |
444 | |
445 | if (prod_id != device_id) |
446 | dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match." , |
447 | device_id, prod_id); |
448 | |
449 | return 0; |
450 | } |
451 | |
452 | static const char * const adis16136_status_error_msgs[] = { |
453 | [ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL] = "Flash update failed" , |
454 | [ADIS16136_DIAG_STAT_SPI_FAIL] = "SPI failure" , |
455 | [ADIS16136_DIAG_STAT_SELF_TEST_FAIL] = "Self test error" , |
456 | [ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL] = "Flash checksum error" , |
457 | }; |
458 | |
459 | #define ADIS16136_DATA(_timeouts) \ |
460 | { \ |
461 | .diag_stat_reg = ADIS16136_REG_DIAG_STAT, \ |
462 | .glob_cmd_reg = ADIS16136_REG_GLOB_CMD, \ |
463 | .msc_ctrl_reg = ADIS16136_REG_MSC_CTRL, \ |
464 | .self_test_reg = ADIS16136_REG_MSC_CTRL, \ |
465 | .self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST, \ |
466 | .read_delay = 10, \ |
467 | .write_delay = 10, \ |
468 | .status_error_msgs = adis16136_status_error_msgs, \ |
469 | .status_error_mask = BIT(ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL) | \ |
470 | BIT(ADIS16136_DIAG_STAT_SPI_FAIL) | \ |
471 | BIT(ADIS16136_DIAG_STAT_SELF_TEST_FAIL) | \ |
472 | BIT(ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL), \ |
473 | .timeouts = (_timeouts), \ |
474 | } |
475 | |
476 | enum adis16136_id { |
477 | ID_ADIS16133, |
478 | ID_ADIS16135, |
479 | ID_ADIS16136, |
480 | ID_ADIS16137, |
481 | }; |
482 | |
483 | static const struct adis_timeout adis16133_timeouts = { |
484 | .reset_ms = 75, |
485 | .sw_reset_ms = 75, |
486 | .self_test_ms = 50, |
487 | }; |
488 | |
489 | static const struct adis_timeout adis16136_timeouts = { |
490 | .reset_ms = 128, |
491 | .sw_reset_ms = 75, |
492 | .self_test_ms = 245, |
493 | }; |
494 | |
495 | static const struct adis16136_chip_info adis16136_chip_info[] = { |
496 | [ID_ADIS16133] = { |
497 | .precision = IIO_DEGREE_TO_RAD(1200), |
498 | .fullscale = 24000, |
499 | .adis_data = ADIS16136_DATA(&adis16133_timeouts), |
500 | }, |
501 | [ID_ADIS16135] = { |
502 | .precision = IIO_DEGREE_TO_RAD(300), |
503 | .fullscale = 24000, |
504 | .adis_data = ADIS16136_DATA(&adis16133_timeouts), |
505 | }, |
506 | [ID_ADIS16136] = { |
507 | .precision = IIO_DEGREE_TO_RAD(450), |
508 | .fullscale = 24623, |
509 | .adis_data = ADIS16136_DATA(&adis16136_timeouts), |
510 | }, |
511 | [ID_ADIS16137] = { |
512 | .precision = IIO_DEGREE_TO_RAD(1000), |
513 | .fullscale = 24609, |
514 | .adis_data = ADIS16136_DATA(&adis16136_timeouts), |
515 | }, |
516 | }; |
517 | |
518 | static void adis16136_stop(void *data) |
519 | { |
520 | adis16136_stop_device(indio_dev: data); |
521 | } |
522 | |
523 | static int adis16136_probe(struct spi_device *spi) |
524 | { |
525 | const struct spi_device_id *id = spi_get_device_id(sdev: spi); |
526 | struct adis16136 *adis16136; |
527 | struct iio_dev *indio_dev; |
528 | const struct adis_data *adis16136_data; |
529 | int ret; |
530 | |
531 | indio_dev = devm_iio_device_alloc(parent: &spi->dev, sizeof_priv: sizeof(*adis16136)); |
532 | if (indio_dev == NULL) |
533 | return -ENOMEM; |
534 | |
535 | spi_set_drvdata(spi, data: indio_dev); |
536 | |
537 | adis16136 = iio_priv(indio_dev); |
538 | |
539 | adis16136->chip_info = &adis16136_chip_info[id->driver_data]; |
540 | indio_dev->name = spi_get_device_id(sdev: spi)->name; |
541 | indio_dev->channels = adis16136_channels; |
542 | indio_dev->num_channels = ARRAY_SIZE(adis16136_channels); |
543 | indio_dev->info = &adis16136_info; |
544 | indio_dev->modes = INDIO_DIRECT_MODE; |
545 | |
546 | adis16136_data = &adis16136->chip_info->adis_data; |
547 | |
548 | ret = adis_init(adis: &adis16136->adis, indio_dev, spi, data: adis16136_data); |
549 | if (ret) |
550 | return ret; |
551 | |
552 | ret = devm_adis_setup_buffer_and_trigger(adis: &adis16136->adis, indio_dev, NULL); |
553 | if (ret) |
554 | return ret; |
555 | |
556 | ret = adis16136_initial_setup(indio_dev); |
557 | if (ret) |
558 | return ret; |
559 | |
560 | ret = devm_add_action_or_reset(&spi->dev, adis16136_stop, indio_dev); |
561 | if (ret) |
562 | return ret; |
563 | |
564 | ret = devm_iio_device_register(&spi->dev, indio_dev); |
565 | if (ret) |
566 | return ret; |
567 | |
568 | adis16136_debugfs_init(indio_dev); |
569 | |
570 | return 0; |
571 | } |
572 | |
573 | static const struct spi_device_id adis16136_ids[] = { |
574 | { "adis16133" , ID_ADIS16133 }, |
575 | { "adis16135" , ID_ADIS16135 }, |
576 | { "adis16136" , ID_ADIS16136 }, |
577 | { "adis16137" , ID_ADIS16137 }, |
578 | { } |
579 | }; |
580 | MODULE_DEVICE_TABLE(spi, adis16136_ids); |
581 | |
582 | static struct spi_driver adis16136_driver = { |
583 | .driver = { |
584 | .name = "adis16136" , |
585 | }, |
586 | .id_table = adis16136_ids, |
587 | .probe = adis16136_probe, |
588 | }; |
589 | module_spi_driver(adis16136_driver); |
590 | |
591 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>" ); |
592 | MODULE_DESCRIPTION("Analog Devices ADIS16133/ADIS16135/ADIS16136 gyroscope driver" ); |
593 | MODULE_LICENSE("GPL v2" ); |
594 | MODULE_IMPORT_NS(IIO_ADISLIB); |
595 | |