Commit | Line | Data |
---|---|---|
671ece14 BS |
1 | /* |
2 | * ADIS16209 Programmable Digital Vibration Sensor driver | |
3 | * | |
4 | * Copyright 2010 Analog Devices Inc. | |
5 | * | |
6 | * Licensed under the GPL-2 or later. | |
7 | */ | |
8 | ||
671ece14 BS |
9 | #include <linux/delay.h> |
10 | #include <linux/mutex.h> | |
11 | #include <linux/device.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/spi/spi.h> | |
1cb6c1f5 | 14 | #include <linux/slab.h> |
671ece14 BS |
15 | #include <linux/sysfs.h> |
16 | #include <linux/list.h> | |
99c97852 | 17 | #include <linux/module.h> |
671ece14 | 18 | |
06458e27 JC |
19 | #include <linux/iio/iio.h> |
20 | #include <linux/iio/sysfs.h> | |
21 | #include <linux/iio/buffer.h> | |
ec04cb04 | 22 | #include <linux/iio/imu/adis.h> |
671ece14 BS |
23 | |
24 | #include "adis16209.h" | |
511fb29e LPC |
25 | |
26 | static const u8 adis16209_addresses[8][1] = { | |
27 | [ADIS16209_SCAN_SUPPLY] = { }, | |
28 | [ADIS16209_SCAN_AUX_ADC] = { }, | |
29 | [ADIS16209_SCAN_ACC_X] = { ADIS16209_XACCL_NULL }, | |
30 | [ADIS16209_SCAN_ACC_Y] = { ADIS16209_YACCL_NULL }, | |
31 | [ADIS16209_SCAN_INCLI_X] = { ADIS16209_XINCL_NULL }, | |
32 | [ADIS16209_SCAN_INCLI_Y] = { ADIS16209_YINCL_NULL }, | |
33 | [ADIS16209_SCAN_ROT] = { }, | |
34 | [ADIS16209_SCAN_TEMP] = { }, | |
a34c26dc JC |
35 | }; |
36 | ||
37 | static int adis16209_write_raw(struct iio_dev *indio_dev, | |
38 | struct iio_chan_spec const *chan, | |
39 | int val, | |
40 | int val2, | |
41 | long mask) | |
42 | { | |
511fb29e | 43 | struct adis *st = iio_priv(indio_dev); |
a34c26dc JC |
44 | int bits; |
45 | s16 val16; | |
46 | u8 addr; | |
47 | switch (mask) { | |
c8a9f805 | 48 | case IIO_CHAN_INFO_CALIBBIAS: |
a34c26dc JC |
49 | switch (chan->type) { |
50 | case IIO_ACCEL: | |
51 | case IIO_INCLI: | |
52 | bits = 14; | |
53 | break; | |
54 | default: | |
55 | return -EINVAL; | |
73327b4c | 56 | } |
a34c26dc | 57 | val16 = val & ((1 << bits) - 1); |
511fb29e LPC |
58 | addr = adis16209_addresses[chan->scan_index][0]; |
59 | return adis_write_reg_16(st, addr, val16); | |
a34c26dc JC |
60 | } |
61 | return -EINVAL; | |
62 | } | |
63 | ||
64 | static int adis16209_read_raw(struct iio_dev *indio_dev, | |
65 | struct iio_chan_spec const *chan, | |
66 | int *val, int *val2, | |
67 | long mask) | |
68 | { | |
511fb29e | 69 | struct adis *st = iio_priv(indio_dev); |
a34c26dc JC |
70 | int ret; |
71 | int bits; | |
72 | u8 addr; | |
73 | s16 val16; | |
74 | ||
75 | switch (mask) { | |
31313fc6 | 76 | case IIO_CHAN_INFO_RAW: |
511fb29e LPC |
77 | return adis_single_conversion(indio_dev, chan, |
78 | ADIS16209_ERROR_ACTIVE, val); | |
c8a9f805 | 79 | case IIO_CHAN_INFO_SCALE: |
a34c26dc | 80 | switch (chan->type) { |
6835cb6b | 81 | case IIO_VOLTAGE: |
a34c26dc JC |
82 | *val = 0; |
83 | if (chan->channel == 0) | |
d5304b77 | 84 | *val2 = 305180; /* 0.30518 mV */ |
a34c26dc | 85 | else |
d5304b77 | 86 | *val2 = 610500; /* 0.6105 mV */ |
a34c26dc JC |
87 | return IIO_VAL_INT_PLUS_MICRO; |
88 | case IIO_TEMP: | |
d5304b77 LPC |
89 | *val = -470; /* -0.47 C */ |
90 | *val2 = 0; | |
a34c26dc JC |
91 | return IIO_VAL_INT_PLUS_MICRO; |
92 | case IIO_ACCEL: | |
93 | *val = 0; | |
d5304b77 LPC |
94 | *val2 = IIO_G_TO_M_S_2(244140); /* 0.244140 mg */ |
95 | return IIO_VAL_INT_PLUS_NANO; | |
a34c26dc | 96 | case IIO_INCLI: |
d5304b77 | 97 | case IIO_ROT: |
a34c26dc | 98 | *val = 0; |
d5304b77 | 99 | *val2 = 25000; /* 0.025 degree */ |
a34c26dc JC |
100 | return IIO_VAL_INT_PLUS_MICRO; |
101 | default: | |
102 | return -EINVAL; | |
103 | } | |
104 | break; | |
c8a9f805 | 105 | case IIO_CHAN_INFO_OFFSET: |
d5304b77 | 106 | *val = 25000 / -470 - 0x4FE; /* 25 C = 0x4FE */ |
a34c26dc | 107 | return IIO_VAL_INT; |
c8a9f805 | 108 | case IIO_CHAN_INFO_CALIBBIAS: |
a34c26dc JC |
109 | switch (chan->type) { |
110 | case IIO_ACCEL: | |
111 | bits = 14; | |
112 | break; | |
113 | default: | |
114 | return -EINVAL; | |
73327b4c | 115 | } |
a34c26dc | 116 | mutex_lock(&indio_dev->mlock); |
511fb29e LPC |
117 | addr = adis16209_addresses[chan->scan_index][0]; |
118 | ret = adis_read_reg_16(st, addr, &val16); | |
a34c26dc JC |
119 | if (ret) { |
120 | mutex_unlock(&indio_dev->mlock); | |
121 | return ret; | |
122 | } | |
123 | val16 &= (1 << bits) - 1; | |
124 | val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); | |
125 | *val = val16; | |
126 | mutex_unlock(&indio_dev->mlock); | |
127 | return IIO_VAL_INT; | |
128 | } | |
129 | return -EINVAL; | |
130 | } | |
131 | ||
f4e4b955 | 132 | static const struct iio_chan_spec adis16209_channels[] = { |
511fb29e LPC |
133 | ADIS_SUPPLY_CHAN(ADIS16209_SUPPLY_OUT, ADIS16209_SCAN_SUPPLY, 14), |
134 | ADIS_TEMP_CHAN(ADIS16209_TEMP_OUT, ADIS16209_SCAN_TEMP, 12), | |
135 | ADIS_ACCEL_CHAN(X, ADIS16209_XACCL_OUT, ADIS16209_SCAN_ACC_X, | |
136 | IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, 14), | |
137 | ADIS_ACCEL_CHAN(Y, ADIS16209_YACCL_OUT, ADIS16209_SCAN_ACC_Y, | |
138 | IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, 14), | |
139 | ADIS_AUX_ADC_CHAN(ADIS16209_AUX_ADC, ADIS16209_SCAN_AUX_ADC, 12), | |
140 | ADIS_INCLI_CHAN(X, ADIS16209_XINCL_OUT, ADIS16209_SCAN_INCLI_X, 0, 14), | |
141 | ADIS_INCLI_CHAN(Y, ADIS16209_YINCL_OUT, ADIS16209_SCAN_INCLI_Y, 0, 14), | |
142 | ADIS_ROT_CHAN(X, ADIS16209_ROT_OUT, ADIS16209_SCAN_ROT, 0, 14), | |
a34c26dc JC |
143 | IIO_CHAN_SOFT_TIMESTAMP(8) |
144 | }; | |
671ece14 | 145 | |
6fe8135f | 146 | static const struct iio_info adis16209_info = { |
6fe8135f JC |
147 | .read_raw = &adis16209_read_raw, |
148 | .write_raw = &adis16209_write_raw, | |
aacff892 | 149 | .update_scan_mode = adis_update_scan_mode, |
6fe8135f JC |
150 | .driver_module = THIS_MODULE, |
151 | }; | |
152 | ||
511fb29e LPC |
153 | static const char * const adis16209_status_error_msgs[] = { |
154 | [ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure", | |
155 | [ADIS16209_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", | |
156 | [ADIS16209_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", | |
157 | [ADIS16209_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", | |
158 | [ADIS16209_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V", | |
159 | }; | |
160 | ||
161 | static const struct adis_data adis16209_data = { | |
162 | .read_delay = 30, | |
163 | .msc_ctrl_reg = ADIS16209_MSC_CTRL, | |
164 | .glob_cmd_reg = ADIS16209_GLOB_CMD, | |
165 | .diag_stat_reg = ADIS16209_DIAG_STAT, | |
166 | ||
167 | .self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN, | |
168 | .startup_delay = ADIS16209_STARTUP_DELAY, | |
169 | ||
170 | .status_error_msgs = adis16209_status_error_msgs, | |
171 | .status_error_mask = BIT(ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT) | | |
172 | BIT(ADIS16209_DIAG_STAT_SPI_FAIL_BIT) | | |
173 | BIT(ADIS16209_DIAG_STAT_FLASH_UPT_BIT) | | |
174 | BIT(ADIS16209_DIAG_STAT_POWER_HIGH_BIT) | | |
175 | BIT(ADIS16209_DIAG_STAT_POWER_LOW_BIT), | |
176 | }; | |
177 | ||
178 | ||
4ae1c61f | 179 | static int adis16209_probe(struct spi_device *spi) |
671ece14 | 180 | { |
26d25ae3 | 181 | int ret; |
511fb29e | 182 | struct adis *st; |
35212dfa | 183 | struct iio_dev *indio_dev; |
671ece14 | 184 | |
35212dfa | 185 | /* setup the industrialio driver allocated elements */ |
7cbb7537 | 186 | indio_dev = iio_device_alloc(sizeof(*st)); |
35212dfa | 187 | if (indio_dev == NULL) { |
671ece14 | 188 | ret = -ENOMEM; |
35212dfa | 189 | goto error_ret; |
671ece14 | 190 | } |
35212dfa JC |
191 | st = iio_priv(indio_dev); |
192 | /* this is only used for removal purposes */ | |
193 | spi_set_drvdata(spi, indio_dev); | |
671ece14 | 194 | |
35212dfa JC |
195 | indio_dev->name = spi->dev.driver->name; |
196 | indio_dev->dev.parent = &spi->dev; | |
197 | indio_dev->info = &adis16209_info; | |
198 | indio_dev->channels = adis16209_channels; | |
199 | indio_dev->num_channels = ARRAY_SIZE(adis16209_channels); | |
200 | indio_dev->modes = INDIO_DIRECT_MODE; | |
671ece14 | 201 | |
511fb29e LPC |
202 | ret = adis_init(st, indio_dev, spi, &adis16209_data); |
203 | if (ret) | |
204 | goto error_free_dev; | |
205 | ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); | |
671ece14 BS |
206 | if (ret) |
207 | goto error_free_dev; | |
671ece14 BS |
208 | |
209 | /* Get the device into a sane initial state */ | |
511fb29e | 210 | ret = adis_initial_startup(st); |
671ece14 | 211 | if (ret) |
511fb29e | 212 | goto error_cleanup_buffer_trigger; |
26d25ae3 JC |
213 | ret = iio_device_register(indio_dev); |
214 | if (ret) | |
511fb29e | 215 | goto error_cleanup_buffer_trigger; |
26d25ae3 | 216 | |
671ece14 BS |
217 | return 0; |
218 | ||
511fb29e LPC |
219 | error_cleanup_buffer_trigger: |
220 | adis_cleanup_buffer_and_trigger(st, indio_dev); | |
671ece14 | 221 | error_free_dev: |
7cbb7537 | 222 | iio_device_free(indio_dev); |
671ece14 BS |
223 | error_ret: |
224 | return ret; | |
225 | } | |
226 | ||
8e828752 | 227 | static int __devexit adis16209_remove(struct spi_device *spi) |
671ece14 | 228 | { |
35212dfa | 229 | struct iio_dev *indio_dev = spi_get_drvdata(spi); |
511fb29e | 230 | struct adis *st = iio_priv(indio_dev); |
671ece14 | 231 | |
d2fffd6c | 232 | iio_device_unregister(indio_dev); |
511fb29e | 233 | adis_cleanup_buffer_and_trigger(st, indio_dev); |
7cbb7537 | 234 | iio_device_free(indio_dev); |
671ece14 BS |
235 | |
236 | return 0; | |
237 | } | |
238 | ||
239 | static struct spi_driver adis16209_driver = { | |
240 | .driver = { | |
241 | .name = "adis16209", | |
242 | .owner = THIS_MODULE, | |
243 | }, | |
244 | .probe = adis16209_probe, | |
245 | .remove = __devexit_p(adis16209_remove), | |
246 | }; | |
ae6ae6fe | 247 | module_spi_driver(adis16209_driver); |
671ece14 BS |
248 | |
249 | MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); | |
250 | MODULE_DESCRIPTION("Analog Devices ADIS16209 Digital Vibration Sensor driver"); | |
251 | MODULE_LICENSE("GPL v2"); | |
55e4390c | 252 | MODULE_ALIAS("spi:adis16209"); |