1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * ADIS16460 IMU driver |
4 | * |
5 | * Copyright 2019 Analog Devices Inc. |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/spi/spi.h> |
10 | |
11 | #include <linux/iio/iio.h> |
12 | #include <linux/iio/imu/adis.h> |
13 | |
14 | #include <linux/debugfs.h> |
15 | |
16 | #define ADIS16460_REG_FLASH_CNT 0x00 |
17 | #define ADIS16460_REG_DIAG_STAT 0x02 |
18 | #define ADIS16460_REG_X_GYRO_LOW 0x04 |
19 | #define ADIS16460_REG_X_GYRO_OUT 0x06 |
20 | #define ADIS16460_REG_Y_GYRO_LOW 0x08 |
21 | #define ADIS16460_REG_Y_GYRO_OUT 0x0A |
22 | #define ADIS16460_REG_Z_GYRO_LOW 0x0C |
23 | #define ADIS16460_REG_Z_GYRO_OUT 0x0E |
24 | #define ADIS16460_REG_X_ACCL_LOW 0x10 |
25 | #define ADIS16460_REG_X_ACCL_OUT 0x12 |
26 | #define ADIS16460_REG_Y_ACCL_LOW 0x14 |
27 | #define ADIS16460_REG_Y_ACCL_OUT 0x16 |
28 | #define ADIS16460_REG_Z_ACCL_LOW 0x18 |
29 | #define ADIS16460_REG_Z_ACCL_OUT 0x1A |
30 | #define ADIS16460_REG_SMPL_CNTR 0x1C |
31 | #define ADIS16460_REG_TEMP_OUT 0x1E |
32 | #define ADIS16460_REG_X_DELT_ANG 0x24 |
33 | #define ADIS16460_REG_Y_DELT_ANG 0x26 |
34 | #define ADIS16460_REG_Z_DELT_ANG 0x28 |
35 | #define ADIS16460_REG_X_DELT_VEL 0x2A |
36 | #define ADIS16460_REG_Y_DELT_VEL 0x2C |
37 | #define ADIS16460_REG_Z_DELT_VEL 0x2E |
38 | #define ADIS16460_REG_MSC_CTRL 0x32 |
39 | #define ADIS16460_REG_SYNC_SCAL 0x34 |
40 | #define ADIS16460_REG_DEC_RATE 0x36 |
41 | #define ADIS16460_REG_FLTR_CTRL 0x38 |
42 | #define ADIS16460_REG_GLOB_CMD 0x3E |
43 | #define ADIS16460_REG_X_GYRO_OFF 0x40 |
44 | #define ADIS16460_REG_Y_GYRO_OFF 0x42 |
45 | #define ADIS16460_REG_Z_GYRO_OFF 0x44 |
46 | #define ADIS16460_REG_X_ACCL_OFF 0x46 |
47 | #define ADIS16460_REG_Y_ACCL_OFF 0x48 |
48 | #define ADIS16460_REG_Z_ACCL_OFF 0x4A |
49 | #define ADIS16460_REG_LOT_ID1 0x52 |
50 | #define ADIS16460_REG_LOT_ID2 0x54 |
51 | #define ADIS16460_REG_PROD_ID 0x56 |
52 | #define ADIS16460_REG_SERIAL_NUM 0x58 |
53 | #define ADIS16460_REG_CAL_SGNTR 0x60 |
54 | #define ADIS16460_REG_CAL_CRC 0x62 |
55 | #define ADIS16460_REG_CODE_SGNTR 0x64 |
56 | #define ADIS16460_REG_CODE_CRC 0x66 |
57 | |
58 | struct adis16460_chip_info { |
59 | unsigned int num_channels; |
60 | const struct iio_chan_spec *channels; |
61 | unsigned int gyro_max_val; |
62 | unsigned int gyro_max_scale; |
63 | unsigned int accel_max_val; |
64 | unsigned int accel_max_scale; |
65 | }; |
66 | |
67 | struct adis16460 { |
68 | const struct adis16460_chip_info *chip_info; |
69 | struct adis adis; |
70 | }; |
71 | |
72 | #ifdef CONFIG_DEBUG_FS |
73 | |
74 | static int adis16460_show_serial_number(void *arg, u64 *val) |
75 | { |
76 | struct adis16460 *adis16460 = arg; |
77 | u16 serial; |
78 | int ret; |
79 | |
80 | ret = adis_read_reg_16(adis: &adis16460->adis, ADIS16460_REG_SERIAL_NUM, |
81 | val: &serial); |
82 | if (ret) |
83 | return ret; |
84 | |
85 | *val = serial; |
86 | |
87 | return 0; |
88 | } |
89 | DEFINE_DEBUGFS_ATTRIBUTE(adis16460_serial_number_fops, |
90 | adis16460_show_serial_number, NULL, "0x%.4llx\n" ); |
91 | |
92 | static int adis16460_show_product_id(void *arg, u64 *val) |
93 | { |
94 | struct adis16460 *adis16460 = arg; |
95 | u16 prod_id; |
96 | int ret; |
97 | |
98 | ret = adis_read_reg_16(adis: &adis16460->adis, ADIS16460_REG_PROD_ID, |
99 | val: &prod_id); |
100 | if (ret) |
101 | return ret; |
102 | |
103 | *val = prod_id; |
104 | |
105 | return 0; |
106 | } |
107 | DEFINE_DEBUGFS_ATTRIBUTE(adis16460_product_id_fops, |
108 | adis16460_show_product_id, NULL, "%llu\n" ); |
109 | |
110 | static int adis16460_show_flash_count(void *arg, u64 *val) |
111 | { |
112 | struct adis16460 *adis16460 = arg; |
113 | u32 flash_count; |
114 | int ret; |
115 | |
116 | ret = adis_read_reg_32(adis: &adis16460->adis, ADIS16460_REG_FLASH_CNT, |
117 | val: &flash_count); |
118 | if (ret) |
119 | return ret; |
120 | |
121 | *val = flash_count; |
122 | |
123 | return 0; |
124 | } |
125 | DEFINE_DEBUGFS_ATTRIBUTE(adis16460_flash_count_fops, |
126 | adis16460_show_flash_count, NULL, "%lld\n" ); |
127 | |
128 | static int adis16460_debugfs_init(struct iio_dev *indio_dev) |
129 | { |
130 | struct adis16460 *adis16460 = iio_priv(indio_dev); |
131 | struct dentry *d = iio_get_debugfs_dentry(indio_dev); |
132 | |
133 | debugfs_create_file_unsafe(name: "serial_number" , mode: 0400, |
134 | parent: d, data: adis16460, fops: &adis16460_serial_number_fops); |
135 | debugfs_create_file_unsafe(name: "product_id" , mode: 0400, |
136 | parent: d, data: adis16460, fops: &adis16460_product_id_fops); |
137 | debugfs_create_file_unsafe(name: "flash_count" , mode: 0400, |
138 | parent: d, data: adis16460, fops: &adis16460_flash_count_fops); |
139 | |
140 | return 0; |
141 | } |
142 | |
143 | #else |
144 | |
145 | static int adis16460_debugfs_init(struct iio_dev *indio_dev) |
146 | { |
147 | return 0; |
148 | } |
149 | |
150 | #endif |
151 | |
152 | static int adis16460_set_freq(struct iio_dev *indio_dev, int val, int val2) |
153 | { |
154 | struct adis16460 *st = iio_priv(indio_dev); |
155 | int t; |
156 | |
157 | t = val * 1000 + val2 / 1000; |
158 | if (t <= 0) |
159 | return -EINVAL; |
160 | |
161 | t = 2048000 / t; |
162 | if (t > 2048) |
163 | t = 2048; |
164 | |
165 | if (t != 0) |
166 | t--; |
167 | |
168 | return adis_write_reg_16(adis: &st->adis, ADIS16460_REG_DEC_RATE, val: t); |
169 | } |
170 | |
171 | static int adis16460_get_freq(struct iio_dev *indio_dev, int *val, int *val2) |
172 | { |
173 | struct adis16460 *st = iio_priv(indio_dev); |
174 | uint16_t t; |
175 | int ret; |
176 | unsigned int freq; |
177 | |
178 | ret = adis_read_reg_16(adis: &st->adis, ADIS16460_REG_DEC_RATE, val: &t); |
179 | if (ret) |
180 | return ret; |
181 | |
182 | freq = 2048000 / (t + 1); |
183 | *val = freq / 1000; |
184 | *val2 = (freq % 1000) * 1000; |
185 | |
186 | return IIO_VAL_INT_PLUS_MICRO; |
187 | } |
188 | |
189 | static int adis16460_read_raw(struct iio_dev *indio_dev, |
190 | const struct iio_chan_spec *chan, int *val, int *val2, long info) |
191 | { |
192 | struct adis16460 *st = iio_priv(indio_dev); |
193 | |
194 | switch (info) { |
195 | case IIO_CHAN_INFO_RAW: |
196 | return adis_single_conversion(indio_dev, chan, error_mask: 0, val); |
197 | case IIO_CHAN_INFO_SCALE: |
198 | switch (chan->type) { |
199 | case IIO_ANGL_VEL: |
200 | *val = st->chip_info->gyro_max_scale; |
201 | *val2 = st->chip_info->gyro_max_val; |
202 | return IIO_VAL_FRACTIONAL; |
203 | case IIO_ACCEL: |
204 | *val = st->chip_info->accel_max_scale; |
205 | *val2 = st->chip_info->accel_max_val; |
206 | return IIO_VAL_FRACTIONAL; |
207 | case IIO_TEMP: |
208 | *val = 50; /* 50 milli degrees Celsius/LSB */ |
209 | return IIO_VAL_INT; |
210 | default: |
211 | return -EINVAL; |
212 | } |
213 | case IIO_CHAN_INFO_OFFSET: |
214 | *val = 500; /* 25 degrees Celsius = 0x0000 */ |
215 | return IIO_VAL_INT; |
216 | case IIO_CHAN_INFO_SAMP_FREQ: |
217 | return adis16460_get_freq(indio_dev, val, val2); |
218 | default: |
219 | return -EINVAL; |
220 | } |
221 | } |
222 | |
223 | static int adis16460_write_raw(struct iio_dev *indio_dev, |
224 | const struct iio_chan_spec *chan, int val, int val2, long info) |
225 | { |
226 | switch (info) { |
227 | case IIO_CHAN_INFO_SAMP_FREQ: |
228 | return adis16460_set_freq(indio_dev, val, val2); |
229 | default: |
230 | return -EINVAL; |
231 | } |
232 | } |
233 | |
234 | enum { |
235 | ADIS16460_SCAN_GYRO_X, |
236 | ADIS16460_SCAN_GYRO_Y, |
237 | ADIS16460_SCAN_GYRO_Z, |
238 | ADIS16460_SCAN_ACCEL_X, |
239 | ADIS16460_SCAN_ACCEL_Y, |
240 | ADIS16460_SCAN_ACCEL_Z, |
241 | ADIS16460_SCAN_TEMP, |
242 | }; |
243 | |
244 | #define ADIS16460_MOD_CHANNEL(_type, _mod, _address, _si, _bits) \ |
245 | { \ |
246 | .type = (_type), \ |
247 | .modified = 1, \ |
248 | .channel2 = (_mod), \ |
249 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ |
250 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ |
251 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ |
252 | .address = (_address), \ |
253 | .scan_index = (_si), \ |
254 | .scan_type = { \ |
255 | .sign = 's', \ |
256 | .realbits = (_bits), \ |
257 | .storagebits = (_bits), \ |
258 | .endianness = IIO_BE, \ |
259 | }, \ |
260 | } |
261 | |
262 | #define ADIS16460_GYRO_CHANNEL(_mod) \ |
263 | ADIS16460_MOD_CHANNEL(IIO_ANGL_VEL, IIO_MOD_ ## _mod, \ |
264 | ADIS16460_REG_ ## _mod ## _GYRO_LOW, ADIS16460_SCAN_GYRO_ ## _mod, \ |
265 | 32) |
266 | |
267 | #define ADIS16460_ACCEL_CHANNEL(_mod) \ |
268 | ADIS16460_MOD_CHANNEL(IIO_ACCEL, IIO_MOD_ ## _mod, \ |
269 | ADIS16460_REG_ ## _mod ## _ACCL_LOW, ADIS16460_SCAN_ACCEL_ ## _mod, \ |
270 | 32) |
271 | |
272 | #define ADIS16460_TEMP_CHANNEL() { \ |
273 | .type = IIO_TEMP, \ |
274 | .indexed = 1, \ |
275 | .channel = 0, \ |
276 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ |
277 | BIT(IIO_CHAN_INFO_SCALE) | \ |
278 | BIT(IIO_CHAN_INFO_OFFSET), \ |
279 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ |
280 | .address = ADIS16460_REG_TEMP_OUT, \ |
281 | .scan_index = ADIS16460_SCAN_TEMP, \ |
282 | .scan_type = { \ |
283 | .sign = 's', \ |
284 | .realbits = 16, \ |
285 | .storagebits = 16, \ |
286 | .endianness = IIO_BE, \ |
287 | }, \ |
288 | } |
289 | |
290 | static const struct iio_chan_spec adis16460_channels[] = { |
291 | ADIS16460_GYRO_CHANNEL(X), |
292 | ADIS16460_GYRO_CHANNEL(Y), |
293 | ADIS16460_GYRO_CHANNEL(Z), |
294 | ADIS16460_ACCEL_CHANNEL(X), |
295 | ADIS16460_ACCEL_CHANNEL(Y), |
296 | ADIS16460_ACCEL_CHANNEL(Z), |
297 | ADIS16460_TEMP_CHANNEL(), |
298 | IIO_CHAN_SOFT_TIMESTAMP(7) |
299 | }; |
300 | |
301 | static const struct adis16460_chip_info adis16460_chip_info = { |
302 | .channels = adis16460_channels, |
303 | .num_channels = ARRAY_SIZE(adis16460_channels), |
304 | /* |
305 | * storing the value in rad/degree and the scale in degree |
306 | * gives us the result in rad and better precession than |
307 | * storing the scale directly in rad. |
308 | */ |
309 | .gyro_max_val = IIO_RAD_TO_DEGREE(200 << 16), |
310 | .gyro_max_scale = 1, |
311 | .accel_max_val = IIO_M_S_2_TO_G(20000 << 16), |
312 | .accel_max_scale = 5, |
313 | }; |
314 | |
315 | static const struct iio_info adis16460_info = { |
316 | .read_raw = &adis16460_read_raw, |
317 | .write_raw = &adis16460_write_raw, |
318 | .update_scan_mode = adis_update_scan_mode, |
319 | .debugfs_reg_access = adis_debugfs_reg_access, |
320 | }; |
321 | |
322 | #define ADIS16460_DIAG_STAT_IN_CLK_OOS 7 |
323 | #define ADIS16460_DIAG_STAT_FLASH_MEM 6 |
324 | #define ADIS16460_DIAG_STAT_SELF_TEST 5 |
325 | #define ADIS16460_DIAG_STAT_OVERRANGE 4 |
326 | #define ADIS16460_DIAG_STAT_SPI_COMM 3 |
327 | #define ADIS16460_DIAG_STAT_FLASH_UPT 2 |
328 | |
329 | static const char * const adis16460_status_error_msgs[] = { |
330 | [ADIS16460_DIAG_STAT_IN_CLK_OOS] = "Input clock out of sync" , |
331 | [ADIS16460_DIAG_STAT_FLASH_MEM] = "Flash memory failure" , |
332 | [ADIS16460_DIAG_STAT_SELF_TEST] = "Self test diagnostic failure" , |
333 | [ADIS16460_DIAG_STAT_OVERRANGE] = "Sensor overrange" , |
334 | [ADIS16460_DIAG_STAT_SPI_COMM] = "SPI communication failure" , |
335 | [ADIS16460_DIAG_STAT_FLASH_UPT] = "Flash update failure" , |
336 | }; |
337 | |
338 | static const struct adis_timeout adis16460_timeouts = { |
339 | .reset_ms = 225, |
340 | .sw_reset_ms = 225, |
341 | .self_test_ms = 10, |
342 | }; |
343 | |
344 | static const struct adis_data adis16460_data = { |
345 | .diag_stat_reg = ADIS16460_REG_DIAG_STAT, |
346 | .glob_cmd_reg = ADIS16460_REG_GLOB_CMD, |
347 | .prod_id_reg = ADIS16460_REG_PROD_ID, |
348 | .prod_id = 16460, |
349 | .self_test_mask = BIT(2), |
350 | .self_test_reg = ADIS16460_REG_GLOB_CMD, |
351 | .has_paging = false, |
352 | .read_delay = 5, |
353 | .write_delay = 5, |
354 | .cs_change_delay = 16, |
355 | .status_error_msgs = adis16460_status_error_msgs, |
356 | .status_error_mask = BIT(ADIS16460_DIAG_STAT_IN_CLK_OOS) | |
357 | BIT(ADIS16460_DIAG_STAT_FLASH_MEM) | |
358 | BIT(ADIS16460_DIAG_STAT_SELF_TEST) | |
359 | BIT(ADIS16460_DIAG_STAT_OVERRANGE) | |
360 | BIT(ADIS16460_DIAG_STAT_SPI_COMM) | |
361 | BIT(ADIS16460_DIAG_STAT_FLASH_UPT), |
362 | .unmasked_drdy = true, |
363 | .timeouts = &adis16460_timeouts, |
364 | }; |
365 | |
366 | static int adis16460_probe(struct spi_device *spi) |
367 | { |
368 | struct iio_dev *indio_dev; |
369 | struct adis16460 *st; |
370 | int ret; |
371 | |
372 | indio_dev = devm_iio_device_alloc(parent: &spi->dev, sizeof_priv: sizeof(*st)); |
373 | if (indio_dev == NULL) |
374 | return -ENOMEM; |
375 | |
376 | st = iio_priv(indio_dev); |
377 | |
378 | st->chip_info = &adis16460_chip_info; |
379 | indio_dev->name = spi_get_device_id(sdev: spi)->name; |
380 | indio_dev->channels = st->chip_info->channels; |
381 | indio_dev->num_channels = st->chip_info->num_channels; |
382 | indio_dev->info = &adis16460_info; |
383 | indio_dev->modes = INDIO_DIRECT_MODE; |
384 | |
385 | ret = adis_init(adis: &st->adis, indio_dev, spi, data: &adis16460_data); |
386 | if (ret) |
387 | return ret; |
388 | |
389 | ret = devm_adis_setup_buffer_and_trigger(adis: &st->adis, indio_dev, NULL); |
390 | if (ret) |
391 | return ret; |
392 | |
393 | ret = __adis_initial_startup(adis: &st->adis); |
394 | if (ret) |
395 | return ret; |
396 | |
397 | ret = devm_iio_device_register(&spi->dev, indio_dev); |
398 | if (ret) |
399 | return ret; |
400 | |
401 | adis16460_debugfs_init(indio_dev); |
402 | |
403 | return 0; |
404 | } |
405 | |
406 | static const struct spi_device_id adis16460_ids[] = { |
407 | { "adis16460" , 0 }, |
408 | {} |
409 | }; |
410 | MODULE_DEVICE_TABLE(spi, adis16460_ids); |
411 | |
412 | static const struct of_device_id adis16460_of_match[] = { |
413 | { .compatible = "adi,adis16460" }, |
414 | {} |
415 | }; |
416 | MODULE_DEVICE_TABLE(of, adis16460_of_match); |
417 | |
418 | static struct spi_driver adis16460_driver = { |
419 | .driver = { |
420 | .name = "adis16460" , |
421 | .of_match_table = adis16460_of_match, |
422 | }, |
423 | .id_table = adis16460_ids, |
424 | .probe = adis16460_probe, |
425 | }; |
426 | module_spi_driver(adis16460_driver); |
427 | |
428 | MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>" ); |
429 | MODULE_DESCRIPTION("Analog Devices ADIS16460 IMU driver" ); |
430 | MODULE_LICENSE("GPL" ); |
431 | MODULE_IMPORT_NS(IIO_ADISLIB); |
432 | |